NV21格式图像旋转(原理--代码实现),平移、缩放实现代码

版权声明:本文为博主原创文章,转载请注明出处! https://blog.csdn.net/cgwang_1580/article/details/79645645

NV21格式图像旋转

刚刚接触Android开发,开始学习一些图像像素格式,需要完成NV21的图像进行旋转,这里我们将从原理开始分析,分享一下我对NV21图像旋转的理解与实现。

基础知识
在开始旋转NV21图像时,需要对图像的RGB格式与YUV格式有一定的了解,没有概念的同学可以看看这儿:

常用视频像素格式NV12、NV2、I420、、Yv12、YUYV

image pixel format

NV21属于YUV420,其采样方式如下:

YUV420采样方式

上图为一个4*4的图像,即宽度width=4,高度height=4;
于是,其图像数据特点是四个Y共用一个组UV,且共用方式是Y1、Y2、Y5、Y6公用(UV)1,而Y3、Y4、Y7、Y8共用(UV)2,这是由它的采样方式得到的<共用方式很重要>
需要记住的是:在内存中,YUV的存储一般为,Y是连续存储,而UV打包一起存储,即在内存中其存储方式如下:当用一个一维数组data来保存该数据时,其在内存中的分布则是:

data= {Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8,Y9,Y10,Y11,Y12,Y13,Y14,Y15,Y16,V1,U1,V2,U2,V3,U3,V4,U4}

这么看好像不是很好懂,咱么换种方式:

{
        Y1,  Y2,  Y3,  Y4,
        Y5,  Y6,  Y7,  Y8,
        Y9,  Y10, Y11, Y12,
        Y13, Y14, Y15, Y16,
        V1,  U1,  V2,  U2,
        V3,  U3,  V4,  U4
}

同样还是一维数组,只是这次根据图像的宽度和高度把一维数组的堆放方式换一下,可以看到Y1~Y16即为16个像素的Y分量的值(记住:4*4的图像),而Y1、Y2、Y3、Y4共用V1、U1。

逆时针旋转90°
为了对上午进行逆时针旋转,首先将旋转后图像数据在内存中的分布画出来,一开始我画出的图像如下:

{
        Y4,  Y8,  Y12,  Y16,  U2,  U4
        Y3,  Y7,  Y11,  Y15,  V2,  V4
        Y2,  Y6,  Y10,  Y14,  U1,  U3
        Y1,  Y5,  Y9,   Y13,  V1,  V3
}
                <错误方式...>

当时我怎么也没有想明白,这样旋转后,即使Y1、Y2、Y5、Y6依然可以共用V1、U1,但它在内存中还如何存储?因为这个数据在内存中使用一个一维数组连续存储,这样的话岂不是Y分量的存储断开了?…
之后,从采样方式出发想了想之后,或许应该是把Y分量和(UV)分量分开旋转,如下:

{ 
        Y4,  Y8,  Y12,  Y16,  
        Y3,  Y7,  Y11,  Y15,  
        Y2,  Y6,  Y10,  Y14,  
        Y1,  Y5,  Y9,   Y13,  
        V2,  U2,  V4,   U4,
        V1,  U1,  V3,   U3
}
< V 和对应的 U 应该为一个整体共同旋转>

于是,Y4、Y8、Y3、Y7仍然共用V2、U2;Y2、Y6、Y1、Y5仍然共用V1、U1;

下面给出一段简单的测试代码:
定义类

class YUVImage{
    public:
        YUVImage();
        int rotateTest(const int* dataIn, int width, int height, int degree, int* dataOut);
    int showIamge(const int* data, int width, int height);
};

成员方法实现

int YUVImage::rotateTest(const int* dataIn, int width, int height, int degree, int* dataOut){

    int k = 0;
    int height2 = (int)(1.5 * height);
    //旋转Y部分
    for (int i = width - 1; i >= 0; --i){
        for (int j = 0; j< height; ++j){
            *(dataOut + k) = *(dataIn + j*width + i);
            ++k;
        }
    }

    //旋转UV部分
    for (int i = width - 1; i>0; --i){
        for (int j = height; j< height2; ++j){
            *(dataOut + k) = *(dataIn + j*width + i - 1);
            ++k;
            *(dataOut + k) = *(dataIn + j*width + i);
            ++k;
        }
        --i;
    }
    return 0;
}

//显示图像方法
int YUVImage::showIamge(const int* data, int width, int height){

    int height2 = int(1.5 * height);
    for (int i = 0; i< height2; ++i){
        for (int j = 0; j<width; ++j){
            cout << *(data + i * width + j) << " ";
        }
        cout << endl;
    }
    cout << endl;

    return 0;

测试

int _tmain(int argc, _TCHAR* argv[])
{
    int old[48] = { 11, 12, 13, 14, 15, 16, 17, 18, 21, 22, 23, 24, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 41, 42, 43, 44, 45, 46, 47, 48, 81, 82, 83, 84, 85, 86, 87, 88, 91, 92, 93, 94, 95, 96, 97, 98 };

    //NV21图像宽度width=8,高度height=4
    int* pOld = old;
    int width = 8;
    int height = 4;
    int height2 = (int)(1.5*height);

    //NV21 image rotate test
    int temp1[48] = { 0 };
    int* pRot = temp1;
    YUVImage nv21;
    nv21.rotateTest(pOld, width, height, 90, pRot);

    cout << "Original NV21 data:" << endl;
    nv21.showIamge(pOld, width, height);

    cout << "Rotate NV21 data:" << endl;
    nv21.showIamge(pRot, width, height);
} 

下面是测试结果:


NV21 data
原始数据
rotate data
旋转后的数据

之后,做了一些NV21图像平移,缩放,画矩形的操作,并输出执行时间消耗,可以在这里下载:
https://download.csdn.net/download/cgwang_1580/10392863

最后,给出几个比较有用的工具,用于查看YUV图像:
https://download.csdn.net/download/ming825569719/10016190
http://www.sunrayimage.com/(YUV tools 没法查看NV21数据)

猜你喜欢

转载自blog.csdn.net/cgwang_1580/article/details/79645645
今日推荐