图像实战 - 裁剪RGB、YUV图像

在开发相机程序时,我们可能并不需要显示全部的预览数据,而是显示部分的数据,因此就会有图像的裁剪需求。本文介绍了一些常见的YUV、RGB数据的裁剪方法。

1. 裁剪NV21NV12

需要注意YUV三者的共用关系,Y的裁剪比较直接;但是对于UV而言,UV的大小各只有Y的大小的1/4(U的宽高和V的宽高都是Y的宽高的一半),因此在横纵向都要进行跳行,在横向由于UV是交叉排序,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. 裁剪YV12I420

和裁剪NV21类似,Y的裁剪比较直接;但是对于UV而言,UV的大小各只有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

NV21I420之类的数据不同,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

无论是BGR24RGB24,在裁剪时,都是以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;
    }
}

你可能感兴趣的:(图像实战 - 裁剪RGB、YUV图像)