最近邻域插值法原理:
根据目标图像的像素点(浮点坐标)找到原始图像中的4个像素点,取距离该像素点最近的一个原始像素值作为该点的值。
未优化之前:
unsigned char* ScaleNearest(unsigned char* srcImg,
int dstWidth, int dstHeight,
int srcWidth, int srcHeight)
{
int i,j;
int dstRowBytes;
int srcRowBytes;
float xScale;
float yScale;
int x,y;
unsigned char* dst;
unsigned char* src;
unsigned char* dstImg;
x = y = 0;
dstRowBytes = (dstWidth * 3 + 3) & ~3;
srcRowBytes = (srcWidth * 3 + 3) & ~3;
dstImg = (unsigned char*)malloc(dstHeight * dstRowBytes);
if(!dstImg)
return NULL;
memset(dstImg, 255, dstRowBytes * dstHeight);
xScale = (float)srcWidth / (float)dstWidth;
yScale = (float)srcHeight / (float)dstHeight;
for(i = 0; i < dstHeight; i++)
{
for(j = 0; j < dstWidth; j++)
{
//根据目标图像坐标、缩放倍数确定原始图像点,取上整
x = (int)(j * xScale + 0.5);
y = (int)(i * yScale + 0.5);
//必须做边界判断,缩放后的边界会出现不想要的结果,比如,原始为白色的,缩放后的图片可能在边界出现彩色
if(x >= srcWidth)
x = srcWidth - 1;
if(y >= srcHeight)
y = srcHeight -1;
dst = dstImg + i * dstRowBytes + 3*j;
src = srcImg + y * srcRowBytes + 3*x;
*dst = *src;
dst++,src++;
*dst = *src;
dst++,src++;
*dst = *src;
}
}
return dstImg;
}
优化思路:对每一行,y其实固定;对每一列,x固定,另外边界也没有必须图像中的每个点都要判断一次,所以把这几行:
//根据目标图像坐标、缩放倍数确定原始图像点,取上整
x = (int)(j * xScale + 0.5);
y = (int)(i * yScale + 0.5);
//必须做边界判断,缩放后的边界会出现不想要的结果,比如,原始为白色的,缩放后的图片可能在边界出现彩色
if(x >= srcWidth)
x = srcWidth - 1;
if(y >= srcHeight)
y = srcHeight -1;
单独处理,做成表格,以供查询。另外,这几行:
dst = dstImg + i * dstRowBytes + 3*j;
src = srcImg + y * srcRowBytes + 3*x;
乘法可以换成加法运算,尽量避免乘法运算。
优化之后的代码如下:
int *x_table = NULL;
int *y_table = NULL;
//创建表格
int init_scale(int dst_width, int dst_height, int src_width, int src_height)
{
int i;
double x_scale;
double y_scale;
int x;
int y;
if(x_table)
free(x_table);
if(y_table)
free(y_table);
x_table = (int*)malloc(dst_width * sizeof(int));
if(!x_table)
return -1;
y_table = (int*)malloc(dst_height * sizeof(int));
if(!y_table)
{
free(x_table);
x_table = NULL;
return -1;
}
x_scale = (double)src_width / (double)dst_width;
y_scale = (double)src_height / (double)dst_height;
for(i = 0; i < dst_width; i++)
{
x = (int) (i * x_scale + 0.5);
if(x >= src_width)
x = src_width - 1;
x_table[i] = x;
}
for(i = 0; i < dst_height; i++)
{
y = (int) (i * y_scale + 0.5);
if(y >= src_height)
y = src_height -1;
y_table[i] = y;
}
return 0;
}
unsigned char* ScaleNearest(unsigned char* srcImg,
int dstWidth, int dstHeight,
int srcWidth, int srcHeight)
{
int i,j;
int dstRowBytes;
int srcRowBytes;
int x,y;
unsigned char* dst;
unsigned char* src;
unsigned char* dstImg;
unsigned char* ptr;
int gap;
x = y = 0;
dstRowBytes = (dstWidth * 3 + 3) & ~3;
srcRowBytes = (srcWidth * 3 + 3) & ~3;
dstImg = (unsigned char*)malloc(dstHeight * dstRowBytes);
gap = dstRowBytes - dstWidth * 3;
if(!dstImg)
return NULL;
dst = dstImg;
src = srcImg;
for(i = 0; i < dstHeight; i++)
{
y = y_table[i];
ptr = srcImg + y*srcRowBytes;
for(j = 0; j < dstWidth; j++)
{
x = x_table[j];
src = ptr + (x << 1) + x;
*dst = *src;
dst++,src++;
*dst = *src;
dst++,src++;
*dst = *src;
dst++;
}
dst += gap;
}
return dstImg;
}
这样处理之后,算法就比之前的快多了。顺带说一句,最近邻域插值跟二次线性、最小立方插值等缩放算法相比,主观效果差很多,但是其保留边缘,在某些特殊环境还是有其用途。