zoukankan      html  css  js  c++  java
  • 第一章: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中重复定义
    “其实并不难,是你太悲观。”看到了这样一句鸡汤,很对。(其实就是链接内博客的名字)
  • 相关阅读:
    持续交付11-构建和部署的脚本化
    腾讯云即时通讯IM 公共整合
    腾讯云应用生成 UserSig
    「USACO 2020 US Open Platinum」Exercise
    async要点
    jQuery实现textarea高度根据内容自适应
    背景图片设置透明度而不改变内容
    input type=file实现图片上传,预览以及图片删除
    vant 字体图标不显示问题
    vue项目使用mock.js
  • 原文地址:https://www.cnblogs.com/studylyn/p/6613278.html
Copyright © 2011-2022 走看看