by chuckGao 2009
最近在研究对视频流放大播放时进行图形缩放的bilinear和bicubic算法。这里给出最近一个项目中对图像进行2倍放大的算法。而对于算法的优化,可以考虑用建立缩放映射表的方法。
bilinear算法:
int bilinear_scale
(long newx,
long newy,
unsigned long oldBytesperline,
unsigned long newBytesperline,
int format,
unsigned char* newImage,
Image image)
{
unsigned char* ptr;
if (newx==0 || newy==0){
return SIZE_ERROR;
}
if (image.width==newx && image.height==newy){
newImage = image.bits;
return NOT_TRANS;
}
float xScale, yScale, fX, fY;
xScale = (float)0.5;
yScale = (float)0.5;
long ifX, ifY, ifX1, ifY1, xmax, ymax;
float ir1, ir2, ig1, ig2, ib1, ib2, dx, dy;
unsigned char r,g,b;
scale_RGBQUAD rgb1, rgb2, rgb3, rgb4;
xmax = image.width-1;
ymax = image.width-1;
for(long y=0; y<newy; y++){ /*算法优化by chuckGao:如果图像height>width,修改使程序减少for跨层计算*/
fY = y * yScale;
ifY = (int)fY;
ifY1 = min(ymax, ifY+1);
dy = fY - ifY;
for(long x=0; x<newx; x++){
fX = x * xScale;
ifX = (int)fX;
ifX1 = min(xmax, ifX+1);
dx = fX - ifX;
// Interpolate using the four nearest pixels in the source
unsigned char* ptr;
ptr = image.bits + ifY*oldBytesperline + ifX*format;
rgb1.rgbRed = *ptr++;
rgb1.rgbGreen= *ptr++;
rgb1.rgbBlue =*ptr;
ptr = image.bits + ifY*oldBytesperline + ifX1*format;
rgb2.rgbRed = *ptr++;
rgb2.rgbGreen= *ptr++;
rgb2.rgbBlue =*ptr;
ptr = image.bits + ifY1*oldBytesperline + ifX*format;
rgb3.rgbRed = *ptr++;
rgb3.rgbGreen= *ptr++;
rgb3.rgbBlue =*ptr;
ptr = image.bits + ifY1*oldBytesperline + ifX1*format;
rgb4.rgbRed = *ptr++;
rgb4.rgbGreen= *ptr++;
rgb4.rgbBlue =*ptr;
// Interplate in x direction:
ir1 = rgb1.rgbRed + (rgb3.rgbRed - rgb1.rgbRed) * dy;
ig1 = rgb1.rgbGreen + (rgb3.rgbGreen - rgb1.rgbGreen) * dy;
ib1 = rgb1.rgbBlue + (rgb3.rgbBlue - rgb1.rgbBlue) * dy;
ir2 = rgb2.rgbRed + (rgb4.rgbRed - rgb2.rgbRed) * dy;
ig2 = rgb2.rgbGreen + (rgb4.rgbGreen - rgb2.rgbGreen) * dy;
ib2 = rgb2.rgbBlue + (rgb4.rgbBlue - rgb2.rgbBlue) * dy;
// Interpolate in y:
r = (unsigned char)(ir1 + (ir2-ir1) * dx);
g = (unsigned char)(ig1 + (ig2-ig1) * dx);
b = (unsigned char)(ib1 + (ib2-ib1) * dx);
// Set output
ptr = newImage + y*newBytesperline + x*format;
*ptr++ = (unsigned char)r;
*ptr++ = (unsigned char)g;
*ptr = (unsigned char)b;
}
}
return 0;
}
bicubic算法:
int bicubic_scale
(long newx,
long newy,
unsigned long oldBytesperline,
unsigned long newBytesperline,
int format,
unsigned char* newImage,
Image image)
{
unsigned char* ptr;
if (newx==0 || newy==0){
return SIZE_ERROR;
}
if (image.width==newx && image.height==newy){
return NOT_TRANS;
}
float xScale, yScale, fX, fY;
xScale = (float)0.5;
yScale = (float)0.5;
// bicubic interpolation by chuckGao
float f_x, f_y, a, b, rr, gg, bb, r1, r2;
int i_x, i_y, xx, yy;
scale_RGBQUAD rgb;
for(long y=0; y<newy; y++){
f_y = (float) y * yScale - 0.5f;
i_y = (int) floor(f_y);
a = f_y - (float)floor(f_y);
for(long x=0; x<newx; x++){
f_x = (float) x * xScale - 0.5f;
i_x = (int) floor(f_x);
b = f_x - (float)floor(f_x);
rr = gg = bb = 0.0f;
for(int m=-1; m<3; m++) {
r1 = kernelBSpline((float) m - a);
yy = i_y+m;
if (yy<0) yy=0;
if (yy>=image.height) yy = image.height-1;
for(int n=-1; n<3; n++) {
r2 = r1 * kernelBSpline(b - (float)n);
xx = i_x+n;
if (xx<0) xx=0;
if (xx>=image.width){
xx=image.width-1;
}
ptr = image.bits + yy*oldBytesperline + xx*format;
rgb.rgbRed = *ptr++;
rgb.rgbGreen= *ptr++;
rgb.rgbBlue = *ptr;
rr += rgb.rgbRed * r2;
gg += rgb.rgbGreen * r2;
bb += rgb.rgbBlue * r2;
}
}
ptr = newImage + y*newBytesperline + x*format;
*ptr++ = (unsigned char)rr;
*ptr++ = (unsigned char)gg;
*ptr = (unsigned char)bb;
}
}
return 0;
}
float kernelBSpline(const float x)
{
if (x>2.0f) return 0.0f;
// thanks to Kristian Kratzenstein
float a, b, c, d;
float xm1 = x - 1.0f; // Was calculatet anyway cause the "if((x-1.0f) < 0)"
float xp1 = x + 1.0f;
float xp2 = x + 2.0f;
if ((xp2) <= 0.0f) a = 0.0f; else a = xp2*xp2*xp2; // Only float, not float -> double -> float
if ((xp1) <= 0.0f) b = 0.0f; else b = xp1*xp1*xp1;
if (x <= 0) c = 0.0f; else c = x*x*x;
if ((xm1) <= 0.0f) d = 0.0f; else d = xm1*xm1*xm1;
return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d)));
}
在windows上用QT写了一个opencv的视频采集程序,今天测试的结果为:
1.对于640x480放大到1280x960,采用bicubic算法,fps = 1 or 2,采用bilinear算法,fps为6,而不进行缩放的图像播放fps为12
2.预计改换显示大小为320x240,放大到640x480时fps有所增强
3.这里的fps值只是近似得到,并不绝对,只能进行相对比较
(9月22日补充)
对于图像缩放算法的优化,在很大程度上是对其运算速度进行c语言级甚至是汇编级的优化。首先要做c语言级的优化,一般采用两种方法:
1.运算流程的优化,尽量减少跨层计算
2.以空间换时间,牺牲内存空间以加快计算速度
在基本清楚优化原则的前提下,也要对如下概念有清楚的认识:
1.算法中涉及到的int、long、short(包括unsigned)以及float、double等数值类型的掌握。要知道,对于图像缩放后精度的控制,必须清楚的知道计算时这些类型数据的情况,比如在对float类型数据(假设变量名为rgb)赋值时:
float rgb = 0.5 和float rgb = 0.5f在精度上就有所差异,前者自动转换类型为double型,64位数据,而后者为float 32位
2.在32-bit计算机中,long和int的size一般是相等的,即占4bytes,32位。但计算时,特别在malloc时,还是应慎重使用sizeof(long)来确定其大小
3.抛开算法本身不谈,计算中数据类型为unsigned char的运算肯定比float的快,但精确度肯定不如后者。在实际编程中的取舍可以根据图像显示效果和速度来决定