【问题标题】:ARCore's ArFrame_acquireCameraImage method returns green imageARCore 的 ArFrame_acquireCameraImage 方法返回绿色图像
【发布时间】:2022-09-01 19:24:12
【问题描述】:

我正在尝试使用 ARCore 从相机获取图像。

我打电话给ArFrame_acquireCameraImage,也就是returns image with YUV_420_888 format。我还使用ArImage_getFormat 方法检查了它。 它返回给我 640x480 图像。然后我获得 U 平面的像素步长以区分 NV21 或 YV12 格式的图像。

然后我使用 memcpy 将 Y、U、V 数组组合成一个数组,将其编码为 Base64(使用 function by J. Malinen)并将其打印到日志中。

我还尝试使用RenderScript Intrinsics Replacement Toolkit 执行 YUV420p -> RGBA 转换。

我有这个代码:

  LOGD("take frame");
  ArImage *image = nullptr;
  if (mArSession != nullptr && mArFrame != nullptr &&
      ArFrame_acquireCameraImage(mArSession, mArFrame, &image) == AR_SUCCESS) {
    const uint8_t *y;
    const uint8_t *u;
    const uint8_t *v;

    int planesCount = 0;
    ArImage_getNumberOfPlanes(mArSession, image, &planesCount);
    LOGD("%i", planesCount);

    int yLength, uLength, vLength;
    ArImage_getPlaneData(mArSession, image, 0, &y, &yLength);
    ArImage_getPlaneData(mArSession, image, 1, &u, &uLength);
    ArImage_getPlaneData(mArSession, image, 2, &v, &vLength);

    auto *yuv420 = new uint8_t[yLength + uLength + vLength];
    memcpy(yuv420, y, yLength);
    memcpy(yuv420 + yLength, u, uLength);
    memcpy(yuv420 + yLength + uLength, v, vLength);

    int width, height, stride;
    ArImage_getWidth(mArSession, image, &width);
    ArImage_getHeight(mArSession, image, &height);

    ArImage_getPlanePixelStride(mArSession, image, 1, &stride);

    //auto *argb8888 = new uint8_t[width * height * 4];

    renderscript::RenderScriptToolkit::YuvFormat format = renderscript::RenderScriptToolkit::YuvFormat::YV12;
    if(stride != 1) {
      format = renderscript::RenderScriptToolkit::YuvFormat::NV21;
    }
    LOGD("%i %i %i", width, height, format);
    
    /*renderscript::RenderScriptToolkit toolkit;
    toolkit.yuvToRgb(yuv420, argb8888, width, height, format);*/

    LOGD("%s", base64_encode(yuv420, yLength + uLength + vLength).c_str());

    // delete[](argb8888);
    delete[](yuv420);
  }
  if (image != nullptr) {
    ArImage_release(image);
  }

Full code in repo

我的手机是小米米A3。也尝试在模拟器上运行它,但它仍然给我同样的画面。

实际图像应如下所示:

然而,我的代码打印了这张图片(我使用RAW Pixels对其进行了解码):

解码参数:

如果我取消注释 YUV420 -> ARGB 转换的代码并为 argb8888 数组打印 Base64,我将得到这个图像:

预设:RGB32,宽度:640,高度:480。 Base64 of this image

【问题讨论】:

    标签: android c++ arcore yuv rgba


    【解决方案1】:

    我用code taken from TensorFlow 替换了RenderScript Intrinsics Replacement Toolkit(具有多线程和SMID)。 我看到了这个优点:

    1. 更简单。 下面是使用 RSIRT 的尝试:
          auto *yuv420 = new uint8_t[yLength + uLength + vLength];
          memcpy(yuv420, y, yLength);
          memcpy(yuv420 + yLength, u, uLength);
          memcpy(yuv420 + yLength + uLength, v, vLength);
      
          renderscript::RenderScriptToolkit::YuvFormat format = 
          renderscript::RenderScriptToolkit::YuvFormat::YV12;
          if(stride != 1) {
            format = renderscript::RenderScriptToolkit::YuvFormat::NV21;
          }
      
          renderscript::RenderScriptToolkit toolkit;
          toolkit.yuvToRgb(yuv420, argb8888, width, height, format);
      

      这是我为使用 TensorFlow 代码而编写的代码:

      ConvertYUV420ToARGB8888(y, u, v, argb8888, width, height, yStride, uvStride, uvPixelStride);
      

      如您所见,RSIRT 仅采用平面图像,而 Tensorflow 代码被编写为使用由 3 个平面分割的图像,因此您不需要使用 memcpy。这就是为什么这个决定不会影响性能的原因。

      1. 我发现原始图像很大(1.2Mb),所以我不应该使用 Base64(我认为 Logcat 只是切断了我的输出,所以我看不到图像)。现在我将图像写入应用程序缓存并使用 adb 获取它。

      完整代码:

        ArImage *image = nullptr;
        if (mArSession != nullptr && mArFrame != nullptr &&
            ArFrame_acquireCameraImage(mArSession, mArFrame, &image) == AR_SUCCESS) {
          // It's image with Android YUV 420 format https://developer.android.com/reference/android/graphics/ImageFormat#YUV_420_888
      
          const uint8_t *y;
          const uint8_t *u;
          const uint8_t *v;
      
          int planesCount = 0;
          ArImage_getNumberOfPlanes(mArSession, image, &planesCount);
          LOGD("%i", planesCount);
      
          int yLength, uLength, vLength, yStride, uvStride, uvPixelStride;
          ArImage_getPlaneData(mArSession, image, 0, &y, &yLength);
          ArImage_getPlaneData(mArSession, image, 1, &u, &uLength);
          ArImage_getPlaneData(mArSession, image, 2, &v, &vLength);
      
          ArImage_getPlaneRowStride(mArSession, image, 0, &yStride);
          ArImage_getPlaneRowStride(mArSession, image, 1, &uvStride);
          ArImage_getPlanePixelStride(mArSession, image, 1, &uvPixelStride);
      
          int width, height;
          ArImage_getWidth(mArSession, image, &width);
          ArImage_getHeight(mArSession, image, &height);
      
          auto *argb8888 = new uint32_t[width * height];
          ConvertYUV420ToARGB8888(y, u, v, argb8888, width, height, yStride, uvStride, uvPixelStride);
      
          std::ofstream stream("/data/user/0/{your app package name}/cache/img", std::ios::out | std::ios::binary);
          for(int i = 0; i < width * height; i++)
            stream.write((char *) &argb8888[i], sizeof(uint32_t));
      
          stream.close();
          LOGD("%i %i", width, height);
      
          delete[](argb8888);
        }
        if (image != nullptr) {
          ArImage_release(image);
        }
      

      然而,为了我的目的,我做了另一件事来应用 Tensorflow yuv2rgb 代码。 yuv2rgb.cc里面的YUV2RGB有BRGA顺序,而AndroidARGB_8888有ARGB顺序。更简单地说,在内联 YUV2RGB 方法中,您需要更改此行:

      return 0xff000000 | (nR << 16) | (nG << 8) | nB;
      

      return 0xff000000 | nB << 16 | nG << 8 | nR;
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 1970-01-01
      • 1970-01-01
      • 2020-10-08
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      相关资源
      最近更新 更多