android - 使用 libyuv 缩放 YUV420 图像会产生奇怪的输出

标签 android tensorflow android-ndk tensorflow-lite libyuv

This 可能重复问题的主要部分摘自 here .我已经尝试了那里提供的任何解决方案,但它们对我不起作用。

背景

我正在 YUV_420_888 中拍摄图像从 ARCore 的 frame.acquireCameraImage() 方法返回的图像格式。由于我已将相机配置设置为 1920*1080 分辨率,我需要将其缩小到 224*224 以将其传递给我的 tensorflow-lite 实现。我通过使用 LibYuv 来做到这一点通过 Android NDK 库。

实现

准备图像帧

    //Figure out the source image dimensions
    int y_size = srcWidth * srcHeight;

    //Get dimensions of the desired output image
    int out_size = destWidth * destHeight;

    //Generate input frame
    i420_input_frame.width = srcWidth;
    i420_input_frame.height = srcHeight;
    i420_input_frame.data = (uint8_t*) yuvArray;
    i420_input_frame.y = i420_input_frame.data;
    i420_input_frame.u = i420_input_frame.y + y_size;
    i420_input_frame.v = i420_input_frame.u + (y_size / 4);

    //Generate output frame
    free(i420_output_frame.data);
    i420_output_frame.width = destWidth;
    i420_output_frame.height = destHeight;
    i420_output_frame.data = new unsigned char[out_size * 3 / 2];
    i420_output_frame.y = i420_output_frame.data;
    i420_output_frame.u = i420_output_frame.y + out_size;
    i420_output_frame.v = i420_output_frame.u + (out_size / 4);

我使用 Libyuv 的 I420Scale 方法缩放图像

libyuv::FilterMode mode = libyuv::FilterModeEnum::kFilterBox;
jint result = libyuv::I420Scale(i420_input_frame.y, i420_input_frame.width,
                                i420_input_frame.u, i420_input_frame.width / 2,
                                i420_input_frame.v, i420_input_frame.width / 2,
                                i420_input_frame.width, i420_input_frame.height,
                                i420_output_frame.y, i420_output_frame.width,
                                i420_output_frame.u, i420_output_frame.width / 2,
                                i420_output_frame.v, i420_output_frame.width / 2,
                                i420_output_frame.width, i420_output_frame.height,
                                mode);

返回给java

    //Create a new byte array to return to the caller in Java
    jbyteArray outputArray = env -> NewByteArray(out_size * 3 / 2);
    env -> SetByteArrayRegion(outputArray, 0, out_size, (jbyte*) i420_output_frame.y);
    env -> SetByteArrayRegion(outputArray, out_size, out_size / 4, (jbyte*) i420_output_frame.u);
    env -> SetByteArrayRegion(outputArray, out_size + (out_size / 4), out_size / 4, (jbyte*) i420_output_frame.v);

实际图像:enter image description here

缩放后的样子:

enter image description here

如果我从 i420_input_frame 创建一个没有缩放的图像会是什么样子:

enter image description here

由于缩放很长时间会弄乱颜色,tensorflow 无法正确识别对象。 (它在他们的示例应用程序中正确识别)我做错了什么把颜色弄乱了?

最佳答案

要么是我做错了什么(我无法修复),要么是 LibYuv 在处理来自 Android 的 YUV 图像时没有正确处理颜色。

引用 Libyuv 库上发布的官方错误:https://bugs.chromium.org/p/libyuv/issues/detail?id=815&can=1&q=&sort=-id

他们建议我先使用 Android420ToI420() 方法,然后应用我需要的任何转换。我最终首先使用了 Android420ToI420(),然后是缩放,然后转换为 RGB。最后,输出比上面发布的杯子图像稍微好一些,但仍然存在扭曲的颜色。我最终使用 OpenCV 缩小图像并将其转换为 RGBA 或 RGB 格式。

// The camera image received is in YUV YCbCr Format at preview dimensions
// so we will scale it down to 224x224 size using OpenCV
// Y plane (0) non-interleaved => stride == 1; U/V plane interleaved => stride == 2
// Refer : https://developer.android.com/reference/android/graphics/ImageFormat.html#YUV_420_888
val cameraPlaneY = cameraImage.planes[0].buffer
val cameraPlaneUV = cameraImage.planes[1].buffer

// Create a new Mat with OpenCV. One for each plane - Y and UV
val y_mat = Mat(cameraImage.height, cameraImage.width, CvType.CV_8UC1, cameraPlaneY)
val uv_mat = Mat(cameraImage.height / 2, cameraImage.width / 2, CvType.CV_8UC2, cameraPlaneUV)
var mat224 = Mat()
var cvFrameRGBA = Mat()

// Retrieve an RGBA frame from the produced YUV
Imgproc.cvtColorTwoPlane(y_mat, uv_mat, cvFrameRGBA, Imgproc.COLOR_YUV2BGRA_NV21)


//Then use this frame to retrieve all RGB channel data
//Iterate over all pixels and retrieve information of RGB channels
  for(rows in 1 until cvFrameRGBA.rows())
      for(cols in 1 until cvFrameRGBA.cols()) {
          val imageData = cvFrameRGBA.get(rows, cols)
          // Type of Mat is 24
          // Channels is 4
          // Depth is 0
          rgbBytes.put(imageData[0].toByte())
          rgbBytes.put(imageData[1].toByte())
          rgbBytes.put(imageData[2].toByte())
      }

关于android - 使用 libyuv 缩放 YUV420 图像会产生奇怪的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52072540/

相关文章:

android - SurfaceView 和 ANativeWindow

android - 从反射(reflect)在先前创建的 Activity 中的 Activity 更改语言

tensorflow - 如何在tensorflow中正确使用tf.layers.batch_normalization()?

machine-learning - 使用基于三元组的训练时 Tensorflow CNN 网络中的 Dropout

java - Android NDK/一般 JNI 问题 : Converting object/jobject to c++ user defiend type

java - Android:线程以未捕获的异常退出

android - Intent Intent.ACTION_CALL :android. permission.CALL_PHONE 需要缺少权限

tensorflow - 节点号 X (RESHAPE) 准备失败。使用 tflite v2.2 调整张量大小

android - 有没有办法在 C++ 中以编程方式执行 adb 命令?这部分 C++ 代码是使用 android studio 中的 ndk build 构建的。那里

android - 将现有的 Android NDK 项目导入 VS2015 RC 和 RTM