让你的软件飞起来:RGB转为YUV【转】

简介:

转自:http://blog.csdn.net/wxzking/article/details/5905195

朋友曾经给我推荐了一个有关代码优化的pdf文档《让你的软件飞起来》,看完之后,感受颇深。为了推广其,同时也为了自己加深印象,故将其总结为word文档。下面就是其的详细内容总结,希望能于己于人都有所帮助。

 

速度取决于算法

同样的事情,方法不一样,效果也不一样。比如,汽车引擎,可以让你的速度超越马车,却无法超越音速;涡轮引擎,可以轻松 超越音障,却无法飞出地球;如果有火箭发动机,就可以到达火星。

 

代码的运算速度取决于以下几个方面

1、  算法本身的复杂度,比如MPEG比JPEG复杂,JPEG比BMP图片的编码复杂。

2、  CPU自身的速度和设计架构

3、  CPU的总线带宽

4、  您自己代码的写法

本文主要介绍如何优化您自己的code,实现软件的加速。

 

先看看我的需求

我们一个图象模式识别的项目,需要将RGB格式的彩色图像先转换成黑白图像。

图像转换的公式如下:

Y = 0.299 * R + 0.587 * G + 0.114 * B;

图像尺寸640*480*24bit,RGB图像已经按照RGBRGB顺序排列的格式,放在内存里面了。

 

我已经悄悄的完成了第一个优化

以下是输入和输出的定义:

#define XSIZE 640

#define YSIZE 480

#define IMGSIZE XSIZE * YSIZE

typedef struct RGB

{

       unsigned char R;

       unsigned char G;

       unsigned char B;

}RGB;

struct RGB in[IMGSIZE]; //需要计算的原始数据

unsigned char out[IMGSIZE]; //计算后的结果

 

第一个优化

优化原则:图像是一个2D数组,我用一个一维数组来存储。编译器处理一维数组的效率要高过二维数组。

 

先写一个代码:

Y = 0.299 * R + 0.587 * G + 0.114 * B;

 

void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i++)

    {

       double r,g,b,y;

       unsigned char yy;

        r = in[i].r;

        g = in[i].g;

        b = in[i].b;

        y = 0.299 * r + 0.587 * g + 0.114 * b;

        yy = y;

        out[i] = yy;

    }

}

这大概是能想得出来的最简单的写法了,实在看不出有什么毛病,好了,编译一下跑一跑吧。

第一次试跑

这个代码分别用vc6.0和gcc编译,生成2个版本,分别在pc上和我的embedded system上面跑。

速度多少?

在PC上,由于存在硬件浮点处理器,CPU频率也够高,计算速度为20秒。

我的embedded system,没有以上2个优势,浮点操作被编译器分解成了整数运算,运算速度为120秒左右。

 

去掉浮点运算

上面这个代码还没有跑,我已经知道会很慢了,因为这其中有大量的浮点运算。只要能不用浮点运算,一定能快很多。

 

Y = 0.299 * R + 0.587 * G + 0.114 * B;

这个公式怎么能用定点的整数运算替代呢?

0.299 * R可以如何化简?

Y = 0.299 * R + 0.587 * G + 0.114 * B;

Y = D + E + F;

D = 0.299 * R;

E = 0.587 * G;

F = 0.114 * B;

我们就先简化算式D吧!

RGB的取值范围都是0~255,都是整数,只是这个系数比较麻烦,不过这个系数可以表示为:0.299 = 299 / 1000;

所以 D = ( R * 299) / 1000;

Y = (R * 299 + G * 587 + B * 114) / 1000;

 

这一下,能快多少呢?

Embedded system上的速度为45秒;

PC上的速度为2秒;

0.299 * R可以如何化简

Y = 0.299 * R + 0.587 * G + 0.114 * B;

Y = (R * 299 + G * 587 + B * 114) / 1000;

这个式子好像还有点复杂,可以再砍掉一个除法运算。

前面的算式D可以这样写:

0.299=299/1000=1224/4096

所以 D = (R * 1224) / 4096

Y=(R*1224)/4096+(G*2404)/4096+(B*467)/4096

再简化为:

Y=(R*1224+G*2404+B*467)/4096

