第一章:CDib类库的建立

VC++图像处理程序设计(第1版)    杨淑莹 编著     边奠英 主审
第一章 位图基础
Joanna-In-Hdu&Hust 手工打,印象更深刻
使用工具 VS2010 mfc
 整本书的代码文件、测试图片和程序运行exe请在这里下载https://github.com/CaptainLYN/VCPictureProcessing
 
图形是矢量,显式地表示图画内容坐标值;图像是位图,适于表现大量细节,一般需要压缩。
 
红、绿、蓝,简称RGB三原色。每一个点都是由RGB三个分量的颜色来共同决定。
分辨率:单位长度内的像素数,单位是每英寸的点数DPI(dots per inch)。
图像分辨率是实际精度,显示分辨率是表现精度。
 
单色图像:0表示黑,1表示白。
灰度图像:带有颜色表,共256项,RGB三分量值相同;每个像素由8位组成,0~255,每个图像的的f(x,y)是颜色表的表项入口。
伪彩色图像:RGB三分量不全相等;像素8位,每个像素的像素值表示的不是颜色分量而是颜色表的表项入口,整个图像只有256种颜色。
24位真彩色:不带有颜色表;RGB三分量各占8位,0~255,,所以每个像素是24位,像素值就是颜色值;从左到右存储蓝、绿、红颜色值。
 
位图:位数据以行为单位存储,每行长度为4字节倍数,不足补0;
位图行的存储次序是颠倒的,即第一行对应位图最底行。
位图文件头结构 BITMAPFILEHEADER
位图信息头结构 BITMAPINFOHEADER
位图颜色表 RGBQUAD
位图像素数据
 
位图文件头:
1 typedef struct tagBITMAPFILEHEADER
2 {
3 WORD bftype;//位图文件的类型,必须为BMP,0x4d42
4 DWORD bfsize;//位图文件的大小,以字节为单位
5 WORD bfReaserved1;//位图文件保留字,必须为0
6 WORD bfReaserved2;//位图文件保留字,必须为0
7 DWORD bfOffBits;//位图数据的起始位置,相对于位图文件头的偏移量表示,以字节为单位
8 }BITMAPFILEHEADER;
位图信息头:
 1 typedef struct tagBITMAPINFOHEADER
 2 {
 3 DWORD biSize;//本结构所占用字节数
 4 LONG biWidth;//位图的宽度,以像素为单位
 5 LONG biHeight;//位图的高度,以像素为单位
 6 WORD biPlanes;//目标设备的级别,必须为1
 7 WORD biBitCount;//每个像素所需的位数,1、4、8、24
 8 DWORD biCompression;/*位图压缩类型,必须为0(不压缩)、1(BI_RLE8压缩类型)、2(BI_RLE4压缩类型)之一*/
 9 DWORD biSizeImage;//位图的大小,以字节为单位
10 LONG biXPelsPerMeter;//位图水平分辨率,每米像素数
11 LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数
12 DWORD biClrUsed;//位图实际使用的颜色表的颜色数
13 DWORD biClrImportant;//位图显示过程中重要的颜色数
14 }BITMAPINFOHEADER;
颜色表:
1 typedef struct tagRGBQUAD
2 {
3 BYTE rgbBlue;//蓝色的亮度(0~255)
4 BYTE rgbGreen;//绿色
5 BYTE rgbRed;//红色
6 BYTE rgbReserved;//保留,必须为0
7 }RGBQUAD;

 

颜色表中RGBQUAD结构数据的个数由biBitCount来确定:biBitCount=1,4,8时,分别有2,16,256个表项;biBitCount=24时,没有表项。
 
typedef是用后面代替前面,define是前面代替后面。
 
