【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现

要识别图像中的字符,首先要会处理图像,把图像的信息读出来。这就必须先了解图像的结构,存储方式。清华大学出版的一本《数字图像处理编程入门》给了我不少帮助。第一章的Windows位图和调色板让我对bmp图像有了基本了解。对于彩色图,可以用RGB模型来表示。基本上所有颜色都可以用这三种颜色的组合来形成。但实际上也有一些差别,小于24位图都利用到了调色板,也就是一张R、G、B表,主要是为了节省存储空间。bmp文件结构如下:

第一部分为位图文件头BITMAPFILEHEADER,是一个结构,其定义如下:

typedef struct tagBITMAPFILEHEADER {

WORD           bfType;

DWORD bfSize;

WORD           bfReserved1;

WORD           bfReserved2;

DWORD bfOffBits;

} BITMAPFILEHEADER;

这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数),各个域的说明如下:

bfType

指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。

bfSize

指定文件大小,包括这14个字节。

bfReserved1,bfReserved2     

为保留字,不用考虑

bfOffBits

为从文件头到实际的位图数据的偏移字节数,即图1.3中前三个部分的长度之和。

第二部分为位图信息头BITMAPINFOHEADER,也是一个结构,其定义如下:

typedef struct tagBITMAPINFOHEADER{

DWORD  biSize;

LONG            biWidth;

LONG            biHeight;

WORD           biPlanes;

WORD           biBitCount

DWORD  biCompression;

DWORD  biSizeImage;

LONG            biXPelsPerMeter;

LONG            biYPelsPerMeter;

DWORD  biClrUsed;

DWORD  biClrImportant;

} BITMAPINFOHEADER;

这个结构的长度是固定的,为40个字节(LONG32位整数),各个域的说明如下:

biSize

指定这个结构的长度,为40

biWidth

指定图象的宽度,单位是象素。

biHeight

指定图象的高度,单位是象素。

biPlanes

必须是1,不用考虑。

biBitCount

指定表示颜色时要用到的位数,常用的值为1(黑白二色图), 4(16色图), 8(256), 24(真彩色图)(新的.bmp格式支持32位色,这里就不做讨论了)

biCompression

指定位图是否压缩,有效的值为BI_RGBBI_RLE8BI_RLE4BI_BITFIELDS(都是一些Windows定义好的常量)。要说明的是,Windows位图可以采用RLE4,和RLE8的压缩格式,但用的不多。我们今后所讨论的只有第一种不压缩的情况,即biCompressionBI_RGB的情况。

biSizeImage

指定实际的位图数据占用的字节数,其实也可以从以下的公式中计算出来:

biSizeImage=biWidth’ × biHeight

要注意的是:上述公式中的biWidth’必须是4的整倍数(所以不是biWidth,而是biWidth’,表示大于或等于biWidth的,最接近4的整倍数。举个例子,如果biWidth=240,则biWidth’=240;如果biWidth=241biWidth’=244)

如果biCompressionBI_RGB,则该项可能为零

biXPelsPerMeter

指定目标设备的水平分辨率,单位是每米的象素个数,关于分辨率的概念,我们将在第4章详细介绍。

biYPelsPerMeter

指定目标设备的垂直分辨率,单位同上。

biClrUsed

指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2biBitCount

biClrImportant

指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的。

第三部分为调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。有些位图,如真彩色图,前面已经讲过,是不需要调色板的,BITMAPINFOHEADER后直接是位图数据。

调色板实际上是一个数组,共有biClrUsed个元素(如果该值为零,则有2biBitCount个元素)。数组中每个元素的类型是一个RGBQUAD结构,占4个字节,其定义如下:

typedef struct tagRGBQUAD {

BYTE    rgbBlue; //该颜色的蓝色分量

BYTE    rgbGreen; //该颜色的绿色分量

BYTE    rgbRed; //该颜色的红色分量

BYTE    rgbReserved; //保留值

} RGBQUAD;

第四部分就是实际的图象数据了。对于用到调色板的位图,图象数据就是该象素颜在调色板中的索引值。对于真彩色图,图象数据就是实际的RGB值。下面针对2色、16色、256色位图和真彩色位图分别介绍。

