1. 程式人生 > >影象插值演算法(最近臨插值演算法)

影象插值演算法(最近臨插值演算法)

對於影象縮放演算法來說,最近臨插值演算法是最簡單的。最近臨插值演算法的原理是在原影象中找到最近臨的一個點,然後把這個點的畫素值插入到目標影象中,最近臨插值演算法優點是演算法簡單,易於實現,但是缺點是由於相鄰畫素點的畫素值相同,容易出現色塊現象。

那麼如何在原影象中找這個目標點呢,演算法公式如下:

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,同理,最後得到的矩陣結果如下:

234382222
67441212
89656363
89656363

這部分測試程式碼如下:

#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對應雙線性插值演算法。