本文主要介绍如何优化您自己的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数组,我用一个一维数组来存储。编译器处理一维数组的效率要高过二维数组。 第一步,先写一个代码:

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的取值永远都大于等于 ,小于等于 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的潜能是很大的!看你如何去挖掘。

RGB到YUV的转换算法做以总结。

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) {
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
}
}

这种算法应该是非常快的了.

===========================================================
转载请注明出处:http://blog.csdn.net/songzitea/article/details/10474207
===========================================================

【性能优化】优化笔记之一:图像RGB与YUV转换优化的更多相关文章

  1. mysql性能优化学习笔记(3)常见sql语句优化

    一.max()优化mysql> explain select max(payment_date) from payment;+----+-------------+---------+----- ...

  2. 多媒体编程基础之RGB和YUV

    一.概念 1.什么是RGB? 对一种颜色进行编码的方法统称为“颜色空间”或“色域”.用最简单的话说,世界上任何一种颜色的“颜色空间”都可定义成一个固定的数字或变量.RGB(红.绿.蓝)只是众多颜色空间 ...

  3. KVM性能优化学习笔记

    本学习笔记系列都是采用CentOS6.x操作系统,KVM虚拟机的管理也是采用virsh方式,网上的很多的文章都基于ubuntu高版本内核下,KVM的一些新的特性支持更好,本文只是记录了CentOS6. ...

  4. Pandas 性能优化 学习笔记

    摘要 本文介绍了使用 Pandas 进行数据挖掘时常用的加速技巧. 实验环境 import numpy as np import pandas as pd print(np.__version__) ...

  5. js性能优化--学习笔记

    <高性能网站建设进阶指南>: 1.使用局部变量,避免深入作用域查找,局部变量是读写速度最快的:把函数中使用次数超过一次的对象属性和数组存储为局部变量是一个好方法:比如for循环中的.len ...

  6. 《嵌入式Linux内存使用与性能优化》笔记

    这本书有两个关切点:系统内存(用户层)和性能优化. 这本书和Brendan Gregg的<Systems Performance>相比,无论是技术层次还是更高的理论都有较大差距.但是这不影 ...

  7. 慕课网 -- 性能优化之PHP优化总结笔记

    视频链接,感兴趣的可以去看看,对我来说耳目一新. http://www.imooc.com/learn/205 什么情况下遇到PHP性能问题 1 :PHP语法使用不恰当 2 :使用了PHP语言他不擅长 ...

  8. VS2010/2012配置优化记录笔记

    VS2010/2012配置优化记录笔记 在某些情况下VS2010/2012运行真的实在是太卡了,有什么办法可以提高速度吗?下面介绍几个优化策略,感兴趣的朋友可以参考下,希望可以帮助到你   有的时候V ...

  9. Linux实战教学笔记37:企业级Nginx Web服务优化实战(上)

    一,Nginx基本安全优化 1.1 调整参数隐藏Nginx软件版本号信息 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的.因此,我们应尽量隐 ...

随机推荐

  1. spring源码分析构建

    命令如下: ant ant install-maven ant jar package E:\download\spring-framework-3.1.3.RELEASE\build-spring- ...

  2. oracle、db2、sybase大型数据库面试总结

    1. oracle数据库单例.多例模式. 数据库创建之后会有一系列为该数据库提供服务的内存空间和后台进程,称为该数据库的实例. 每一个数据库至少会有一个实例为其服务. 2. mysql获取字段的长度用 ...

  3. 关于JS、JQuery、CSS的小知识点

    1.将字符串转换成json列表格式如下: var getaddress = appcan.libuser.getAddress(); var address=JSON.parse(getaddress ...

  4. android 屏幕适配1 ——dimens.xml的适配

    1.如果是才开始做项目,已经有设计图:720*1280 1).默认values文件夹:1dp=1px values/dimens_x.xml: name: x1~x720   value:1px~72 ...

  5. zepto源码研究 - zepto.js-4(常用的工具)

    $.each: /** * 以集合每一个元素作为上下文,来执行回调函数 * @param elements * @param callback * @returns {*} */ $.each = f ...

  6. 自己动手写框架——IoC的实现

    先看看 IoC百度百科 优化过程 namespace Test { class Program { static void Main(string[] args) { //场景 某公司客服要回访一些客 ...

  7. Apache的配置

    Apache的配置由httpd.conf文件配置,因此下面的配置指令都是在httpd.conf文件中修改. 主站点的配置(基本配置) (1) 基本配置: ServerRoot "/mnt/s ...

  8. 不在界面上用控件 动态创建idhttp,IdAntiFreeze来用

    不在界面上用控件 动态创建idhttp,IdAntiFreeze来用 var IdHTTP: Tidhttp; IdAntiFreeze: TidAntiFreeze; begin IdAntiFre ...

  9. 【结构型】Adapter模式

    Adapter模式主要意图是将类或接口转换成客户期望的接口,从而使得原本不兼容.无法在一起工作的接口可以在一起工作.该模式有两种形式的Adapter法,一是继承方式:二是对象关联依赖方式. 继承方式A ...

  10. CWnd类

    CWnd类的成员 .数据成员 m_hWnd 指明与这个CWnd对象相关联的HWND句柄 .构造和析构 CWnd 构造一个CWnd对象 DestroyWindow 销毁相关联的Windows窗口 .初始 ...