Android 使用libyuv库缩放YUV图像时出现的问题

Android 使用libyuv库缩放YUV图像时出现的问题,android,c++,image,yuv,libyuv,Android,C++,Image,Yuv,Libyuv,我正在开发一个基于camera API 2的camera应用程序,我在使用中发现了几个问题。 我想转换从ImageReader检索的YUV_420_888图像,但在可再处理曲面中缩放时遇到一些问题 本质上:图像显示为绿色色调,而不是相应的色调(我正在导出.yuv文件并使用检查它们) 您可以在此处看到一个输入示例: 以及执行缩放后得到的结果: 我认为我的步幅有问题,或者提供了无效的YUV格式(可能我必须将图像转换为另一种格式?)。但是,我不知道错误在哪里,因为我不知道如何将绿色与缩放算法关联起来

我正在开发一个基于
camera API 2
的camera应用程序,我在使用中发现了几个问题。 我想转换从ImageReader检索的
YUV_420_888
图像,但在可再处理曲面中缩放时遇到一些问题

本质上:图像显示为绿色色调,而不是相应的色调(我正在导出.yuv文件并使用检查它们)

您可以在此处看到一个输入示例:

以及执行缩放后得到的结果:

我认为我的步幅有问题,或者提供了无效的YUV格式(可能我必须将图像转换为另一种格式?)。但是,我不知道错误在哪里,因为我不知道如何将绿色与缩放算法关联起来

这是我正在使用的转换代码,您可以忽略返回NULL,因为还有与问题无关的进一步处理

#include <jni.h>
#include <stdint.h>
#include <android/log.h>
#include <inc/libyuv/scale.h>
#include <inc/libyuv.h>
#include <stdio.h>


#define  LOG_TAG    "libyuv-jni"

#define unused(x) UNUSED_ ## x __attribute__((__unused__))
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS_)

struct YuvFrame {
    int width;
    int height;
    uint8_t *data;
    uint8_t *y;
    uint8_t *u;
    uint8_t *v;
};

static struct YuvFrame i420_input_frame;
static struct YuvFrame i420_output_frame;

