欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-01-24 15:37:16
...

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

1、最近邻插值

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

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

       设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截图所得)

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

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

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

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

       如上面所言,我们需要算出目标图像像素跟原图像像素的映射关系,我们从遍历目标图像像素点开始,对每一个像素点进行比例计算,算出其如果投影到原图像应该是处于原图像的哪个位置,通常所得到的结果正如上图的所示,求得的点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;
}