最近邻域插值法原理:
根据目标图像的像素点(浮点坐标)找到原始图像中的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; }这样处理之后,算法就比之前的快多了。顺带说一句,最近邻域插值跟二次线性、最小立方插值等缩放算法相比,主观效果差很多,但是其保留边缘,在某些特殊环境还是有其用途。