2014-11-22 56 views
5

我遇到一个奇怪的问题,使用Android模拟器& OpenCV CameraBridgeViewBaseOpenCV Android - 颜色问题使用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商店和OpenCV的包2.4.9 & 2.4.10)。
  8. 最后,正如我在5.2注意到,来自OpenCV的包装预编译教程的apk文件是由问题也受到影响。

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

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

我使用的是Mac OS X的10.10优胜美地和MacBook Air用 “的Facetime HD” 的相机,如果该事项。

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

回答

7

所以,钻入问题后,我已经找到了问题的根源。

让我们来看看OpenCV JavaCameraView类和它的CameraBridgeViewBase基类。 问题在于,在onPreviewFrame方法中收到的byte[]数组中的相机帧被错误地解码。

的代码的确切地方,解码过程发生是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感觉)和模拟器报道为使用确实NV21。但是,如果我们将COLOR_YUV2RGBA_NV21更改为COLOR_YUV2RGB_I420(YV12和I420是相同的东西,只需将Y和V倒置;),我们将看到仿真器最终将获得适当的颜色空间。在params.setPreviewFormat(ImageFormat.NV21);中将NV21更改为YV12我们会得到类似的结果。看起来像是在Imgproc.cvtColor或Android中有bug。

这是解决方案。 更改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

使用Android Studio 1.5.1并模拟API 23时,上述仅在''generic''被交换到'“android”'时才起作用。尽管如此,改变之后的工作仍然完美无瑕。 – 2016-01-26 19:55:20

+0

我只是试过这个,它工作正常。虽然,我不确定这是否是首选的方法,或者应该以不同的方式修复。我在OpenCV存储库中创建了一个问题,如果接受它,可以将其作为请求提交。 – 2017-02-09 23:24:10

+1

为了可追溯性:https://github.com/opencv/opencv/issues/8166 – 2017-02-14 01:26:16

0

对于那些使用genymotion的人,BRAND不会被称为通用。 为了解决这个只是简单的代码更改为

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

,它应该工作,或只是打印输出您正在使用的品牌,取代它的“Android”的

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

干杯

+0

也许我应该进一步解释,如果您使用的genymotion是仿真器并且通常由某些人使用,则第二个“或条件”将会覆盖。 – PDragon 2016-02-20 03:47:53

+0

请注意,条件应为||而不是| – 2017-02-09 22:20:55