Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/214.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 使用libyuv缩放YUV420图像会产生奇怪的输出_Android_Tensorflow_Android Ndk_Tensorflow Lite_Libyuv - Fatal编程技术网

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

Android 使用libyuv缩放YUV420图像会产生奇怪的输出,android,tensorflow,android-ndk,tensorflow-lite,libyuv,Android,Tensorflow,Android Ndk,Tensorflow Lite,Libyuv,问题可能重复,主要部分从中选取。我尝试过那里提供的任何解决方案,它们对我都不起作用 背景 我正在以ARCore的frame.acquireCameraImage()方法返回的图像格式捕获图像。由于我已将相机配置设置为1920*1080分辨率,因此需要将其缩小到224*224,以将其传递给tensorflow lite实现。我通过Android NDK使用库来实现这一点 实施 准备图像帧 //Figure out the source image dimensions int y_

问题可能重复,主要部分从中选取。我尝试过那里提供的任何解决方案,它们对我都不起作用

背景

我正在以ARCore的
frame.acquireCameraImage()方法返回的图像格式捕获图像。由于我已将相机配置设置为1920*1080分辨率,因此需要将其缩小到224*224,以将其传递给tensorflow lite实现。我通过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);
实际图像:

缩放后的外观:

如果我在不缩放的情况下从
i420\u input\u帧创建图像,会是什么样子:


由于缩放会将颜色弄得一团糟,tensorflow无法正确识别对象。(在他们的示例应用程序中可以正确识别)我做错了什么,把颜色弄得一团糟了?

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

参考Libyuv库上发布的官方错误:

他们建议我先使用一个方法
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())
      }

导致颜色问题的原因是您使用的是不同的YUV格式。摄影机框架使用的YUV格式是YUV NV21此格式(NV21)是Android摄像头预览上的标准图片格式。YUV 4:2:0平面图像,具有8位Y采样,然后是具有8位2x2次采样色度采样的交错V/U平面。

如果颜色颠倒,则表示:

  • 您正在使用YUV NV12(平面U为V,V为U)
  • 你的一个颜色平面正在做一些奇怪的事情
要正确使用
libyuv
,我建议您使用
transformI420
方法将相机输出转换为YUV I420,并按参数发送格式:

return libyuv::ConvertToI420(src, srcSize, //src data
                             dstY, dstWidth, //dst planes
                             dstU, dstWidth / 2,
                             dstV, dstWidth / 2,
                             cropLeft, cropTop, //crop start
                             srcWidth, srcHeight, //src dimensions
                             cropRight - cropLeft, cropBottom - cropTop, //dst dimensions
                             rotationMode,
                             libyuv::FOURCC_NV21); //libyuv::FOURCC_NV12
完成此转换后,您将能够使用所有的
I420scale
I420rotate
。。。等等您的缩放方法应如下所示:

JNIEXPORT jint JNICALL
Java_com_aa_project_images_yuv_myJNIcl_scaleI420(JNIEnv *env, jclass type,
                                                 jobject srcBufferY,
                                                 jobject srcBufferU,
                                                 jobject srcBufferV,
                                                 jint srcWidth, jint srcHeight,
                                                 jobject dstBufferY,
                                                 jobject dstBufferU,
                                                 jobject dstBufferV,
                                                 jint dstWidth, jint dstHeight,
                                                 jint filterMode) {

    const uint8_t *srcY = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferY));
    const uint8_t *srcU = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferU));
    const uint8_t *srcV = static_cast<uint8_t *>(env->GetDirectBufferAddress(srcBufferV));
    uint8_t *dstY = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferY));
    uint8_t *dstU = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferU));
    uint8_t *dstV = static_cast<uint8_t *>(env->GetDirectBufferAddress(dstBufferV));

    return libyuv::I420Scale(srcY, srcWidth,
                             srcU, srcWidth / 2,
                             srcV, srcWidth / 2,
                             srcWidth, srcHeight,
                             dstY, dstWidth,
                             dstU, dstWidth / 2,
                             dstV, dstWidth / 2,
                             dstWidth, dstHeight,
                             static_cast<libyuv::FilterMode>(filterMode));
}
JNIEXPORT jint JNICALL
Java_com_aa_project_images_yuv_myJNIcl_scaleI420(JNIEnv*env,jclass类型,
乔比,
jobject srcBufferU,
jobject srcBufferV,
jint src宽度,jint src高度,
乔比,
jobject dstBufferU,
jobject DSTV,
吉特宽,吉特高,
jint filterMode){
const uint8_t*srcY=static_cast(env->GetDirectBufferAddress(srcBufferY));
const uint8_t*srcU=static_cast(env->GetDirectBufferAddress(srcBufferU));
const uint8_t*srcV=static_cast(env->GetDirectBufferAddress(srcBufferV));
uint8_t*dstY=static_cast(env->GetDirectBufferAddress(dstbufferty));
uint8_t*dstU=static_cast(env->GetDirectBufferAddress(dstBufferU));
uint8_t*dstV=static_cast(env->GetDirectBufferAddress(dstBufferV));
返回libyuv::I420Scale(srcY、srcWidth、,
srcU,srcWidth/2,
srcV,srcWidth/2,
srcWidth,srcHeight,
dstY,dstWidth,
dstU,dstU宽度/2,
dstV,dstWidth/2,
宽度,高度,
静态_cast(filterMode));
}
如果要在所有过程之后将此图像转换为JPEG。您可以使用
I420toNV21
方法,然后使用android本机从YUV到JPEG的转换。您还可以使用它作为这种情况下的补充库