这里的/4096除法,因为它是2的N次方,所以可以用移位操作替代,往右移位12bit就是把某个数除以4096了。

 

void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i++)

    {

       int r,g,b,y;

        r = 1224 * in[i].r;

        g = 2404 * in[i].g;

        b = 467 * in[i].b;

        y = r + g + b;

        y = y >> 12; //这里去掉了除法运算

        out[i] = y;

    }

}

这个代码编译后,又快了20%。

虽然快了不少,还是太慢了一些,20秒处理一幅图像,地球人都不能接受。

 

仔细端详一下这个式子!

Y = 0.299 * R + 0.587 * G + 0.114 * B;

Y=D+E+F;

D=0.299*R;

E=0.587*G;

F=0.114*B;

 

RGB的取值有文章可做,RGB的取值永远都大于等于0,小于等于255,我们能不能将D,E,F都预先计算好呢?然后用查表算法计算呢?

我们使用3个数组分别存放DEF的256种可能的取值,然后。。。

 

查表数组初始化

int D[256],F[256],E[256];

void table_init()

{

    int i;

    for(i=0;i<256;i++)

    {

        D[i]=i*1224; 

        D[i]=D[i]>>12;

        E[i]=i*2404; 

        E[i]=E[i]>>12; 

        F[i]=i*467; 

        F[i]=F[i]>>12;

    }

}

void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i++)

    {

       int r,g,b,y;

        r = D[in[i].r];//查表

        g = E[in[i].g];

        b = F[in[i].b];

        y = r + g + b;

        out[i] = y;

    }

}

 

这一次的成绩把我吓出一身冷汗,执行时间居然从30秒一下提高到了2秒!在PC上测试这段代码,眼皮还没眨一下,代码就执行完了。一下提高15倍,爽不爽?

继续优化
很多embedded system的32bit CPU,都至少有2个ALU,能不能让2个ALU都跑起来?

 

void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i += 2) //一次并行处理2个数据

    {

       int r,g,b,y,r1,g1,b1,y1;

        r = D[in[i].r];//查表 //这里给第一个ALU执行

        g = E[in[i].g];

        b = F[in[i].b];

        y = r + g + b;

        out[i] = y;

        r1 = D[in[i + 1].r];//查表 //这里给第二个ALU执行

        g1 = E[in[i + 1].g];

        b1 = F[in[i + 1].b];

        y = r1 + g1 + b1;

        out[i + 1] = y;

    }

}

2个ALU处理的数据不能有数据依赖,也就是说:某个ALU的输入条件不能是别的ALU的输出,这样才可以并行。

这次成绩是1秒。

 

查看这个代码

int D[256],F[256],E[256]; //查表数组

void table_init()

{

    int i;

    for(i=0;i<256;i++)

    {

        D[i]=i*1224; 

        D[i]=D[i]>>12;

        E[i]=i*2404; 

        E[i]=E[i]>>12; 

        F[i]=i*467; 

        F[i]=F[i]>>12;

    }

}

到这里,似乎已经足够快了,但是我们反复实验,发现,还有办法再快!

可以将int D[256],F[256],E[256]; //查表数组

更改为

unsigned short D[256],F[256],E[256]; //查表数组

 

这是因为编译器处理int类型和处理unsigned short类型的效率不一样。

再改动

inline void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i += 2) //一次并行处理2个数据

    {

       int r,g,b,y,r1,g1,b1,y1;

        r = D[in[i].r];//查表 //这里给第一个ALU执行

        g = E[in[i].g];

        b = F[in[i].b];

        y = r + g + b;

        out[i] = y;

        r1 = D[in[i + 1].r];//查表 //这里给第二个ALU执行

        g1 = E[in[i + 1].g];

        b1 = F[in[i + 1].b];

        y = r1 + g1 + b1;

        out[i + 1] = y;

    }

}

将函数声明为inline,这样编译器就会将其嵌入到母函数中,可以减少CPU调用子函数所产生的开销。

这次速度:0.5秒。

 

其实,我们还可以飞出地球的!

如果加上以下措施,应该还可以更快:

1、  把查表的数据放置在CPU的高速数据CACHE里面;

