4

I have an application that has zxing integrated. I've been looking at trying to store a photo when a QR code is scanned. Sean Owen recommended the following:

"The app is getting a continuous stream of frames from the camera to analyze. You can store off any of them by intercepting them in the preview callback."

As far as I am aware the only instances of preview callback is within the CameraManager.java activity (https://code.google.com/p/zxing/source/browse/trunk/android/src/com/google/zxing/client/android/camera/CameraManager.java).

In particular:

public synchronized void requestPreviewFrame(Handler handler, int message) {
Camera theCamera = camera;
if (theCamera != null && previewing) {
  previewCallback.setHandler(handler, message);
  theCamera.setOneShotPreviewCallback(previewCallback);
}}

Since this runs every frame I don't have a method of saving (preferably as byte date) any particular frame. I would have assumed there to be a point where something is passed back to the CaptureActivity.java class (Link given at bottom) however I haven't found anything myself.

Anyone who has used Zxing will know that after a scan a ghostly image is shown on screen of the scan data, if it is possible to hijack this part of the code and convert and/or save that data as byte code that may also be useful.

Any help, or other ideas would be very appreciated. Requests for any further information will be responded to quickly. Thank you.

Full code available within this folder: https://code.google.com/p/zxing/source/browse/trunk#trunk%2Fandroid%2Fsrc%2Fcom%2Fgoogle%2Fzxing%2Fclient%2Fandroid

Update:

So far the following sections of code appear to be possible places to save byte data, both are within the DecodeHandler.java class.

private void decode(byte[] data, int width, int height) {
long start = System.currentTimeMillis();
Result rawResult = null;
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
if (source != null) {
  BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

  //here?

  try {
    rawResult = multiFormatReader.decodeWithState(bitmap);
  } catch (ReaderException re) {
    // continue
  } finally {
    multiFormatReader.reset();
  }
}

Handler handler = activity.getHandler();
if (rawResult != null) {
  // Don't log the barcode contents for security.
  long end = System.currentTimeMillis();
  Log.d(TAG, "Found barcode in " + (end - start) + " ms");
  if (handler != null) {
    Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
    Bundle bundle = new Bundle();
    Bitmap grayscaleBitmap = toBitmap(source, source.renderCroppedGreyscaleBitmap());

    //I believe this bitmap is the one shown on screen after a scan has been performed

    bundle.putParcelable(DecodeThread.BARCODE_BITMAP, grayscaleBitmap);
    message.setData(bundle);
    message.sendToTarget();
  }
} else {
  if (handler != null) {
    Message message = Message.obtain(handler, R.id.decode_failed);
    message.sendToTarget();
  }
}}


  private static Bitmap toBitmap(LuminanceSource source, int[] pixels) {
int width = source.getWidth();
int height = source.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, width, 0, 0, width, height);

//saving the bitmnap at this point or slightly sooner, before grey scaling could work.

return bitmap;}

Update: Requested code found within PreviewCallback.java

public void onPreviewFrame(byte[] data, Camera camera) {
Point cameraResolution = configManager.getCameraResolution();
Handler thePreviewHandler = previewHandler;
if (cameraResolution != null && thePreviewHandler != null) {
  Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
      cameraResolution.y, data);
  message.sendToTarget();
  previewHandler = null;
} else {
  Log.d(TAG, "Got preview callback, but no handler or resolution available");
}
Jørgen R
  • 10,568
  • 7
  • 42
  • 59

1 Answers1

3

The data from preview callback is NV21 format. So if you wanna save it, you could use such code:

YuvImage im = new YuvImage(byteArray, ImageFormat.NV21, width,
                        height, null);
            Rect r = new Rect(0, 0, width, height);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            im.compressToJpeg(r, 50, baos);

            try {
                  FileOutputStream output = new FileOutputStream("/sdcard/test_jpg.jpg");
                  output.write(baos.toByteArray());
                  output.flush();
                  output.close();
            } catch (FileNotFoundException e) {
            } catch (IOException e) {
            }

The save point is when the ZXing could decode the byte[] and return content String successfully.

yushulx
  • 11,695
  • 8
  • 37
  • 64
  • Am I correct in thinking I should be saving the data within this method: `public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) { Rect rect = getFramingRectInPreview(); if (rect == null) { return null; } // Go ahead and assume it's YUV rather than die. return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top, rect.width(), rect.height(), false); }` –  Aug 03 '13 at 13:55
  • 1
    The byte[] data is from preview callback, follow it and find a suitable point to save it. you can also search the keyword "decode" – yushulx Aug 03 '13 at 14:05
  • I've updated the original post with some possible save points, however they don't seem to fall under the necessary criterion to save through your method. Thoughts and feedback appreciated. –  Aug 03 '13 at 16:02
  • can you find onPreviewFrame (byte[] data, Camera camera), in which you get data from camera preview? – yushulx Aug 04 '13 at 08:07
  • The code 'works', however the image is corrupted, I believe this is because it's trying to save an image every frame, instead of on just one frame. –  Aug 09 '13 at 14:28
  • 1
    I have asked a new question to further elaborate upon this one, any help would be greatly appreciated. Thankyou. http://stackoverflow.com/questions/18149620/android-saving-previewcallback-image-as-a-jpg-from-a-byte-array-results-in-a-c –  Aug 09 '13 at 15:03