接下来是CDib.h的代码:
 1 /*
 2 #ifndef _CDIB_H_  //代表这个头文件里的变量只编译一次
 3 #define _CDIB_H_
 4 */
 5 #pragma once
 6 
 7 class CDib:public CObject
 8 {
 9 public:
10     RGBQUAD* m_pRGB;//颜色表指针
11     BYTE* m_pData,*m_pData2;//m_pData指向原数据,m_pData2指向灰度数据
12     UINT m_numberOfColors;//位图颜色数目
13     BOOL m_valid;//是否载入了位图文件
14     BITMAPFILEHEADER bitmapFileHeader;//位图文件头
15     
16     BITMAPINFOHEADER* m_pBitmapInfoHeader;//位图信息头
17     BITMAPINFO* m_pBitmapInfo;//位图信息指针
18     int byBitCount;
19     DWORD dwWidthBytes;//DWORD 就是Double Word,4个字节,32位,位图的宽度字节数
20     BYTE* pDib;//文件中位图总数据指针,也就是读入内存所以数据的第一个
21     DWORD size;//位图总数据的长度
22 
23 public:
24     CDib();
25     ~CDib();
26 
27     char m_fileName[256];//文件名
28     char* GetFileName();
29     BOOL IsValid();//是否载入了位图文件
30     DWORD GetSize();
31     UINT GetWidth();
32     UINT GetHeight();
33     UINT GetNumberOfColors();
34     RGBQUAD* GetRGB();//获取颜色表指针
35     BYTE* GetData();
36     BYTE* GetData2();
37     DWORD GetDibWidthBytes();
38     BITMAPINFO* GetInfo();//获取位图信息结构的指针
39     WORD PaletteSize(LPBYTE IpDIB);//位图指针指向的位图调色板的大小
40     WORD DIBNumColors(LPBYTE IpDIB);//。。。。。。颜色的数目
41     void SaveFile(const CString filename);
42     //DWORD GetFilesize();
43 public:
44     void GradetoRGB();
45     void RGBtoGrade();
46     void LoadFile(CString dibFileName);
47 };
48 
49 //#endif

 

