Merge pull request #26656 from warped-rudi:mediandk

AndroidMediaNdkCapture pixel format enhancement #26656

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [ ] The PR is proposed to the proper branch
- [ x] There is a reference to the original bug report and related work
- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Rüdiger Ihle 2024-12-30 09:09:11 +01:00 committed by GitHub
parent 9c33baebbd
commit d39aae6bdf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 101 additions and 34 deletions

View File

@ -2,6 +2,7 @@ package org.opencv.android;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.core.MatOfInt;
import org.opencv.imgproc.Imgproc;
@ -123,8 +124,11 @@ public class NativeCameraView extends CameraBridgeViewBase {
return false;
}
MatOfInt params = new MatOfInt(Videoio.CAP_PROP_FRAME_WIDTH, width,
Videoio.CAP_PROP_FRAME_HEIGHT, height);
Log.d(TAG, "Try to open camera with index " + localCameraIndex);
mCamera = new VideoCapture(localCameraIndex, Videoio.CAP_ANDROID);
mCamera = new VideoCapture(localCameraIndex, Videoio.CAP_ANDROID, params);
if (mCamera == null)
return false;
@ -139,9 +143,6 @@ public class NativeCameraView extends CameraBridgeViewBase {
mFrame = new RotatedCameraFrame(new NativeCameraFrame(mCamera), frameRotation);
mCamera.set(Videoio.CAP_PROP_FRAME_WIDTH, width);
mCamera.set(Videoio.CAP_PROP_FRAME_HEIGHT, height);
if (frameRotation % 180 == 0) {
mFrameWidth = (int) mCamera.get(Videoio.CAP_PROP_FRAME_WIDTH);
mFrameHeight = (int) mCamera.get(Videoio.CAP_PROP_FRAME_HEIGHT);
@ -181,10 +182,9 @@ public class NativeCameraView extends CameraBridgeViewBase {
@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);
mCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','4'));
mCapture.retrieve(mRgba);
Log.d(TAG, "Retrieved frame with size " + mRgba.cols() + "x" + mRgba.rows() + " and channels: " + mRgba.channels());
return mRgba;
}
@ -192,7 +192,7 @@ public class NativeCameraView extends CameraBridgeViewBase {
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());
Log.d(TAG, "Retrieved frame with size " + mGray.cols() + "x" + mGray.rows() + " and channels: " + mGray.channels());
return mGray;
}
@ -200,20 +200,17 @@ public class NativeCameraView extends CameraBridgeViewBase {
mCapture = capture;
mGray = new Mat();
mRgba = new Mat();
mBgr = new Mat();
}
@Override
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 {

View File

@ -21,10 +21,20 @@
#define INPUT_TIMEOUT_MS 2000
#define COLOR_FormatUnknown -1
#define COLOR_FormatYUV420Planar 19
#define COLOR_FormatYUV420SemiPlanar 21
#define COLOR_FormatSurface 0x7f000789 //See https://developer.android.com/reference/android/media/MediaCodecInfo.CodecCapabilities for codes
#define FOURCC_BGR CV_FOURCC_MACRO('B','G','R','3')
#define FOURCC_RGB CV_FOURCC_MACRO('R','G','B','3')
#define FOURCC_BGRA CV_FOURCC_MACRO('B','G','R','4')
#define FOURCC_RGBA CV_FOURCC_MACRO('R','G','B','4')
#define FOURCC_GRAY CV_FOURCC_MACRO('G','R','E','Y')
#define FOURCC_NV12 CV_FOURCC_MACRO('N','V','1','2')
#define FOURCC_YV12 CV_FOURCC_MACRO('Y','V','1','2')
#define FOURCC_UNKNOWN 0xFFFFFFFF
using namespace cv;
#define TAG "NativeCodec"
@ -51,9 +61,9 @@ class AndroidMediaNdkCapture : public IVideoCapture
public:
AndroidMediaNdkCapture():
sawInputEOS(false), sawOutputEOS(false),
frameStride(0), frameWidth(0), frameHeight(0), colorFormat(0),
videoWidth(0), videoHeight(0),
videoFrameCount(0),
frameStride(0), frameWidth(0), frameHeight(0),
colorFormat(COLOR_FormatUnknown), fourCC(FOURCC_BGR),
videoWidth(0), videoHeight(0), videoFrameCount(0),
videoRotation(0), videoRotationCode(-1),
videoOrientationAuto(false) {}
@ -65,6 +75,7 @@ public:
int32_t frameWidth;
int32_t frameHeight;
int32_t colorFormat;
uint32_t fourCC;
int32_t videoWidth;
int32_t videoHeight;
float videoFrameRate;
@ -73,7 +84,6 @@ public:
int32_t videoRotationCode;
bool videoOrientationAuto;
std::vector<uint8_t> buffer;
Mat frame;
~AndroidMediaNdkCapture() { cleanUp(); }
@ -157,23 +167,51 @@ public:
return false;
}
Mat yuv(frameHeight + frameHeight/2, frameStride, CV_8UC1, buffer.data());
ColorConversionCodes ccCode;
const Mat yuv(frameHeight + frameHeight/2,
frameWidth, CV_8UC1, buffer.data(), frameStride);
if (colorFormat == COLOR_FormatYUV420Planar) {
cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_YV12);
switch(fourCC)
{
case FOURCC_BGR: ccCode = COLOR_YUV2BGR_YV12; break;
case FOURCC_RGB: ccCode = COLOR_YUV2RGB_YV12; break;
case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_YV12; break;
case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_YV12; break;
case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_YV12; break;
case FOURCC_YV12: break;
case FOURCC_UNKNOWN: fourCC = FOURCC_YV12; break;
default: LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else if (colorFormat == COLOR_FormatYUV420SemiPlanar) {
cv::cvtColor(yuv, frame, cv::COLOR_YUV2BGR_NV21);
// Attention: COLOR_FormatYUV420SemiPlanar seems to correspond to NV12.
// This is different from the Camera2 interface, where NV21
// is used in this situation.
switch(fourCC)
{
case FOURCC_BGR: ccCode = COLOR_YUV2BGR_NV12; break;
case FOURCC_RGB: ccCode = COLOR_YUV2RGB_NV12; break;
case FOURCC_BGRA: ccCode = COLOR_YUV2BGRA_NV12; break;
case FOURCC_RGBA: ccCode = COLOR_YUV2RGBA_NV12; break;
case FOURCC_GRAY: ccCode = COLOR_YUV2GRAY_NV12; break;
case FOURCC_NV12: break;
case FOURCC_UNKNOWN: fourCC = FOURCC_NV12; break;
default: LOGE("Unexpected FOURCC value: %d", fourCC);
return false;
}
} else {
LOGE("Unsupported video format: %d", colorFormat);
return false;
}
Mat croppedFrame = frame(Rect(0, 0, videoWidth, videoHeight));
out.assign(croppedFrame);
if (fourCC == FOURCC_YV12 || fourCC == FOURCC_NV12)
yuv.copyTo(out);
else
cvtColor(yuv, out, ccCode);
if (videoOrientationAuto && -1 != videoRotationCode) {
cv::rotate(out, out, videoRotationCode);
}
if (videoOrientationAuto && -1 != videoRotationCode)
rotate(out, out, videoRotationCode);
return true;
}
@ -194,8 +232,11 @@ public:
case CAP_PROP_FRAME_COUNT: return videoFrameCount;
case CAP_PROP_ORIENTATION_META: return videoRotation;
case CAP_PROP_ORIENTATION_AUTO: return videoOrientationAuto ? 1 : 0;
case CAP_PROP_FOURCC: return fourCC;
}
return 0;
// unknown parameter or value not available
return -1;
}
bool setProperty(int property_id, double value) CV_OVERRIDE
@ -206,6 +247,31 @@ public:
videoOrientationAuto = value != 0 ? true : false;
return true;
}
case CAP_PROP_FOURCC: {
uint32_t newFourCC = cvRound(value);
switch (newFourCC)
{
case FOURCC_BGR:
case FOURCC_RGB:
case FOURCC_BGRA:
case FOURCC_RGBA:
case FOURCC_GRAY:
fourCC = newFourCC;
return true;
case FOURCC_YV12:
if (colorFormat != COLOR_FormatYUV420SemiPlanar) {
fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_YV12;
return true;
}
break;
case FOURCC_NV12:
if (colorFormat != COLOR_FormatYUV420Planar) {
fourCC = (colorFormat == COLOR_FormatUnknown) ? FOURCC_UNKNOWN : FOURCC_NV12;
return true;
}
break;
}
}
}
return false;

