So, after drilling the problem, I found the root of the problem.
Let's look at the OpenCV class JavaCameraView and its base class CameraBridgeViewBase . The problem was that the camera frames received as the byte[] array in onPreviewFrame were incorrectly decoded.
The exact place where the decoding process takes place is the implementation of the Mat rgba() method in the internal JavaCameraFrame class of JavaCameraView :
public Mat rgba() { Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4); return mRgba; }
As we can see, the Imgproc.cvtColor (...) method is used to convert a frame from YUV to RGBA. The NV21 YUV -> RGBA conversion takes place there. During the initialization process, we set the format to NV21, so this should be correct. In addition, each Android device must support NV21 . In addition, we can check if the device has adopted the format using the debugger:
protected boolean initializeCamera(int width, int height) { ... params.setPreviewFormat(ImageFormat.NV21); ... mCamera.setParameters(params); ... params = mCamera.getParameters(); Log.d(TAG, String.format("Actual preview format is 0x%X", params.getPreviewFormat())); }
Both phones (HTC Sensation) and the emulator reported using NV21.
However, if we change COLOR_YUV2RGBA_NV21 to COLOR_YUV2RGB_I420 (YV12 and I420 are the same, only with inverted Y and V;), we will see that the emulator finally gets the correct color space. Changing NV21 to YV12 in params.setPreviewFormat(ImageFormat.NV21); , we get similar results. Looks like an error in Imgproc.cvtColor or on Android.
Here is the solution. Change the public Mat rgba() as follows:
public Mat rgba() { if (previewFormat == ImageFormat.NV21) { Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4); } else if (previewFormat == ImageFormat.YV12) { Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGB_I420, 4);
previewFormat is a new int variable that is declared as follows:
private int previewFormat = ImageFormat.NV21;
Add the following initialization changes:
protected boolean initializeCamera(int width, int height) { ... params.setPreviewFormat(ImageFormat.NV21);
Important:
Please note: this is just a temporary solution to make OpenCV usable with the emulator in my case. Further studies should be performed. It's easy enough to check if your device is using the correct image format in onPreviewFrame. I will come back to this when I have time.