extern "C" {

JNIEXPORT jbyteArray JNICALL
Java_com_android_camera3_camera_hardware_session_output_photo_yuv_YuvJniInterface_scale420YuvByteArray(
        JNIEnv *env, jclass /*clazz*/, jbyteArray yuvByteArray_, jint src_width, jint src_height,
        jint out_width, jint out_height) {

    jbyte *yuvByteArray = env->GetByteArrayElements(yuvByteArray_, NULL);

    //Get input and output length
    int input_size = env->GetArrayLength(yuvByteArray_);
    int out_size = out_height * out_width;

    //Generate input frame
    i420_input_frame.width = src_width;
    i420_input_frame.height = src_height;
    i420_input_frame.data = (uint8_t *) yuvByteArray;
    i420_input_frame.y = i420_input_frame.data;
    i420_input_frame.u = i420_input_frame.y + input_size;
    i420_input_frame.v = i420_input_frame.u + input_size / 4;

    //Generate output frame
    free(i420_output_frame.data);
    i420_output_frame.width = out_width;
    i420_output_frame.height = out_height;
    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::FilterMode mode = libyuv::FilterModeEnum::kFilterBilinear;

    int result = 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);
    LOGD("Image result %d", result);
    env->ReleaseByteArrayElements(yuvByteArray_, yuvByteArray, 0);
    return NULL;
}
#包括
#包括
#包括
#包括
#包括
#包括
#定义日志标签“libyuv jni”
#定义未使用的(x)未使用的属性((u未使用的)
#定义LOGD(…)\uuuuuAndroid\uLog\uPrint(android\uLog\uDebug、log\uTag、\uuuu VA\uArgs\uuuuu)
#定义日志(…)\uuuuuAndroid\uLog\uPrint(android\uLog\uError,log\uTag,\uuuVa\uArgs)
结构YuvFrame{
整数宽度;
内部高度;
uint8_t*数据;
uint8_t*y;
uint8_t*u;
uint8_t*v;
};
静态结构YuvFrame i420_输入_帧;
静态结构YuvFrame i420_输出_frame;
外部“C”{
JNIEXPORT jbyteArray JNICALL
Java_com_android_camera3_camera3_camera_硬件_会话_输出_照片_yuv_YuvJniInterface_scale420YuvByteArray(
JNIEnv*env,jclass/*clazz*/,jbyterarray yuvbyterarray,jint src_宽度,jint src_高度,
进货(宽度,进货(高度){
jbyte*yuvByteArray=env->getbytearrayements(yuvByteArray,NULL);
//获取输入和输出长度
int input_size=env->GetArrayLength(yuvByteArray_);
int out_size=out_高度*out_宽度;
//生成输入帧
i420_input_frame.width=src_width;
i420_input_frame.height=src_height;
i420_input_frame.data=(uint8_t*)yuvByteArray;
i420_input_frame.y=i420_input_frame.data;
i420_input_frame.u=i420_input_frame.y+input_size;
i420_输入_帧。v=i420_输入_帧。u+输入_大小/4;
//生成输出帧
自由(i420_输出_帧数据);
i420_输出_frame.width=输出_宽度;
i420_输出_帧高度=输出_高度;
i420_output_frame.data=新的无符号字符[out_size*3/2];
i420_output_frame.y=i420_output_frame.data;
i420_output_frame.u=i420_output_frame.y+out_size;
i420_输出_帧。v=i420_输出_帧。u+输出_大小/4;
libyuv::FilterMode mode=libyuv::FilterModeEnum::kfilterbilear;
int result=I420Scale(i420_input_frame.y,i420_input_frame.width,
i420_输入_frame.u,i420_输入_frame.width/2,
i420_输入_frame.v,i420_输入_frame.width/2,
i420_输入_帧宽度,i420_输入_帧高度,
i420_输出_frame.y,i420_输出_frame.width,
i420_输出_frame.u,i420_输出_frame.width/2,
i420_输出_frame.v,i420_输出_frame.width/2,
i420_输出_帧宽度,i420_输出_帧高度,
模式);
LOGD(“图像结果%d”,结果);
环境->释放字节数组元素(yuvByteArray,yuvByteArray,0);
返回NULL;
}

您可以尝试使用
y\u大小的代码,而不是数组的完整大小

    ...
    //Get input and output length
    int input_size = env->GetArrayLength(yuvByteArray_);
    int y_size = src_width * src_height;
    int out_size = out_height * out_width;

    //Generate input frame
    i420_input_frame.width = src_width;
    i420_input_frame.height = src_height;
    i420_input_frame.data = (uint8_t *) yuvByteArray;
    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 = out_width;
    i420_output_frame.height = out_height;
    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;
    ...

您的代码可能就是基于此,根据此代码,您必须使用
y\u大小

gmetax几乎是正确的

您使用的是整个数组的大小,其中应使用Y组件的大小,即
src\u width*src\u height

gmetax的答案是错误的,因为他在定义输出帧时用
y\u size
代替了
out\u size
。我认为正确的代码片段如下所示:

//Get input and output length
int input_size = env->GetArrayLength(yuvByteArray_);
int y_size = src_width * src_height;
int out_size = out_height * out_width;

//Generate input frame
i420_input_frame.width = src_width;
i420_input_frame.height = src_height;
i420_input_frame.data = (uint8_t *) yuvByteArray;
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 = out_width;
i420_output_frame.height = out_height;
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;

框架的输入大小有问题:

应该是:

int input_array_size = env->GetArrayLength(yuvByteArray_);
int input_size = input_array_size * 2 / 3; //This is the frame size
例如,如果您有一个6x4的帧

香奈儿y尺寸:6*4=24

 1 2 3 4 5 6
 _ _ _ _ _ _
|_|_|_|_|_|_| 1
|_|_|_|_|_|_| 2
|_|_|_|_|_|_| 3
|_|_|_|_|_|_| 4
香奈儿u尺寸:3*2=6

  1   2   3 
 _ _ _ _ _ _
|   |   |   | 
|_ _|_ _|_ _| 1
|   |   |   | 
|_ _|_ _|_ _| 2
  1   2   3 
 _ _ _ _ _ _
|   |   |   | 
|_ _|_ _|_ _| 1
|   |   |   | 
|_ _|_ _|_ _| 2
香奈儿v尺寸:3*2=6

  1   2   3 
 _ _ _ _ _ _
|   |   |   | 
|_ _|_ _|_ _| 1
|   |   |   | 
|_ _|_ _|_ _| 2
  1   2   3 
 _ _ _ _ _ _
|   |   |   | 
|_ _|_ _|_ _| 1
|   |   |   | 
|_ _|_ _|_ _| 2
数组大小=6*4+3*2+3*2=36

但实际帧大小=通道ySize=36*2/3=24

您正试图缩放YUV422图像,就好像它是YUV420一样,难怪颜色都乱七八糟。首先,您需要弄清楚YUV输入缓冲区的确切格式。从它的文档来看,它可能代表平面和交错格式(如果像素步长不是1)。从结果看,源是平面的,Y平面的处理是正常的,但错误在于处理U和V平面。要正确缩放,请执行以下操作:

  • 你必须弄清楚你的
    U
    V
    平面是交错的还是交错的 平面的。很可能它们也是平面的
  • 分别使用
    ScalePlane
    从libyuv缩放
    U
    V
    。也许 如果您进入
    I420Scale
    ,它会为单个用户调用
    ScalePlane
    平面。执行相同的操作,但为
    U
    V
    使用正确的线条尺寸 平面(每个平面都比I420Scale预期的大两倍)
一些提示如何确定您的图像是平面的还是交错的
U
V
:尝试跳过图像缩放并保存,以确保获得正确的结果(与源图像相同)。然后试着调零
U
帧或
V
帧,看看你得到了什么。如果
U
V
是平面的,并且你将
U
平面设为零,你应该可以看到整个图片在改变颜色。如果它们是交错的,你会看到一半图片在改变颜色