对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。

对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。

对于256色位图,一个字节刚好可以表示1个象素。

对于真彩色图,三个字节才能表示1个象素,哇,好费空间呀!没办法,谁叫你想让图的颜色显得更亮丽呢,有得必有失嘛。

要注意两点:

(1)    每一行的字节数必须是4的整倍数,如果不是,则需要补齐。这在前面介绍biSizeImage时已经提到了。

(2)    一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。

 

 

      当了解了这些后,就可以将图片灰度化,编程黑白二色图片。再读出bmp文件的像素信息,可以将其存储在一个一维数组里面,其他的信息还有宽度和高度。以后处理图片就是直接对这个数组进行处理。接下来是进行去噪处理。一些图片常常有噪点,对识别效果造成影响,所以必须进行去噪。去噪方法很多。我的做法是对一个像素点作如下处理:取它和周围8个点共9个点的像素的平均值,效果还可以。

      接下来的操作我都是参考的一篇哈尔滨工业大学工学硕士学位论文,上面的思路很清晰,感觉很不错。首先是归一化,即将图片编程32*32大小的图片。

      另一种方法是非线性归一化,但是上面的求质心和散度公式看不清楚,而且没有告诉怎么用质心和散度去实现归一化。所以我就采用了线性归一化。效果比非线性归一化要差一些。

      归一化之后是特征提取。

    网格特征就是将32*32的图片分成4*4共16块,每个方块64个小方块。求黑色像素的个数就行了。穿越特征包括水平穿越特征和垂直穿越特征。水平穿越特征即把图片按行分成8行,每行4小行。计算每一行由白色像素到黑色像素的变化次数即可。即得到前8维水平穿越特征t1,t2,..,t8。后8维水平穿越特征利用公式求解。Pi=ti/[(t1+t2+..+t8)*10+0.5]。垂直穿越特征则类似。

      16维网格特征、16维水平穿越特征和16维垂直穿越特征合起来总共48维特征。还可以求加权特征,形成64维特征。

      最后是模板匹配。根据相应特征值的差值的平方和进行匹配。

通过我这些天用C++读写bmp图像的经历,摸索再摸索,终于对bmp文件的结构、操作有了一定的了解,下面就大概介绍bmp图片纯C++的读取、旋转和保存的实现过程。

要用C++读取bmp图片文件,首先要弄清楚bmp格式图片文件的结构。可以参考这篇文章:http://blog.csdn.net/xiajun07061225/article/details/5813726

有几点需要注意的是:

在读取bmp图片的时候,一定要注意内存对齐的问题,譬如文件头,否则无法读取出正确结果。

关于图片的像素数据,每一行的像素的字节数必须是4的整数倍。如果不是,则需要补齐。一般来说,bmp图像文件的数据是从下到上,从左到右的。即从文件中最先读到的是图像最下面一行的左边第一个像素,然后是坐标第二个.....接下来是倒数第二行的第一个像素。

采用的编译环境是VS2008。

关于图像旋转,并不难。只需要搞清楚像素坐标变换公式就行。我以图像的中心点为坐标原点。先把像素在目标图像中的位置变化为坐标系中的位置,做旋转变换求出变换之前的在坐标系中的坐标,再变换为在图片中的位置。

公式:(x1,y1)是变换之前的坐标系中的坐标,(x2,y2)是变换之后的坐标系中的坐标。angle为逆时针旋转的角度数。

x1 = cos(angle)*x2-sin(angle)*y2;
y1 = sin(angle)*x2-cos(angle)*y2;


我的代码分为两个版本:灰度图的和彩色图的。


灰度图:

灰度图是只含亮度信息,不含彩色信息的图像。bmp格式文件中并没有灰度图这个概念,但是我们很容易地用bmp文件来表示灰度图。方法是用256色的调色板,只不过这个调色板有点特殊,每一项的RGB值都是相同的,从(0,0,0),(1,1,1),...,一直到(255,255,255)。这样,灰度图就可以用256色图来表示了。其图像数据就是调色板索引值,也就是实际的RGB的亮度值。另外因为是256色的调色板,所以图像数据中的一个字节代表一个像素。如果是彩色的256色图,图像处理后可能会产生不属于这256色的颜色,所以,图像处理一般采用灰度图。这也可以更好地将重点放在算法上。

