您的位置:首页 > 编程语言 > C语言/C++

【数字图像处理】<纯C++>读取、裁剪、缩放、旋转和存储8位bmp灰度图像

2013-12-09 11:31 791 查看
C++实现以下功能:1、实现对硬盘中的8位BMP图像的读取和存储;2、实现对指定矩形框区域图像的剪切;3、实现对任意形状图像的任意比例缩放;4、实现对任意形状图像的任意角度选装。
程序编辑环境:在visual c++6.0中建立console application工程,win32平台,无MFC,空Project
程序运行说明:    1、添加文件Ex1.cpp、Ex1.h    2、编译、运行    3、按程序提示完成操作(注意文件名称的大小写)    4、进入文件夹,查看Cutresult.bmp(裁剪结果)、Scalresult.bmp(缩放结果)和Rotresult.bmp(旋转结果)文件。 [code]感谢以下三篇博文:http://blog.csdn.net/xiajun07061225/article/details/5813726http://blog.csdn.net/wangyadong/article/details/3549284http://blog.csdn.net/xiajun07061225/article/details/6633938
以下代码是参照以上内容做适当修改完成的。
头文件Ex1.h内容如下:
typedef unsigned char BYTE;typedef unsigned short WORD;typedef unsigned int DWORD;typedef long LONG;
//位图文件头定义;//其中不包含文件类型信息(由于结构体的内存结构决定,//要是加了的话将不能正确读取文件信息)typedef struct  tagBITMAPFILEHEADER{//	WORD bfType;//文件类型,必须是0x424D,即字符“BM”	DWORD bfSize;//文件大小	WORD bfReserved1;//保留字	WORD bfReserved2;//保留字	DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER{	DWORD biSize;//信息头大小	LONG biWidth;//图像宽度	LONG biHeight;//图像高度	WORD biPlanes;//位平面数,必须为1	WORD biBitCount;//每像素位数	DWORD  biCompression; //压缩类型	DWORD  biSizeImage; //压缩图像大小字节数	LONG  biXPelsPerMeter; //水平分辨率	LONG  biYPelsPerMeter; //垂直分辨率	DWORD  biClrUsed; //位图实际用到的色彩数	DWORD  biClrImportant; //本位图中重要的色彩数}BITMAPINFOHEADER; //位图信息头定义
typedef struct tagRGBQUAD{	BYTE rgbBlue; //该颜色的蓝色分量	BYTE rgbGreen; //该颜色的绿色分量	BYTE rgbRed; //该颜色的红色分量	BYTE rgbReserved; //保留值}RGBQUAD;//调色板定义
//像素信息typedef struct tagIMAGEDATA{	BYTE blue;///8位灰度图用其中1个	//BYTE green;	//BYTE red;}IMAGEDATA;

源文件Ex1.cpp内容如下:
// Ex1.cpp : Defines the entry point for the console application.//
#include <stdio.h>#include "Ex1.h"#include "stdlib.h"#include "math.h"#include <iostream>
#define PI 3.14159//圆周率宏定义#define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度
using namespace std;
//变量定义BITMAPFILEHEADER strHead; //位图文件头RGBQUAD strPla[256];//256色调色板BITMAPINFOHEADER strInfo;
//显示位图文件头信息void showBmpHead(BITMAPFILEHEADER pBmpHead){	cout<<"读取的位图文件信息:"<<endl;	cout<<"文件大小:"<<pBmpHead.bfSize<<endl;//	cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;//	cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl;	cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<endl;}
void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead){//	cout<<"读取的位图信息头:"<<endl;	cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl;	cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl;	cout<<"位图高:"<<pBmpInforHead.biHeight<<endl;	cout<<"biPlanes平面数:"<<pBmpInforHead.biPlanes<<endl;	cout<<"biBitCount采用颜色位数:"<<pBmpInforHead.biBitCount<<endl;	cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl;	cout<<"biSizeImage实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl;	cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl;	cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl;	cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl;	cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl<<endl;}
int max(int i,int j){	if(i>=j)		return i;	else		return j;
}int main(){	char strFile[LENGTH_NAME_BMP]; //bmp文件名	WORD bfType_w=0x4d42;	IMAGEDATA *imagedata = NULL;   //动态分配存储原图片的像素信息的二维数组	IMAGEDATA *imagedataCut = NULL;//动态分配存储裁剪后的图片的像素信息的二维数组	IMAGEDATA *imagedataScal = NULL;//动态分配存储缩放后的图片的像素信息的二维数组	IMAGEDATA *imagedataRot = NULL;//动态分配存储旋转后的图片的像素信息的二维数组
int width,height;//图片的宽度和高度	float ExpScalValue=0; ////期望的缩放倍数(允许小数)	int FloatToIntwidth,FloatToIntheight;/////小数变成整数(float To Int)	int RotateAngle=90;//要缩放的角度,默认90
cout<<"请输入所要读取的文件名(w.bmp或者06.bmp或者07.bmp):"<<endl;	cin>>strFile;	FILE *fpi,*fpw;	fpi=fopen(strFile,"rb");	if(fpi != NULL)	{		//先读取文件类型		WORD bfType;		fread(&bfType,1,sizeof(WORD),fpi);		if(0x4d42!=bfType)///if !=BMP		{			cout<<"the file is not a bmp file!"<<endl;			return NULL;		}				printf("\n读取到的文件是%s。\n\n",strFile);		//读取bmp文件的文件头和信息头		fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);		showBmpHead(strHead);//显示文件头		fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);		showBmpInforHead(strInfo);//显示文件信息头
//读取调色板		for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)		{			fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi);			fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi);			fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi);			fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi);		}

}	else	{		cout<<"file open error!"<<endl;		return NULL;	}		width = strInfo.biWidth;		height = strInfo.biHeight;			imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));///为原始图像分配存储空间		//初始化原始图片的像素数组		for(int i = 0;i < height;++i)		{			for(int j = 0;j < width;++j)			{								(*(imagedata + i * width + j)).blue = 0;				//(*(imagedata + i * width + j)).green = 0;				//(*(imagedata + i *  width + j)).red = 0;			}		}      				//读出图片的像素数据  读取时一次一行  读高度数的行		fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);/////size_t fread( void *buffer, size_t size, size_t count, FILE *stream );		fclose(fpi);	    //////////=====图片裁剪处理======//////////		int leftdownx,leftdowny,rightupx,rightupy;/////用户输入数值		int Rleftdownx,Rleftdowny,Rrightupx,Rrightupy;/////转换成实际可以使用数值		cout<<"请输入要裁剪的矩形区域的左下角和右上角的坐标(连续四个整数值,如50 50 300 300):"<<endl;		cin>>leftdownx;		cin>>leftdowny;		cin>>rightupx;		cin>>rightupy;		if(leftdownx<=0||leftdowny<=0)////将用户输入的矩形框限定在原图像中		{			Rleftdownx=0;			Rleftdowny=0;		}		else		{			Rleftdownx=leftdownx;			Rleftdowny=leftdowny;		}		if(rightupx>=width)		{			Rrightupx=width;		}		else		{			Rrightupx=rightupx;
}		if(rightupy>=height)		{			Rrightupy=height;		}		else		{			Rrightupy=rightupy;		}		int CutWidth=Rrightupx-Rleftdownx;		////////图像每一行的字节数必须是4的整数倍		CutWidth = (CutWidth * sizeof(IMAGEDATA) + 3) / 4 * 4;////矩形框实际宽度
int CutHeight=Rrightupy-Rleftdowny;////矩形框实际高度				imagedataCut = (IMAGEDATA*)malloc(CutWidth * CutHeight * sizeof(IMAGEDATA));///为裁剪后图像分配存储空间		//初始化裁剪后图片的像素数组  一个字节一个字的写入		for(  i = 1;i < CutHeight ;++i)		{			for(int j = 0;j < CutWidth ;++j)			{								(*(imagedataCut + i * CutWidth + j)).blue = 0;							}		}
imagedata+=(Rleftdowny)*width+Rleftdownx;////原始图像数据数组指针移动到矩形框的左下角。		for(int k = 0;k < CutHeight;++k)////裁剪区域数据提取		{			for(int l = 0;l < CutWidth;++l)				*(imagedataCut + k * CutWidth + l) = *(imagedata+ k * width + l + Rleftdownx);////此式子一定要注意写法。主要是注意二维数组指针的用法。
}		//保存bmp图片		if((fpw=fopen("Cutresult.bmp","wb"))==NULL)		{			cout<<"create the bmp file error!"<<endl;			return NULL;		}		fwrite(&bfType_w,1,sizeof(WORD),fpw);		fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);		strInfo.biWidth = CutWidth;		strInfo.biHeight = CutHeight;		fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);		//保存调色板数据		for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)		{			fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw);		}		//保存像素数据		for(i =0;i < CutHeight;++i)		{			for(int j = 0;j < CutWidth;++j)			{				fwrite( &((*(imagedataCut + i * CutWidth + j)).blue),1,sizeof(BYTE),fpw);				//fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).green),1,sizeof(BYTE),fpw);				//fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).red),1,sizeof(BYTE),fpw);			}		}		printf("裁剪变换完成,请查看Cutresult.bmp文件。\n\n");				fclose(fpw);		//释放内存        //delete[] imagedata;///不能释放imagedata,里面还有数据。		delete[] imagedataCut;		///////===========图片裁剪处理结束======////////
///////==========重新打开文件======////////		cout<<"请输入所要读取的文件名(Cutresult.bmp):"<<endl;		cin>>strFile;				fpi=fopen(strFile,"rb");		if(fpi != NULL)		{			//先读取文件类型			WORD bfType;			fread(&bfType,1,sizeof(WORD),fpi);			if(0x4d42!=bfType)///if !=BMP			{				cout<<"the file is not a bmp file!"<<endl;				return NULL;			}			printf("\n读取到的文件是%s。\n\n",strFile);			//读取bmp文件的文件头和信息头			fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);			showBmpHead(strHead);//显示文件头			fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);			showBmpInforHead(strInfo);//显示文件信息头			//读取调色板			for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)			{				fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi);				fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi);				fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi);				fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi);			}			width = strInfo.biWidth;			height = strInfo.biHeight;			imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));///为原始图像分配存储空间			//初始化原始图片的像素数组			for(int i = 0;i < height;++i)			{				for(int j = 0;j < width;++j)				{					(*(imagedata + i * width + j)).blue = 0;					//(*(imagedata + i * width + j)).green = 0;					//(*(imagedata + i *  width + j)).red = 0;				}			} 			//读出图片的像素数据  读取时一次一行  读高度数的行			fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);/////			fclose(fpi);		}		else		{			cout<<"file open error!"<<endl;			return NULL;		}		//图片缩放处理		cout<<"请输入要缩放的倍数:"<<endl;		cin>>ExpScalValue;		///如果ExpScalValue含有小数,需要整数化		///对期望的缩放结果取整		FloatToIntwidth=(int)(ExpScalValue*width);		FloatToIntheight=(int)(ExpScalValue*height);		//图像每一行的字节数必须是4的整数倍		FloatToIntwidth = (FloatToIntwidth * sizeof(IMAGEDATA) + 3) / 4 * 4;		imagedataScal = (IMAGEDATA*)malloc(FloatToIntwidth * FloatToIntheight * sizeof(IMAGEDATA));///为缩放后图像分配存储空间		//初始化缩放后图片的像素数组  一个字节一个字的写入		for( i = 1;i < FloatToIntheight ;++i)		{			for(int j = 0;j < FloatToIntwidth ;++j)			{				(*(imagedataScal + i * FloatToIntwidth + j)).blue = 0;				//(*(imagedataRot + i * FloatToIntwidth + j)).green = 0;				//(*(imagedataRot + i * FloatToIntwidth + j)).red = 0;			}		}		int pre_i,pre_j,after_i,after_j;//缩放前后对应的像素点坐标		for( i=0;i<FloatToIntheight;++i)		{			for(int j=0;j<FloatToIntwidth;++j)			{				after_i=i;				after_j=j;				pre_i = (int)(after_i/ExpScalValue);/////取整,插值方法为:最邻近插值(近邻取样法)				pre_j = (int)(after_j/ExpScalValue);				if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内					*(imagedataScal + i * FloatToIntwidth + j) = *(imagedata + pre_i * width + pre_j);			}		}		//保存bmp图片		if((fpw=fopen("Scalresult.bmp","wb"))==NULL)		{			cout<<"create the bmp file error!"<<endl;			return NULL;		}		fwrite(&bfType_w,1,sizeof(WORD),fpw);		fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);		strInfo.biWidth = FloatToIntwidth;		strInfo.biHeight = FloatToIntheight;		fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);		//保存调色板数据		for( nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)		{			fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw);			fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw);		}		//保存像素数据		for(i =0;i < FloatToIntheight;++i)		{			for(int j = 0;j < FloatToIntwidth;++j)			{				fwrite( &((*(imagedataScal + i * FloatToIntwidth + j)).blue),1,sizeof(BYTE),fpw);				//fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).green),1,sizeof(BYTE),fpw);				//fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).red),1,sizeof(BYTE),fpw);			}		}		printf("缩放变换完成,请查看Scalresult.bmp文件。\n\n");			fclose(fpw);		//释放内存		delete[] imagedata;		delete[] imagedataScal;////////==============图像缩放处理结束==============///////	///////===============图片旋转处理====================///////
cout<<"请输入所要读取的文件名(Scalresult.bmp):"<<endl;	cin>>strFile;	FILE *fRor,*fRow;	int AnyX=0;////为适应任意形状做的长宽调整参数	fRor=fopen(strFile,"rb");	if(fRor != NULL){		//先读取文件类型		WORD bfType;		fread(&bfType,1,sizeof(WORD),fRor);		if(0x4d42!=bfType)		{			cout<<"the file is not a bmp file!"<<endl;			return NULL;		}		printf("\n读取到的文件是%s。\n\n",strFile);		//读取bmp文件的文件头和信息头		fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fRor);		showBmpHead(strHead);//显示文件头		fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fRor);		showBmpInforHead(strInfo);//显示文件信息头
//读取调色板		for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)		{			fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fRor);			fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fRor);			fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fRor);			fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fRor);		}
width = strInfo.biWidth;		height = strInfo.biHeight;		AnyX=max(width,height);///适合任意形状图形		//图像每一行的字节数必须是4的整数倍		width = (width * sizeof(IMAGEDATA) + 3) / 4 * 4;		//imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));		imagedata = (IMAGEDATA*)malloc(width * height);//		imagedataRot = (IMAGEDATA*)malloc(2 * width * 2 * height * sizeof(IMAGEDATA));		imagedataRot = (IMAGEDATA*)malloc(2 * AnyX * 2 * AnyX * sizeof(IMAGEDATA));		//初始化原始图片的像素数组		for(int i = 0;i < height;++i)		{			for(int j = 0;j < width;++j)			{				(*(imagedata + i * width + j)).blue = 0;				//(*(imagedata + i * width + j)).green = 0;				//(*(imagedata + i *  width + j)).red = 0;			}		}		//初始化旋转后图片的像素数组		//for( i = 0;i < 2 * height;++i)        for( i = 0;i < 2 * AnyX;++i)		{			for(int j = 0;j < 2 * AnyX;++j)			{				(*(imagedataRot + i * 2 * AnyX + j)).blue = 0;				//(*(imagedataRot + i * 2 * width + j)).green = 0;				//(*(imagedataRot + i * 2 * width + j)).red = 0;			}		}		//读出图片的像素数据		fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fRor);		fclose(fRor);	}	else	{		cout<<"file open error!"<<endl;		return NULL;	}

