项目场景:
在视频处理中,经常会遇到 YUV420 格式图像数据的宽度或高度不是8的倍数的情况。这种情况可能会导致在编码为 JPEG 图像时出现问题,例如出现编码出来的图片错乱或其他异常。
原因分析:
YUV420 图像数据由亮度(Y)和两个色度(UV)分量组成,其中 U 和 V 分量通常按照下采样方式存储。当图像的宽度或高度不是8的倍数时,可能会影响色度分量的对齐和处理,从而导致编码后的图像出现异常。
解决方案:
为了确保 YUV420 图像数据的宽度和高度能够被8整除,可以采用两种主要的方法:填充(Padding)和裁剪(Cropping)。
方式一:填充方式
填充方式通过在图像边缘添加额外像素来增加图像的宽度和高度,使其能够被8整除:
void adjustYUV420SizeToMultipleOf8(unsigned char* &yuvData, int& width, int& height) {
int new_width = ((width + 7) / 8) * 8;
int new_height = ((height + 7) / 8) * 8;
if (new_width != width || new_height != height) {
// Calculate sizes
int old_y_size = width * height;
int old_uv_size = width * height / 4; // YUV420: U and V size is 1/4 of Y size
int new_y_size = new_width * new_height;
int new_uv_size = new_width * new_height / 4; // YUV420: U and V size is 1/4 of Y size
// Allocate new buffers for Y, U, V
unsigned char* newY = new unsigned char[new_y_size];
unsigned char* newU = new unsigned char[new_uv_size];
unsigned char* newV = new unsigned char[new_uv_size];
// Initialize new buffers (optional: memset or copy existing data)
memset(newY, 0, new_y_size);
memset(newU, 0, new_uv_size);
memset(newV, 0, new_uv_size);
// Copy existing Y, U, V data to new buffers
for (int y = 0; y < height; ++y) {
memcpy(newY + y * new_width, yuvData + y * width, width);
}
for (int y = 0; y < height / 2; ++y) {
// U component (even rows)
memcpy(newU + y * (new_width / 2), yuvData + old_y_size + y * (width / 2), width / 2);
// V component (odd rows)
memcpy(newV + y * (new_width / 2), yuvData + old_y_size + old_uv_size + y * (width / 2), width / 2);
}
// Free old YUV data
delete[] yuvData;
// Update pointers and dimensions
yuvData = new unsigned char[new_y_size + 2 * new_uv_size]; // Allocate new space for YUV420 data
memcpy(yuvData, newY, new_y_size);
memcpy(yuvData + new_y_size, newU, new_uv_size);
memcpy(yuvData + new_y_size + new_uv_size, newV, new_uv_size);
// Update width and height
width = new_width;
height = new_height;
// Free new buffers
delete[] newY;
delete[] newU;
delete[] newV;
}
}
方式二:裁剪方式
裁剪方式通过删除图像边缘的像素来减少图像的宽度和高度,使其能够被8整除:
void cropYUV420ToMultipleOf8(unsigned char* &yuvData, int& width, int& height) {
int new_width = (width / 8) * 8;
int new_height = (height / 8) * 8;
if (new_width != width || new_height != height) {
// Calculate sizes
int old_y_size = width * height;
int old_uv_size = width * height / 4; // YUV420: U and V size is 1/4 of Y size
int new_y_size = new_width * new_height;
int new_uv_size = new_width * new_height / 4; // YUV420: U and V size is 1/4 of Y size
// Allocate new buffers for Y, U, V
unsigned char* newY = new unsigned char[new_y_size];
unsigned char* newU = new unsigned char[new_uv_size];
unsigned char* newV = new unsigned char[new_uv_size];
// Copy cropped Y, U, V data
for (int y = 0; y < new_height; ++y) {
memcpy(newY + y * new_width, yuvData + y * width, new_width);
}
for (int y = 0; y < new_height / 2; ++y) {
// U component (even rows)
memcpy(newU + y * (new_width / 2), yuvData + old_y_size + y * (width / 2), new_width / 2);
// V component (odd rows)
memcpy(newV + y * (new_width / 2), yuvData + old_y_size + old_uv_size + y * (width / 2), new_width / 2);
}
// Free old YUV data
delete[] yuvData;
// Update pointers and dimensions
yuvData = new unsigned char[new_y_size + 2 * new_uv_size]; // Allocate new space for YUV420 data
memcpy(yuvData, newY, new_y_size);
memcpy(yuvData + new_y_size, newU, new_uv_size);
memcpy(yuvData + new_y_size + new_uv_size, newV, new_uv_size);
// Update width and height
width = new_width;
height = new_height;
// Free new buffers
delete[] newY;
delete[] newU;
delete[] newV;
}
}