纯C语言利用位图底层结构实现位图缩放

此为课程设计的第四题,这道题是找了各种大神写的范本(比如那个双线性插值的实现)和位图结构的分析东拼西凑写出来的东西,过程中也学了一些知识。如果不是要求纯C语言实现或许能用opencv,但是我也没有深入了解过。希望对一部分同学能有帮助,要用的话请至少改一下变量名和函数顺序并且搞懂为什么,不要直接抄袭。
看之前请先搞懂位图的内部结构,否则会搞不懂这些名词的意思,要用的话请至少改变量名和函数顺序。
位图内部结构分析

原题:

涉及知识点:文件读写、结构体定义、内存管理、基本图像处理算法、命令行参数

要求:

编写一个程序,可以在命令行输入参数,完成指定文件的缩放,并存储到新文件,命令行参数如下

zoom file1.bmp 200 file2.bmp

第一个参数为可执行程序名称,第二个参数为原始图像文件名,第三个参数为缩放比例(百分比),第四个参数为新文件名

问题分析

要完成位图文件缩放,需要考虑的问题有:

1.怎么通过命令行参数读取对应的BMP文件?

2.怎么把存储压缩后生成的BMP文件?

3.在程序中如何操纵BMP文件进行压缩,采用何种图像处理算法?

解决方案(思路)

要做这道题首先要自己去学习一下位图的内部结构,这部分我自学后撰写了一篇博客,链接如下。因为原文太长,所以就不附在报告内部了。后面的报告内容会直接引用博客中的概念

https://blog.csdn.net/zimuzi2019/article/details/106188788

扫描二维码关注公众号,回复: 11326855 查看本文章

通过学习位图的内部结构,就可以知道如何保存和读取位图。读取位图的关键是通过位图信息头结构变量获取图像的基本信息(如高,宽,每像素所占位数等),并获取源文件的像素信息。保存文件的关键是自己填入修改后的图像的位图文件头,位图信息图,像素数据等信息生成完整的图像。

所谓的压缩主要是根据特定算法,依据原有的像素数据来生成更大或者更小的图像,这里选择双向线性插值算法来进行图像处理。先根据命令行参数设定新图像的高度宽度等信息并申请存储空间等待填入像素信息,然后利用双向线性插值来填入像素数据。关于双向线性插值的具体概念可以参考下面这篇博文。

https://blog.csdn.net/sinat_33718563/article/details/78825971

算法分析:

转化输入的压缩参数便于后续处理后将命令行参数传入zoom函数进行处理

在Zoom函数内先调用LoadBmp函数并传入原图图像名。在LoadBmp函数内先跳过位图文件头,然后定义位图信息头结构体变量head。从文件读入位图信息头结构体到head并从中获取图像的高,宽和每像素所占的位数。然后计算原图像每行字节数(注意位图数据中每个扫描行的字节数必须是4的倍数,这里+3是为了处理不满足4的倍数的情况,如果是4的倍数则结果和不+3的结果一样,如果不是4的倍数则结果进1位,先乘4然后除以4则是为了把数据归为4的倍数)。然后定义一个矩阵用以存放原图像像素信息,指针pIBuf指向该矩阵首地址。然后将源文件像素信息读入plBuf,单个元素大小为1字节,元素个数为每行字节数*行数。

然后在Zoom函数中根据命令行参数设定新图像的长度和宽度(加0.5是由于强制转换时不四舍五入而是直接截去小数部分),新图像的每行字节数。然后定义一个矩阵用以存放新图像像素信息,pINewBuf指向该矩阵首地址。这些准备工作完成后双重循环遍历新图像的每一个像素点进行双向线性插值。完成像素数据填充后调用SaveBmp函数并传入新图像的名字,存储新图像像素数据的矩阵和新图像的高度宽度,每像素位数进行保存操作。保存成功后释放像素矩阵指针。

在SaveBmp函数中,先计算新图像每行字节数。打开文件写入图像数据,根据我博客内的位图结构知识依次申请位图文件头结构变量,位图信息头结构变量并一项项填入图像信息(很多参数都可以直接填入默认值)。填完后将结构变量信息写入文件,最后将像素数据矩阵数据写入并关闭文件。

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <windows.h>
#include <stdlib.h>

long        IWidth;      
long        IHeight;     
long        biBitCount;    
long        INewWidth;   
long        INewHeight;  
long		ILineByte;  
long        INewLineByte;   
int         zoomnumberinput=100;    
float       zoomnumber;
unsigned char *pIBuf;   
unsigned char *pINewBuf;