下面是灰度图旋转代码,能处理任意尺寸的bmp灰度图,以及旋转任意角度(逆时针)。

代码包括两个文件:BmpRot.h和BmpRot.cpp

BmpRot.h:

  1. typedef unsigned char BYTE;  
  2. typedef unsigned short WORD;  
  3. typedef unsigned int DWORD;  
  4. typedef long LONG;  
  5.   
  6. //位图文件头定义;  
  7. //其中不包含文件类型信息(由于结构体的内存结构决定,  
  8. //要是加了的话将不能正确读取文件信息)  
  9. typedef struct  tagBITMAPFILEHEADER{  
  10.     //WORD bfType;//文件类型,必须是0x424D,即字符“BM”  
  11.     DWORD bfSize;//文件大小  
  12.     WORD bfReserved1;//保留字  
  13.     WORD bfReserved2;//保留字  
  14.     DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数  
  15. }BITMAPFILEHEADER;  
  16.   
  17. typedef struct tagBITMAPINFOHEADER{  
  18.     DWORD biSize;//信息头大小  
  19.     LONG biWidth;//图像宽度  
  20.     LONG biHeight;//图像高度  
  21.     WORD biPlanes;//位平面数,必须为1  
  22.     WORD biBitCount;//每像素位数  
  23.     DWORD  biCompression; //压缩类型  
  24.     DWORD  biSizeImage; //压缩图像大小字节数  
  25.     LONG  biXPelsPerMeter; //水平分辨率  
  26.     LONG  biYPelsPerMeter; //垂直分辨率  
  27.     DWORD  biClrUsed; //位图实际用到的色彩数  
  28.     DWORD  biClrImportant; //本位图中重要的色彩数  
  29. }BITMAPINFOHEADER; //位图信息头定义  
  30.   
  31. typedef struct tagRGBQUAD{  
  32.     BYTE rgbBlue; //该颜色的蓝色分量  
  33.     BYTE rgbGreen; //该颜色的绿色分量  
  34.     BYTE rgbRed; //该颜色的红色分量  
  35.     BYTE rgbReserved; //保留值  
  36. }RGBQUAD;//调色板定义  
  37.   
  38. //像素信息  
  39. typedef struct tagIMAGEDATA  
  40. {  
  41.     BYTE blue;  
  42.     //BYTE green;  
  43.     //BYTE red;  
  44. }IMAGEDATA;  
