移动设备上的 YUV (NV21) 到 BGR 转换(本机代码)

YUV (NV21) to BGR conversion on mobile devices (Native Code)

本文关键字:BGR 转换 本机代码 NV21 YUV 移动      更新时间:2023-10-16

我正在开发一个在Android和IOS上运行的移动应用程序。它能够实时处理视频流。在Android上,我通过android.hardware.Camera.PreviewCallback.onPreviewFrame获得相机的Preview-Videostream。我决定使用NV21格式,因为它应该被所有Android设备支持,而RGB则不是(或者只是RGB565)。

对于我的算法,主要用于模式识别,我需要灰度图像以及颜色信息。灰度不是问题,但是从NV21到BGR的颜色转换需要太长时间。

如上所述,我使用以下方法来捕获图像;

在应用程序中,我覆盖了相机的onPreviewFrame处理程序。这是在 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 对象中获取数据作为本地引用。然后将其转换为无符号 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 30 需要 ~4 毫秒,这对于这样一个"微不足道"的预处理步骤来说是不可接受的。(我的分析方法经过仔细检查,可以正常工作以进行实时测量)

现在,我拼命地寻找从 NV21 到 BGR 的颜色转换的更快实现。这是我已经做的;

  1. 采用本主题中提供的代码"convertYUV420_NV21toRGB8888"C++(转换时间的倍数)
  2. 将代码从 1 修改为仅使用整数运算(openCV 解决方案的转换时间加倍)
  3. 浏览了其他几个实现,所有这些实现都具有相似的转换时间
  4. 检查了OpenCV实现,他们使用大量的位移来获得性能。猜猜我靠自己不能做得更好

您是否有建议/了解良好的实施,甚至有完全不同的方法来解决此问题?我需要以某种方式从Android相机捕获RGB/BGR帧,它应该在尽可能多的Android设备上工作。

感谢您的回复!

你试过libyuv吗?我过去使用过它,如果您在 NEON 支持下编译它,它使用针对 ARM 处理器优化的 asm 代码,您可以从那里开始进一步优化您的特殊情况。