OpenCV Android - 使用CameraBridgeViewBase的颜色问题

Max*_*ysh 5 java macos android opencv android-emulator

我在使用Android模拟器和OpenCV时遇到了一个奇怪的问题CameraBridgeViewBase.

使用onCameraFrame我得到一张看起来没有正确解码的图片.

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    return inputFrame.rgba();
}
Run Code Online (Sandbox Code Playgroud)

使用'inputFrame.gray()'我得到了预期的结果 - 黑白图像没有文物或任何其他问题.

这就是我得到的:

第一张照片

还有一张照片(一张更大的照片)

到目前为止我尝试过的:

  1. 不同的API级别(从15到21).
  2. 不同的模拟器:Genymotion和谷歌Android模拟器.
  3. 不同的平台架构 - ARM和Intel x86.
  4. 使用Linux在不同的笔记本电脑上启动模拟器:按预期工作,问题消失了!
  5. 使用从Play商店下载的OpenCV启动应用程序.他们工作!然而:
    1. 启动按预期工作的应用程序,然后关闭它.
    2. 启动您的应用程序(或其中一个OpenCV教程),然后关闭它.
    3. 再次从5.1启动应用程序我发现它受到同一个bug的影响!
  6. 不同的OpenCV版本(2.4.9和2.4.10).
  7. 不同版本的OpenCV管理器(一个来自Play商店,2.4.9和2.4.10来自OpenCV包).
  8. 最后,正如我在5.2中注意到的那样,来自OpenCV包的预编译教程.apk文件也受到该问题的影响.

一切都在我真正的Android设备上按预期工作.

在查看了CameraBridgeViewBaseJava和Native相机类的来源之后,我决定在解码图像时出现问题.平台特定的相机输出格式(YUV,NV21)可能存在问题.然而,奇怪的是.gray()给出了一个合适的图像(没有工件).

如果重要的话,我正在使用带有"Facetime HD"相机的Mac OS X 10.10 Yosemite和MacBook Air.

任何关于如何克服这个问题并帮助找到问题根源的想法都非常感谢!

Max*_*ysh 7

因此,在深入研究问题之后,我找到了问题的根源.

我们来看看OpenCV JavaCameraView类及其CameraBridgeViewBase基类.问题是byte[]onPreviewFrame方法中作为数组接收的相机帧被错误地解码.

解码过程发生的代码的确切位置是Mat rgba()内部JavaCameraFrame类中的方法的实现JavaCameraView:

    public Mat rgba() {
        Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4);
        return mRgba;
    }
Run Code Online (Sandbox Code Playgroud)

如我们所见,Imgproc.cvtColor(...)方法用于将帧从YUV转换为RGBA.NV21 YUV -> RGBA转换发生在那里.在初始化过程中,我们将格式设置为NV21,因此这应该是正确的.此外,每个Android设备都应该支持NV21.另外,我们可以使用调试器检查设备是否接受了格式:

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()));
}
Run Code Online (Sandbox Code Playgroud)

据报道,手机(HTC Sensation)和仿真器都使用NV21.

但是,如果我们COLOR_YUV2RGBA_NV21改为COLOR_YUV2RGB_I420(YV12和I420是相同的,只有Y和V反转;)我们将看到模拟器最终会得到一个合适的颜色空间.将NV21改为YV12 params.setPreviewFormat(ImageFormat.NV21);我们会得到类似的结果.看起来像Imgproc.cvtColor或Android中的错误.

这是解决方案. 更改public Mat rgba()以下方式:

    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);  // COLOR_YUV2RGBA_YV12 produces inverted colors
        }
        return mRgba;
    }
Run Code Online (Sandbox Code Playgroud)

previewFormat它是一个新int变量,它以这种方式声明:

private int previewFormat = ImageFormat.NV21;
Run Code Online (Sandbox Code Playgroud)

在初始化中添加以下更改:

protected boolean initializeCamera(int width, int height) {
        ...
                params.setPreviewFormat(ImageFormat.NV21);
                // "generic" = android emulator
                if (Build.BRAND.equalsIgnoreCase("generic")) {
                    params.setPreviewFormat(ImageFormat.YV12);
                }
                ...
                mCamera.setParameters(params);
                params = mCamera.getParameters();
                previewFormat = params.getPreviewFormat();
        ...
}
Run Code Online (Sandbox Code Playgroud)

重要提示:
请注意:这只是一个临时解决方案,以便在我的情况下使OpenCV可以与模拟器一起使用.应该进行进一步的研究.检查设备是否在onPreviewFrame中使用正确的图像格式非常容易.我有时间的时候会回到这里.