图像缩放算法及速度优化——(二)双线性插值
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
图像缩放算法及速度优化——(⼆)双线性插值
双线性插值作为OpenCV中默认使⽤的图像缩放算法,其效果和速度都是不错的。
并且效果也⽐较稳定,计算复杂度并不算太⾼。
我看了很多⽹上的算法,⾃⼰也没看太懂,下⾯是从⽹上找的双线性插值算法的讲解。
“图像的双线性插值放⼤算法中,⽬标图像中新创造的象素值,是由源图像位置在它附近的2*2区域4个邻近象素的值通过加权平均计算得出的。
双线性内插值算法放⼤后的图像质量较⾼,不会出现像素值不连续的的情况。
然⽽次算法具有低通滤波器的性质,使⾼频分量受损,所以可能会使图像轮廓在⼀定程度上变得模糊。
”
下⾯还是根据我⾃⼰的理解来继续讲述吧,相信读者中有很多⾼⼿,希望读者能给予我指点⼀下,让我也能更明⽩⼀些。
双线性插值算法和最近邻插值算法⽐较类似。
在最近邻插值算法中,⽬标图像中的某个点(x,y)是去源图像中找最邻近的⼀个点(x0, y0)即可。
⽬标图像中的点(x, y)对应于源图像中的点(x0',y0'),x0'、y0'很可能不是整数,⽽是⼩数,⽽最近邻插值算法是找其邻近整型值(int(x0'+0.5f),int(y0'+0.5f))(上篇⽂章中没有进⾏四舍五⼊)。
我们现在找x0', y0'所在位置旁边的四个点,根据这四个点与(x0',y0')距离的关系计算⽬标图像中(x,y)⼀点的像素值。
算法描述如下:
(1)计算源图像与⽬标图像宽与⾼的⽐例
w0 : 表⽰源图像的宽度
h0 : 表⽰源图像的⾼度
w1 : 表⽰⽬标图像的宽度
h1 : 表⽰⽬标图像的⾼度
float fw = float(w0-1)/(w1-1);
float fh = float(h0-1)/(h1-1);
(2)针对⽬标图像的⼀个点(x, y),计算在源图像中的对应坐标,结果为浮点数。
float x0 = x * fw;
float y0 = y * fh;
int x1 = int(x0);
int x2 = x1 + 1;
int y1 = int(y0);
int y2 = y1+1;
所求的源图像中的四个点坐标为(x1, y1) (x1, y2) (x2, y1) (x2,y2)
(3)求周围四个点所占的权重⽐值
如上图,
fx1 = x0 - x1;
fx2 = 1.0f - fx1;
fy1 = y0 - y1;
fy2 = 1.0f - fy1;
float s1 = fx1*fy1;
float s2 = fx2*fy1;
float s3 = fx2*fy2;
float s4 = fx1*fy2;
我们以value(坐标)来代表取得此点的坐标值,则:
value(x0,y0) = value(x2,y2)*s1+value(x1,y2)*s2+value(x1,y1)*s3+value(x2,y1)*s4;
如果对上述运算不够明⽩的话,可以这样来求。
我们先要求得(x0, y1) 和(x0,y2)的像素值。
则float value(x0,y1) = value(x1,y1)*fx2 + value(x2,y1)*fx1;
float value(x0,y2) = value(x1,y2)*fx2 + value(x2,y2)*fx1;
注释:离某点越近,离权重越⼤,故取其与1的差值。
float value(x0,y0) = value(x0,y1)*fy2 + value(x0,y2)*fy1;
验证后与上边公式⼀样。
(4)求得值后填充到⽬标图像上就可以了。
为了能让⼈更容易理解,咱还是使⽤GetPixel和SetPixel进⾏取值和赋值。
void ResizeLinear01(CImage& src, CImage& dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();
int pitch0 = src.GetPitch();
int w1 = dst.GetWidth();
int h1 = dst.GetHeight();
int pitch1 = dst.GetPitch();
float fw = float(w0) / w1;
float fh = float(h0) / h1;
int y1,y2, x1,x2, x0,y0;
float fx1,fx2, fy1, fy2;
for(int y=0; y<h1; y++)
{
y0 = y*fh;
y1 = int(y0);
if(y1 == h0-1) y2 = y1;
else y2 = y1 + 1;
fy1 = y1-y0;
fy2 = 1.0f - fy1;
for(int x=0; x<w1; x++)
{
x0 = x*fw;
x1 = int(x0);
if(x1 == w0-1) x2 = x1;
else x2 = x1+1;
fx1 = y1-y0;
fx2 = 1.0f - fx1;
float s1 = fx1*fy1;
float s2 = fx2*fy1;
float s3 = fx2*fy2;
float s4 = fx1*fy2;
COLORREF c1,c2,c3,c4, color;
c1 = src.GetPixel(x1,y1);
c2 = src.GetPixel(x2,y1);
c3 = src.GetPixel(x1,y2);
c4 = src.GetPixel(x2,y2);
BYTE r,g,b;
r = (BYTE)(GetRValue(c1)*s3) + (BYTE)(GetRValue(c2)*s4) + (BYTE)(GetRValue(c3)*s2) + (BYTE)(GetRValue(c4)*s1);
g = (BYTE)(GetGValue(c1)*s3) + (BYTE)(GetGValue(c2)*s4) + (BYTE)(GetGValue(c3)*s2) + (BYTE)(GetGValue(c4)*s1);
b = (BYTE)(GetBValue(c1)*s3) + (BYTE)(GetBValue(c2)*s4) + (BYTE)(GetBValue(c3)*s2) + (BYTE)(GetBValue(c4)*s1);
dst.SetPixelRGB(x, y, r, g, b);
}
}
}
测试程序仍是将670*503尺⼨的图⽚缩放为200*160。
经过测试,上边的算法执⾏⼀次就需要0.5秒左右,可以说是⾮常的慢。
如果将其缩放成2000*1600⼤⼩的图⽚,⼀次就需要50秒。
这是⾮常可怕的,假如现在我们做⼀个类似PHOTOSHOP的软件,⽤户想将其扩⼤若⼲倍,却要等50秒,估计⽤户已经没有耐⼼使⽤您的软件了。
我们来分析⼀下怎样可以优化程序。
在每⼀步优化算法后,我们都再⽤上边同样的程序和步骤进⾏测试,观察运⾏所⽤时间。
(1)改函数调⽤为指针操作如第1节。
(进⾏完此步后,程序只需要0.2秒左右,速度提⾼了250倍,哈哈!)
(2)将x0,y0坐标的计算提取到循环外,因为第⼆层循环⾥的x坐标每次循环都要重复⼀次,并且是重复的。
(仍然是需要0.2秒左右。
我们让其循环⼀百次,再⽐较下所⽤时间。
使⽤ResizeLinear02⽅法⽤时19.6秒,使⽤ResizeLinear02⽅法都是⽤时17.4秒,看来还是有作⽤的。
)
下⾯是最终的代码。
void ResizeLinear04(CImage& src, CImage& dst)
{
int w0 = src.GetWidth();
int h0 = src.GetHeight();
int pitch0 = src.GetPitch();
int w1 = dst.GetWidth();
int h1 = dst.GetHeight();
int pitch1 = dst.GetPitch();
BYTE* pSrc = (BYTE*)src.GetBits();
BYTE* pDst = (BYTE*)dst.GetBits();
BYTE* p0, *p1 = pDst;
float fw = float(w0-1) / (w1-1);
float fh = float(h0-1) / (h1-1);
float x0, y0;
int y1, y2, x1, x2;
float fx1, fx2, fy1, fy2;
int* arr_x1 = new int[w1];
int* arr_x2 = new int[w1];
float* arr_fx1 = new float[w1];
for(int x=0; x<w1; x++)
{
x0 = x*fw;
arr_x1[x] = int(x0);
arr_x2[x] = int(x0+0.5f);
arr_fx1[x] = x0 - arr_x1[x];
//TRACE(L"x=%6d; x0=%6.3f; x1=%6d; x2=%6d; fx1=%6.3f;\n", x, x0, arr_x1[x], arr_x2[x], arr_fx1[x]); }
for(int y=0; y<h1; y++)
{
y0 = y*fh;
y1 = int(y0);
y2 = int(y0+0.5f);
fy1 = y0-y1;
fy2 = 1.0f - fy1;
//TRACE(L"y=%6d; y0=%6.3f; y1=%6d; y2=%6d; fy1=%6.3f;\n", y, y0, y1, y2, fy1);
for(int x=0; x<w1; x++)
{
x1 = arr_x1[x];
x2 = arr_x2[x];
fx1 = arr_fx1[x];
fx2 = 1.0f-fx1;
float s1 = fx2*fy2;
float s2 = fx1*fy2;
float s3 = fx1*fy1;
float s4 = fx2*fy1;
//TRACE(L"s1=%6.3f; s2=%6.3f; s3=%6.3f; s4=%6.3f; sum=%6.3f\n", s1,s2,s3,s4, s1+s2+s3+s4); BYTE* p11 = pSrc + pitch0*y1 + 3*x1;
BYTE* p12 = pSrc + pitch0*y1 + 3*x2;
BYTE* p21 = pSrc + pitch0*y2 + 3*x1;
BYTE* p22 = pSrc + pitch0*y2 + 3*x2;
*p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++; *p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++; p11++; p12++; p21++; p22++; *p1 = BYTE((*p11)*s1 + (*p12)*s2 + (*p21)*s4 + (*p22)*s3); p1++;
}
p1 = pDst + y*pitch1;
}
delete []arr_x1;
delete []arr_x2;
delete []arr_fx1;
}。