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
平面设为零,你应该可以看到整个图片在改变颜色。如果它们是交错的,你会看到一半图片在改变颜色