int LoadBmp(const char* bmpName)
{
	FILE *fp=fopen(bmpName,"rb");
	if(fp==0)
	{ 
		printf("打开文件失败\n");
		return 0;
	}

	fseek(fp,sizeof(BITMAPFILEHEADER),0);
	BITMAPINFOHEADER head;
	fread(&head,sizeof(BITMAPINFOHEADER),1,fp);
	IWidth=head.biWidth;
	IHeight=head.biHeight;
	biBitCount=head.biBitCount;
	ILineByte=(IWidth*biBitCount/8+3)/4*4;
    pIBuf=(unsigned char*)malloc(sizeof(unsigned char)*ILineByte*IHeight);
	fread(pIBuf,1,ILineByte *IHeight,fp);
	return 1;
}

int SaveBmp(const char* bmpName,unsigned char *imgBuf,int width,int height,int biBitCount)
{
	if(!imgBuf){
	    return 0;
	}
	INewLineByte=(width*biBitCount/8+3)/4*4;
	FILE *fp=fopen(bmpName,"wb");
	if(fp==0){ 
		return 0;
	}
	BITMAPFILEHEADER fileHead;
	fileHead.bfType=0x4d42;
	fileHead.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+INewLineByte*height;
	fileHead.bfReserved1=0;
	fileHead.bfReserved2=0;
	fileHead.bfOffBits=54;
	fwrite(&fileHead,sizeof(BITMAPFILEHEADER),1,fp);
	BITMAPINFOHEADER head;
	head.biBitCount=biBitCount;
	head.biClrImportant=0;
	head.biClrUsed=0;
	head.biCompression=0;
	head.biHeight=height;
	head.biPlanes=1;
	head.biSize=40;
	head.biSizeImage=ILineByte *height;
	head.biWidth=width;
	head.biXPelsPerMeter=0;
	head.biYPelsPerMeter=0;
	fwrite(&head,sizeof(BITMAPINFOHEADER),1,fp);
	fwrite(imgBuf,height*INewLineByte,1,fp);
	fclose(fp);
	return 1;
}

int Zoom(const char* bmpName,const char* address)
{
    LoadBmp(bmpName);
	INewWidth=(int)((IWidth*zoomnumber)+0.5);
	INewHeight=(int)(IHeight*zoomnumber+0.5);
	ILineByte=(IWidth*biBitCount/8+3)/4*4;
	INewLineByte=(INewWidth*biBitCount/8+3)/4*4;
    pINewBuf=(unsigned char*)malloc(sizeof(unsigned char)*INewLineByte*INewHeight);

	long i;     
	long j;
	long k;	     
	long i0;	  
	long j0;
	

	for(i=0;i<INewHeight;i++)
   {
	    for(j=0;j<INewWidth;j++)
	   {
		    for(k=0;k<3;k++)  
		   {  
			i0=(int)i/zoomnumber; 
			j0=(int)j/zoomnumber;
			int i1=(int)i0;
			int i2=i1+1;
			int j1=(int)j0;
			int j2=j1+1;
			double fx1=i0-i1;
			double fx2=1.0-fx1;
			double fy1=j0-j1;
			double fy2=1.0-fy1;
			double s1=fx1*fy1;
			double s2=(1.0-fx1)*fy1;
			double s3=(1.0-fx1)*(1.0-fy1);
			double s4=fx1*(1.0-fy1);

			if((j0>=0)&&(j0<IWidth-1)&&(i0>=0)&&(i0<IHeight-1))
		   {
			*(pINewBuf+i*INewLineByte+j*3+k) =*(pIBuf+i2*ILineByte+j2*3+k)*s1
											 +*(pIBuf+i1*ILineByte+j2*3+k)*s2
											 +*(pIBuf+i1*ILineByte+j1*3+k)*s3
											 +*(pIBuf+i2*ILineByte+j1*3+k)*s4;
		   }else{
				  *(pINewBuf+i*INewLineByte+j*3+k)=255;
		   }
		}
	}
}

    SaveBmp(address,pINewBuf,INewWidth,INewHeight,biBitCount);
	free(pINewBuf);
	return 1;
}

int main(int argc,char** argv){
    zoomnumberinput=atoi(argv[2]);
    zoomnumber=zoomnumberinput*1.0/100.0;
    Zoom(argv[1],argv[3]); 
    return 0; 	
}

测试数据(输入,输出):

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/zimuzi2019/article/details/106721230