1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #24662 from asmorkalov:as/android_native_camera

Added experimental NativeCameraView class for Android 24+.
This commit is contained in:
Alexander Smorkalov
2023-12-11 16:40:57 +03:00
committed by GitHub
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);
}
}
}
+2 -1
View File
@@ -1419,7 +1419,8 @@ if __name__ == "__main__":
java_base_path = os.path.join(dstdir, 'java'); mkdir_p(java_base_path)
java_test_base_path = os.path.join(dstdir, 'test'); mkdir_p(java_test_base_path)
for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None), ('android-21/java', None)]:
for (subdir, target_subdir) in [('src/java', 'java'), ('android/java', None),
('android-21/java', None), ('android-24/java', None)]:
if target_subdir is None:
target_subdir = subdir
java_files_dir = os.path.join(SCRIPT_DIR, subdir)