mirror of https://github.com/opencv/opencv.git
Merge pull request #24662 from asmorkalov:as/android_native_camera
Added experimental NativeCameraView class for Android 24+.pull/24592/head
commit
06ff35c9af
6 changed files with 214 additions and 3 deletions
@ -0,0 +1,191 @@ |
||||
package org.opencv.android; |
||||
|
||||
import org.opencv.core.Mat; |
||||
import org.opencv.core.Size; |
||||
|
||||
import org.opencv.imgproc.Imgproc; |
||||
|
||||
import org.opencv.videoio.Videoio; |
||||
import org.opencv.videoio.VideoCapture; |
||||
import org.opencv.videoio.VideoWriter; |
||||
|
||||
import android.content.Context; |
||||
import android.util.AttributeSet; |
||||
import android.util.Log; |
||||
import android.view.ViewGroup.LayoutParams; |
||||
|
||||
/** |
||||
* This class is an implementation of a bridge between SurfaceView and OpenCV VideoCapture. |
||||
* The class is experimental implementation and not recoomended for production usage. |
||||
*/ |
||||
public class NativeCameraView extends CameraBridgeViewBase { |
||||
|
||||
public static final String TAG = "NativeCameraView"; |
||||
private boolean mStopThread; |
||||
private Thread mThread; |
||||
|
||||
protected VideoCapture mCamera; |
||||
protected NativeCameraFrame mFrame; |
||||
|
||||
public NativeCameraView(Context context, int cameraId) { |
||||
super(context, cameraId); |
||||
} |
||||
|
||||
public NativeCameraView(Context context, AttributeSet attrs) { |
||||
super(context, attrs); |
||||
} |
||||
|
||||
@Override |
||||
protected boolean 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 */ |
||||
if (!initializeCamera(width, height)) |
||||
return false; |
||||
|
||||
/* now we can start update thread */ |
||||
mThread = new Thread(new CameraWorker()); |
||||
mThread.start(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
protected void disconnectCamera() { |
||||
/* 1. We need to stop thread which updating the frames |
||||
* 2. Stop camera and release it |
||||
*/ |
||||
if (mThread != null) { |
||||
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 { |
||||
|
||||
public int getWidth(Object obj) { |
||||
Size size = (Size)obj; |
||||
return (int)size.width; |
||||
} |
||||
|
||||
public int getHeight(Object obj) { |
||||
Size size = (Size)obj; |
||||
return (int)size.height; |
||||
} |
||||
|
||||
} |
||||
|
||||
private boolean initializeCamera(int width, int height) { |
||||
synchronized (this) { |
||||
|
||||
if (mCameraIndex == -1) { |
||||
Log.d(TAG, "Try to open default camera"); |
||||
mCamera = new VideoCapture(0, Videoio.CAP_ANDROID); |
||||
} else { |
||||
Log.d(TAG, "Try to open camera with index " + mCameraIndex); |
||||
mCamera = new VideoCapture(mCameraIndex, Videoio.CAP_ANDROID); |
||||
} |
||||
|
||||
if (mCamera == null) |
||||
return false; |
||||
|
||||
if (mCamera.isOpened() == false) |
||||
return false; |
||||
|
||||
mFrame = new NativeCameraFrame(mCamera); |
||||
|
||||
mCamera.set(Videoio.CAP_PROP_FRAME_WIDTH, width); |
||||
mCamera.set(Videoio.CAP_PROP_FRAME_HEIGHT, height); |
||||
|
||||
mFrameWidth = (int)mCamera.get(Videoio.CAP_PROP_FRAME_WIDTH); |
||||
mFrameHeight = (int)mCamera.get(Videoio.CAP_PROP_FRAME_HEIGHT); |
||||
|
||||
if ((getLayoutParams().width == LayoutParams.MATCH_PARENT) && (getLayoutParams().height == LayoutParams.MATCH_PARENT)) |
||||
mScale = Math.min(((float)height)/mFrameHeight, ((float)width)/mFrameWidth); |
||||
else |
||||
mScale = 0; |
||||
|
||||
if (mFpsMeter != null) { |
||||
mFpsMeter.setResolution(mFrameWidth, mFrameHeight); |
||||
} |
||||
|
||||
AllocateCache(); |
||||
} |
||||
|
||||
Log.i(TAG, "Selected camera frame size = (" + mFrameWidth + ", " + mFrameHeight + ")"); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
private void releaseCamera() { |
||||
synchronized (this) { |
||||
if (mFrame != null) mFrame.release(); |
||||
if (mCamera != null) mCamera.release(); |
||||
} |
||||
} |
||||
|
||||
private static class NativeCameraFrame implements CvCameraViewFrame { |
||||
|
||||
@Override |
||||
public Mat rgba() { |
||||
mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','3')); |
||||
mCapture.retrieve(mBgr); |
||||
Log.d(TAG, "Retrived frame with size " + mBgr.cols() + "x" + mBgr.rows() + " and channels: " + mBgr.channels()); |
||||
Imgproc.cvtColor(mBgr, mRgba, Imgproc.COLOR_RGB2RGBA); |
||||
return mRgba; |
||||
} |
||||
|
||||
@Override |
||||
public Mat gray() { |
||||
mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('G','R','E','Y')); |
||||
mCapture.retrieve(mGray); |
||||
Log.d(TAG, "Retrived frame with size " + mGray.cols() + "x" + mGray.rows() + " and channels: " + mGray.channels()); |
||||
return mGray; |
||||
} |
||||
|
||||
public NativeCameraFrame(VideoCapture capture) { |
||||
mCapture = capture; |
||||
mGray = new Mat(); |
||||
mRgba = new Mat(); |
||||
mBgr = new Mat(); |
||||
} |
||||
|
||||
public void release() { |
||||
if (mGray != null) mGray.release(); |
||||
if (mRgba != null) mRgba.release(); |
||||
if (mBgr != null) mBgr.release(); |
||||
} |
||||
|
||||
private VideoCapture mCapture; |
||||
private Mat mRgba; |
||||
private Mat mGray; |
||||
private Mat mBgr; |
||||
}; |
||||
|
||||
private class CameraWorker implements Runnable { |
||||
|
||||
public void run() { |
||||
do { |
||||
if (!mCamera.grab()) { |
||||
Log.e(TAG, "Camera frame grab failed"); |
||||
break; |
||||
} |
||||
|
||||
deliverAndDrawFrame(mFrame); |
||||
} while (!mStopThread); |
||||
} |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue