对I420格式的YUV图片实现填充padding效果
使用libyuv对I420的yuv图像进行处理的过程中,需要自己实现图像填充,通过在边界填充固定的颜色值形成一个正方形图像。类似于OpenCV中的copyMakeBorder函数,可以达到对图像扩充边界的效果。
- I420的存储格式
属于YUV Planar格式的一种,先连续存储所有像素点的 Y 分量,紧接着存储所有像素点的 U 分量,再是 V 分量。相对于其它YUV存储格式是比较简单的,因为三个分量是按顺序连续存储的,所以libyuv对于图像数据的裁剪、缩放、旋转等操作很多都是基于I420格式的处理。
Y Y Y Y
Y Y Y Y
U U
V V
对于一组I420数据,Y的size为width * height,U和V的size都是width * height * 1 / 4 ,总size为width * height * 3 / 2。所以U分量的起始位置是width * height ,V分量的起始位置是width * height * 5 / 4。
- 如何实现边界填充?
对于一个320 * 240分辨率大小的图片,需要对底部80像素进行颜色填充,处理后的图片分辨率为320 * 320 。
我们这里填充YUV色值(0,128,128)即黑色。代码如下:
先填充Y分量
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (i < topBorder || i >= bottomBorder || j < leftBorder || j >= rightBorder) {
dst_i420_data[i * w + j] = Y; //给超出边界的Y分量赋值0
} else {
if (row < src_i420_y_size) {
//范围内的Y分量沿用原I420的Y分量
dst_i420_data[i * w + j] = src_i420_data[row++];
}
}
}
}
//再分别填充U、V分量
int row_u = src_i420_y_size; //U分量的起始位置
int row_v = src_i420_y_size * 5 / 4; //V分量的起始位置
int row_v_fixed = row_v;
int ySize = w * h;
int uSize = w * h * 5 / 4;
int uvW = w >> 1; //U、V分量的宽
int uvH = h >> 1; //U、V分量的高
//避免在循环内重复计算
leftBorder = leftBorder >> 1;
rightBorder = rightBorder >> 1;
topBorder = topBorder >> 1;
bottomBorder = bottomBorder >> 1;
for (int i = 0; i < uvH; i ++) {
for (int j = 0 ; j < uvW; j ++) {
if (j < leftBorder || j >= rightBorder || i < topBorder || i >= bottomBorder) {
//给超出边界的U、V分量分别赋值128
dst_i420_data[ySize + i * uvW + j] = U;
dst_i420_data[uSize + i * uvW + j] = V;
} else {
if (row_u < row_v_fixed) {
//范围内的U、V分量沿用原I420的U、V分量
dst_i420_data[ySize + i * uvW + j] = src_i420_data[row_u++];
dst_i420_data[uSize + i * uvW + j] = src_i420_data[row_v++];
}
}
}
}
- 测试效果
内部测试速度还是蛮快的,能在1ms内处理完毕。