Auto focus added;

Old Android support added;
Android 4.1.x fixes added;
Thead sync added;
Camera errors handling added.
pull/80/head
Alexander Smorkalov 12 years ago committed by Andrey Kamaev
parent f8bcff8ff3
commit 8393fd0c9d
  1. 482
      samples/android/15-puzzle-framework/src/org/opencv/framework/OpenCvCameraBridgeViewBase.java
  2. 110
      samples/android/15-puzzle-framework/src/org/opencv/framework/OpenCvJavaCameraView.java
  3. 146
      samples/android/15-puzzle-framework/src/org/opencv/framework/OpenCvNativeCameraView.java

@ -26,295 +26,295 @@ import android.view.SurfaceView;
*/ */
public abstract class OpenCvCameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback { public abstract class OpenCvCameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback {
private static final int MAX_UNSPECIFIED = -1; private static final int MAX_UNSPECIFIED = -1;
protected int mFrameWidth; protected int mFrameWidth;
protected int mFrameHeight; protected int mFrameHeight;
protected int mMaxHeight; protected int mMaxHeight;
protected int mMaxWidth; protected int mMaxWidth;
private Bitmap mCacheBitmap; private Bitmap mCacheBitmap;
public OpenCvCameraBridgeViewBase(Context context, AttributeSet attrs) { public OpenCvCameraBridgeViewBase(Context context, AttributeSet attrs) {
super(context,attrs); super(context,attrs);
getHolder().addCallback(this); getHolder().addCallback(this);
mMaxWidth = MAX_UNSPECIFIED; mMaxWidth = MAX_UNSPECIFIED;
mMaxHeight = MAX_UNSPECIFIED; mMaxHeight = MAX_UNSPECIFIED;
} }
public interface CvCameraViewListener {
/**
* This method is invoked when camera preview has started. After this method is invoked
* the frames will start to be delivered to client via the onCameraFrame() callback.
* @param width - the width of the frames that will be delivered
* @param height - the height of the frames that will be delivered
*/
public void onCameraViewStarted(int width, int height);
/**
* This method is invoked when camera preview has been stopped for some reason.
* No frames will be delivered via onCameraFrame() callback after this method is called.
*/
public void onCameraViewStopped();
/**
* This method is invoked when delivery of the frame needs to be done.
* The returned values - is a modified frame which needs to be displayed on the screen.
* TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
*/
public Mat onCameraFrame(Mat inputFrame);
}
public class FrameSize {
public int width;
public int height;
public FrameSize(int w, int h) {
width = w;
height = h;
}
}
private static final int STOPPED = 0;
private static final int STARTED = 1;
private static final String TAG = "SampleCvBase";
private CvCameraViewListener mListener;
private int mState = STOPPED;
private boolean mEnabled;
private boolean mSurfaceExist;
private Object mSyncObject = new Object();
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
synchronized(mSyncObject) {
if (!mSurfaceExist) {
mSurfaceExist = true;
checkCurrentState();
} else {
/** Surface changed. We need to stop camera and restart with new parameters */
/* Pretend that old surface has been destroyed */
mSurfaceExist = false;
checkCurrentState();
/* Now use new surface. Say we have it now */
mSurfaceExist = true;
checkCurrentState();
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
/* Do nothing. Wait until surfaceChanged delivered */
}
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized(mSyncObject) {
mSurfaceExist = false;
checkCurrentState();
}
}
public interface CvCameraViewListener {
/**
* This method is invoked when camera preview has started. After this method is invoked
* the frames will start to be delivered to client via the onCameraFrame() callback.
* @param width - the width of the frames that will be delivered
* @param height - the height of the frames that will be delivered
*/
public void onCameraViewStarted(int width, int height);
/** /**
* This method is provided for clients, so they can enable the camera connection. * This method is invoked when camera preview has been stopped for some reason.
* The actuall onCameraViewStarted callback will be delivered only after both this method is called and surface is available * No frames will be delivered via onCameraFrame() callback after this method is called.
*/ */
public void enableView() { public void onCameraViewStopped();
synchronized(mSyncObject) {
mEnabled = true;
checkCurrentState();
}
}
/** /**
* This method is provided for clients, so they can disable camera connection and stop * This method is invoked when delivery of the frame needs to be done.
* the delivery of frames eventhough the surfaceview itself is not destroyed and still stays on the scren * The returned values - is a modified frame which needs to be displayed on the screen.
* TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
*/ */
public void disableView() { public Mat onCameraFrame(Mat inputFrame);
synchronized(mSyncObject) {
mEnabled = false;
checkCurrentState();
}
}
}
public void setCvCameraViewListener(CvCameraViewListener listener) { public class FrameSize {
mListener = listener; public int width;
} public int height;
/** public FrameSize(int w, int h) {
* This method sets the maximum size that camera frame is allowed to be. When selecting width = w;
* size - the biggest size which less or equal the size set will be selected. height = h;
* As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
* preview frame will be selected with 176x152 size.
* This method is usefull when need to restrict the size of preview frame for some reason (for example for video recording)
* @param maxWidth - the maximum width allowed for camera frame.
* @param maxHeight - the maxumum height allowed for camera frame
*/
public void setMaxFrameSize(int maxWidth, int maxHeight) {
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
} }
}
/**
* Called when mSyncObject lock is held
*/
private void checkCurrentState() {
int targetState;
if (mEnabled && mSurfaceExist) { private static final int STOPPED = 0;
targetState = STARTED; private static final int STARTED = 1;
} else { private static final String TAG = "SampleCvBase";
targetState = STOPPED;
}
if (targetState != mState) { private CvCameraViewListener mListener;
/* The state change detected. Need to exit the current state and enter target state */ private int mState = STOPPED;
processExitState(mState);
mState = targetState;
processEnterState(mState);
}
}
private void processEnterState(int state) { private boolean mEnabled;
switch(state) { private boolean mSurfaceExist;
case STARTED:
onEnterStartedState();
if (mListener != null) {
mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
}
break;
case STOPPED:
onEnterStoppedState();
if (mListener != null) {
mListener.onCameraViewStopped();
}
break;
};
}
private void processExitState(int state) {
switch(state) {
case STARTED:
onExitStartedState();
break;
case STOPPED:
onExitStoppedState();
break;
};
}
private void onEnterStoppedState() { private Object mSyncObject = new Object();
/* nothing to do */
}
private void onExitStoppedState() { public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
/* nothing to do */ synchronized(mSyncObject) {
if (!mSurfaceExist) {
mSurfaceExist = true;
checkCurrentState();
} else {
/** Surface changed. We need to stop camera and restart with new parameters */
/* Pretend that old surface has been destroyed */
mSurfaceExist = false;
checkCurrentState();
/* Now use new surface. Say we have it now */
mSurfaceExist = true;
checkCurrentState();
}
} }
}
private void onEnterStartedState() { public void surfaceCreated(SurfaceHolder holder) {
/* Do nothing. Wait until surfaceChanged delivered */
connectCamera(getWidth(), getHeight()); }
/* Now create cahe Bitmap */
mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized(mSyncObject) {
mSurfaceExist = false;
checkCurrentState();
} }
}
private void onExitStartedState() {
disconnectCamera(); /**
if (mCacheBitmap != null) { * This method is provided for clients, so they can enable the camera connection.
mCacheBitmap.recycle(); * The actuall onCameraViewStarted callback will be delivered only after both this method is called and surface is available
} */
public void enableView() {
synchronized(mSyncObject) {
mEnabled = true;
checkCurrentState();
}
}
/**
* This method is provided for clients, so they can disable camera connection and stop
* the delivery of frames eventhough the surfaceview itself is not destroyed and still stays on the scren
*/
public void disableView() {
synchronized(mSyncObject) {
mEnabled = false;
checkCurrentState();
}
}
public void setCvCameraViewListener(CvCameraViewListener listener) {
mListener = listener;
}
/**
* This method sets the maximum size that camera frame is allowed to be. When selecting
* size - the biggest size which less or equal the size set will be selected.
* As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
* preview frame will be selected with 176x152 size.
* This method is usefull when need to restrict the size of preview frame for some reason (for example for video recording)
* @param maxWidth - the maximum width allowed for camera frame.
* @param maxHeight - the maxumum height allowed for camera frame
*/
public void setMaxFrameSize(int maxWidth, int maxHeight) {
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
}
/**
* Called when mSyncObject lock is held
*/
private void checkCurrentState() {
int targetState;
if (mEnabled && mSurfaceExist) {
targetState = STARTED;
} else {
targetState = STOPPED;
} }
if (targetState != mState) {
/* The state change detected. Need to exit the current state and enter target state */
processExitState(mState);
mState = targetState;
processEnterState(mState);
}
}
private void processEnterState(int state) {
switch(state) {
case STARTED:
onEnterStartedState();
if (mListener != null) {
mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
}
break;
case STOPPED:
onEnterStoppedState();
if (mListener != null) {
mListener.onCameraViewStopped();
}
break;
};
}
/**
* This method shall be called by the subclasses when they have valid
* object and want it to be delivered to external client (via callback) and
* then displayed on the screen.
* @param frame - the current frame to be delivered
*/
protected void deliverAndDrawFrame(Mat frame) {
Mat modified;
if (mListener != null) { private void processExitState(int state) {
modified = mListener.onCameraFrame(frame); switch(state) {
} else { case STARTED:
modified = frame; onExitStartedState();
} break;
case STOPPED:
onExitStoppedState();
break;
};
}
if (modified != null) { private void onEnterStoppedState() {
Utils.matToBitmap(modified, mCacheBitmap); /* nothing to do */
} }
if (mCacheBitmap != null) { private void onExitStoppedState() {
Canvas canvas = getHolder().lockCanvas(); /* nothing to do */
if (canvas != null) { }
canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null);
getHolder().unlockCanvasAndPost(canvas);
}
}
}
/** private void onEnterStartedState() {
* This method is invoked shall perform concrete operation to initialize the camera.
* CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
* initialized with the size of the Camera frames that will be delivered to external processor.
* @param width - the width of this SurfaceView
* @param height - the height of this SurfaceView
*/
protected abstract void connectCamera(int width, int height);
/** connectCamera(getWidth(), getHeight());
* Disconnects and release the particular camera object beeing connected to this surface view. /* Now create cahe Bitmap */
* Called when syncObject lock is held mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
*/
protected abstract void disconnectCamera();
}
public interface ListItemAccessor { private void onExitStartedState() {
public int getWidth(Object obj);
public int getHeight(Object obj);
};
disconnectCamera();
if (mCacheBitmap != null) {
mCacheBitmap.recycle();
}
}
/**
* This method shall be called by the subclasses when they have valid
* object and want it to be delivered to external client (via callback) and
* then displayed on the screen.
* @param frame - the current frame to be delivered
*/
protected void deliverAndDrawFrame(Mat frame) {
Mat modified;
if (mListener != null) {
modified = mListener.onCameraFrame(frame);
} else {
modified = frame;
}
/** if (modified != null) {
* This helper method can be called by subclasses to select camera preview size. Utils.matToBitmap(modified, mCacheBitmap);
* It goes over the list of the supported preview sizes and selects the maximum one which }
* fits both values set via setMaxFrameSize() and surface frame allocated for this view
* @param supportedSizes
* @param surfaceWidth
* @param surfaceHeight
* @return
*/
protected FrameSize calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
int calcWidth = 0;
int calcHeight = 0;
int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth; if (mCacheBitmap != null) {
int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight; Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
canvas.drawBitmap(mCacheBitmap, (canvas.getWidth() - mCacheBitmap.getWidth()) / 2, (canvas.getHeight() - mCacheBitmap.getHeight()) / 2, null);
getHolder().unlockCanvasAndPost(canvas);
}
}
}
/**
* This method is invoked shall perform concrete operation to initialize the camera.
* CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
* initialized with the size of the Camera frames that will be delivered to external processor.
* @param width - the width of this SurfaceView
* @param height - the height of this SurfaceView
*/
protected abstract void connectCamera(int width, int height);
/**
* Disconnects and release the particular camera object beeing connected to this surface view.
* Called when syncObject lock is held
*/
protected abstract void disconnectCamera();
public interface ListItemAccessor {
public int getWidth(Object obj);
public int getHeight(Object obj);
};
/**
* This helper method can be called by subclasses to select camera preview size.
* It goes over the list of the supported preview sizes and selects the maximum one which
* fits both values set via setMaxFrameSize() and surface frame allocated for this view
* @param supportedSizes
* @param surfaceWidth
* @param surfaceHeight
* @return
*/
protected FrameSize calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
int calcWidth = 0;
int calcHeight = 0;
int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth;
int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight;
for (Object size : supportedSizes) { for (Object size : supportedSizes) {
int width = accessor.getWidth(size); int width = accessor.getWidth(size);
int height = accessor.getHeight(size); int height = accessor.getHeight(size);
if (width <= maxAllowedWidth && height <= maxAllowedHeight) { if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
if (width >= calcWidth && height >= calcHeight) { if (width >= calcWidth && height >= calcHeight) {
calcWidth = (int) width; calcWidth = (int) width;
calcHeight = (int) height; calcHeight = (int) height;
} }
} }
} }
return new FrameSize(calcWidth, calcHeight); return new FrameSize(calcWidth, calcHeight);
} }
} }