然后是DIB.cpp实现文件:  

 

  1 #include"stdafx.h"
  2 #include"CDib.h"
  3 #include<WindowsX.h>
  4 #define WIDTHBYTES(bits) (((bits)+31)/32*4)  //用前面一个代替后面的
  5 CDib::CDib()
  6 {
  7      
  8 }
  9 CDib::~CDib()
 10 {
 11     GlobalFreePtr(m_pBitmapInfo);//释放在loadfile函数中在堆中申请的资源
 12 }
 13 void CDib::LoadFile(CString m_filename)//静态不允许修改
 14 {
 15     //strcpy(m_fileName,dibFileName);
 16     CFile dibFile(m_filename,CFile::modeRead);//构造函数初始化,制度打开文件,LPCTSTR就是const char*,unicode通用字符集
 17     dibFile.Read((void*)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));
 18     if(bitmapFileHeader.bfType==0x4d42)//位图文件的类型,就是十进制19778
 19     {
 20         DWORD fileLength=dibFile.GetLength();
 21         /*DWORD*/ size=fileLength-sizeof(BITMAPFILEHEADER);//图像内容的实际大小+颜色表+位图文件信息头
 22         /*BYTE**/ pDib=(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE,size);//从堆中分配属性为GMEM_MOVEABLE(win32平台和GMEM_FIXED固定的已经没有大区别)、size大小的可移动内存
 23         dibFile.Read((void*)pDib,size);//从上述关联的文件中读取size大小的数据放入指针缓冲区,每读一次读取指针的位置移动size大小,文件头已经读取过了,这是包含信息头的数据
 24         dibFile.Close();
 25 
 26         m_pBitmapInfo=(BITMAPINFO*)pDib;//现在它指向的就是位图信息,也是位图信息的第一个成员的位置
 27         m_pBitmapInfoHeader=(BITMAPINFOHEADER*)pDib;
 28         m_pRGB=(RGBQUAD*)(pDib+m_pBitmapInfoHeader->biSize);//指向颜色表
 29         int m_numberOfColors=GetNumberOfColors();//2、16、256或者是真彩色
 30         if(m_pBitmapInfoHeader->biClrUsed==0)//位图实际使用的颜色表中的颜色数是不对的,就修改
 31             m_pBitmapInfoHeader->biClrUsed=m_numberOfColors;
 32         DWORD colorTableSize=m_numberOfColors*sizeof(RGBQUAD);//调色板的大小
 33         m_pData=pDib+m_pBitmapInfoHeader->biSize+colorTableSize;//颜色数据的真正起始位置
 34         if(m_pRGB==(RGBQUAD*)m_pData)//如果没有调色板
 35             m_pRGB=NULL;
 36         m_pBitmapInfoHeader->biSizeImage=GetSize();//位图的大小,以字节为单位
 37         m_valid=TRUE;
 38     }
 39     else
 40     {
 41         m_valid=FALSE;
 42         MessageBox(NULL,_T("这不是位图文件!"),_T("提示"),MB_OK);//这里做了修改,关于书
 43     }
 44 }
 45 
 46 BOOL CDib::IsValid()
 47 {
 48     return m_valid;
 49 }
 50 
 51 char*CDib::GetFileName()
 52 {
 53     return m_fileName;
 54 }
 55 
 56 UINT CDib::GetWidth()
 57 {
 58     return (UINT)m_pBitmapInfoHeader->biWidth;
 59 }
 60 
 61 UINT CDib::GetHeight()
 62 {
 63     return (UINT)m_pBitmapInfoHeader->biHeight;
 64 }
 65 
 66 DWORD CDib::GetSize()
 67 {
 68     if(m_pBitmapInfoHeader->biSizeImage!=0)//位图的大小
 69         return m_pBitmapInfoHeader->biSizeImage;
 70     else
 71     {//不对就自己计算
 72         DWORD height=(DWORD)GetHeight();
 73         DWORD width=(DWORD)GetWidth();
 74         return height*/*width */GetDibWidthBytes();
 75     }
 76 }
 77 
 78 
 79 //返回行字节数
 80 DWORD CDib::GetDibWidthBytes()
 81 {
 82     byBitCount=m_pBitmapInfoHeader->biBitCount;//每个像素所需的位数:1、4、8、真彩色
 83     LONG nWidth=m_pBitmapInfoHeader->biWidth;//位图的宽度,以像素为单位
 84 
 85     dwWidthBytes=(DWORD)m_pBitmapInfoHeader->biWidth;//位图的宽度字节数
 86     if(byBitCount==1) dwWidthBytes=(nWidth+7)/8;//一位像素,那按字节计算就应该除8
 87     else if(byBitCount==4) dwWidthBytes=(nWidth+1)/2;
 88     else if(byBitCount==24) dwWidthBytes=nWidth*3;//因为真彩色每个像素是24位
 89 
 90     while((dwWidthBytes&3)!=0) dwWidthBytes++;//是否为4的倍数,因为位图每行为4的倍数,不足补0
 91 
 92     return dwWidthBytes;
 93 }
 94 
 95 //返回位图颜色数目
 96 UINT CDib::GetNumberOfColors()
 97 {
 98     int numberOfColors;
 99 
100     if((m_pBitmapInfoHeader->biClrUsed==0)&&(m_pBitmapInfoHeader->biBitCount<9))
101     {
102         switch(m_pBitmapInfoHeader->biBitCount)
103         {
104         case 1:numberOfColors=2;break;
105         case 4:numberOfColors=16;break;
106         case 8:numberOfColors=256;
107         }
108     }
109     else
110         numberOfColors=(int)m_pBitmapInfoHeader->biClrUsed;//如果是真彩色,就是实际使用的颜色
111     return numberOfColors;
112 }
113 
114 BYTE* CDib::GetData()
115 {
116     return m_pData;
117 }
118 
119 BYTE* CDib::GetData2()
120 {
121     if(GetRGB())
122         m_pData2=m_pData;//有颜色表的情况下两个相同
123     return m_pData2;
124 }
125 
126 RGBQUAD* CDib::GetRGB()
127 {
128     return m_pRGB;
129 }
130 
131 BITMAPINFO* CDib::GetInfo()
132 {
133     return m_pBitmapInfo;//位图信息指针
134 }
135 
136 WORD CDib::PaletteSize(LPBYTE lpDIB)//LPBYTE是BYTE指针
137 {
138     return (DIBNumColors(lpDIB)*sizeof(RGBQUAD/*RGBTRIPLE*/));//位图指针指向的颜色的数目*每一个颜色的大小,颜色表的位总大小
139 }
140 
141 //应该是返回颜色表中的数据项数
142 WORD CDib::DIBNumColors(LPBYTE lpDIB)
143 {
144     WORD wBitCount;//设备无关图的位数
145     //wBitCount=((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;//每个颜色的位数
146     wBitCount=((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
147     switch(wBitCount)
148     {
149     case 1:return 2;
150     case 4:return 16;
151     case 8:return 256;
152     default:return 0;
153     }
154 }
155 
156 void CDib::SaveFile(const CString filename)
157 {
158     BITMAPFILEHEADER bmfHdr;
159     LPBITMAPINFOHEADER lpBI;
160     DWORD dwDIBSize;
161 
162     bmfHdr.bfType=0x4d42;//"BM"
163     lpBI=(LPBITMAPINFOHEADER)m_pBitmapInfoHeader;
164 
165     dwDIBSize=*(LPDWORD)lpBI+PaletteSize((LPBYTE)lpBI);//位图数据和信息头+颜色表的位大小,这里只有后两部分
166     //       本结构所占用字节数
167     if((lpBI->biCompression==BI_RLE8)||(lpBI->biCompression==BI_RLE4))
168         dwDIBSize+=lpBI->biSizeImage;//位图的大小字节
169     else
170     {
171         DWORD dwBmBitSize;//只表示位图的位的大小
172         dwBmBitSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount))*lpBI->biHeight;//宽度的字节数*高度
173         dwDIBSize+=dwBmBitSize;
174         lpBI->biSizeImage=dwBmBitSize;//位图数据大小的字节数
175     }
176     bmfHdr.bfSize=dwDIBSize+sizeof(BITMAPFILEHEADER);//位图文件的大小,指的是整个文件
177     bmfHdr.bfReserved1=0;
178     bmfHdr.bfReserved2=0;
179     bmfHdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+lpBI->biSize+PaletteSize((LPBYTE)lpBI);
180 
181     CFile dibFile(filename,CFile::modeWrite|CFile::modeCreate);//|是位或,CFile::modeWrite|CFile::modeCreate??????
182     dibFile.Write(&bmfHdr,sizeof(BITMAPFILEHEADER));//这里必须要写两次,但是不知道为什么
183     dibFile.Write(lpBI,dwDIBSize);
184     dibFile.Close();
185 }
186 
187 //rgb转成灰度图
188 void CDib::RGBtoGrade()
189 {
190     if(GetRGB())//如果有颜色表,即不是真彩色24位
191         m_pData2=m_pData;//颜色组号就是颜色,rgb相等
192     else
193     {
194         BYTE r,g,b;
195         int height,wide,size;
196         height=GetHeight();
197         wide=GetWidth();
198         size=height*wide;
199         m_pData2=(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE,size);
200         LONG lLineBytes=GetDibWidthBytes();
201         for(int j=0;j<height;j++)
202         {
203             for(int i=0;i<wide;i++)
204             {
205                 b=m_pData[j*lLineBytes+3*i];
206                 g=m_pData[j*lLineBytes+3*i+1];
207                 r=m_pData[j*lLineBytes+3*i+2];
208                 m_pData2[j*wide+i]=(BYTE)(0.3*r+0.59*g+0.11*b);//wide是像素,一个像素是24位,所以三个rgb映射一个像素值,rgb给予不同的权重,一个值就代表了rgb三个值,因为是灰度图
209             }
210         }
211     }
212 }
213 
214 //灰度图转为rgb
215 void CDib::GradetoRGB()
216 {
217     if(GetRGB())
218         m_pData=m_pData2;//反正rgb相等就是了
219     else
220     {
221         //BYTE r,g,b;
222         int height,wide;
223         height=GetHeight();
224         wide=GetWidth();
225         LONG lLineBytes=GetDibWidthBytes();
226         for(int j=0;j<height;j++)
227         {
228             for(int i=0;i<wide;i++)//将一个灰度值赋值给三个rgb,从最后一行开始,从一开始应该也是一样的
229             {
230                 m_pData[(height-j-1)*lLineBytes+3*i]=m_pData2[(height-1-j)*wide+i];
231                 m_pData[(height-j-1)*lLineBytes+3*i+1]=m_pData2[(height-1-j)*wide+i];
232                 m_pData[(height-j-1)*lLineBytes+3*i+3]=m_pData2[(height-1-j)*wide+i];
233             }
234         }
235     }
236 }

 (上述代码已进行更新)

