我在使用Android模拟器时遇到了一个奇怪的问题。 OpenCV CameraBridgeViewBase
。
使用onCameraFrame
我得到一张看起来没有正确解码的图片。
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
return inputFrame.rgba();
}
使用'inputFrame.gray()'我得到的是预期的 - 黑白图像没有文物或任何其他问题。
这就是我得到的:
One more picture (a bigger one)
到目前为止我尝试过:
在我的真实Android设备上,一切都按预期工作。
在查看CameraBridgeViewBase
和Java / Native相机类的来源之后,我决定在解码图像时出现问题。平台特定的相机输出格式(YUV,NV21)可能存在问题。然而,奇怪的是.gray()给出了一个合适的图像(没有伪影)。
如果重要的话,我正在使用带有“Facetime HD”相机的Mac OS X 10.10 Yosemite和MacBook Air。
关于如何克服这个问题的任何想法&非常感谢帮助找到问题的根源!
答案 0 :(得分:7)
因此,在深入研究问题之后,我找到了问题的根源。
让我们看一下OpenCV JavaCameraView
类及其CameraBridgeViewBase
基类。
问题是在byte[]
方法中作为onPreviewFrame
数组接收的相机帧被错误地解码。
解码过程发生的确切代码位置是Mat rgba()
内JavaCameraFrame
类JavaCameraView
方法的实现:
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已合并。它应该在下一个发布的版本中提供。