2、  把函数calc_lum()用汇编语言来写

 

其实,CPU的潜力是很大的

1、  不要抱怨你的CPU,记住一句话:“只要功率足够,砖头都能飞!”

2、  同样的需求,写法不一样,速度可以从120秒变化为0.5秒,说明CPU的潜能是很大的!看你如何去挖掘。

3、  我想:要是Microsoft的工程师都像我这样优化代码,我大概就可以用489跑windows XP了!

 

以上就是对《让你的软件飞起来》的摘录,下面,我将按照这位牛人的介绍,对RGB到YCbCr的转换算法做以总结。

 

Y =   0.299R + 0.587G + 0.114B
 U = -0.147R - 0.289G + 0.436B
 V =  0.615R - 0.515G - 0.100B

 

 

#deinfe SIZE 256

#define XSIZE 640

#define YSIZE 480

#define IMGSIZE XSIZE * YSIZE

typedef struct RGB

{

       unsigned char r;

       unsigned char g;

       unsigned char b;

}RGB;

struct RGB in[IMGSIZE]; //需要计算的原始数据

unsigned char out[IMGSIZE * 3]; //计算后的结果

 

unsigned short Y_R[SIZE],Y_G[SIZE],Y_B[SIZE],U_R[SIZE],U_G[SIZE],U_B[SIZE],V_R[SIZE],V_G[SIZE],V_B[SIZE]; //查表数组

void table_init()

{

    int i;

    for(i = 0; i < SIZE; i++)

    {

        Y_R[i] = (i * 1224) >> 12; //Y对应的查表数组

        Y_G[i] = (i * 2404) >> 12; 

        Y_B[i] = (i * 467)  >> 12;

        U_R[i] = (i * 602)  >> 12; //U对应的查表数组

        U_G[i] = (i * 1183) >> 12; 

        U_B[i] = (i * 1785) >> 12;

        V_R[i] = (i * 2519) >> 12; //V对应的查表数组

        V_G[i] = (i * 2109) >> 12; 

        V_B[i] = (i * 409)  >> 12;

    }

}

 

inline void calc_lum()

{

    int i;

    for(i = 0; i < IMGSIZE; i += 2) //一次并行处理2个数据

    {     

        out[i]               = Y_R[in[i].r] + Y_G[in[i].g] + Y_B[in[i].b]; //Y

        out[i + IMGSIZE]     = U_B[in[i].b] - U_R[in[i].r] - U_G[in[i].g]; //U

        out[i + 2 * IMGSIZE] = V_R[in[i].r] - V_G[in[i].g] - V_B[in[i].b]; //V

 

        out[i + 1]                = Y_R[in[i + 1].r] + Y_G[in[i + 1].g] + Y_B[in[i + 1].b]; //Y

        out[i  + 1 + IMGSIZE]     = U_B[in[i + 1].b] - U_R[in[i + 1].r] - U_G[in[i + 1].g]; //U

        out[i  + 1 + 2 * IMGSIZE] = V_R[in[i + 1].r] - V_G[in[i + 1].g] - V_B[in[i + 1].b]; //V

    }

}

 

根据牛人的观点,这种算法应该是非常快的了,以后可直接使用了。^_^








本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/7145420.html,如需转载请自行联系原作者


