对于图像缩放算法来说,最近临插值算法是最简单的。最近临插值算法的原理是在原图像中找到最近临的一个点,然后把这个点的像素值插入到目标图像中,最近临插值算法优点是算法简单,易于实现,但是缺点是由于相邻像素点的像素值相同,容易出现色块现象。
那么如何在原图像中找这个目标点呢,算法公式如下:
src_x = dst_x * (src_width / dst_width); src_y = dst_y * (src_height / dst_height);
那么算出来呢,可能会是小数点,需要四舍五入取整,那么下面来看一个例子(一个3*3的矩阵):
234 38 22 67 44 12 89 65 63
这里呢,我们需要将上面的一个3*3矩阵变换成一个4*4的矩阵,我们根据上面的公式来计算一下,首先是目标点为(0,0),那么原图像的坐标点为(0,0),那么这个坐标点的值就应该为234,然后再来看(0,1)这个坐标点,那么纵坐标点还是0,但是横坐标点变成了1*(3/4) = 0.75,四舍五入得1,那么得到的原图像坐标为(0,1),那么这个坐标点值就应该为38,同理,最后得到的矩阵结果如下:
234 38 22 22 67 44 12 12 89 65 63 63 89 65 63 63
这部分测试代码如下:
#include <stdio.h>
int main(int argc, char *argv[])
{
int i, j, row, col;
unsigned char mat1[3][3] = {{234, 38, 22},
{67, 44, 12},
{89, 65, 63}};
unsigned char mat2[4][4];
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
row = (int)(i * (3.0 / 4.0) + 0.5);
col = (int)(j * (3.0 / 4.0) + 0.5);
mat2[i][j] = mat1[row][col];
printf("%d\t", mat2[i][j]);
}
printf("\n");
}
return 0;
}
关于对于图像的最近临插值算法我也写了一个测试代码(只针对bmp格式的图片,放大两倍):
#include <stdio.h>
#include <stdlib.h>
struct bitmap_fileheader {
unsigned short type;
unsigned int size;
unsigned short reserved1;
unsigned short reserved2;
unsigned int off_bits;
} __attribute__ ((packed));
struct bitmap_infoheader {
unsigned int size;
unsigned int width;
unsigned int height;
unsigned short planes;
unsigned short bit_count;
unsigned int compression;
unsigned int size_image;
unsigned int xpels_per_meter;
unsigned int ypels_per_meter;
unsigned int clr_used;
unsigned int clr_important;
} __attribute__ ((packed));
void resize_near(void *dst, void *src, int dst_width, int dst_height, int src_width, int src_height)
{
int i, j, x, y;
float scale_x, scale_y;
char *buf1 = (char *)src, *buf2 = (char *)dst;
scale_x = (float)src_width / (float)dst_width;
scale_y = (float)src_height / (float)dst_height;
for (i = 0; i < dst_height; i++) {
for (j = 0; j < dst_width; j++) {
y = (int)(i * scale_y + 0.5);
x = (int)(j * scale_x + 0.5);
buf2[i*dst_width*3 + j*3] = buf1[y*src_width*3 + x*3];
buf2[i*dst_width*3 + j*3 + 1] = buf1[y*src_width*3 + x*3 + 1];
buf2[i*dst_width*3 + j*3 + 2] = buf1[y*src_width*3 + x*3 + 2];
}
}
}
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
struct bitmap_fileheader fh;
struct bitmap_infoheader ih;
void *buf1, *buf2;
int src_width, src_height, dst_width, dst_height;
if (argc < 2)
return -1;
fp1 = fopen(argv[1], "rb");
if (fp1 == NULL) {
printf("open file %s failed!\n", argv[1]);
return -1;
}
fread(&fh, 1, sizeof(struct bitmap_fileheader), fp1);
fread(&ih, 1, sizeof(struct bitmap_infoheader), fp1);
src_width = ih.width;
src_height = ih.height;
buf1 = malloc(ih.width * ih.height * (ih.bit_count / 8));
fread(buf1, 1, ih.width * ih.height * (ih.bit_count / 8), fp1);
fp2 = fopen("resize.bmp", "wb");
if (fp2 == NULL) {
printf("open file resize.bmp failed!\n");
return -1;
}
dst_width = src_width * 2;
dst_height = src_height * 2;
ih.width = dst_width;
ih.height = dst_height;
fh.size = fh.off_bits + ih.width * ih.height * (ih.bit_count / 8);
fwrite(&fh, 1, sizeof(struct bitmap_fileheader), fp2);
fwrite(&ih, 1, sizeof(struct bitmap_infoheader), fp2);
buf2 = malloc(ih.width * ih.height * (ih.bit_count / 8));
resize_near(buf2, buf1, dst_width, dst_height, src_width, src_height);
fwrite(buf2, 1, ih.width * ih.height * (ih.bit_count / 8), fp2);
free(buf1);
free(buf2);
fclose(fp1);
fclose(fp2);
return 0;
}
如果用opencv来做,那就很简单了,直接使用opencv提供的cvResize函数,代码如下:
#include <highgui.h>
#include <cv.h>
int main(int argc, char *argv[])
{
IplImage *img1, *img2;
if (argc < 2)
return -1;
img1 = cvLoadImage(argv[1]);
img2 = cvCreateImage(CvSize(img1->width*2, img1->height*2), img1->depth, img1->nChannels);
cvResize(img1, img2, CV_INTER_LINEAR);
/* CV_INTER_NN
CV_INTER_LINEAR
CV_INTER_AREA
CV_INTER_CUBIC */
cvSaveImage("test.jpg", img2);
cvReleaseImage(&img1);
cvReleaseImage(&img2);
return 0;
}
其中参数CV_INTER_NN对应最近临插值算法,CV_INTER_LINEAR对应双线性插值算法。