BmpRot.cpp:

  1. #include <stdio.h>  
  2. #include "BmpRot.h"  
  3. #include "stdlib.h"  
  4. #include "math.h"  
  5. #include <iostream>  
  6.   
  7. #define PI 3.14159//圆周率宏定义  
  8. #define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度  
  9.   
  10. using namespace std;  
  11.   
  12. //变量定义  
  13. BITMAPFILEHEADER strHead;  
  14. RGBQUAD strPla[256];//256色调色板  
  15. BITMAPINFOHEADER strInfo;  
  16.   
  17. //显示位图文件头信息  
  18. void showBmpHead(BITMAPFILEHEADER pBmpHead){  
  19.     cout<<"位图文件头:"<<endl;  
  20.     cout<<"文件大小:"<<pBmpHead.bfSize<<endl;  
  21.     cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;  
  22.     cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl;  
  23.     cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<endl<<endl;  
  24. }  
  25.   
  26. void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead){  
  27.     cout<<"位图信息头:"<<endl;  
  28.     cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl;  
  29.     cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl;  
  30.     cout<<"位图高:"<<pBmpInforHead.biHeight<<endl;  
  31.     cout<<"biPlanes平面数:"<<pBmpInforHead.biPlanes<<endl;  
  32.     cout<<"biBitCount采用颜色位数:"<<pBmpInforHead.biBitCount<<endl;  
  33.     cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl;  
  34.     cout<<"biSizeImage实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl;  
  35.     cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl;  
  36.     cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl;  
  37.     cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl;  
  38.     cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl;  
  39. }  
  40.   
  41.   
  42. int main(){  
  43.     char strFile[LENGTH_NAME_BMP];//bmp文件名  
  44.     IMAGEDATA *imagedata = NULL;//动态分配存储原图片的像素信息的二维数组  
  45.     IMAGEDATA *imagedataRot = NULL;//动态分配存储旋转后的图片的像素信息的二维数组  
  46.     int width,height;//图片的宽度和高度  
  47.     cout<<"请输入所要读取的文件名:"<<endl;  
  48.     cin>>strFile;  
  49.     FILE *fpi,*fpw;  
  50.     fpi=fopen(strFile,"rb");  
  51.     if(fpi != NULL){  
  52.         //先读取文件类型  
  53.         WORD bfType;  
  54.         fread(&bfType,1,sizeof(WORD),fpi);  
  55.         if(0x4d42!=bfType)  
  56.         {  
  57.             cout<<"the file is not a bmp file!"<<endl;  
  58.             return NULL;  
  59.         }  
  60.         //读取bmp文件的文件头和信息头  
  61.         fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);  
  62.         //showBmpHead(strHead);//显示文件头  
  63.         fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);  
  64.         //showBmpInforHead(strInfo);//显示文件信息头  
  65.   
  66.         //读取调色板  
  67.         for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
  68.         {  
  69.             fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi);  
  70.             fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi);  
  71.             fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi);  
  72.             fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi);  
  73.         }  
  74.   
  75.         width = strInfo.biWidth;  
  76.         height = strInfo.biHeight;  
  77.         //图像每一行的字节数必须是4的整数倍  
  78.         width = (width * sizeof(IMAGEDATA) + 3) / 4 * 4;  
  79.         //imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));  
  80.         imagedata = (IMAGEDATA*)malloc(width * height);  
  81.         imagedataRot = (IMAGEDATA*)malloc(2 * width * 2 * height * sizeof(IMAGEDATA));  
  82.         //初始化原始图片的像素数组  
  83.         for(int i = 0;i < height;++i)  
  84.         {  
  85.             for(int j = 0;j < width;++j)  
  86.             {  
  87.                 (*(imagedata + i * width + j)).blue = 0;  
  88.                 //(*(imagedata + i * width + j)).green = 0;  
  89.                 //(*(imagedata + i *  width + j)).red = 0;  
  90.             }  
  91.         }  
  92.         //初始化旋转后图片的像素数组  
  93.         for(int i = 0;i < 2 * height;++i)  
  94.         {  
  95.             for(int j = 0;j < 2 * width;++j)  
  96.             {  
  97.                 (*(imagedataRot + i * 2 * width + j)).blue = 0;  
  98.                 //(*(imagedataRot + i * 2 * width + j)).green = 0;  
  99.                 //(*(imagedataRot + i * 2 * width + j)).red = 0;  
  100.             }  
  101.         }  
  102.         //fseek(fpi,54,SEEK_SET);  
  103.         //读出图片的像素数据  
  104.         fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);  
  105.         fclose(fpi);  
  106.     }  
  107.     else  
  108.     {  
  109.         cout<<"file open error!"<<endl;  
  110.         return NULL;  
  111.     }  
  112.   
  113.     //图片旋转处理  
  114.     int RotateAngle;//要旋转的角度数  
  115.     double angle;//要旋转的弧度数  
  116.     int midX_pre,midY_pre,midX_aft,midY_aft;//旋转所围绕的中心点的坐标  
  117.     midX_pre = width / 2;  
  118.     midY_pre = height / 2;  
  119.     midX_aft = width;  
  120.     midY_aft = height;  
  121.     int pre_i,pre_j,after_i,after_j;//旋转前后对应的像素点坐标  
  122.     cout<<"输入要旋转的角度(0度到360度,逆时针旋转):"<<endl;  
  123.     cin>>RotateAngle;  
  124.     angle = 1.0 * RotateAngle * PI / 180;  
  125.     for(int i = 0;i < 2 * height;++i)  
  126.     {  
  127.         for(int j = 0;j < 2 * width;++j)  
  128.         {  
  129.             after_i = i - midX_aft;//坐标变换  
  130.             after_j = j - midY_aft;  
  131.             pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midX_pre;  
  132.             pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midY_pre;  
  133.             if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内  
  134.                 *(imagedataRot + i * 2 * width + j) = *(imagedata + pre_i * width + pre_j);  
  135.         }  
  136.     }  
  137.   
  138.     //保存bmp图片  
  139.     if((fpw=fopen("b.bmp","wb"))==NULL)  
  140.     {  
  141.         cout<<"create the bmp file error!"<<endl;  
  142.         return NULL;  
  143.     }  
  144.     WORD bfType_w=0x4d42;  
  145.     fwrite(&bfType_w,1,sizeof(WORD),fpw);  
  146.     //fpw +=2;  
  147.     fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);  
  148.     strInfo.biWidth = 2 * width;  
  149.     strInfo.biHeight = 2 * height;  
  150.     fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);  
  151.     //保存调色板数据  
  152.     for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
  153.     {  
  154.         fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw);  
  155.         fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw);  
  156.         fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw);  
  157.         fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw);  
  158.     }  
  159.     //保存像素数据  
  160.     for(int i =0;i < 2 * height;++i)  
  161.     {  
  162.         for(int j = 0;j < 2 * width;++j)  
  163.         {  
  164.             fwrite( &((*(imagedataRot + i * 2 * width + j)).blue),1,sizeof(BYTE),fpw);  
  165.             //fwrite( &((*(imagedataRot + i * 2 * width + j)).green),1,sizeof(BYTE),fpw);  
  166.             //fwrite( &((*(imagedataRot + i * 2 * width + j)).red),1,sizeof(BYTE),fpw);  
  167.         }  
  168.     }  
  169.     fclose(fpw);  
  170.   
  171.     //释放内存  
  172.     delete[] imagedata;  
  173.     delete[] imagedataRot;  
  174. }  

