图像实战 - 裁剪RGB、YUV图像
2019-04-28 本文已影响0人
省油的灯_wsy
在开发相机程序时,我们可能并不需要显示全部的预览数据,而是显示部分的数据,因此就会有图像的裁剪需求。本文介绍了一些常见的YUV、RGB数据的裁剪方法。
1. 裁剪NV21
或NV12
需要注意Y
、U
、V
三者的共用关系,Y
的裁剪比较直接;但是对于U
和V
而言,U
和V
的大小各只有Y
的大小的1/4(U
的宽高和V
的宽高都是Y
的宽高的一半),因此在横纵向都要进行跳行,在横向由于U
和V
是交叉排序,width / 2 的V
和 width / 2 的U
加起来刚好就是Y
的大小,可以直接按偏移量裁剪。
void cropYuv420sp(char *yuv420sp, char *cropYuv420sp, int width, int height, int left, int top,
int right, int bottom) {
int halfWidth = width / 2;
int cropImageWidth = right - left;
int cropImageHeight = bottom - top;
//复制Y
int originalYLineStart = top * width;
int targetYIndex = 0;
//复制UV
int originalUVLineStart = width * height + top * halfWidth;
int targetUVIndex = cropImageWidth * cropImageHeight;
for (int i = top; i < bottom; i++) {
memcpy(cropYuv420sp + targetYIndex, yuv420sp + originalYLineStart + left, cropImageWidth);
originalYLineStart += width;
targetYIndex += cropImageWidth;
if ((i & 1) == 0) {
memcpy(cropYuv420sp + targetUVIndex, yuv420sp + originalUVLineStart + left,
cropImageWidth);
originalUVLineStart += width;
targetUVIndex += cropImageWidth;
}
}
}
2. 裁剪YV12
或I420
和裁剪NV21
类似,Y的裁剪比较直接;但是对于U
和V
而言,U
和V
的大小各只有Y
的大小的1/4,且是各自连续排序(U
的宽高和V
的宽高都是Y
的宽高的一半),因此在横纵向都要进行跳行。
void cropYuv420p(char *yuv420p, char *cropYuv420p, int width, int height, int left, int top,
int right, int bottom) {
int halfWidth = width / 2;
int cropImageWidth = right - left;
int cropImageHeight = bottom - top;
//复制Y
int originalYLineStart = top * width;
int targetYIndex = 0;
//复制UV
int originalULineStart = width * height + top * halfWidth / 2;
int originalVLineStart = width * height + width * height / 4 + top * halfWidth / 2;
int targetUIndex = cropImageWidth * cropImageHeight;
int targetVIndex = cropImageWidth * cropImageHeight * 5 / 4;
int halfImageWidth = halfWidth;
int halfLeft = left / 2;
int halfCropImageWidth = cropImageWidth / 2;
for (int i = top; i < bottom; i++) {
memcpy(cropYuv420p + targetYIndex, yuv420p + originalYLineStart + left, cropImageWidth);
originalYLineStart += width;
targetYIndex += cropImageWidth;
if ((i & 1) == 0) {
memcpy(cropYuv420p + targetUIndex, yuv420p + originalULineStart + halfLeft,
halfCropImageWidth);
memcpy(cropYuv420p + targetVIndex, yuv420p + originalVLineStart + halfLeft,
halfCropImageWidth);
originalULineStart += halfImageWidth;
originalVLineStart += halfImageWidth;
targetUIndex += halfCropImageWidth;
targetVIndex += halfCropImageWidth;
}
}
}
3. 裁剪YUYV
和NV21
、I420
之类的数据不同,YUYV
的排列方式是YUYV YUYV....
,每行的连续4个byte会有共用关系,这4个byte相当于2个像素),每行的数据宽度是 width * 2。
注意:该函数对入参有限制,需要裁剪的边界都需要是偶数。
void cropYuyv(char *yuyv, char *cropYuyv, int width, int height, int left, int top, int right,
int bottom) {
int cropImageWidth = right - left;
int cropImageHeight = bottom - top;
int lineDataSize = width * 2;
int cropLineDataSize = cropImageWidth * 2;
int originalLineStart = top * width * 2;
int targetIndex = 0;
int lineOffsetDataSize = left * 2;
for (int i = top; i < bottom; i++) {
memcpy(cropYuyv + targetIndex, yuyv + originalLineStart + lineOffsetDataSize,
cropLineDataSize);
originalLineStart += lineDataSize;
targetIndex += cropLineDataSize;
}
}
4. 裁剪BGR24
无论是BGR24
、RGB24
,在裁剪时,都是以3个byte为一个整体进行裁剪,因此以下代码兼容24bpp的RGB数据的裁剪。
void cropRgb24(char *rgb24, char *cropRgb24, int width, int height, int left, int top, int right,
int bottom) {
int cropImageWidth = right - left;
int cropImageHeight = bottom - top;
int lineDataSize = width * 3;
int cropLineDataSize = cropImageWidth * 3;
int originalLineStart = top * lineDataSize;
int targetIndex = 0;
int lineOffsetDataSize = left * 3;
for (int i = top; i < bottom; i++) {
memcpy(cropRgb24 + targetIndex, rgb24 + originalLineStart + lineOffsetDataSize,
cropLineDataSize);
originalLineStart += lineDataSize;
targetIndex += cropLineDataSize;
}
}