/* <<如何让你的软件飞起来 >>
* 将RGB彩色图像转换成黑白图像
* Y = 0.299 * R + 0.587 * G + 0.114 * B;
* 图像尺寸640x480x24
* RGB图像已按RGBRGB的方式存在内存中
*/
下面以由RGB转YUV的公式,Y = 0.299 * R + 0.587 * G + 0.114 * B,开始一步步讲述如何
优化coding;
/**************** header define ************/
#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] //计算后的结果
由于编译器处理1维数组的效率要高过2维数组,
这里将原本为2D的RGB数据换成一维数组。
/************* original code *************/
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;
}
}
这是最简单的实现,采用了大量的浮点运算,
速度极其慢。
1.将浮点预算改成定点运算
/********** float to int ************/
Y = 0.299 * R + 0.587 * G + 0.114 * B
Y=D+E+F
D=0.299 * R=299 * R /1000
E=0.587 * G=587 * R /1000
F=0.114 * B=114 * R /1000
Void calc_lum()
{int I;
for(i=0;i<IMGSIZE;i++)
{int r,g,b;
r=1224*in[i].r/1000;
g=2404*in[i].g/1000;
b=467*in[i].b/1000;
out[i]=r+g+b;
}
}
将浮点预算改成定点运算,速度翻一番。
2.将乘法、除法改为移位
/********** division to shift ************/
Y = 0.299 * R + 0.587 * G + 0.114 * B
Y=D+E+F
D=0.299 * R=299 * R /1000 = 1224 * R / 4096
E=0.587 * G=587 * G /1000 = 2404 * G / 4096
F=0.114 * B=114 * B /1000 = 476 * B / 4096
Y= (1224 * R + 2404 * G + 476 * B) / 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%
3.查表法
/*********** table search *************/
Int D[256],E[256],F[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;
}
}
RGB取值范围:0-255,可以将数值预先计算好,
存在3个一维数组里面,采用查表的方法,
读取RGB数值。
4.并行运算
/*********** two 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];
g=E[in[i].g];
b=F[in[i].b]; //查表
y=r+g+b;
out[i]=y;
r1=D[in[i+1].r];
g1=E[in[i+1].g];
b1=F[in[i+1].b]; //查表
y1=r1+g1+b1;
out[i+1]=y1;
}
}
CPU内部有两个ALU,则可以同时计算两个像素值。
/******** usigned short | inline **************/
unsigned short D[256],E[256],F[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;
}
}
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]; g=E[in[i].g]; b=F[in[i].b]; //查表
y=r+g+b;
out[i]=y;
r1=D[in[i+1].r]; g1=E[in[i+1].g]; b1=F[in[i+1].b]; //查表
y1=r1+g1+b1;
out[i+1]=y1;
}
}
将函数声明为inline,这样编译器就会将其嵌入到母函数中,
可以减少CPU调用子函数所产生的开销。
5.cache
/**********************/
把查表的数据放置在CPU的高速数据CACHE里面
6. ASM
/**********************/
•把函数calc_lum()用汇编语言来
(秩名) |