View File

@ -57,7 +57,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList
private VideoWriter mVideoWriter = null;
private VideoCapture mVideoCapture = null;
private Mat mVideoFrame;
private Mat mRenderFrame;
public RecorderActivity() {
Log.i(TAG, "Instantiated new " + this.getClass());
@ -122,7 +121,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList
mTriggerButton.setText("Start Camera");
mVideoFrame.release();
mRenderFrame.release();
}
@Override
@ -132,7 +130,6 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList
super.onResume();
mVideoFrame = new Mat();
mRenderFrame = new Mat();
changeStatus();
}
@ -294,12 +291,16 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList
mVideoCapture = new VideoCapture(mVideoFilename, Videoio.CAP_OPENCV_MJPEG);
}
if (!mVideoCapture.isOpened()) {
if (mVideoCapture == null || !mVideoCapture.isOpened()) {
Log.e(TAG, "Can't open video");
Toast.makeText(this, "Can't open file " + mVideoFilename, Toast.LENGTH_SHORT).show();
return false;
}
if (!mUseBuiltInMJPG){
mVideoCapture.set(Videoio.CAP_PROP_FOURCC, VideoWriter.fourcc('R','G','B','4'));
}
Toast.makeText(this, "Starting playback from file " + mVideoFilename, Toast.LENGTH_SHORT).show();
mPlayerThread = new Runnable() {
@ -315,11 +316,14 @@ public class RecorderActivity extends CameraActivity implements CvCameraViewList
}
return;
}
// VideoCapture with CAP_ANDROID generates RGB frames instead of BGR
// https://github.com/opencv/opencv/issues/24687
Imgproc.cvtColor(mVideoFrame, mRenderFrame, mUseBuiltInMJPG ? Imgproc.COLOR_BGR2RGBA: Imgproc.COLOR_RGB2RGBA);
Bitmap bmp = Bitmap.createBitmap(mRenderFrame.cols(), mRenderFrame.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mRenderFrame, bmp);
// MJPEG codec will output BGR only. So we need to convert to RGBA.
if (mUseBuiltInMJPG) {
Imgproc.cvtColor(mVideoFrame, mVideoFrame, Imgproc.COLOR_BGR2RGBA);
}
Bitmap bmp = Bitmap.createBitmap(mVideoFrame.cols(), mVideoFrame.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mVideoFrame, bmp);
mImageView.setImageBitmap(bmp);
Handler h = new Handler();
h.postDelayed(this, 33);