簡體   English   中英

ARCore 的 ArFrame_acquireCameraImage 方法返回綠色圖像

[英]ARCore's ArFrame_acquireCameraImage method returns green image

我正在嘗試使用 ARCore 從相機獲取圖像。

我正在調用ArFrame_acquireCameraImage ,它返回 YUV_420_888 格式的圖像 我還使用ArImage_getFormat方法檢查了它。 它返回給我 640x480 圖像。 然后我獲得 U 平面的像素步長以區分 NV21 或 YV12 格式的圖像。

然后我使用memcpy將 Y、U、V arrays 組合成單個,將其編碼為 Base64 (使用J. Malinen 的 function )並將其打印到日志。

我還嘗試使用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);
  }

repo 中的完整代碼

我的手機是小米米A3。 也嘗試在模擬器上運行它,但它仍然給我同樣的畫面。

實際圖像應如下所示:

實際圖像

但是,我的代碼會打印此圖像(我使用RAW Pixels對其進行解碼):

只是一個綠色圖像,但頂部像素更亮

解碼參數: NV21 預置半平面像素平面

如果我取消注釋 YUV420 -> ARGB 轉換的代碼並為argb8888數組打印 Base64,我將得到這個圖像: 帶有灰色頂部像素的黑色圖像

預設:RGB32,寬度:640,高度:480 。此圖像的Base64

我用取自 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 剛剛剪切了我的 output,所以我看不到圖像)。 現在我將圖像寫入應用程序緩存並使用 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順序,而Android ARGB_8888有ARGB順序。 更簡單地說,在內聯 YUV2RGB 方法中,您需要更改此行:

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

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM