前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >摄像头图像处理YUV转RGB效率分析

摄像头图像处理YUV转RGB效率分析

作者头像
bigmagic
发布2020-03-17 14:35:24
1.6K0
发布2020-03-17 14:35:24
举报
文章被收录于专栏:嵌入式iot嵌入式iot

1.文章简述2. YUV转RGB的代码优化问题2.1 浮点转换2.2 浮点转整形2.3 浮点运算和整数运算在PC上模拟的效果3. x1000上进行对比测试3.1 使用软浮点测试一帧图像转换时间3.2 开启FPU后转换图像3.3 开启FPU进行测试3.3.1 基本思路3.3.2 程序设计4. 总结

1.文章简述

摄像输出的图像一般都是YUV格式的图像,本文主要从摄像头输出的YUV格式图像的角度出发,对图像格式的转换进行设计。同时对代码的优化进行总结与整理。下面来详细讲述这些问题。

2. YUV转RGB的代码优化问题

从原理上来说,对于一个YUV转RGB的代码,可以从浮点和浮点转整形这两种方式进行转换,而转成整数后又可以利用MXU进行计算,应该可以加快运算速度。

在编写代码时,最开始的解决办法都是从网上查找的资料,感觉可以实现基本的功能,但是对代码没有进行任何的优化,甚至还降低代码的可读性。这是我以前写代码时没有认真总结的问题,经过夏总的指导,确实在这个上面需要认真的下点功夫。

2.1 浮点转换

原始的代码如下

代码语言:javascript
复制
y = 0.299*r + 0.587*g + 0.114*b;
u = -0.1687*r - 0.3313*g + 0.5*b + 128;
v = 0.5*r - 0.4187*g - 0.0813*b + 128;

y1 = 0.299*r1 + 0.587*g1 + 0.114*b1;
u1 = -0.1687*r1 - 0.3313*g1 + 0.5*b1 + 128;
v1 = 0.5*r1 - 0.4187*g1 - 0.0813*b1 + 128;

这样写法逻辑上没有什么问题,但是细节上存在问题。首先是代码的对称性上来说,取的变量名应该是y0,u0,v0,y1,u1,v1这样才能保证对称性。第二可以将这些参数替代成宏,这样的代码更加整齐,阅读性更好。

优化后

头文件定义:

代码语言:javascript
复制
/******************************************
*  YUV转RGB公式:
*    [ 1      0        1.402 ]  [ Y ]    [ R ]
*    [ 1    -0.34414  -0.71414] * [ U ]  =  [ G ]
*    [ 1    1.1772      0  ]  [ V ]    [ B ]
******************************************/
//浮点
#define YUV2RGB_COEF00_FLOAT (1)
#define YUV2RGB_COEF01_FLOAT (0)
#define YUV2RGB_COEF02_FLOAT (1.402f)
#define YUV2RGB_COEF10_FLOAT (1)
#define YUV2RGB_COEF11_FLOAT (-0.34414f)
#define YUV2RGB_COEF12_FLOAT (-0.71414f)
#define YUV2RGB_COEF20_FLOAT (1)
#define YUV2RGB_COEF21_FLOAT (1.1772f)
#define YUV2RGB_COEF22_FLOAT (0)

代码

代码语言:javascript
复制
/*********************************************************************************
*  YUV2转RGB格式(浮点计算),两个rgb像素转换一个yuv2像素
*  r = y1 + 1.4075*(v - 128);
*  g = y - 0.3455*(u - 128) - 0.7169*(v - 128);
*  b = y + 1.779*(u - 128);
**********************************************************************************/
int yuv2rgb_native(unsigned char *rgb, unsigned char *yuv, unsigned int width, unsigned int height)
{
    if (width < 1 || height < 1 || rgb == NULL || yuv == NULL)
    {
        return 0;
    }
    unsigned char y0, u, v, y1;
    int r0, g0, b0, r1, g1, b1;
    unsigned int i;
    int loop = (width*height) >> 1;
    for (i = 0; i < loop; i++)
    {
        y0 = yuv[0];
        u = yuv[1];
        y1 = yuv[2];
        v = yuv[3];
        yuv += 4;
        r0 = YUV2RGB_COEF00_FLOAT*y1 + YUV2RGB_COEF01_FLOAT*u + YUV2RGB_COEF02_FLOAT*(v - 128);
        g0 = YUV2RGB_COEF10_FLOAT*y0 + YUV2RGB_COEF11_FLOAT*(u - 128) + YUV2RGB_COEF12_FLOAT*(v - 128);
        b0 = YUV2RGB_COEF20_FLOAT*y0 + YUV2RGB_COEF21_FLOAT*(u - 128) + YUV2RGB_COEF22_FLOAT*v;
        r1 = YUV2RGB_COEF00_FLOAT*y1 + YUV2RGB_COEF01_FLOAT*u + YUV2RGB_COEF02_FLOAT*(v - 128);
        g1 = YUV2RGB_COEF10_FLOAT*y1 + YUV2RGB_COEF11_FLOAT*(u - 128) + YUV2RGB_COEF12_FLOAT*(v - 128);
        b1 = YUV2RGB_COEF20_FLOAT*y1 + YUV2RGB_COEF21_FLOAT*(u - 128) + YUV2RGB_COEF22_FLOAT*v;

        /*
        rgb[0] = (((TUNE(g0) & 0x1C) << 3) | (TUNE(b0) >> 3));
        rgb[1] = ((TUNE(r0) & 0xF8) | (TUNE(g0) >> 5));
        rgb[2] = (((TUNE(g1) & 0x1C) << 3) | (TUNE(b1) >> 3));
        rgb[3] = ((TUNE(r1) & 0xF8) | (TUNE(g1) >> 5));
        rgb += 4;
      */
        rgb[0] = TUNE(r0);
        rgb[1] = TUNE(g0);
        rgb[2] = TUNE(b0);
        rgb[3] = TUNE(r1);
        rgb[4] = TUNE(g1);
        rgb[5] = TUNE(b1);
        rgb += 6;

    }
    return 1;
}

从整体的代码风格上来说,一定要确保代码尽可能的简洁优美,代码中用的比较多的常数可以用宏来进行表示。往往这些细节问题,容易忽视的问题,一定要重视。

2.2 浮点转整形

在前面的文档中,已经详细描述了浮点转整形的原理,现在只是做一些细节上的优化和叙述。

定义转换精度:

代码语言:javascript
复制
//定义转换精度
#define YUV2RGB_COEF_SHIFT (8)

定义系数宏:

代码语言:javascript
复制
#define YUV2RGB_COEF00_INT (1)
#define YUV2RGB_COEF01_INT (1)
#define YUV2RGB_COEF02_INT ((int)((YUV2RGB_FLOAT_COEF02 - 1) * (1<<YUV2RGB_COEF_SHIFT)))
#define YUV2RGB_COEF10_INT (1)
#define YUV2RGB_COEF11_INT ((int)((YUV2RGB_FLOAT_COEF11) * (1<<YUV2RGB_COEF_SHIFT)))
#define YUV2RGB_COEF12_INT ((int)((YUV2RGB_FLOAT_COEF12) * (1<<YUV2RGB_COEF_SHIFT)))
#define YUV2RGB_COEF20_INT (1)
#define YUV2RGB_COEF21_INT (1)
#define YUV2RGB_COEF22_INT ((int)((YUV2RGB_FLOAT_COEF21 - 1) * (1<<YUV2RGB_COcEF_SHIFT)))

转换函数

代码语言:javascript
复制
/*********************************************************************************
*  YUV2转RGB格式(浮点转整形计算),两个rgb像素转换一个yuv2像素
*  r = y1 + 1.4075*(v - 128);
*  g = y - 0.3455*(u - 128) - 0.7169*(v - 128);
*  b = y + 1.779*(u - 128);
*
*  YUV转RGB公式:
*    [ 1      0        1.402 ]  [ Y ]    [ R ]
*    [ 1    -0.34414  -0.71414] * [ U ]  =  [ G ]
*    [ 1    1.1772      0  ]  [ V ]    [ B ]
*
*  r = y1 + v + ((104*v)>>8) -180
*  g = y - ((89*u)>>8) -((183*v)>>8) + 135
*  b = y + u + ((199*u)<<8) -227
**********************************************************************************/
int yuv2rgb(unsigned char *rgb, unsigned char *yuv, unsigned int width, unsigned int height)
{
    unsigned char y0, u, v, y1;
    int r0, g0, b0, r1, g1, b1;
    unsigned int i;
    int loop = (width*height) >> 1;//yuv图像大小是
    for (i = 0; i < loop; i++)
    {
        y0 = yuv[0];
        u = yuv[1];
        y1 = yuv[2];
        v = yuv[3];
        yuv += 4;
        //简化
        r0 = y1 + v + ((YUV2RGB_INT_COEF02*v) >> YUV2RGB_COEF_SHIFT) - 179;
        g0 = y0 + ((YUV2RGB_INT_COEF11* u) >> YUV2RGB_COEF_SHIFT) + ((YUV2RGB_INT_COEF12*v) >> YUV2RGB_COEF_SHIFT) + 135;
        b0 = y0 + u + ((YUV2RGB_INT_COEF22*u) >> YUV2RGB_COEF_SHIFT) - 150;
        r1 = y1 + v + ((YUV2RGB_INT_COEF02*v) >> YUV2RGB_COEF_SHIFT) - 179;
        g1 = y1 + ((YUV2RGB_INT_COEF11* u) >> YUV2RGB_COEF_SHIFT) + ((YUV2RGB_INT_COEF12*v) >> YUV2RGB_COEF_SHIFT) + 135;
        b1 = y1 + u + ((YUV2RGB_INT_COEF22*u) >> YUV2RGB_COEF_SHIFT) - 150;

        rgb[0] = TUNE(r0);
        rgb[1] = TUNE(g0);
        rgb[2] = TUNE(b0);
        rgb[3] = TUNE(r1);
        rgb[4] = TUNE(g1);
        rgb[5] = TUNE(b1);
        rgb += 6;

        /*
        rgb[0] = (((TUNE(g0) & 0x1C) << 3) | (TUNE(b0) >> 3));
        rgb[1] = ((TUNE(r0) & 0xF8) | (TUNE(g0) >> 5));
        rgb[2] = (((TUNE(g1) & 0x1C) << 3) | (TUNE(b1) >> 3));
        rgb[3] = ((TUNE(r1) & 0xF8) | (TUNE(g1) >> 5));
        rgb += 4;
        */
    }
    return 1;
}

在这个代码中主要注意将函数展开,这样就是让复杂的运算变成乘法或者加法运算,因为MXU有相关的乘法加法和移位运算。

2.3 浮点运算和整数运算在PC上模拟的效果

在PC机上模拟时间测试

可以看到YUV2RGB_Native函数运行时间11158us,也就是浮点转换的时间为11158us。

而转换成整形后需要7444us。明显转换成整数后效率要高。

图像质量比较:

原图:

浮点转换:

经过整形转换后的图

从上面的效果上可以看出,基本上图形效果比较好。

3. x1000上进行对比测试

在开发板上进行测试主要从以下几个方面进行:

  • 不开启FPU的情况下测试浮点和整形一帧图像转换时间
  • 开启FPU的情况下测试浮点和整形一帧图像转换时间
  • 在利用MXU进行优化后的一帧图像转换时间
3.1 使用软浮点测试一帧图像转换时间

开启软浮点需要在编译选项中添加

代码语言:javascript
复制
-msoft-float

然后找到

代码语言:javascript
复制
ingenic-linux-kernel3.10.14-x1000-v5.0-20161213\prebuilts\toolchains\mips-gcc472-glibc216\lib\gcc\mips-linux-gnu\4.7.2\soft-float\libgcc.a

文件添加到到application目录下。

然后编辑application目录中的SConsript

代码语言:javascript
复制
from building import *

cwd = GetCurrentDir()
src = Glob('*.c') + Glob('*.cpp')+ Glob('*.a')

CPPPATH = [cwd, str(Dir('#'))]

group = DefineGroup('Applications', src, depend = [''],CPPPATH = CPPPATH)

Return('group')

浮点运算时间

转换一帧图像需要的时间是137ms

浮点转整形运算时间

经过转换只需要9ms。也就是说,将浮点转换成整形后,效率提高了15倍。

3.2 开启FPU后转换图像

在linux系统下编译,并利用君正提供的gcc。默认情况下是支持FPU的,所以首先需要将编译选项中的-msoft-float去掉。

浮点运算时间

可见转换一帧图像后运算时间为12ms。

浮点转整形运算时间

浮点转整形后速度还是要快一些。

3.3 开启FPU进行测试
3.3.1 基本思路

总体的代码如下

代码语言:javascript
复制
r0 = y1 + v + a1 - 179;
g0 = y0 + b1 + c1 + 135;
b0 = y0 + u + d1 - 150;
r1 = y1 + v + a1 - 179;
g1 = y1 + b1 + c1 + 135;
b1 = y1 + u + d1 - 150;

上面简化其实就是

a1:((YUV2RGB_INT_COEF02*v) >> YUV2RGB_COEF_SHIFT)

b1:((YUV2RGB_INT_COEF11* u) >> YUV2RGB_COEF_SHIFT)

c1:((YUV2RGB_INT_COEF12*v) >> YUV2RGB_COEF_SHIFT)

d1:((YUV2RGB_INT_COEF22*u) >> YUV2RGB_COEF_SHIFT)

实际上这就是一个乘法,加法,移位运算。考虑到乘法和移位比较消耗时间,可以在代码中只做加减操作,乘法和移位用MXU来进行。

主要用到的指令

而在MXU中有一个8位的乘法指令

也就是说可以将四个char类型的数填充到32位的寄存器中,得到的数据是4个16位的short型数据。

所以得到xra,xrd后然后将这两个寄存器的值移位

所以这四个乘法和移位计算由两条MXU指令即可完成

3.3.2 程序设计

将四个char类型系数放在src1中,将四个char类型的u,v分量放在src2中

代码语言:javascript
复制
int yuv2rgb(unsigned char *rgb, unsigned char *yuv, unsigned int width, unsigned int height)
{
    // if (width < 1 || height < 1 || rgb == NULL || yuv == NULL)
    // {
    //    return 0;
    // }
    unsigned char src1[4];
    unsigned char src2[4];
    unsigned short dst1[2];
    unsigned short dst2[2];
    unsigned char y0, u, v, y1;
    int r0, g0, b0, r1, g1, b1;
    src1[0] = YUV2RGB_INT_COEF02 ;
    src1[1] = -YUV2RGB_INT_COEF11;//变成正数
    src1[2] = -YUV2RGB_INT_COEF12;//变成正数
    src1[3] = YUV2RGB_INT_COEF22 ;
    unsigned int i;
    int loop = (width*height) >> 1;

    for (i = 0; i < loop; i++)
    {
        y0 = yuv[0];
        u = yuv[1];
        y1 = yuv[2];
        v = yuv[3];
        yuv += 4;

        src2[0] = v;
        src2[1] = u;
        src2[2] = v;
        src2[3] = u;

        S32LDDR(xr1, src1, 0);//将src1的数据放在xr1中
        S32LDDR(xr2, src2, 0);//将src2的数据放在xr2中
        Q8MUL(xr3, xr1, xr2, xr4);//将xr1与xr2每八位相乘,得到高位放xr3,低位放xr4
        Q16SLR(xr5, xr3, xr4, xr6, YUV2RGB_COEF_SHIFT);//将xr3与xr4进行移位
        S32STD(xr5, dst1, 0);// dst1[1] Short3 dst1[0] Short2
        S32STD(xr6, dst2, 0);// dst2[1] Short1 dst2[0] Short0

        r0 = y1 + v + dst1[1] - 179;
        g0 = y0 - dst1[0] - dst2[1]  + 135;
        b0 = y0 + u + dst2[0] - 150;
        r1 = y1 + v + dst1[1]- 179;
        g1 = y1 - dst1[0] - dst2[1] + 135;
        b1 = y1 + u + dst2[0] - 150;

        rgb[0] = (((TUNE(g0) & 0x1C) << 3) | (TUNE(b0) >> 3));
        rgb[1] = ((TUNE(r0) & 0xF8) | (TUNE(g0) >> 5));
        rgb[2] = (((TUNE(g1) & 0x1C) << 3) | (TUNE(b1) >> 3));
        rgb[3] = ((TUNE(r1) & 0xF8) | (TUNE(g1) >> 5));
        rgb += 4;
    }
    return 1;
}

经过测试,效果如下

发现效果并没有预想中的那么明显。依然和整形转换一帧图像时间差不多。和之前的猜想不相符,如果将几条乘法指令并行执行,可能会效果好很多,但实际测试发现优化好不了多少。后面再将加减法进行一下MXU的优化,看一下能不能有更好的优化方案。

4. 总结

本文主要测试YUV转RGB的几种方法的效率问题,得到的结论是定点化处理更加的高效。如果用浮点运算,会消耗大量的硬件资源。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 嵌入式IoT 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.文章简述
  • 2. YUV转RGB的代码优化问题
    • 2.1 浮点转换
      • 2.2 浮点转整形
        • 2.3 浮点运算和整数运算在PC上模拟的效果
        • 3. x1000上进行对比测试
          • 3.1 使用软浮点测试一帧图像转换时间
            • 3.2 开启FPU后转换图像
              • 3.3 开启FPU进行测试
                • 4. 总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档