代码中的部分函数,不如灰度转换,没有用过,所以不确定是否能正确使用,但是绝大部分函数用过都是可以正确运行的。

 现在给出一个测试的调用实例:

1、建立一个菜单,并在mfc的程序中实现这个菜单,如图:

2、在对话框类中建立一个变量,用于保存文件的路径:

1 class CMfcPictureProcessingDlg : public CDialogEx
2 {
3     
4     CMenu m_Menu;
5     CString filePath;//就是这个
6 。。。。。
7 };

3、对1中的菜单项“打开”,新建事件处理函数,到对话框类cpp文件中:

 1 void CMfcPictureProcessingDlg::On32771()//打开文件菜单
 2 {
 3     TCHAR szFilter[]=_T("所有文件(*.*)|*.*||");//设置过滤器
 4     CFileDialog fileDlg(TRUE,NULL,NULL,0,szFilter,this);//这是一个文件打开对话框
 5     if(IDOK==fileDlg.DoModal())
 6     {
 7         GetFilePath(fileDlg.GetPathName());
 8 
 9         CDib dib;//初始化一个类指针
10         //memset(dib,0,sizeof(CDib));//不行,有中断出现
11         dib.LoadFile(filePath);
12         if(dib.m_valid)//很重要,要记得判断
13         {
14         CDC *pDC=GetDC();
15         CViewImage imageview;//这是第二章的显示函数,可以不写
16         imageview.GetDib(&dib);//我给它加了一个函数,用于获取dib
17         imageview.OnDraw(pDC);
18         }
19     }
20 }