@ -27,94 +27,94 @@ import org.opencv.imgproc.Imgproc;
*/ */
public class OpenCvJavaCameraView extends OpenCvCameraBridgeViewBase implements PreviewCallback { public class OpenCvJavaCameraView extends OpenCvCameraBridgeViewBase implements PreviewCallback {
private static final int MAGIC_TEXTURE_ID = 10; private static final int MAGIC_TEXTURE_ID = 10;
private static final String TAG = "OpenCvJavaCameraView"; private static final String TAG = "OpenCvJavaCameraView";
private Mat mBaseMat; private Mat mBaseMat;
public static class JavaCameraSizeAccessor implements ListItemAccessor { public static class JavaCameraSizeAccessor implements ListItemAccessor {
@Override @Override
public int getWidth(Object obj) { public int getWidth(Object obj) {
Camera.Size size = (Camera.Size) obj; Camera.Size size = (Camera.Size) obj;
return size.width; return size.width;
} }
@Override
public int getHeight(Object obj) {
Camera.Size size = (Camera.Size) obj;
return size.height;
}
@Override
public int getHeight(Object obj) {
Camera.Size size = (Camera.Size) obj;
return size.height;
} }
}
private Camera mCamera; private Camera mCamera;
public OpenCvJavaCameraView(Context context, AttributeSet attrs) { public OpenCvJavaCameraView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
} }
@Override @Override
protected void connectCamera(int width, int height) { protected void connectCamera(int width, int height) {
mCamera = Camera.open(0); mCamera = Camera.open(0);
List<android.hardware.Camera.Size> sizes = mCamera.getParameters().getSupportedPreviewSizes(); List<android.hardware.Camera.Size> sizes = mCamera.getParameters().getSupportedPreviewSizes();
/* Select the size that fits surface considering maximum size allowed */ /* Select the size that fits surface considering maximum size allowed */
FrameSize frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height); FrameSize frameSize = calculateCameraFrameSize(sizes, new JavaCameraSizeAccessor(), width, height);
/* Now set camera parameters */ /* Now set camera parameters */
try { try {
Camera.Parameters params = mCamera.getParameters(); Camera.Parameters params = mCamera.getParameters();
List<Integer> formats = params.getSupportedPictureFormats(); List<Integer> formats = params.getSupportedPictureFormats();
params.setPreviewFormat(ImageFormat.NV21); params.setPreviewFormat(ImageFormat.NV21);
params.setPreviewSize(frameSize.width, frameSize.height); params.setPreviewSize(frameSize.width, frameSize.height);
mCamera.setPreviewCallback(this); mCamera.setPreviewCallback(this);
mCamera.setParameters(params); mCamera.setParameters(params);
//mCamera.setPreviewTexture(new SurfaceTexture(MAGIC_TEXTURE_ID)); //mCamera.setPreviewTexture(new SurfaceTexture(MAGIC_TEXTURE_ID));
SurfaceTexture tex = new SurfaceTexture(MAGIC_TEXTURE_ID); SurfaceTexture tex = new SurfaceTexture(MAGIC_TEXTURE_ID);
mCamera.setPreviewTexture(tex); mCamera.setPreviewTexture(tex);
mFrameWidth = frameSize.width; mFrameWidth = frameSize.width;
mFrameHeight = frameSize.height; mFrameHeight = frameSize.height;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
mBaseMat = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1); mBaseMat = new Mat(mFrameHeight + (mFrameHeight/2), mFrameWidth, CvType.CV_8UC1);
/* Finally we are ready to start the preview */ /* Finally we are ready to start the preview */
mCamera.startPreview(); mCamera.startPreview();
} }
@Override @Override
protected void disconnectCamera() { protected void disconnectCamera() {
mCamera.setPreviewCallback(null); mCamera.setPreviewCallback(null);
mCamera.stopPreview(); mCamera.stopPreview();
mCamera.release(); mCamera.release();
} }
@Override @Override
public void onPreviewFrame(byte[] frame, Camera arg1) { public void onPreviewFrame(byte[] frame, Camera arg1) {
Log.i(TAG, "Preview Frame received. Need to create MAT and deliver it to clients"); Log.i(TAG, "Preview Frame received. Need to create MAT and deliver it to clients");
Log.i(TAG, "Frame size is " + frame.length); Log.i(TAG, "Frame size is " + frame.length);
mBaseMat.put(0, 0, frame); mBaseMat.put(0, 0, frame);
Mat frameMat = new Mat(); Mat frameMat = new Mat();
Imgproc.cvtColor(mBaseMat, frameMat, Imgproc.COLOR_YUV2RGBA_NV21, 4); Imgproc.cvtColor(mBaseMat, frameMat, Imgproc.COLOR_YUV2RGBA_NV21, 4);
deliverAndDrawFrame(frameMat); deliverAndDrawFrame(frameMat);
frameMat.release(); frameMat.release();
} }
} }

