最近邻内插值与双线性插值

前一阵想改进个图像处理的算法,发现基础太差了,还是要从基础开始,毕竟一口出不成胖子大笑

于是自己学习一下图像处理的基本知识。看到两个最简单的算法 一个是最近邻内插值和双线性插值,决定自己实现以下,顺便也能了解一下数字图像内部的存储结构。

以下为简单整理

by the way,基础知识逐渐明朗的感觉还是不错滴~程序员最大的开心应该就是程序无bug跑通的时候,无论是多简单的程序。

一个Width*Height  nChannel个通道的图像 共具有Width * Height 个像素点。每个像素点有nChannel个通道元素。

以3通道为例,3通道为B G R

如下:

如上所示,每一行有效元素共Width*nChannel个元素,实际上,有时候比这个数要多。

为了提高速度,需要按4字节对齐,因此如果每行元素个数不是4的倍数,需要补齐,这个即为平时总提到的widthstep。

基于以上的了解,实现两种算法

1 最近领内插值算法

就是令变换后像素的灰度值等于距它最近的输入像素的灰度值

 

如上图 按照比例一致 可知道

Y = W / w * y;

X = H / h * x;

则目标图(x, y)处的灰度值与原图(X, Y )处的灰度值一致: f(x, y) = f(X, Y)

代码:

//最近邻内插
void NearestInsert(const char *OriImagePath, char *OutImagePath, int Width, int Height)
{
	IplImage * OriImage = cvLoadImage(OriImagePath, 1);
	IplImage * OutImage = cvCreateImage(cvSize(Width, Height), 8, 3);

	unsigned char *OriImage_c = (unsigned char *)OriImage->imageData;
	unsigned char *OutImage_c = (unsigned char *)OutImage->imageData;

	double fx = (double)OriImage->height / (double)Height;//height方向上的比值
	double fy =  (double)OriImage->width / (double)Width;//width方向上的比值
	int channel = OriImage->nChannels;//通道数 一般为RGB 3通道
	
	for (int x = 0; x < Height; x++)
	{
		for(int y = 0; y < Width; y++)
		{
			for (int k = 0; k <channel; k++)
			{
				int out =  x * OutImage->widthStep + y * channel + k;
				int ori = (int)(fx * x) * OriImage->widthStep + (int)(fy * y)  * channel + k;
				OutImage_c[out] = OriImage_c[ori];
			}

		}
			
	}
	
	//test
	cvNamedWindow("OriImage",1);
	cvShowImage("OriImage",OriImage);
	cvWaitKey(0);
	cvNamedWindow("NearestInsert",1);
	cvShowImage("NearestInsert",OutImage);
	cvWaitKey(0);
	//cvSaveImage(OutImagePath,OutImage);
	cvReleaseImage(&OriImage);
	cvReleaseImage(&OutImage);
}


2 双线性插值

在x y两个方向上分别进行一次线性插值,方向先后不影响顺序。

如上图所示

目标图(x, y) 映射到原图是(X + u, Y + v)(计算方法同最邻近插值)。设u与v分别为X + u,Y + v的小数部分。

由于下标都是整数,因此原图其实并不存在该点。

则取其附近四个领域点为(X, Y) (X, Y + 1) (X + 1, Y) (X + 1, Y + 1)

则目标图(x, y)处的值为 f(x , y) = f(X + u, Y + v) =f (X, Y)  * (1 - u) * (1 - v) + f(X, Y + 1) * (1 - u) * v + f(X + 1, Y) * u * (1 - v) + f (X + 1, Y + 1) * u * v;

代码如下:

//双线性内插
void BilinearInsert(const char *OriImagePath, char *OutImagePath, int Width, int Height)
{
	IplImage * OriImage = cvLoadImage(OriImagePath, 1);
	IplImage * OutImage = cvCreateImage(cvSize(Width, Height), 8, 3);

	unsigned char *OriImage_c = (unsigned char *)OriImage->imageData;
	unsigned char *OutImage_c = (unsigned char *)OutImage->imageData;

	double fx = (double)OriImage->height / (double)Height;//height方向上的比值
	double fy =  (double)OriImage->width / (double)Width;//width方向上的比值
	int channel = OriImage->nChannels;//通道数 一般为RGB 3通道

	for (int x = 0; x < Height; x++)
	{
		for(int y = 0; y < Width; y++)
		{
			//对于目标图像(x, y),实际映射到原图的坐标为(fx * x, fy * y),但是若为小数,原图并不存在该店,因此近似为(i, j)
			int i = fx * x;
			int j = fy * y;
			int out =  x * OutImage->widthStep + y * channel;
			//找到四个领域的下标
			int ori1 = i * OriImage->widthStep + j * channel;
			int ori2 = i * OriImage->widthStep + (j + 1)  * channel;
			int ori3 = (i + 1) * OriImage->widthStep + j  * channel;
			int ori4 = (i + 1) * OriImage->widthStep + (j + 1)  * channel;
			//f(i+u,j+v) = (1-u)(1-v)f(i,j) + (1-u)vf(i,j+1) + u(1-v)f(i+1,j) + uvf(i+1,j+1)  
			for (int k = 0; k <channel; k++)
			{
				float u = (float)x * fx - i;//
				float v = (float)y * fy - j;
				OutImage_c[out + k] = OriImage_c[ori1 + k] * (1 - u) * (1 - v) 
					+ OriImage_c[ori2 + k] * (1 - u) * v
					+ OriImage_c[ori3 + k] * u * (1 - v)
					+ OriImage_c[ori4 + k] * u * v;
					
			}

		}

	}

	//test
	cvNamedWindow("BilinearInsert",1);
	cvShowImage("BilinearInsert",OutImage);
	cvWaitKey(0);
	//cvSaveImage(OutImagePath,OutImage);
	cvReleaseImage(&OriImage);
	cvReleaseImage(&OutImage);

}


 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页