数据测试:

旋转前和旋转后的对比(45度):





彩色图:

彩色图的处理和灰度图略有不一样。主要是像素数据不同。由于每行数据的字节数必须是4的整数倍,这个地方处理起来要比灰度图麻烦很多,多以暂时还 没做好。本程序的局限性就是只能处理尺寸是4的整数倍的图片,可以旋转任意角度(逆时针)。

参考代码:分两个文件:BmpRot.h和BmpRot.cpp

BmpRot.h:

  1. typedef unsigned char BYTE;  
  2. typedef unsigned short WORD;  
  3. typedef unsigned int DWORD;  
  4. typedef long LONG;  
  5.   
  6. //位图文件头定义;  
  7. //其中不包含文件类型信息(由于结构体的内存结构决定,  
  8. //要是加了的话将不能正确读取文件信息)  
  9. typedef struct  tagBITMAPFILEHEADER{  
  10.     //WORD bfType;//文件类型,必须是0x424D,即字符“BM”  
  11.     DWORD bfSize;//文件大小  
  12.     WORD bfReserved1;//保留字  
  13.     WORD bfReserved2;//保留字  
  14.     DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数  
  15. }BITMAPFILEHEADER;  
  16.   
  17. typedef struct tagBITMAPINFOHEADER{  
  18.     DWORD biSize;//信息头大小  
  19.     LONG biWidth;//图像宽度  
  20.     LONG biHeight;//图像高度  
  21.     WORD biPlanes;//位平面数,必须为1  
  22.     WORD biBitCount;//每像素位数  
  23.     DWORD  biCompression; //压缩类型  
  24.     DWORD  biSizeImage; //压缩图像大小字节数  
  25.     LONG  biXPelsPerMeter; //水平分辨率  
  26.     LONG  biYPelsPerMeter; //垂直分辨率  
  27.     DWORD  biClrUsed; //位图实际用到的色彩数  
  28.     DWORD  biClrImportant; //本位图中重要的色彩数  
  29. }BITMAPINFOHEADER; //位图信息头定义  
  30.   
  31. typedef struct tagRGBQUAD{  
  32.     BYTE rgbBlue; //该颜色的蓝色分量  
  33.     BYTE rgbGreen; //该颜色的绿色分量  
  34.     BYTE rgbRed; //该颜色的红色分量  
  35.     BYTE rgbReserved; //保留值  
  36. }RGBQUAD;//调色板定义  
  37.   
  38. //像素信息  
  39. typedef struct tagIMAGEDATA  
  40. {  
  41.     BYTE red;  
  42.     BYTE green;  
  43.     BYTE blue;  
  44. }IMAGEDATA;  