相关文章
|
供应链 数据挖掘 API
获取亚马逊畅销榜API接口的获取与应用
本文详细介绍了如何获取亚马逊畅销榜API接口及其应用方法。首先,需注册并获取API凭证,了解常用API接口如Product Advertising API、Sponsored Products API等。接着,选择合适的API端点,发送请求并解析响应。最后,探讨了API在商品查询、数据分析、跨境电商及商业工具开发等方面的应用,强调了遵守服务条款的重要性。
332 0
|
算法 编译器 C++
C++学习——坚持(二)(1)
C++学习——坚持(二)
187 0
C++学习——坚持(二)(1)
|
大数据 Linux 数据库
6月19日云栖精选夜读:血泪总结!创业公司CTO要避免哪些坑?
对于想做 CTO 的人,或者正在做 CTO 的人,或者做技术管理的人而言,技术的锤炼和知识的提升非常重要。本文作者将向大家讲述创业中踩过的那些坑和他们的血泪总结。 一、技术的锤炼 先让我从印象最深的一次宕机讲起。
4818 0
|
6天前
|
人工智能 自然语言处理 JavaScript
2026年Windows+Ollama本地部署OpenClaw保姆级教程:本地AI Agent+阿里云上快速搭建
2026年OpenClaw凭借本地部署、私有化运行的特性,成为打造个人智能体的核心工具,而Ollama作为轻量级本地大模型管理工具,能让OpenClaw摆脱对云端大模型的依赖,实现**本地推理、数据不泄露、全流程私有化**的智能体验。本文基于Windows 11系统,从硬件环境准备、Ollama安装与模型定制、OpenClaw部署配置、技能扩展到常见问题排查,打造保姆级本地部署教程,同时补充阿里云OpenClaw(Clawdbot)快速部署步骤,兼顾本地私有化需求与云端7×24小时运行需求,文中所有代码命令均可直接复制执行,确保零基础用户也能快速搭建属于自己的本地智能体。
7607 55
|
3天前
|
人工智能 安全 API
CoPaw:3分钟部署你的 AI助理
源自阿里巴巴开源生态的个人 AI 助理——CoPaw。作为阿里倾力打造的开源力作,CoPaw 完美打通钉钉、飞书、Discord 等多平台对话通道,支持定时任务自动化。内置 PDF/Office 深度处理、新闻摘要等强大技能,更开放自定义扩展接口。坚持数据全程私有化部署,绝不上传云端,让每一位用户都能在大厂技术加持下,拥有安全、专属的智能助手。
|
7天前
|
人工智能 JSON JavaScript
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
手把手教你用 OpenClaw(v2026.2.22-2)+ 飞书,10分钟零代码搭建专属AI机器人!内置飞书插件,无需额外安装;支持Claude等主流模型,命令行一键配置。告别复杂开发,像聊同事一样自然对话。
3673 10
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
|
5天前
|
人工智能 自然语言处理 机器人
保姆级教程:Mac本地搭建OpenClaw及阿里云上1分钟部署OpenClaw+飞书集成实战指南
OpenClaw(曾用名Clawdbot、Moltbot)作为2026年最热门的开源个人AI助手平台,以“自然语言驱动自动化”为核心,支持对接飞书、Telegram等主流通讯工具,可替代人工完成文件操作、日历管理、邮件处理等重复性工作。其模块化架构适配多系统环境,既可以在Mac上本地化部署打造私人助手,也能通过阿里云实现7×24小时稳定运行,完美兼顾隐私性与便捷性。
3213 4
|
3天前
|
人工智能 安全 JavaScript
阿里云上+本地部署OpenClaw(小龙虾)新手攻略:解锁10大必备Skills,零基础也能玩转AI助手
2026年,开源AI代理工具OpenClaw(昵称“小龙虾”)凭借“能实际做事”的核心优势,在GitHub斩获25万+星标,成为现象级AI工具。它最强大的魅力在于可扩展的Skills(技能包)系统——通过ClawHub插件市场的数百个技能,能让AI助手从简单聊天升级为处理办公、学习、日常事务的全能帮手。
2828 7
|
6天前
|
人工智能 监控 机器人
2026年零门槛部署 OpenClaw(Clawdbot)接入A股数据,实现24小时股票分析保姆级教程
在AI赋能金融分析的浪潮中,OpenClaw(原Clawdbot/Moltbot)凭借开源灵活的架构,成为个人投资者打造专属智能分析助手的首选。通过接入A股实时数据,它能实现24小时市场监控、涨跌预警、潜力股推荐等核心功能,彻底解放人工盯盘的繁琐。而阿里云的稳定部署环境,更让这套系统实现全天候不间断运行,成为真正的“金融AI助手”。 本文基于OpenClaw v2026.1.25稳定版与QVeris免费A股数据接口,详细拆解阿里云OpenClaw部署步骤、A股数据接入流程、高级分析功能配置及多平台联动技巧,所有代码命令均可直接复制复用,即使无技术基础也能在1小时内完成从部署到实战的全流程。
2646 9