图像缩放与插值
一. 最近邻插值
这是最简单的插值办法, 为了方便描述, 先考虑一维的情况:
最近邻插值将连续坐标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)
三. 三次插值
抽空再写…