@ -19,68 +19,68 @@ import android.util.Log;
*/ */
public class OpenCvNativeCameraView extends OpenCvCameraBridgeViewBase { public class OpenCvNativeCameraView extends OpenCvCameraBridgeViewBase {
public static final String TAG = "OpenCvNativeCameraView"; public static final String TAG = "OpenCvNativeCameraView";
private boolean mStopThread; private boolean mStopThread;
private Thread mThread; private Thread mThread;
private VideoCapture mCamera; private VideoCapture mCamera;
public OpenCvNativeCameraView(Context context, AttributeSet attrs) { public OpenCvNativeCameraView(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
}
@Override
protected void connectCamera(int width, int height) {
/* 1. We need to instantiate camera
* 2. We need to start thread which will be getting frames
*/
/* First step - initialize camera connection */
initializeCamera(getWidth(), getHeight());
/* now we can start update thread */
mThread = new Thread(new CameraWorker(getWidth(), getHeight()));
mThread.start();
}
@Override
protected void disconnectCamera() {
/* 1. We need to stop thread which updating the frames
* 2. Stop camera and release it
*/
try {
mStopThread = true;
mThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mThread = null;
mStopThread = false;
} }
/* Now release camera */
releaseCamera();
@Override }
protected void connectCamera(int width, int height) {
/* 1. We need to instantiate camera public static class OpenCvSizeAccessor implements ListItemAccessor {
* 2. We need to start thread which will be getting frames
*/
/* First step - initialize camera connection */
initializeCamera(getWidth(), getHeight());
/* now we can start update thread */ @Override
mThread = new Thread(new CameraWorker(getWidth(), getHeight())); public int getWidth(Object obj) {
mThread.start(); Size size = (Size)obj;
return (int)size.width;
} }
@Override @Override
protected void disconnectCamera() { public int getHeight(Object obj) {
/* 1. We need to stop thread which updating the frames Size size = (Size)obj;
* 2. Stop camera and release it return (int)size.height;
*/
try {
mStopThread = true;
mThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mThread = null;
mStopThread = false;
}
/* Now release camera */
releaseCamera();
} }
public static class OpenCvSizeAccessor implements ListItemAccessor { }
@Override
public int getWidth(Object obj) {
Size size = (Size)obj;
return (int)size.width;
}
@Override
public int getHeight(Object obj) {
Size size = (Size)obj;
return (int)size.height;
}
} private void initializeCamera(int width, int height) {
private void initializeCamera(int width, int height) {
mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID); mCamera = new VideoCapture(Highgui.CV_CAP_ANDROID);
//TODO: improve error handling //TODO: improve error handling
@ -101,42 +101,42 @@ public class OpenCvNativeCameraView extends OpenCvCameraBridgeViewBase {
mFrameHeight = (int)frameHeight; mFrameHeight = (int)frameHeight;
Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")"); Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")");
} }
private void releaseCamera() { private void releaseCamera() {
if (mCamera != null) { if (mCamera != null) {
mCamera.release(); mCamera.release();
}
} }
}
private class CameraWorker implements Runnable { private class CameraWorker implements Runnable {
private Mat mRgba = new Mat(); private Mat mRgba = new Mat();
private int mWidth; private int mWidth;
private int mHeight; private int mHeight;
CameraWorker(int w, int h) { CameraWorker(int w, int h) {
mWidth = w; mWidth = w;
mHeight = h; mHeight = h;
} }
@Override @Override
public void run() { public void run() {
Mat modified; Mat modified;
do { do {
if (!mCamera.grab()) { if (!mCamera.grab()) {
Log.e(TAG, "Camera frame grab failed"); Log.e(TAG, "Camera frame grab failed");
break; break;
} }
mCamera.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA); mCamera.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
deliverAndDrawFrame(mRgba); deliverAndDrawFrame(mRgba);
} while (!mStopThread); } while (!mStopThread);
}
} }
}
} }

Loading…
Cancel
Save