BmpRot.cpp:

  1. #include <stdio.h>  
  2. #include "BmpRot.h"  
  3. #include "stdlib.h"  
  4. #include "math.h"  
  5. #include <iostream>  
  6.   
  7. #define PI 3.14159//圆周率宏定义  
  8. #define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度  
  9.   
  10. using namespace std;  
  11.   
  12. //变量定义  
  13. BITMAPFILEHEADER strHead;  
  14. RGBQUAD strPla[256];//256色调色板  
  15. BITMAPINFOHEADER strInfo;  
  16.   
  17. //显示位图文件头信息  
  18. void showBmpHead(BITMAPFILEHEADER pBmpHead){  
  19.     cout<<"位图文件头:"<<endl;  
  20.     cout<<"文件大小:"<<pBmpHead.bfSize<<endl;  
  21.     cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;  
  22.     cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl;  
  23.     cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<endl<<endl;  
  24. }  
  25.   
  26. void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead){  
  27.     cout<<"位图信息头:"<<endl;  
  28.     cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl;  
  29.     cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl;  
  30.     cout<<"位图高:"<<pBmpInforHead.biHeight<<endl;  
  31.     cout<<"biPlanes平面数:"<<pBmpInforHead.biPlanes<<endl;  
  32.     cout<<"biBitCount采用颜色位数:"<<pBmpInforHead.biBitCount<<endl;  
  33.     cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl;  
  34.     cout<<"biSizeImage实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl;  
  35.     cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl;  
  36.     cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl;  
  37.     cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl;  
  38.     cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl;  
  39. }  
  40.   
  41.   
  42. int main(){  
  43.     char strFile[LENGTH_NAME_BMP];//bmp文件名  
  44.     IMAGEDATA *imagedata = NULL;//动态分配存储原图片的像素信息的二维数组  
  45.     IMAGEDATA *imagedataRot = NULL;//动态分配存储旋转后的图片的像素信息的二维数组  
  46.     int width,height;//图片的宽度和高度  
  47.     cout<<"请输入所要读取的文件名:"<<endl;  
  48.     cin>>strFile;  
  49.     FILE *fpi,*fpw;  
  50.     fpi=fopen(strFile,"rb");  
  51.     if(fpi != NULL){  
  52.         //先读取文件类型  
  53.         WORD bfType;  
  54.         fread(&bfType,1,sizeof(WORD),fpi);  
  55.         if(0x4d42!=bfType)  
  56.         {  
  57.             cout<<"the file is not a bmp file!"<<endl;  
  58.             return NULL;  
  59.         }  
  60.         //读取bmp文件的文件头和信息头  
  61.         fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi);  
  62.         //showBmpHead(strHead);//显示文件头  
  63.         fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi);  
  64.         //showBmpInforHead(strInfo);//显示文件信息头  
  65.   
  66.         //读取调色板  
  67.         for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
  68.         {  
  69.             //存储的时候,一般去掉保留字rgbReserved  
  70.             fread((char *)&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpi);  
  71.             fread((char *)&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpi);  
  72.             fread((char *)&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpi);  
  73.             cout<<"strPla[nCounti].rgbBlue"<<strPla[nCounti].rgbBlue<<endl;  
  74.             cout<<"strPla[nCounti].rgbGreen"<<strPla[nCounti].rgbGreen<<endl;  
  75.             cout<<"strPla[nCounti].rgbRed"<<strPla[nCounti].rgbRed<<endl;  
  76.         }  
  77.   
  78.         width = strInfo.biWidth;  
  79.         height = strInfo.biHeight;  
  80.         imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));  
  81.         imagedataRot = (IMAGEDATA*)malloc(2 * width * 2 * height * sizeof(IMAGEDATA));  
  82.         //初始化原始图片的像素数组  
  83.         for(int i = 0;i < height;++i)  
  84.         {  
  85.             for(int j = 0;j < width;++j)  
  86.             {  
  87.                 (*(imagedata + i * width + j)).blue = 0;  
  88.                 (*(imagedata + i * width + j)).green = 0;  
  89.                 (*(imagedata + i *  width + j)).red = 0;  
  90.             }  
  91.         }  
  92.         //初始化旋转后图片的像素数组  
  93.         for(int i = 0;i < 2 * height;++i)  
  94.         {  
  95.             for(int j = 0;j < 2 * width;++j)  
  96.             {  
  97.                 (*(imagedataRot + i * 2 * width + j)).blue = 0;  
  98.                 (*(imagedataRot + i * 2 * width + j)).green = 0;  
  99.                 (*(imagedataRot + i * 2 * width + j)).red = 0;  
  100.             }  
  101.         }  
  102.         //fseek(fpi,54,SEEK_SET);  
  103.         //读出图片的像素数据  
  104.         fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);  
  105.         /* 
  106.         for(int i = 0;i < height;++i) 
  107.         { 
  108.             fread(imagedata + i * width * sizeof(IMAGEDATA),sizeof(struct tagIMAGEDATA) * width,height,fpi); 
  109.         }*/  
  110.         fclose(fpi);  
  111.     }  
  112.     else  
  113.     {  
  114.         cout<<"file open error!"<<endl;  
  115.         return NULL;  
  116.     }  
  117.   
  118.     //图片旋转处理  
  119.     int RotateAngle;//要旋转的角度数  
  120.     double angle;//要旋转的弧度数  
  121.     int midX_pre,midY_pre,midX_aft,midY_aft;//旋转所围绕的中心点的坐标  
  122.     midX_pre = width / 2;  
  123.     midY_pre = height / 2;  
  124.     midX_aft = width;  
  125.     midY_aft = height;  
  126.     int pre_i,pre_j,after_i,after_j;//旋转前后对应的像素点坐标  
  127.     cout<<"输入要旋转的角度(0度到360度,逆时针旋转):"<<endl;  
  128.     cin>>RotateAngle;  
  129.     angle = 1.0 * RotateAngle * PI / 180;  
  130.     for(int i = 0;i < 2 * height;++i)  
  131.     {  
  132.         for(int j = 0;j < 2 * width;++j)  
  133.         {  
  134.             after_i = i - midY_aft;//坐标变换  
  135.             after_j = j - midX_aft;  
  136.             pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midY_pre;  
  137.             pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midX_pre;  
  138.             if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内  
  139.                 *(imagedataRot + i * 2 * width + j) = *(imagedata + pre_i * width + pre_j);  
  140.         }  
  141.     }  
  142.   
  143.     //保存bmp图片  
  144.     if((fpw=fopen("b.bmp","wb"))==NULL)  
  145.     {  
  146.         cout<<"create the bmp file error!"<<endl;  
  147.         return NULL;  
  148.     }  
  149.     WORD bfType_w=0x4d42;  
  150.     fwrite(&bfType_w,1,sizeof(WORD),fpw);  
  151.     //fpw +=2;  
  152.     fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw);  
  153.     strInfo.biWidth = 2 * width;  
  154.     strInfo.biHeight = 2 * height;  
  155.     fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw);  
  156.     //保存调色板数据  
  157.     for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++)  
  158.     {  
  159.         fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw);  
  160.         fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw);  
  161.         fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw);  
  162.     }  
  163.     //保存像素数据  
  164.     for(int i =0;i < 2 * height;++i)  
  165.     {  
  166.         for(int j = 0;j < 2 * width;++j)  
  167.         {  
  168.             fwrite( &(*(imagedataRot + i * 2 * width + j)).red,1,sizeof(BYTE),fpw);//注意三条语句的顺序:否则颜色会发生变化  
  169.             fwrite( &(*(imagedataRot + i * 2 * width + j)).green,1,sizeof(BYTE),fpw);  
  170.             fwrite( &(*(imagedataRot + i * 2 * width + j)).blue,1,sizeof(BYTE),fpw);  
  171.         }  
  172.     }  
  173.     fclose(fpw);  
  174.   
  175.     //释放内存  
  176.     delete[] imagedata;  
  177.     delete[] imagedataRot;  
  178. }  

数据测试:(旋转10°)






注意:颜色问题已解决。请看代码中注释部分。


评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值