我正在开发一个在 Android 和 IOS 上运行的移动应用程序。它能够实时处理视频流。在 Android 上,我通过 android.hardware.Camera.PreviewCallback.onPreviewFrame 获得相机的 Preview-Videostream。我决定使用 NV21 格式,因为所有 Android 设备都应该支持它,而 RGB 不支持(或者只是 RGB565)。
对于我的算法(主要用于模式识别),我需要灰度图像以及颜色信息。灰度不是问题,但从 NV21 到 BGR 的颜色转换耗时太长。
如上所述,我使用以下方法捕获图像;
在应用程序中,我覆盖了相机的 onPreviewFrame-Handler。这是在 CameraPreviewFrameHandler.java 中完成的:
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
{
try {
AvCore.getInstance().onFrame(data, _prevWidth, _prevHeight, AvStreamEncoding.NV21);
} catch (NativeException e)
{
e.printStackTrace();
}
}
然后 onFrame-Function 调用一个本地函数,该函数从 Java 对象中获取数据作为本地引用。然后将其转换为 unsigned char* 字节流并调用以下 c++ 函数,该函数使用 OpenCV 从 NV21 转换为 BGR:
void CoreManager::api_onFrame(unsigned char* rImageData, avStreamEncoding_t vImageFormat, int vWidth, int vHeight)
{
// rImageData is a local JNI-reference to the java-byte-array "data" from onPreviewFrame
Mat bgrMat; // Holds the converted image
Mat origImg; // Holds the original image (OpenCV-Wrapping around rImageData)
double ts; // for profiling
switch(vImageFormat)
{
// other formats
case NV21:
origImg = Mat(vHeight + vHeight/2, vWidth, CV_8UC1, rImageData); // fast, only creates header around rImageData
bgrMat = Mat(vHeight, vWidth, CV_8UC3); // Prepare Mat for target image
ts = avUtils::gettime(); // PROFILING START
cvtColor(origImg, bgrMat, CV_YUV2BGRA_NV21);
_onFrameBGRConversion.push_back(avUtils::gettime()-ts); // PROFILING END
break;
}
[...APPLICATION LOGIC...]
}
正如人们可能从代码中的注释中得出的结论一样,我已经对转换进行了分析,结果证明在我的 Nexus 4 上它需要大约 30 毫秒,对于这样一个“微不足道”的预处理步骤来说,这是不可接受的。(我的分析方法经过双重检查并且可以正常工作以进行实时测量)
现在我正在拼命地寻找从 NV21 到 BGR 的颜色转换的更快实现。这是我已经做过的;
- 采用本主题提供的代码“convertYUV420_NV21toRGB8888”到C++ (转换时间的倍数)
- 将代码从 1 修改为仅使用整数运算(openCV-Solution 的转换时间加倍)
- 浏览了其他几个实现,都具有相似的转换时间
- 检查 OpenCV-Implementation,他们使用大量位移来获得性能。猜我自己不能做得更好
您是否有建议/知道好的实现,甚至有完全不同的方法来解决这个问题?我不知何故需要从 Android 相机捕获 RGB/BGR 帧,它应该可以在尽可能多的 Android 设备上工作。
感谢您的回复!