位图图像文件缩放(c++)

位图图像文件缩放(c++)

前言: c语言课程设计需要实现位图图像缩放,但并没有提供很详细的位图图像的理解,所以颇费了一番功夫,这里给大家整理下以供参考,如有不当之处,还希望纠正


理解位图图像(24位)

本篇文章只介绍biBitCount=24的位图图像,且代码也只适用于24位图像,且无压缩

定义

由多个像素点组成的图像

组成元素

共三部分

  • 位图文件头数据结构,包含BMP图像文件的类型、显示内容等信息
  • 位图信息数据结构,包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息
  • 每个像素点信息 (主要部分)

结构体定义

头文件
#include

  • BITMAPFILEHEADER (对应文件头)
  typedef struct tagBITMAPFILEHEADER {
    WORD bfType;//固定为0x4d42
    DWORD bfSize;//文件大小
    WORD bfReserved1;//保留字
    WORD bfReserved2;//保留字
    DWORD bfOffBits;//实际位图数据的偏移字节数,即前两个部分长度之和
  } BITMAPFILEHEADER,*LPBITMAPFILEHEADER,*PBITMAPFILEHEADER;
  • BITMAPINFOHEADER (对应信息数据)
  typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;//指定此结构体的长度,为40
    LONG biWidth;//位图宽(单位:像素)
    LONG biHeight;//位图高(单位:像素)
    WORD biPlanes;//平面数,为1
    WORD biBitCount;//采用颜色位数,可以是1 2 4 8 16 24 32
    DWORD biCompression;//压缩方式,可以是0 1 2,其中0表示不压缩
    DWORD biSizeImage;//实际位图数据占用的字节数
    LONG biXPelsPerMeter;//X方向分辨率
    LONG biYPelsPerMeter;//Y方向分辨率
    DWORD biClrUsed;//使用的颜色数	
    DWORD biClrImportant;//重要颜色数
  } BITMAPINFOHEADER,*LPBITMAPINFOHEADER,*PBITMAPINFOHEADER;

每个属性的具体意义可参考 这里

看起来属性很多,你只需要关注这些:

  • biWidth
  • biHeight
  • biBitCount

下面主要介绍biBitCount和像素点的存储方式

  • biBitCount 可以理解为每个像素点采用多少位二进制数来描述,例如biBitCount=24代表,每个像素点用24位二进制数描述,而每个像素点又由三个颜色通道组成(rgb),所以每个颜色通道由8位二进制数(1个字节)组成
  • 像素点的存储方式 可以先想象成一个矩阵,开始是矩阵的左下角,然后按行存储位图图像文件缩放(c++)_第1张图片
    所以你只需要按照顺序依次存储新的像素单位即可,但这里有一个比较坑的地方,位图规定每一行元素的大小必须是4byte的整数倍,意思就是如果每一行有1个像素点,即24bit,需要在每一行结尾填充8bit,这8bit数据没有任何意义,只是为了凑齐4byte的整数倍

主要算法

  • 计算出缩放后图像的height和width(单位:像素点数量)
  • 遍历目标图像的每一个像素点,找到它在源图像的位置,根据双线性插值获得该像素点的三个颜色通道的值
    位图图像文件缩放(c++)_第2张图片
    上图为目标图像的一个像素点(E)在源图像的位置,其中x和y均为整数,E点必定落在源图像的一个像素格中,设 F(E) 代表E点一个颜色通道的值,那么根据双线性插值可以求得
    F ( E ) = d x d y F ( B ) + ( 1 − d x ) ( 1 − d y ) F ( D ) + ( 1 − d x ) d y F ( A ) + d x ( 1 − d y ) F ( C ) F(E)=dxdyF(B) +(1-dx)(1-dy)F(D)+(1-dx)dyF(A)+dx(1-dy)F(C) F(E)=dxdyF(B)+(1dx)(1dy)F(D)+(1dx)dyF(A)+dx(1dy)F(C)

代码

运行方法
例如源程序名为 convert.cpp 源图像名 cat.bmp 且在同一路径下
目标图像名为 cat_a.bmp 需要放大1.5倍
编译好后,在命令行输入
convert cat.bmp 150 cat_a.bmp

/*@auther:gtyinstinct*/
/*该程序只能处理biBitCount为24的bmp图片*/
#include
#include
#define   WIDTHBYTES(bits) (((bits)+31)/32*4)//用于使图像宽度所占字节数为4byte的倍数
 
