From be20370a3d8500d0f95e022ce8ab675cff7fc21e Mon Sep 17 00:00:00 2001 From: Alexander Smorkalov Date: Mon, 9 Apr 2012 07:36:54 +0000 Subject: [PATCH] Autofocus issues on Android ICS fixed. Continuous-video focus mode is set. Performance problems fixed. Shared camera buffer is used. --- .../opencv/samples/tutorial0/Sample0Base.java | 12 +- .../opencv/samples/tutorial0/Sample0View.java | 65 ++++++++--- .../samples/tutorial0/SampleViewBase.java | 59 ++++++++-- .../opencv/samples/tutorial1/Sample1Java.java | 25 ++-- .../opencv/samples/tutorial1/Sample1View.java | 93 ++++++++------- .../samples/tutorial1/SampleViewBase.java | 53 +++++++-- .../opencv/samples/tutorial3/Sample3View.java | 31 +++-- .../samples/tutorial3/SampleViewBase.java | 54 +++++++-- .../samples/tutorial4/Sample4Mixed.java | 29 +++-- .../opencv/samples/tutorial4/Sample4View.java | 107 +++++++++++------- .../samples/tutorial4/SampleViewBase.java | 60 +++++++--- 11 files changed, 409 insertions(+), 179 deletions(-) diff --git a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java index f49b3587d5..afeaa2fc73 100644 --- a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java +++ b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0Base.java @@ -10,13 +10,10 @@ import android.view.Window; public class Sample0Base extends Activity { private static final String TAG = "Sample::Activity"; - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_GRAY = 1; - private MenuItem mItemPreviewRGBA; private MenuItem mItemPreviewGray; + private Sample0View mView; - public static int viewMode = VIEW_MODE_RGBA; public Sample0Base() { Log.i(TAG, "Instantiated new " + this.getClass()); @@ -28,7 +25,8 @@ public class Sample0Base extends Activity { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(new Sample0View(this)); + mView = new Sample0View(this); + setContentView(mView); } @Override @@ -43,9 +41,9 @@ public class Sample0Base extends Activity { public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "Menu Item selected " + item); if (item == mItemPreviewRGBA) - viewMode = VIEW_MODE_RGBA; + mView.setViewMode(Sample0View.VIEW_MODE_RGBA); else if (item == mItemPreviewGray) - viewMode = VIEW_MODE_GRAY; + mView.setViewMode(Sample0View.VIEW_MODE_GRAY); return true; } } diff --git a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0View.java b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0View.java index b167230160..f38c1f95e4 100644 --- a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0View.java +++ b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/Sample0View.java @@ -2,34 +2,52 @@ package org.opencv.samples.tutorial0; import android.content.Context; import android.graphics.Bitmap; +import android.util.Log; class Sample0View extends SampleViewBase { + + private static final String TAG = "Sample0View"; + int mSize; + int[] mRGBA; + private Bitmap mBitmap; + private int mViewMode; + + public static final int VIEW_MODE_RGBA = 0; + public static final int VIEW_MODE_GRAY = 1; + + public Sample0View(Context context) { super(context); + mSize = 0; + mViewMode = VIEW_MODE_RGBA; } @Override protected Bitmap processFrame(byte[] data) { int frameSize = getFrameWidth() * getFrameHeight(); - int[] rgba = new int[frameSize]; + + int[] rgba = mRGBA; - int view_mode = Sample0Base.viewMode; - if (view_mode == Sample0Base.VIEW_MODE_GRAY) { + final int view_mode = mViewMode; + if (view_mode == VIEW_MODE_GRAY) { for (int i = 0; i < frameSize; i++) { int y = (0xff & ((int) data[i])); rgba[i] = 0xff000000 + (y << 16) + (y << 8) + y; } - } else if (view_mode == Sample0Base.VIEW_MODE_RGBA) { + } else if (view_mode == VIEW_MODE_RGBA) { for (int i = 0; i < getFrameHeight(); i++) for (int j = 0; j < getFrameWidth(); j++) { - int y = (0xff & ((int) data[i * getFrameWidth() + j])); - int u = (0xff & ((int) data[frameSize + (i >> 1) * getFrameWidth() + (j & ~1) + 0])); - int v = (0xff & ((int) data[frameSize + (i >> 1) * getFrameWidth() + (j & ~1) + 1])); + int index = i * getFrameWidth() + j; + int supply_index = frameSize + (i >> 1) * getFrameWidth() + (j & ~1); + int y = (0xff & ((int) data[index])); + int u = (0xff & ((int) data[supply_index + 0])); + int v = (0xff & ((int) data[supply_index + 1])); y = y < 16 ? 16 : y; - - int r = Math.round(1.164f * (y - 16) + 1.596f * (v - 128)); - int g = Math.round(1.164f * (y - 16) - 0.813f * (v - 128) - 0.391f * (u - 128)); - int b = Math.round(1.164f * (y - 16) + 2.018f * (u - 128)); + + float y_conv = 1.164f * (y - 16); + int r = Math.round(y_conv + 1.596f * (v - 128)); + int g = Math.round(y_conv - 0.813f * (v - 128) - 0.391f * (u - 128)); + int b = Math.round(y_conv + 2.018f * (u - 128)); r = r < 0 ? 0 : (r > 255 ? 255 : r); g = g < 0 ? 0 : (g > 255 ? 255 : g); @@ -38,9 +56,26 @@ class Sample0View extends SampleViewBase { rgba[i * getFrameWidth() + j] = 0xff000000 + (b << 16) + (g << 8) + r; } } - - Bitmap bmp = Bitmap.createBitmap(getFrameWidth(), getFrameHeight(), Bitmap.Config.ARGB_8888); - bmp.setPixels(rgba, 0/* offset */, getFrameWidth() /* stride */, 0, 0, getFrameWidth(), getFrameHeight()); - return bmp; + + mBitmap.setPixels(rgba, 0/* offset */, getFrameWidth() /* stride */, 0, 0, getFrameWidth(), getFrameHeight()); + return mBitmap; } + + @Override + protected void onPreviewStared(int previewWidth, int previewHeight) { + /* Create a bitmap that will be used through to calculate the image to */ + mBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); + mRGBA = new int[previewWidth * previewHeight]; + } + + @Override + protected void onPreviewStopped() { + mBitmap.recycle(); + mBitmap = null; + mRGBA = null; + } + + public void setViewMode(int viewMode) { + mViewMode = viewMode; + } } \ No newline at end of file diff --git a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java index ecf30c64bd..cf31d5fe86 100644 --- a/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java +++ b/samples/android/tutorial-0-androidcamera/src/org/opencv/samples/tutorial0/SampleViewBase.java @@ -6,6 +6,7 @@ import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; @@ -23,6 +24,8 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde private int mFrameHeight; private byte[] mFrame; private boolean mThreadRun; + private byte[] mBuffer; + public SampleViewBase(Context context) { super(context); @@ -43,9 +46,10 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) mCamera.setPreviewTexture( new SurfaceTexture(10) ); else - mCamera.setPreviewDisplay(null); - } - + mCamera.setPreviewDisplay(null); + } + + public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { Log.i(TAG, "surfaceCreated"); if (mCamera != null) { @@ -56,7 +60,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde // selecting optimal camera preview size { - double minDiff = Double.MAX_VALUE; + int minDiff = Integer.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - height) < minDiff) { mFrameWidth = size.width; @@ -67,12 +71,28 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde } params.setPreviewSize(getFrameWidth(), getFrameHeight()); + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); mCamera.setParameters(params); - try { - setPreview(); + + /* Now allocate the buffer */ + params = mCamera.getParameters(); + int size = params.getPreviewSize().width * params.getPreviewSize().height; + size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; + mBuffer = new byte[size]; + /* The buffer where the current frame will be coppied */ + mFrame = new byte [size]; + mCamera.addCallbackBuffer(mBuffer); + + try { + setPreview(); } catch (IOException e) { Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); } + + /* Notify that the preview is about to be started and deliver preview size */ + onPreviewStared(params.getPreviewSize().width, params.getPreviewSize().height); + + /* Now we can start a preview */ mCamera.startPreview(); } } @@ -80,14 +100,17 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); mCamera = Camera.open(); - mCamera.setPreviewCallback(new PreviewCallback() { + + mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { synchronized (SampleViewBase.this) { - mFrame = data; - SampleViewBase.this.notify(); + System.arraycopy(data, 0, mFrame, 0, data.length); + SampleViewBase.this.notify(); } + camera.addCallbackBuffer(mBuffer); } }); + (new Thread(this)).start(); } @@ -102,10 +125,27 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde mCamera = null; } } + onPreviewStopped(); } + /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ protected abstract Bitmap processFrame(byte[] data); + /** + * This method is called when the preview process is beeing started. It is called before the first frame delivered and processFrame is called + * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. + * @param previewWidth - the width of the preview frames that will be delivered via processFrame + * @param previewHeight - the height of the preview frames that will be delivered via processFrame + */ + protected abstract void onPreviewStared(int previewWidtd, int previewHeight); + + /** + * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. + * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. + * Any other resourcses used during the preview can be released. + */ + protected abstract void onPreviewStopped(); + public void run() { mThreadRun = true; Log.i(TAG, "Starting processing thread"); @@ -127,7 +167,6 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); mHolder.unlockCanvasAndPost(canvas); } - bmp.recycle(); } } } diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java index f05ee333d9..de463f7984 100644 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java +++ b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1Java.java @@ -8,17 +8,12 @@ import android.view.MenuItem; import android.view.Window; public class Sample1Java extends Activity { - private static final String TAG = "Sample::Activity"; - - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_GRAY = 1; - public static final int VIEW_MODE_CANNY = 2; + private static final String TAG = "Sample::Activity"; private MenuItem mItemPreviewRGBA; private MenuItem mItemPreviewGray; private MenuItem mItemPreviewCanny; - - public static int viewMode = VIEW_MODE_RGBA; + private Sample1View mView; public Sample1Java() { Log.i(TAG, "Instantiated new " + this.getClass()); @@ -30,7 +25,8 @@ public class Sample1Java extends Activity { Log.i(TAG, "onCreate"); super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(new Sample1View(this)); + mView = new Sample1View(this); + setContentView(mView); } @Override @@ -45,12 +41,13 @@ public class Sample1Java extends Activity { @Override public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "Menu Item selected " + item); - if (item == mItemPreviewRGBA) - viewMode = VIEW_MODE_RGBA; - else if (item == mItemPreviewGray) - viewMode = VIEW_MODE_GRAY; - else if (item == mItemPreviewCanny) - viewMode = VIEW_MODE_CANNY; + if (item == mItemPreviewRGBA) { + mView.setViewMode(Sample1View.VIEW_MODE_RGBA); + } else if (item == mItemPreviewGray) { + mView.setViewMode(Sample1View.VIEW_MODE_GRAY); + } else if (item == mItemPreviewCanny) { + mView.setViewMode(Sample1View.VIEW_MODE_CANNY); + } return true; } } diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java index f7da21168d..987fca8754 100644 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java +++ b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/Sample1View.java @@ -14,26 +14,58 @@ import android.util.Log; import android.view.SurfaceHolder; class Sample1View extends SampleViewBase { + + public static final int VIEW_MODE_RGBA = 0; + public static final int VIEW_MODE_GRAY = 1; + public static final int VIEW_MODE_CANNY = 2; + private Mat mYuv; private Mat mRgba; private Mat mGraySubmat; private Mat mIntermediateMat; + private Bitmap mBitmap; + private int mViewMode; public Sample1View(Context context) { super(context); + mViewMode = VIEW_MODE_RGBA; } - @Override - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - super.surfaceChanged(_holder, format, width, height); + @Override + protected void onPreviewStared(int previewWidth, int previewHeight) { + synchronized (this) { + // initialize Mats before usage + mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); + mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); + + mRgba = new Mat(); + mIntermediateMat = new Mat(); + + mBitmap = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888); + } + } - synchronized (this) { - // initialize Mats before usage - mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); - mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); + @Override + protected void onPreviewStopped() { + if(mBitmap != null) { + mBitmap.recycle(); + } - mRgba = new Mat(); - mIntermediateMat = new Mat(); + synchronized (this) { + // Explicitly deallocate Mats + if (mYuv != null) + mYuv.release(); + if (mRgba != null) + mRgba.release(); + if (mGraySubmat != null) + mGraySubmat.release(); + if (mIntermediateMat != null) + mIntermediateMat.release(); + + mYuv = null; + mRgba = null; + mGraySubmat = null; + mIntermediateMat = null; } } @@ -41,51 +73,36 @@ class Sample1View extends SampleViewBase { protected Bitmap processFrame(byte[] data) { mYuv.put(0, 0, data); - switch (Sample1Java.viewMode) { - case Sample1Java.VIEW_MODE_GRAY: + final int viewMode = mViewMode; + + switch (viewMode) { + case VIEW_MODE_GRAY: Imgproc.cvtColor(mGraySubmat, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); break; - case Sample1Java.VIEW_MODE_RGBA: + case VIEW_MODE_RGBA: Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); Core.putText(mRgba, "OpenCV + Android", new Point(10, 100), 3/* CV_FONT_HERSHEY_COMPLEX */, 2, new Scalar(255, 0, 0, 255), 3); break; - case Sample1Java.VIEW_MODE_CANNY: + case VIEW_MODE_CANNY: Imgproc.Canny(mGraySubmat, mIntermediateMat, 80, 100); Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); break; } - Bitmap bmp = Bitmap.createBitmap(getFrameWidth(), getFrameHeight(), Bitmap.Config.ARGB_8888); + Bitmap bmp = mBitmap; try { - Utils.matToBitmap(mRgba, bmp); - return bmp; + Utils.matToBitmap(mRgba, bmp); } catch(Exception e) { - Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); + Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); bmp.recycle(); - return null; + bmp = null; } + return bmp; } - @Override - public void run() { - super.run(); - - synchronized (this) { - // Explicitly deallocate Mats - if (mYuv != null) - mYuv.release(); - if (mRgba != null) - mRgba.release(); - if (mGraySubmat != null) - mGraySubmat.release(); - if (mIntermediateMat != null) - mIntermediateMat.release(); - - mYuv = null; - mRgba = null; - mGraySubmat = null; - mIntermediateMat = null; - } + public void setViewMode(int viewMode) { + mViewMode = viewMode; } + } diff --git a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java index f9ebdfc8fa..9c734e656b 100644 --- a/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java +++ b/samples/android/tutorial-1-addopencv/src/org/opencv/samples/tutorial1/SampleViewBase.java @@ -6,6 +6,7 @@ import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; @@ -23,6 +24,8 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde private int mFrameHeight; private byte[] mFrame; private boolean mThreadRun; + private byte[] mBuffer; + public SampleViewBase(Context context) { super(context); @@ -49,6 +52,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { Log.i(TAG, "surfaceCreated"); if (mCamera != null) { + Camera.Parameters params = mCamera.getParameters(); List sizes = params.getSupportedPreviewSizes(); mFrameWidth = width; @@ -56,7 +60,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde // selecting optimal camera preview size { - double minDiff = Double.MAX_VALUE; + int minDiff = Integer.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - height) < minDiff) { mFrameWidth = size.width; @@ -67,12 +71,28 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde } params.setPreviewSize(getFrameWidth(), getFrameHeight()); + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); mCamera.setParameters(params); - try { - setPreview(); + + /* Now allocate the buffer */ + params = mCamera.getParameters(); + int size = params.getPreviewSize().width * params.getPreviewSize().height; + size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; + mBuffer = new byte[size]; + /* The buffer where the current frame will be coppied */ + mFrame = new byte [size]; + mCamera.addCallbackBuffer(mBuffer); + + try { + setPreview(); } catch (IOException e) { Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); } + + /* Notify that the preview is about to be started and deliver preview size */ + onPreviewStared(params.getPreviewSize().width, params.getPreviewSize().height); + + /* Now we can start a preview */ mCamera.startPreview(); } } @@ -80,14 +100,17 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); mCamera = Camera.open(); - mCamera.setPreviewCallback(new PreviewCallback() { + + mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { synchronized (SampleViewBase.this) { - mFrame = data; - SampleViewBase.this.notify(); + System.arraycopy(data, 0, mFrame, 0, data.length); + SampleViewBase.this.notify(); } + camera.addCallbackBuffer(mBuffer); } }); + (new Thread(this)).start(); } @@ -102,10 +125,27 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde mCamera = null; } } + onPreviewStopped(); } + /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ protected abstract Bitmap processFrame(byte[] data); + /** + * This method is called when the preview process is beeing started. It is called before the first frame delivered and processFrame is called + * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. + * @param previewWidth - the width of the preview frames that will be delivered via processFrame + * @param previewHeight - the height of the preview frames that will be delivered via processFrame + */ + protected abstract void onPreviewStared(int previewWidtd, int previewHeight); + + /** + * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. + * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. + * Any other resourcses used during the preview can be released. + */ + protected abstract void onPreviewStopped(); + public void run() { mThreadRun = true; Log.i(TAG, "Starting processing thread"); @@ -127,7 +167,6 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); mHolder.unlockCanvasAndPost(canvas); } - bmp.recycle(); } } } diff --git a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java index f940f98514..43450d5c4b 100644 --- a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java +++ b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/Sample3View.java @@ -4,19 +4,40 @@ import android.content.Context; import android.graphics.Bitmap; class Sample3View extends SampleViewBase { + + private int mFrameSize; + private Bitmap mBitmap; + private int[] mRGBA; public Sample3View(Context context) { super(context); } + @Override + protected void onPreviewStared(int previewWidtd, int previewHeight) { + mFrameSize = previewWidtd * previewHeight; + mRGBA = new int[mFrameSize]; + mBitmap = Bitmap.createBitmap(previewWidtd, previewHeight, Bitmap.Config.ARGB_8888); + } + + @Override + protected void onPreviewStopped() { + if(mBitmap != null) { + mBitmap.recycle(); + mBitmap = null; + } + mRGBA = null; + + + } + @Override protected Bitmap processFrame(byte[] data) { - int frameSize = getFrameWidth() * getFrameHeight(); - int[] rgba = new int[frameSize]; + int[] rgba = mRGBA; FindFeatures(getFrameWidth(), getFrameHeight(), data, rgba); - Bitmap bmp = Bitmap.createBitmap(getFrameWidth(), getFrameHeight(), Bitmap.Config.ARGB_8888); + Bitmap bmp = mBitmap; bmp.setPixels(rgba, 0/* offset */, getFrameWidth() /* stride */, 0, 0, getFrameWidth(), getFrameHeight()); return bmp; } @@ -24,10 +45,6 @@ class Sample3View extends SampleViewBase { public native void FindFeatures(int width, int height, byte yuv[], int[] rgba); static { - try { - System.loadLibrary("opencv_java"); - } catch(Exception e) { - } System.loadLibrary("native_sample"); } } diff --git a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java index 71df0f84e6..bd7fb8cb35 100644 --- a/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java +++ b/samples/android/tutorial-3-native/src/org/opencv/samples/tutorial3/SampleViewBase.java @@ -6,6 +6,7 @@ import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; @@ -23,6 +24,8 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde private int mFrameHeight; private byte[] mFrame; private boolean mThreadRun; + private byte[] mBuffer; + public SampleViewBase(Context context) { super(context); @@ -45,7 +48,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde else mCamera.setPreviewDisplay(null); } - + public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { Log.i(TAG, "surfaceCreated"); if (mCamera != null) { @@ -56,7 +59,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde // selecting optimal camera preview size { - double minDiff = Double.MAX_VALUE; + int minDiff = Integer.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - height) < minDiff) { mFrameWidth = size.width; @@ -67,12 +70,28 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde } params.setPreviewSize(getFrameWidth(), getFrameHeight()); + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); mCamera.setParameters(params); - try { - setPreview(); + + /* Now allocate the buffer */ + params = mCamera.getParameters(); + int size = params.getPreviewSize().width * params.getPreviewSize().height; + size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; + mBuffer = new byte[size]; + /* The buffer where the current frame will be coppied */ + mFrame = new byte [size]; + mCamera.addCallbackBuffer(mBuffer); + + try { + setPreview(); } catch (IOException e) { Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); } + + /* Notify that the preview is about to be started and deliver preview size */ + onPreviewStared(params.getPreviewSize().width, params.getPreviewSize().height); + + /* Now we can start a preview */ mCamera.startPreview(); } } @@ -80,14 +99,17 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); mCamera = Camera.open(); - mCamera.setPreviewCallback(new PreviewCallback() { + + mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { synchronized (SampleViewBase.this) { - mFrame = data; - SampleViewBase.this.notify(); + System.arraycopy(data, 0, mFrame, 0, data.length); + SampleViewBase.this.notify(); } + camera.addCallbackBuffer(mBuffer); } }); + (new Thread(this)).start(); } @@ -102,10 +124,27 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde mCamera = null; } } + onPreviewStopped(); } + /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ protected abstract Bitmap processFrame(byte[] data); + /** + * This method is called when the preview process is beeing started. It is called before the first frame delivered and processFrame is called + * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. + * @param previewWidth - the width of the preview frames that will be delivered via processFrame + * @param previewHeight - the height of the preview frames that will be delivered via processFrame + */ + protected abstract void onPreviewStared(int previewWidtd, int previewHeight); + + /** + * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. + * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. + * Any other resourcses used during the preview can be released. + */ + protected abstract void onPreviewStopped(); + public void run() { mThreadRun = true; Log.i(TAG, "Starting processing thread"); @@ -127,7 +166,6 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); mHolder.unlockCanvasAndPost(canvas); } - bmp.recycle(); } } } diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java index 0c5eda42b9..ea36d7f5ef 100644 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java +++ b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4Mixed.java @@ -8,19 +8,14 @@ import android.view.MenuItem; import android.view.Window; public class Sample4Mixed extends Activity { - private static final String TAG = "Sample::Activity"; - - public static final int VIEW_MODE_RGBA = 0; - public static final int VIEW_MODE_GRAY = 1; - public static final int VIEW_MODE_CANNY = 2; - public static final int VIEW_MODE_FEATURES = 5; + private static final String TAG = "Sample::Activity"; private MenuItem mItemPreviewRGBA; private MenuItem mItemPreviewGray; private MenuItem mItemPreviewCanny; private MenuItem mItemPreviewFeatures; + private Sample4View mView; - public static int viewMode = VIEW_MODE_RGBA; public Sample4Mixed() { Log.i(TAG, "Instantiated new " + this.getClass()); @@ -32,7 +27,8 @@ public class Sample4Mixed extends Activity { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate"); requestWindowFeature(Window.FEATURE_NO_TITLE); - setContentView(new Sample4View(this)); + mView = new Sample4View(this); + setContentView(mView); } public boolean onCreateOptionsMenu(Menu menu) { @@ -46,14 +42,15 @@ public class Sample4Mixed extends Activity { public boolean onOptionsItemSelected(MenuItem item) { Log.i(TAG, "Menu Item selected " + item); - if (item == mItemPreviewRGBA) - viewMode = VIEW_MODE_RGBA; - else if (item == mItemPreviewGray) - viewMode = VIEW_MODE_GRAY; - else if (item == mItemPreviewCanny) - viewMode = VIEW_MODE_CANNY; - else if (item == mItemPreviewFeatures) - viewMode = VIEW_MODE_FEATURES; + if (item == mItemPreviewRGBA) { + mView.setViewMode(Sample4View.VIEW_MODE_RGBA); + } else if (item == mItemPreviewGray) { + mView.setViewMode(Sample4View.VIEW_MODE_GRAY); + } else if (item == mItemPreviewCanny) { + mView.setViewMode(Sample4View.VIEW_MODE_CANNY); + } else if (item == mItemPreviewFeatures) { + mView.setViewMode(Sample4View.VIEW_MODE_FEATURES); + } return true; } } diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java index 29f782d4c1..c2d458457a 100644 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java +++ b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/Sample4View.java @@ -11,87 +11,106 @@ import android.util.Log; import android.view.SurfaceHolder; class Sample4View extends SampleViewBase { + + public static final int VIEW_MODE_RGBA = 0; + public static final int VIEW_MODE_GRAY = 1; + public static final int VIEW_MODE_CANNY = 2; + public static final int VIEW_MODE_FEATURES = 5; + private Mat mYuv; private Mat mRgba; private Mat mGraySubmat; private Mat mIntermediateMat; + private int mViewMode; + private Bitmap mBitmap; + public Sample4View(Context context) { super(context); } + + @Override + protected void onPreviewStared(int previewWidtd, int previewHeight) { + // initialize Mats before usage + mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); + mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); + + mRgba = new Mat(); + mIntermediateMat = new Mat(); + + mBitmap = Bitmap.createBitmap(previewWidtd, previewHeight, Bitmap.Config.ARGB_8888); + } + + @Override + protected void onPreviewStopped() { + + if (mBitmap != null) { + mBitmap.recycle(); + mBitmap = null; + } + + // Explicitly deallocate Mats + if (mYuv != null) + mYuv.release(); + if (mRgba != null) + mRgba.release(); + if (mGraySubmat != null) + mGraySubmat.release(); + if (mIntermediateMat != null) + mIntermediateMat.release(); + + mYuv = null; + mRgba = null; + mGraySubmat = null; + mIntermediateMat = null; + + } - @Override - public void surfaceChanged(SurfaceHolder _holder, int format, int width, int height) { - super.surfaceChanged(_holder, format, width, height); - - synchronized (this) { - // initialize Mats before usage - mYuv = new Mat(getFrameHeight() + getFrameHeight() / 2, getFrameWidth(), CvType.CV_8UC1); - mGraySubmat = mYuv.submat(0, getFrameHeight(), 0, getFrameWidth()); - - mRgba = new Mat(); - mIntermediateMat = new Mat(); - } - } @Override protected Bitmap processFrame(byte[] data) { mYuv.put(0, 0, data); - switch (Sample4Mixed.viewMode) { - case Sample4Mixed.VIEW_MODE_GRAY: + final int viewMode = mViewMode; + + switch (viewMode) { + case VIEW_MODE_GRAY: Imgproc.cvtColor(mGraySubmat, mRgba, Imgproc.COLOR_GRAY2RGBA, 4); break; - case Sample4Mixed.VIEW_MODE_RGBA: + case VIEW_MODE_RGBA: Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); break; - case Sample4Mixed.VIEW_MODE_CANNY: + case VIEW_MODE_CANNY: Imgproc.Canny(mGraySubmat, mIntermediateMat, 80, 100); Imgproc.cvtColor(mIntermediateMat, mRgba, Imgproc.COLOR_GRAY2BGRA, 4); break; - case Sample4Mixed.VIEW_MODE_FEATURES: + case VIEW_MODE_FEATURES: Imgproc.cvtColor(mYuv, mRgba, Imgproc.COLOR_YUV420sp2RGB, 4); FindFeatures(mGraySubmat.getNativeObjAddr(), mRgba.getNativeObjAddr()); break; } - Bitmap bmp = Bitmap.createBitmap(getFrameWidth(), getFrameHeight(), Bitmap.Config.ARGB_8888); + Bitmap bmp = mBitmap; try { - Utils.matToBitmap(mRgba, bmp); - return bmp; + Utils.matToBitmap(mRgba, bmp); } catch(Exception e) { - Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); + Log.e("org.opencv.samples.puzzle15", "Utils.matToBitmap() throws an exception: " + e.getMessage()); bmp.recycle(); - return null; + bmp = null; } - } - @Override - public void run() { - super.run(); - - synchronized (this) { - // Explicitly deallocate Mats - if (mYuv != null) - mYuv.release(); - if (mRgba != null) - mRgba.release(); - if (mGraySubmat != null) - mGraySubmat.release(); - if (mIntermediateMat != null) - mIntermediateMat.release(); - - mYuv = null; - mRgba = null; - mGraySubmat = null; - mIntermediateMat = null; - } + return bmp; } public native void FindFeatures(long matAddrGr, long matAddrRgba); static { + System.loadLibrary("opencv_java"); System.loadLibrary("mixed_sample"); } + + public void setViewMode(int viewMode) { + mViewMode = viewMode; + } } diff --git a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java index 87f1cdc6f5..f0d8bf6ab0 100644 --- a/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java +++ b/samples/android/tutorial-4-mixed/src/org/opencv/samples/tutorial4/SampleViewBase.java @@ -6,6 +6,7 @@ import java.util.List; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.PreviewCallback; @@ -23,6 +24,8 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde private int mFrameHeight; private byte[] mFrame; private boolean mThreadRun; + private byte[] mBuffer; + public SampleViewBase(Context context) { super(context); @@ -56,7 +59,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde // selecting optimal camera preview size { - double minDiff = Double.MAX_VALUE; + int minDiff = Integer.MAX_VALUE; for (Camera.Size size : sizes) { if (Math.abs(size.height - height) < minDiff) { mFrameWidth = size.width; @@ -67,12 +70,28 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde } params.setPreviewSize(getFrameWidth(), getFrameHeight()); + params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); mCamera.setParameters(params); + + /* Now allocate the buffer */ + params = mCamera.getParameters(); + int size = params.getPreviewSize().width * params.getPreviewSize().height; + size = size * ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8; + mBuffer = new byte[size]; + /* The buffer where the current frame will be coppied */ + mFrame = new byte [size]; + mCamera.addCallbackBuffer(mBuffer); + try { - setPreview(); - } catch (IOException e) { - Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); - } + setPreview(); + } catch (IOException e) { + Log.e(TAG, "mCamera.setPreviewDisplay/setPreviewTexture fails: " + e); + } + + /* Notify that the preview is about to be started and deliver preview size */ + onPreviewStared(params.getPreviewSize().width, params.getPreviewSize().height); + + /* Now we can start a preview */ mCamera.startPreview(); } } @@ -80,14 +99,17 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde public void surfaceCreated(SurfaceHolder holder) { Log.i(TAG, "surfaceCreated"); mCamera = Camera.open(); - mCamera.setPreviewCallback(new PreviewCallback() { + + mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() { public void onPreviewFrame(byte[] data, Camera camera) { synchronized (SampleViewBase.this) { - mFrame = data; - SampleViewBase.this.notify(); + System.arraycopy(data, 0, mFrame, 0, data.length); + SampleViewBase.this.notify(); } + camera.addCallbackBuffer(mBuffer); } }); + (new Thread(this)).start(); } @@ -102,10 +124,27 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde mCamera = null; } } + onPreviewStopped(); } + /* The bitmap returned by this method shall be owned by the child and released in onPreviewStopped() */ protected abstract Bitmap processFrame(byte[] data); + /** + * This method is called when the preview process is beeing started. It is called before the first frame delivered and processFrame is called + * It is called with the width and height parameters of the preview process. It can be used to prepare the data needed during the frame processing. + * @param previewWidth - the width of the preview frames that will be delivered via processFrame + * @param previewHeight - the height of the preview frames that will be delivered via processFrame + */ + protected abstract void onPreviewStared(int previewWidtd, int previewHeight); + + /** + * This method is called when preview is stopped. When this method is called the preview stopped and all the processing of frames already completed. + * If the Bitmap object returned via processFrame is cached - it is a good time to recycle it. + * Any other resourcses used during the preview can be released. + */ + protected abstract void onPreviewStopped(); + public void run() { mThreadRun = true; Log.i(TAG, "Starting processing thread"); @@ -127,12 +166,7 @@ public abstract class SampleViewBase extends SurfaceView implements SurfaceHolde canvas.drawBitmap(bmp, (canvas.getWidth() - getFrameWidth()) / 2, (canvas.getHeight() - getFrameHeight()) / 2, null); mHolder.unlockCanvasAndPost(canvas); } - bmp.recycle(); } } } - - static { - System.loadLibrary("opencv_java"); - } } \ No newline at end of file