C++——bmp图像缩放(插值)

       本文要实现的功能是使用最近邻插值以及双线性插值完成bmp图像的缩放。

1、最近邻插值

       不需要计算,在待求象素的四邻象素中,将距离待求象素最近的邻象素灰度赋给待求象素:

                                    

       设i+u, j+v(i, j为正整数, u, v为大于零小于1的小数,下同)为待求象素坐标,则待求象素灰度的值 f(i+u, j+v);如果(i+u, j+v)落在A区,即u<0.5, v<0.5,则将左上角象素的灰度值赋给待求象素,同理,落在B区则赋予右上角的象素灰度值,落在C区则赋予左下角象素的灰度值,落在D区则赋予右下角象素的灰度值。

最邻近算法计算量较小,但可能会造成插值生成的图像灰度上的不连续,在灰度变化的地方可能出现明显的锯齿状。

代码如下:

void image_scaling_nearest()
{
	char readPath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\lunpan.bmp";
	readBmp(readPath);

	unsigned char *imagedata = NULL; //动态分配存储原图片的像素信息的二维数组
	unsigned char *imagedataScal = NULL;//动态分配存储缩放后的图片的像素信息的二维数组
	imagedata = pBmpBuf;

	float ExpScalValue = 0; ////期望的缩放倍数(允许小数)
	int FloatToIntwidth, FloatToIntheight;/////小数变成整数(float To Int)
	int RotateAngle = 90;//要缩放的角度,默认90

	//图片缩放处理
	cout << "请输入要缩放的倍数:" << endl;
	cin >> ExpScalValue;

	///如果ExpScalValue含有小数,需要整数化
	///对期望的缩放结果取整
	FloatToIntwidth = (int)(ExpScalValue*bmpWidth);
	FloatToIntheight = (int)(ExpScalValue*bmpHeight);
	//图像每一行的字节数必须是4的整数倍
	int lineByte2 = (FloatToIntwidth * biBitCount / 8 + 3) / 4 * 4;
	imagedataScal = new unsigned char[lineByte2 * FloatToIntheight];///为缩放后图像分配存储空间

	int pre_i, pre_j, after_i, after_j;//缩放前后对应的像素点坐标
	for (int i = 0; i<FloatToIntheight; i++)
	{
		for (int j = 0; j<FloatToIntwidth; j++)
		{
			for (int k = 0; k < 3;k++)
			{
			after_i = i;
			after_j = j;
			pre_i = (int)(after_i / ExpScalValue);/////取整,插值方法为:最邻近插值(近邻取样法)
			pre_j = (int)(after_j / ExpScalValue);
			if (pre_i >= 0 && pre_i < bmpHeight && pre_j >= 0 && pre_j < bmpWidth)//在原图范围内
				*(imagedataScal + i * lineByte2 + j*3+k) = *(imagedata + pre_i * bmpWidth*3 + pre_j*3+k);
			}
		}
	}
	//保存bmp图片
	char writePath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\111.bmp";
	saveBmp(writePath, imagedataScal, FloatToIntwidth, FloatToIntheight, biBitCount, pColorTable);
	printf("缩放变换完成,请查看Scalresult.bmp文件。\n\n");

	//释放内存
	delete[] imagedata;
	delete[] imagedataScal;
}