4、为菜单中的“保存”,添加事件处理函数:

 1 void CMfcPictureProcessingDlg::On32799()//保存文件
 2 {
 3     CDib dib;
 4     dib.LoadFile(filePath);
 5     if(dib.m_valid)
 6     {
 7         //------------------------
 8         CDC* pDC=GetDC();
 9         JHBHDib jdib;
10         jdib.GetDib(&dib);
11         jdib.JingXiang(false);
12         CViewImage imageview;
13         imageview.GetDib(&dib);
14         imageview.OnDraw2(pDC,dib.GetWidth()+5,0);
15         //这之间是我随便选的一个第三章的处理函数,也可以将这部分去掉,先试试是否能直接保存图片
16         //-------------------------
17         dib.SaveFile(_T("wod.bmp"));
18         //delete((BYTE*)dib.GetInfo());//不需要再释放了,释放函数在析构函数中
19     }
20 }

5、程序开启,试试效果:

(1)菜单中的“打开”:

(2)菜单中的保存:

(3)工程文件夹:

进去找到我们的图片,我取名是“wod.bmp”:

打开图片:

//-----------------------------------------------------------------结束

 

零零散散敲了两天真是酸爽啊,自己敲才能知道哪里不会~

  

http://blog.csdn.net/mad1989/article/details/7920173 讲#ifndef、头文件变量在cpp中重复定义
“其实并不难,是你太悲观。”看到了这样一句鸡汤,很对。(其实就是链接内博客的名字)
posted @ 2017-03-24 20:33  Joanna-In-Hdu&Hust  阅读(3702)  评论(12编辑  收藏  举报