using namespace std;
int bmpwidth,bmpheight; //原图像的宽度和高度(单位:像素)
int src_width,dest_width; //实际存储每一行宽度(单位:字节)
int new_bmpwidth,new_bmpheight;//新图像的宽度和高度(单位:像素)
unsigned char *pBmpBuf;  //存储原图像像素数据
unsigned char *new_pBmpBuf; //存储新图像像素数据
BITMAPFILEHEADER header;//位图文件头
BITMAPINFOHEADER info;//位图文件信息
int idx(int i,int j,int k){
	return i*src_width + j*3 + k;
}
int idx_n(int i,int j,int k){
	return i*dest_width + j*3 + k;
}
unsigned char process(int i1,int i2,int j1,int j2,int k,double i_t,double j_t){
	unsigned int tmp[] = {pBmpBuf[idx(i1,j1,k)],
						  pBmpBuf[idx(i1,j2,k)],
						  pBmpBuf[idx(i2,j1,k)],
						  pBmpBuf[idx(i2,j2,k)]};
	unsigned int ret =  (unsigned int)(tmp[0] * (1-i_t) * (1-j_t) +
									   tmp[1] * (1-i_t) * j_t +
									   tmp[2] * i_t * (1-j_t) +
									   tmp[3] * i_t * j_t);
	return (unsigned char) ret;
}
bool convert(char *bmpName,char *new_bmpName,double scale)
{
	/*检查缩放比例是否正确*/
	if(scale <=0){
		cout << "scale is not correct" << endl;
		return 0;
	}
	
	/*以二进制的方式打开文件*/
    FILE *src,*dest;
	dest = fopen(new_bmpName,"wb");
    if( (src = fopen(bmpName,"rb")) == NULL)  
    {
        cout<<"File "<< bmpName <<"failed to open"<<endl;
        return 0;
    }
    
	/*读取文件头和位图信息*/
	fread(&header,sizeof(BITMAPFILEHEADER),1,src); 
    fread(&info,sizeof(BITMAPINFOHEADER),1,src);   
	
	/*获取并打印位图大小*/
    bmpwidth = info.biWidth;
    bmpheight = info.biHeight;
	if(info.biBitCount!=24){
		cout << "biBitCount is not equal to 24" << endl;
		return 0;
	}
    cout << "source image:" << endl;
    cout << "width:" << bmpwidth << " height:" << bmpheight << endl;
	
	/*计算缩放后的位图大小*/
	new_bmpwidth = (int) (bmpwidth*scale); 
	new_bmpheight = (int) (bmpheight*scale); 
	cout << "dest image:" << endl;
	cout << "width:" << new_bmpwidth << " height:" << new_bmpheight << endl;
	
	/*修改位图信息的大小信息*/
	info.biWidth = new_bmpwidth;
	info.biHeight = new_bmpheight;
	
	/*计算位图的实际宽度并确保是4byte的倍数*/
	src_width = WIDTHBYTES(bmpwidth*info.biBitCount);
	dest_width = WIDTHBYTES(new_bmpwidth*info.biBitCount);
	
	/*写入新位图的位图信息和文件头*/
	fwrite(&header,sizeof(BITMAPFILEHEADER),1,dest);
	fwrite(&info,sizeof(BITMAPINFOHEADER),1,dest);

	/*读取原位图的像素阵列*/
    pBmpBuf = new unsigned char[bmpheight*src_width];
    fread(pBmpBuf,sizeof(unsigned char),bmpheight*src_width,src);
	
	/*计算新位图的像素阵列并写入*/
	new_pBmpBuf = new unsigned char[dest_width*new_bmpheight];
	for(int i=0;i<new_bmpheight;i++){
		for(int j=0;j<new_bmpwidth;j++){
			double i_,j_;
			i_ = 1.0 * i * (bmpheight-1) / (new_bmpheight-1);
			j_ = 1.0 * j * (bmpwidth-1) / (new_bmpwidth-1);		
			int i1,i2,j1,j2;
			i1 = (int)i_;
			j1 = (int)j_;
			i2 = i1 + 1;
			j2 = j1 + 1;
			double i_t,j_t;
			i_t = i_ - i1;
			j_t = j_ - j1;
			for(int k=0;k<3;k++){
				new_pBmpBuf[idx_n(i,j,k)] = process(i1,i2,j1,j2,k,i_t,j_t);
			}
		}
	}
	
	fwrite(new_pBmpBuf,sizeof(unsigned char),dest_width*new_bmpheight,dest);
	
	/*关闭文件*/
    fclose(src); 
	fclose(dest);
    return 1;
}
 
int main(int argc,char** argv)
{
	if(convert(argv[1],argv[3],(double)atoi(argv[2])/100))
		cout << "convert successfully";
	else 
		cout << "fail to convert";
    return 0;
}
/*@auther:gtyinstinct*/

ps:仅供参考,xder别直接拿源码交差

你可能感兴趣的:(计算机基础)