//图片旋转处理	double angle;//要旋转的弧度数	int midX_pre,midY_pre,midX_aft,midY_aft;//旋转所围绕的中心点的坐标	midX_pre = width / 2;	midY_pre = height / 2;	midX_aft = AnyX;	midY_aft = AnyX;
cout<<"请输入要旋转的角度(0度到360度,逆时针旋转):"<<endl;	cin>>RotateAngle;	angle = 1.0 * RotateAngle * PI / 180;	/* 图像旋转的几何公式	x2=x1*cos(θ2)-y1*sin(θ2);	y2=x1*sin(θ2)+y1*cos(θ2);	*///	for( i = 0;i < 2 * height;++i)	for( i = 0;i < 2 * AnyX;++i)	{		for(int j = 0;j < 2 * AnyX;++j)		{			after_i = i - midX_aft;//坐标变换,如果不变换坐标,以(0,0)为参考点旋转后图片将转出坐标外。			after_j = j - midY_aft;              			pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midX_pre ; /////取整,插值方法为:最邻近插值(近邻取样法)			pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midY_pre;						/*after_i = i;			after_j = j; ////坐标不变换
pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) ;			pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) ;*/////坐标不变换效果
if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内				*(imagedataRot + i * 2 * AnyX + j) = *(imagedata + pre_i * AnyX + pre_j);
}	}
//保存bmp图片	if((fRow=fopen("Rotresult.bmp","wb"))==NULL)	{		cout<<"create the bmp file error!"<<endl;		return NULL;	}	bfType_w=0x4d42;	fwrite(&bfType_w,1,sizeof(WORD),fRow);//fRow自动+=2;		fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fRow);	strInfo.biWidth = 2 * AnyX;//width;	strInfo.biHeight = 2 * AnyX;//height;	fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fRow);	//保存调色板数据	for( nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)	{		fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fRow);		fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fRow);		fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fRow);		fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fRow);	}	//保存像素数据	for( i =0;i < 2 * AnyX;++i)	{		//for(int j = 0;j < 2 * width;++j)		for(int j = 0;j < 2 * AnyX;++j)		{			fwrite( &((*(imagedataRot + i * 2 * AnyX + j)).blue),1,sizeof(BYTE),fRow);			//fwrite( &((*(imagedataRot + i * 2 * width + j)).green),1,sizeof(BYTE),fpw);			//fwrite( &((*(imagedataRot + i * 2 * width + j)).red),1,sizeof(BYTE),fpw);		}	}	printf("旋转变换完成,请查看Rotresult.bmp文件。\n\n");	fclose(fRow);
//释放内存	delete[] imagedata;	delete[] imagedataRot;/////==================图像旋转处理结束============///////////
return 1;}

/*程序用到的图片从谷歌图片里可以搜到,只要是8位BMP位图即可。 将名字改成06.bmp、07.bmp或w.bmp*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