NV21格式图像旋转 c++,平移、缩放实现代码

本文详细介绍了NV21图像格式的基本知识,并通过实例演示了如何实现NV21格式图像的逆时针旋转90°。文章包括了图像旋转前后的数据对比及旋转原理的解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NV21格式图像旋转

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

基础知识

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GSO2CMrK-1649641050030)(//img-blog.youkuaiyun.com/20180321201019127?watermark/2/text/Ly9ibG9nLmNzZG4ubmV0L2Nnd2FuZ18xNTgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)]

上图为一个 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);
    // rotate Y
    for (int i = width - 1; i >= 0; --i)
    {
        for (int j = 0; j < height; ++j)
        {
            *(dataOut + k) = *(dataIn + j * width + i);
            ++k;
        }
    }

    // rotate 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;
}
// show image
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);
} 

下面是测试结果:

[外链图片转存中...(img-i90eW5tQ-1649641050032)] 原始数据 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGJ6DdST-1649641050032)(//img-blog.youkuaiyun.com/20180321203852456?watermark/2/text/Ly9ibG9nLmNzZG4ubmV0L2Nnd2FuZ18xNTgw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)] 旋转后的数据

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值