2、双线性插值

       双线性插值是有两个变量的插值函数的线性插值扩展,其核心思想是在两个方向分别进行一次线性插值,线性插值的结果与插值的顺序无关。(下图从https://blog.csdn.net/zhangla1220/article/details/41014541截图所得)

                              

       目标图(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;

为更加形象地说明该原理,我们给出下图:

                                         

       如上面所言,我们需要算出目标图像像素跟原图像像素的映射关系,我们从遍历目标图像像素点开始,对每一个像素点进行比例计算,算出其如果投影到原图像应该是处于原图像的哪个位置,通常所得到的结果正如上图的所示,求得的点P处于原图像的四个真实像素点中。

       我们假定原图像像素点间的RGB(或灰度)值是呈线性变化的,则我们可以通过A、B两点RGB(或灰度)值算出AB线段上面的E点的RGB(或灰度)值,同理亦可算出F点的RGB(或灰度)值。得到了E、F两点的RGB(或灰度)值后可经由相同的方法得到P点的RGB(或灰度)值。到此,我们就知道该如何通过映射关系去求得目标图像的RGB(或灰度)值了。

    我们把点A、B、C、D、E、F、P的RGB(或灰度)值分别记为F_A、F_B、F_C、F_D、F_E、F_F、F_P(注意由于RGB是三个值,这个记法其实不严谨,可以理解为是FA等是RGB的其中一个值,然后另外两个也可同理得到),则有

F_E=(1-0.7)*F_A+(1-0.3)*F_B,

F_F=(1-0.7)*F_C+(1-0.3)*F_D,

最终 得到目标点的值:

F_P=(1-0.7)*F_E+(1-0.3)*F_F=0.3*0.3*F_A+0.3*0.7*F_B+0.7*0.3*F_C+0.7*0.7*F_D。

代码如下:

void image_scaling_doubleline()
{

	char readPath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\lunpan.bmp";
	readBmp(readPath);

	unsigned char *imagedata ; //动态分配存储原图片的像素信息的二维数组
	unsigned char *imagedataScal ;//动态分配存储缩放后的图片的像素信息的二维数组
	imagedata = pBmpBuf;

	float ExpScalValue = 0; ////期望的缩放倍数(允许小数)
	int FloatToIntwidth, FloatToIntheight;/////小数变成整数(float To Int)
	int RotateAngle = 90;//要缩放的角度,默认90

	//图片缩放处理
	cout << "请输入要缩放的倍数:" << endl;
	cin >> ExpScalValue;

	///如果ExpScalValue含有小数,需要整数化
	///对期望的缩放结果取整
	FloatToIntwidth = (int)(ExpScalValue*bmpWidth);
	FloatToIntheight = (int)(ExpScalValue*bmpHeight);
	//图像每一行的字节数必须是4的整数倍
	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
	int lineByte2 = (FloatToIntwidth * biBitCount / 8 + 3) / 4 * 4;
	imagedataScal = new unsigned char[lineByte2 * FloatToIntheight];///为缩放后图像分配存储空间
 
	/*******************图像处理部分******************/
	/*******************双线性插值******************/
	for (int i = 0; i < FloatToIntheight; i++)
		for (int j = 0; j < FloatToIntwidth; j++)
		{
			
				float d_original_img_hnum = i / ExpScalValue;
				float d_original_img_wnum = j / ExpScalValue;
				int i_original_img_hnum = d_original_img_hnum;
				int i_original_img_wnum = d_original_img_wnum;
				float distance_to_a_x = d_original_img_wnum - i_original_img_wnum;//在原图像中与a点的水平距离    
				float distance_to_a_y = d_original_img_hnum - i_original_img_hnum;//在原图像中与a点的垂直距离    
			
				int original_point_a = i_original_img_hnum*lineByte + i_original_img_wnum * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点A      
				int original_point_b = i_original_img_hnum* lineByte + (i_original_img_wnum + 1) * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点B    
				int original_point_c = (i_original_img_hnum + 1)* lineByte + i_original_img_wnum * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点C     
				int original_point_d = (i_original_img_hnum + 1)* lineByte + (i_original_img_wnum + 1) * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点,相当于点D 
    
				if (i_original_img_hnum  == bmpHeight - 1)
				{
					original_point_c = original_point_a;
					original_point_d = original_point_b;
				}
				if (i_original_img_wnum  == bmpWidth - 1)
				{
					original_point_b = original_point_a;
					original_point_d = original_point_c;
				}

				int pixel_point = i*lineByte2 + j * 3;//映射尺度变换图像数组位置偏移量    
				for (int k = 0; k < 3; k++)
				{
					
				imagedataScal[pixel_point + k] =
					imagedata[original_point_a + k] * (1 - distance_to_a_x)*(1 - distance_to_a_y) +
					imagedata[original_point_b + k ]* distance_to_a_x*(1 - distance_to_a_y) +
					imagedata[original_point_c + k ]* distance_to_a_y*(1 - distance_to_a_x) +
					imagedata[original_point_d + k ]* distance_to_a_y*distance_to_a_x;
				/*assert((pixel_point + k)<(lineByte2 * FloatToIntheight));
					assert((original_point_a + k)<(lineByte * bmpHeight));
					assert((original_point_b + k)<(lineByte * bmpHeight));
					assert((original_point_c + k)<(lineByte * bmpHeight));
					assert((original_point_d + k)<(lineByte * bmpHeight));*/

			}

		}
	/*******************双线性插值******************/
	/*******************图像处理部分******************/
	//保存bmp图片
	char writePath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11-1.bmp";
	saveBmp(writePath, imagedataScal, FloatToIntwidth, FloatToIntheight, biBitCount, pColorTable);
	printf("缩放变换完成,请查看Scalresult.bmp文件。\n\n");
	//释放内存
	delete[] imagedata;
	delete[] imagedataScal;
}

猜你喜欢

转载自blog.csdn.net/wanty_chen/article/details/80283872