图像缩放与插值
一. 最近邻插值
这是最简单的插值办法, 为了方便描述, 先考虑一维的情况:
最近邻插值将连续坐标x近似为最近的整数u0, 输出值g_out(x) = g_in(u0) , 其中u0 = round(x)
假设给定一个输入 123, 60, 255 长度为3, 那么每个点对应的坐标分别是0, 1, 2
如果现在要放大到长度为5, 那么每个点对应的原坐标分别为 -0.2, 0.4, 1.0, 1.6, 2.2
选择最近邻的值作为插值, 对应的输出就是: 123, 123, 60, 255, 255
void nearest_neighbor(uchar *input, uchar *output, int len_in, int len_out)
{
double scale = (double) len_in / len_out, xn = scale * 0.5;
int i;
for (i = 0; i < len_out; ++i)
{
*output++ = *(input + (int)xn);
xn += scale;
}
}
图像缩放中近邻插值实现起来很方便, 代码很容易写, 处理速度也特别快, 但效果实在太差
用QImage做了个测试
void ImgProcess::nearestZoom(
const QImage &imgSrc, QImage &imgDest,
const QSize &sizeSrc, const QSize &sizeDest)
{
int
depthSrc = imgSrc.depth(),
depthDest = imgDest.depth();
if(depthSrc != depthDest)
{ //different depth
return ;
}
if(depthSrc != 32)
{ // not 32-bit color
return;
}
typedef int Pix;
Pix
*pSrcCurrentLine, *pDestCurrentPix,
*pSrcData = (Pix *)imgSrc.bits(),
*pDestData = (Pix *)imgDest.bits();
int
wSrc = sizeSrc.width(),
hSrc = sizeSrc.height(),
wDest = sizeDest.width(),
hDest = sizeDest.height();
double
xSrc, ySrc,
xScale = (double)wSrc/wDest,
yScale = (double)hSrc/hDest,
initXSrc = xScale * 0.5,
initYSrc = yScale * 0.5;
pDestCurrentPix = pDestData;
ySrc = initYSrc;
for(int i = 0; i< hDest; ++i)
{
xSrc = initXSrc;
pSrcCurrentLine = pSrcData + wSrc * (int)ySrc;
for(int j = 0; j< wDest; ++j)
{
*pDestCurrentPix++ = *(pSrcCurrentLine + (int)xSrc);
xSrc += xScale;
}
ySrc += yScale;
}
}
下面是缩放前后对比(缩放比例分别是0.8, 1.25)
(原图)
(*0.8)
(*1.25)
可以看到, 效果非常不好, 失真很严重
二. 线性插值
还是先考虑一维的情形:
估计值由相邻两个输入值决定, 每个输入值的权重与其和估计值位置的临近程度成正相关
g_out(x) = (u0+1-x) * g_in(u0) + (x-u0) * g_in(u0+1) , 其中u0 = round(x)
void linear(uchar *input, uchar *output, int len_in, int len_out)
{
int max_in = len_in - 1, max_out = len_out -1;
double scale = (double) max_in / max_out;
double xf, weight_xn, weight_xn1;
int i, xn, xn1;
for (i = 0, xf = 0.0; i < max_out; ++i)
{
xn = (int)xf, xn1 = xn + 1;
weight_xn1 = xf -xn;
weight_xn = 1 - weight_xn;
*output++ = *(input + xn) * weight_xn + *(input + xn1) * weight_xn1;
xf += scale;
}
*output = *(input + max_in);
}
如果是二维就需要两个方向进行插值, 估计值由临近四个点决定
g_out(u,v)
=(u1-u)*(v1-v)*g_in(u0,v0)
+(u-u0)*(v-v0)*g_in(u1,v1)
+(u-u0)*(v1-v)*g_in(u1,v0)
+(u1-u)*(v-v0)*g_in(u0,v1)
其中u0 = round(u), v0 = round(v), u1 = u0+1, v1 = v0 +1
线性插值的缩放效果比最近邻插值就好得多了, 下面还是QImage测试的输出图像:
(*0.8)
(*1.25)
三. 三次插值
抽空再写…