OpenCV Android - 使用CameraBridgeViewBase的颜色问题

时间:2014-11-22 22:08:15

标签: java android macos opencv android-emulator

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

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

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    return inputFrame.rgba();
}

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

这就是我得到的:

First picture

One more picture (a bigger one)

到目前为止我尝试过:

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

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

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

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

3 个答案:

答案 0 :(得分:7)

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

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

解码过程发生的确切代码位置是Mat rgba()JavaCameraFrameJavaCameraView方法的实现:

    public Mat rgba() {
        Imgproc.cvtColor(mYuvFrameData, mRgba, Imgproc.COLOR_YUV2RGBA_NV21, 4);
        return mRgba;
    }

如我们所见,Imgproc.cvtColor(...)方法用于将帧从YUV转换为RGBA。 NV21 YUV -> RGBA转换发生在那里。在初始化过程中,我们将格式设置为NV21,因此这应该是正确的。而且,every Android device should support 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()));
}

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

但是,如果我们将COLOR_YUV2RGBA_NV21更改为COLOR_YUV2RGB_I420(YV12和I420是相同的,只需将Y和V反转;)我们将看到模拟器最终将获得正确的颜色空间。在params.setPreviewFormat(ImageFormat.NV21);中将NV21更改为YV12,我们会得到类似的结果。看起来在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;
    }

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

private int previewFormat = ImageFormat.NV21;

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

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();
        ...
}

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

答案 1 :(得分:0)

对于那些使用genymotion的人来说,BRAND将不会被称为通用。 要解决这个问题,只需将代码更改为

即可
...
if (Build.BRAND.equalsIgnoreCase("generic") | Build.BRAND.equalsIgnoreCase("Android")) {
...

它应该可以使用,或者只打印出你正在使用的品牌并将其替换为“Android”

P.S第二个条件将包括你使用的genymotion也是一个模拟器。

干杯

答案 2 :(得分:0)

此问题已在git中的代码中修复,因为Pull Request https://github.com/opencv/opencv/pull/8168已合并。它应该在下一个发布的版本中提供。