ZXing with Android quick start.

ZXing(https://code.google.com/p/zxing/) is a great library for barcode image processing, which can be used for Android apps development. There are many tutorials for integrating it, but i have not found proper one so far(don’t think that copying the whole source code of example app to your project is such a great idea to do http://damianflannery.wordpress.com/2011/06/13/integrate-zxing-barcode-scanner-into-your-android-app-natively-using-eclipse/). You’ll need ZXing core lib in project for it to work.

The simple usage of this lib can be described in this steps:

  1. Get data from preview callback
  2. Convert it to PlanarYUVLuminanceSource
  3. Binarize PlanarYUVLuminanceSource and use MultiFormatReader to get results.

That’s it! Sound simple, isn’t it? Of course there is some more work to be done (there is no need to use full sized image, small rectangle area will do the trick), but those are main steps.

Capture activity is as simple as it can be and is used only to create camera instance and start previewing camera data. It also request frame for the first time for ZXing to try find barcode on it.

public class CaptureActivity extends Activity {
    private CameraPreviewView cameraPreview;
    private CameraManager cameraManager;
    private Handler captureHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_capture);
        // Create an instance of Camera
        cameraManager = new CameraManager();
        captureHandler = new CaptureHandler(cameraManager, this, new OnDecoded());
        //requesting next frame for decoding
        cameraManager.requestNextFrame(new PreviewCallback(captureHandler, cameraManager));
        cameraPreview = (CameraPreviewView) findViewById(R.id.camera_preview);
        cameraPreview.setCameraManager(cameraManager);
        ((BoundingView) findViewById(R.id.bounding_view)).setCameraManager(cameraManager);
    }
    ...
}

Camera instance focuses with Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, so there is no need to implement focusing by hands. But there is no need to try to find barcode on preview every time app retrieves camera preview(this is too bad for performance), so it’s a good idea to run one decoding at a time. It can be done with Camera.setOneShotPreviewCallback(Camera.PreviewCallback). If decoding is successful, than there is no need to setOneShotPreviewCallback again, otherwise setOneShotPreviewCallback must be called again.

Here is example of Camera.PreviewCallback implementation:

public class PreviewCallback implements Camera.PreviewCallback {
    ...
    private final MultiFormatReader multiFormatReader = new MultiFormatReader();
    ...
    private CameraManager cameraManager;
    ...
    @Override
    public void onPreviewFrame(byte[] bytes, Camera camera) {
        Camera.Size previewSize = camera.getParameters().getPreviewSize();
        new DecodeAsyncTask(previewSize.width, previewSize.height).execute(bytes);
    }
    /**
     * Asynchronous task for decoding and finding barcode
     */
    private class DecodeAsyncTask extends AsyncTask<byte[], Void, Result> {
        private int width;
        private int height;
        /**
         * @param width  Width of image
         * @param height Height of image
         */
        private DecodeAsyncTask(int width, int height) {
            this.width = width;
            this.height = height;
        }
        ...
        @Override
        protected Result doInBackground(byte[]... datas) {
            if (!cameraManager.hasCamera()) {
                return null;
            }
            Result rawResult = null;
            final PlanarYUVLuminanceSource source =
                    cameraManager.buildLuminanceSource(datas[0], width,
                            height, cameraManager.getBoundingRect());
            if (source != null) {
                BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
                try {
                    rawResult = multiFormatReader.decodeWithState(bitmap);
                } catch (ReaderException re) {
                    // nothing to do here
                } finally {
                    multiFormatReader.reset();
                }
            }
            return rawResult;
        }
    }
}

MultiFormatReader is in ZXing lib and is used to decoding different barcode formats at the same time.

And building PlanarYUVLuminanceSource is pretty simple too:

return new PlanarYUVLuminanceSource(data, height, width, boundingRect.top, boundingRect.left, boundingRect.height(), boundingRect.width(), false);

where boundingRect is rectangle area of image to search barcode on.

Source of this example simple app:
https://github.com/ShyykoSerhiy/ZXingQuickStart

Advertisements