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

原文:http://blog.csdn.net/zhengtu009/article/details/17220319


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(旋转结果)文件。
 

感谢以下三篇博文:
http://blog.csdn.net/xiajun07061225/article/details/5813726
http://blog.csdn.net/wangyadong/article/details/3549284
http://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
*/


  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值