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
位图像素数据
 
位图文件头:
 typedef struct tagBITMAPFILEHEADER
{
WORD bftype;//位图文件的类型,必须为BMP,0x4d42
DWORD bfsize;//位图文件的大小,以字节为单位
WORD bfReaserved1;//位图文件保留字,必须为0
WORD bfReaserved2;//位图文件保留字,必须为0
DWORD bfOffBits;//位图数据的起始位置,相对于位图文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER;
位图信息头:
 typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;//本结构所占用字节数
LONG biWidth;//位图的宽度,以像素为单位
LONG biHeight;//位图的高度,以像素为单位
WORD biPlanes;//目标设备的级别,必须为1
WORD biBitCount;//每个像素所需的位数,1、4、8、24
DWORD biCompression;/*位图压缩类型,必须为0(不压缩)、1(BI_RLE8压缩类型)、2(BI_RLE4压缩类型)之一*/
DWORD biSizeImage;//位图的大小,以字节为单位
LONG biXPelsPerMeter;//位图水平分辨率,每米像素数
LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数
DWORD biClrUsed;//位图实际使用的颜色表的颜色数
DWORD biClrImportant;//位图显示过程中重要的颜色数
}BITMAPINFOHEADER;
颜色表:
 typedef struct tagRGBQUAD
{
BYTE rgbBlue;//蓝色的亮度(0~255)
BYTE rgbGreen;//绿色
BYTE rgbRed;//红色
BYTE rgbReserved;//保留,必须为0
}RGBQUAD;
颜色表中RGBQUAD结构数据的个数由biBitCount来确定:biBitCount=1,4,8时,分别有2,16,256个表项;biBitCount=24时,没有表项。
 
typedef是用后面代替前面,define是前面代替后面。
 
接下来是CDib.h的代码:
 /*
#ifndef _CDIB_H_ //代表这个头文件里的变量只编译一次
#define _CDIB_H_
*/
#pragma once class CDib:public CObject
{
public:
RGBQUAD* m_pRGB;//颜色表指针
BYTE* m_pData,*m_pData2;//m_pData指向原数据,m_pData2指向灰度数据
UINT m_numberOfColors;//位图颜色数目
BOOL m_valid;//是否载入了位图文件
BITMAPFILEHEADER bitmapFileHeader;//位图文件头 BITMAPINFOHEADER* m_pBitmapInfoHeader;//位图信息头
BITMAPINFO* m_pBitmapInfo;//位图信息指针
int byBitCount;
DWORD dwWidthBytes;//DWORD 就是Double Word,4个字节,32位,位图的宽度字节数
BYTE* pDib;//文件中位图总数据指针,也就是读入内存所以数据的第一个
DWORD size;//位图总数据的长度 public:
CDib();
~CDib(); char m_fileName[];//文件名
char* GetFileName();
BOOL IsValid();//是否载入了位图文件
DWORD GetSize();
UINT GetWidth();
UINT GetHeight();
UINT GetNumberOfColors();
RGBQUAD* GetRGB();//获取颜色表指针
BYTE* GetData();
BYTE* GetData2();
DWORD GetDibWidthBytes();
BITMAPINFO* GetInfo();//获取位图信息结构的指针
WORD PaletteSize(LPBYTE IpDIB);//位图指针指向的位图调色板的大小
WORD DIBNumColors(LPBYTE IpDIB);//。。。。。。颜色的数目
void SaveFile(const CString filename);
//DWORD GetFilesize();
public:
void GradetoRGB();
void RGBtoGrade();
void LoadFile(CString dibFileName);
}; //#endif

然后是DIB.cpp实现文件:

 #include"stdafx.h"
#include"CDib.h"
#include<WindowsX.h>
#define WIDTHBYTES(bits) (((bits)+31)/32*4) //用前面一个代替后面的
CDib::CDib()
{ }
CDib::~CDib()
{
GlobalFreePtr(m_pBitmapInfo);//释放在loadfile函数中在堆中申请的资源
}
void CDib::LoadFile(CString m_filename)//静态不允许修改
{
//strcpy(m_fileName,dibFileName);
CFile dibFile(m_filename,CFile::modeRead);//构造函数初始化,制度打开文件,LPCTSTR就是const char*,unicode通用字符集
dibFile.Read((void*)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));
if(bitmapFileHeader.bfType==0x4d42)//位图文件的类型,就是十进制19778
{
DWORD fileLength=dibFile.GetLength();
/*DWORD*/ size=fileLength-sizeof(BITMAPFILEHEADER);//图像内容的实际大小+颜色表+位图文件信息头
/*BYTE**/ pDib=(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE,size);//从堆中分配属性为GMEM_MOVEABLE(win32平台和GMEM_FIXED固定的已经没有大区别)、size大小的可移动内存
dibFile.Read((void*)pDib,size);//从上述关联的文件中读取size大小的数据放入指针缓冲区,每读一次读取指针的位置移动size大小,文件头已经读取过了,这是包含信息头的数据
dibFile.Close(); m_pBitmapInfo=(BITMAPINFO*)pDib;//现在它指向的就是位图信息,也是位图信息的第一个成员的位置
m_pBitmapInfoHeader=(BITMAPINFOHEADER*)pDib;
m_pRGB=(RGBQUAD*)(pDib+m_pBitmapInfoHeader->biSize);//指向颜色表
int m_numberOfColors=GetNumberOfColors();//2、16、256或者是真彩色
if(m_pBitmapInfoHeader->biClrUsed==)//位图实际使用的颜色表中的颜色数是不对的,就修改
m_pBitmapInfoHeader->biClrUsed=m_numberOfColors;
DWORD colorTableSize=m_numberOfColors*sizeof(RGBQUAD);//调色板的大小
m_pData=pDib+m_pBitmapInfoHeader->biSize+colorTableSize;//颜色数据的真正起始位置
if(m_pRGB==(RGBQUAD*)m_pData)//如果没有调色板
m_pRGB=NULL;
m_pBitmapInfoHeader->biSizeImage=GetSize();//位图的大小,以字节为单位
m_valid=TRUE;
}
else
{
m_valid=FALSE;
MessageBox(NULL,_T("这不是位图文件!"),_T("提示"),MB_OK);//这里做了修改,关于书
}
} BOOL CDib::IsValid()
{
return m_valid;
} char*CDib::GetFileName()
{
return m_fileName;
} UINT CDib::GetWidth()
{
return (UINT)m_pBitmapInfoHeader->biWidth;
} UINT CDib::GetHeight()
{
return (UINT)m_pBitmapInfoHeader->biHeight;
} DWORD CDib::GetSize()
{
if(m_pBitmapInfoHeader->biSizeImage!=)//位图的大小
return m_pBitmapInfoHeader->biSizeImage;
else
{//不对就自己计算
DWORD height=(DWORD)GetHeight();
DWORD width=(DWORD)GetWidth();
return height*/*width */GetDibWidthBytes();
}
} //返回行字节数
DWORD CDib::GetDibWidthBytes()
{
byBitCount=m_pBitmapInfoHeader->biBitCount;//每个像素所需的位数:1、4、8、真彩色
LONG nWidth=m_pBitmapInfoHeader->biWidth;//位图的宽度,以像素为单位 dwWidthBytes=(DWORD)m_pBitmapInfoHeader->biWidth;//位图的宽度字节数
if(byBitCount==) dwWidthBytes=(nWidth+)/;//一位像素,那按字节计算就应该除8
else if(byBitCount==) dwWidthBytes=(nWidth+)/;
else if(byBitCount==) dwWidthBytes=nWidth*;//因为真彩色每个像素是24位 while((dwWidthBytes&)!=) dwWidthBytes++;//是否为4的倍数,因为位图每行为4的倍数,不足补0 return dwWidthBytes;
} //返回位图颜色数目
UINT CDib::GetNumberOfColors()
{
int numberOfColors; if((m_pBitmapInfoHeader->biClrUsed==)&&(m_pBitmapInfoHeader->biBitCount<))
{
switch(m_pBitmapInfoHeader->biBitCount)
{
case :numberOfColors=;break;
case :numberOfColors=;break;
case :numberOfColors=;
}
}
else
numberOfColors=(int)m_pBitmapInfoHeader->biClrUsed;//如果是真彩色,就是实际使用的颜色
return numberOfColors;
} BYTE* CDib::GetData()
{
return m_pData;
} BYTE* CDib::GetData2()
{
if(GetRGB())
m_pData2=m_pData;//有颜色表的情况下两个相同
return m_pData2;
} RGBQUAD* CDib::GetRGB()
{
return m_pRGB;
} BITMAPINFO* CDib::GetInfo()
{
return m_pBitmapInfo;//位图信息指针
} WORD CDib::PaletteSize(LPBYTE lpDIB)//LPBYTE是BYTE指针
{
return (DIBNumColors(lpDIB)*sizeof(RGBQUAD/*RGBTRIPLE*/));//位图指针指向的颜色的数目*每一个颜色的大小,颜色表的位总大小
} //应该是返回颜色表中的数据项数
WORD CDib::DIBNumColors(LPBYTE lpDIB)
{
WORD wBitCount;//设备无关图的位数
//wBitCount=((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;//每个颜色的位数
wBitCount=((LPBITMAPINFOHEADER)lpDIB)->biBitCount;
switch(wBitCount)
{
case :return ;
case :return ;
case :return ;
default:return ;
}
} void CDib::SaveFile(const CString filename)
{
BITMAPFILEHEADER bmfHdr;
LPBITMAPINFOHEADER lpBI;
DWORD dwDIBSize; bmfHdr.bfType=0x4d42;//"BM"
lpBI=(LPBITMAPINFOHEADER)m_pBitmapInfoHeader; dwDIBSize=*(LPDWORD)lpBI+PaletteSize((LPBYTE)lpBI);//位图数据和信息头+颜色表的位大小,这里只有后两部分
// 本结构所占用字节数
if((lpBI->biCompression==BI_RLE8)||(lpBI->biCompression==BI_RLE4))
dwDIBSize+=lpBI->biSizeImage;//位图的大小字节
else
{
DWORD dwBmBitSize;//只表示位图的位的大小
dwBmBitSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount))*lpBI->biHeight;//宽度的字节数*高度
dwDIBSize+=dwBmBitSize;
lpBI->biSizeImage=dwBmBitSize;//位图数据大小的字节数
}
bmfHdr.bfSize=dwDIBSize+sizeof(BITMAPFILEHEADER);//位图文件的大小,指的是整个文件
bmfHdr.bfReserved1=;
bmfHdr.bfReserved2=;
bmfHdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+lpBI->biSize+PaletteSize((LPBYTE)lpBI); CFile dibFile(filename,CFile::modeWrite|CFile::modeCreate);//|是位或,CFile::modeWrite|CFile::modeCreate??????
dibFile.Write(&bmfHdr,sizeof(BITMAPFILEHEADER));//这里必须要写两次,但是不知道为什么
dibFile.Write(lpBI,dwDIBSize);
dibFile.Close();
} //rgb转成灰度图
void CDib::RGBtoGrade()
{
if(GetRGB())//如果有颜色表,即不是真彩色24位
m_pData2=m_pData;//颜色组号就是颜色,rgb相等
else
{
BYTE r,g,b;
int height,wide,size;
height=GetHeight();
wide=GetWidth();
size=height*wide;
m_pData2=(BYTE*)GlobalAllocPtr(GMEM_MOVEABLE,size);
LONG lLineBytes=GetDibWidthBytes();
for(int j=;j<height;j++)
{
for(int i=;i<wide;i++)
{
b=m_pData[j*lLineBytes+*i];
g=m_pData[j*lLineBytes+*i+];
r=m_pData[j*lLineBytes+*i+];
m_pData2[j*wide+i]=(BYTE)(0.3*r+0.59*g+0.11*b);//wide是像素,一个像素是24位,所以三个rgb映射一个像素值,rgb给予不同的权重,一个值就代表了rgb三个值,因为是灰度图
}
}
}
} //灰度图转为rgb
void CDib::GradetoRGB()
{
if(GetRGB())
m_pData=m_pData2;//反正rgb相等就是了
else
{
//BYTE r,g,b;
int height,wide;
height=GetHeight();
wide=GetWidth();
LONG lLineBytes=GetDibWidthBytes();
for(int j=;j<height;j++)
{
for(int i=;i<wide;i++)//将一个灰度值赋值给三个rgb,从最后一行开始,从一开始应该也是一样的
{
m_pData[(height-j-)*lLineBytes+*i]=m_pData2[(height--j)*wide+i];
m_pData[(height-j-)*lLineBytes+*i+]=m_pData2[(height--j)*wide+i];
m_pData[(height-j-)*lLineBytes+*i+]=m_pData2[(height--j)*wide+i];
}
}
}
}

(上述代码已进行更新)

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

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

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

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

 class CMfcPictureProcessingDlg : public CDialogEx
{ CMenu m_Menu;
CString filePath;//就是这个
。。。。。
};

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

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

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

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

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

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

(2)菜单中的保存:

(3)工程文件夹:

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

打开图片:

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

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

  

http://blog.csdn.net/mad1989/article/details/7920173 讲#ifndef、头文件变量在cpp中重复定义
“其实并不难,是你太悲观。”看到了这样一句鸡汤,很对。(其实就是链接内博客的名字)

第一章:CDib类库的建立的更多相关文章

  1. 第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现

    第一章 “我要点爆”微信小程序云开发之项目建立与我的页面功能实现 开发环境搭建 使用自己的AppID新建小程序项目,后端服务选择小程序·云开发,点击新建,完成项目新建. 新建成功后跳转到开发者工具界面 ...

  2. 第一章ASP.NET SignalR简介

    第一章ASP.NET SignalR简介 1.1概述: ASP.NET SignalR是微软新开发的类库,为的是帮助ASP.NET开发人员很方便地开发实时网络功能. SignalR允许服务器端和客户端 ...

  3. OpenGL笔记<第一章> 构建 GLSL class

    恭喜,我们终于很扎实地完成了第一章——glsl 入门 不幸的是,it's not the basic of GLSL shader ,我们下一节开篇,basic of GLSL shader 在下一章 ...

  4. 16第一章 ASP.Net编程基础知识

    第一章        ASP.Net编程基础知识 第一章        ASP.Net编程基础知识 本章首先介绍用ASP.Net技术编制服务器端动态网页所需的网络和HTML标记语言方面的有关知识.然后 ...

  5. ASM学习笔记--ASM 4 user guide 第一章翻译

    ASM是什么? 借用别人的话 :ASM 是一个 Java 字节码操控框架.它能被用来动态生成类或者增强既有类的功能. ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机 ...

  6. 虚拟机--第一章走进java--(抄书)

    这是本人阅读周志明老师的<深入理解Java虚拟机>第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除) 第一章走近java 世界上并没有 ...

  7. 【读书笔记】《编程珠玑》第一章之位向量&位图

    此书的叙述模式是借由一个具体问题来引出的一系列算法,数据结构等等方面的技巧性策略.共分三篇,基础,性能,应用.每篇涵盖数章,章内案例都非常切实棘手,解说也生动有趣. 自个呢也是头一次接触编程技巧类的书 ...

  8. 《Entity Framework 6 Recipes》翻译系列 (1) -----第一章 开始使用实体框架之历史和框架简述

    微软的Entity Framework 受到越来越多人的关注和使用,Entity Framework7.0版本也即将发行.虽然已经开源,可遗憾的是,国内没有关于它的书籍,更不用说好书了,可能是因为EF ...

  9. Asp.Net MVC4 + Oracle + EasyUI 学习 第一章

    Asp.Net MVC4 + Oracle + EasyUI  第一章 --操作数据和验证 本文链接:http://www.cnblogs.com/likeli/p/4234238.html 文章集合 ...

随机推荐

  1. telnet命令详解

    基础命令学习目录 原文链接:https://www.cnblogs.com/PatrickLiu/p/8556762.html telnet命令用于登录远程主机,对远程主机进行管理.telnet因为采 ...

  2. Django_分页

    目录 基本语法 示例 示例1 使用django内置Paginator模块 示例2 改写Paginator 示例3 自定义pager组件 示例3.1 objs与pager各自单独使用 示例3.2 obj ...

  3. React Native iOS 离线包

    平时使用React Native 时候, js代码和图片资源运行在一个Debug Server上(需要cd 到RN目录,然后终端执行 npm start 命令开启本地服务 ).每次更新代码之后只需要使 ...

  4. 图解Raid5数据存储的原理

  5. final发布视频展示博客

    Part One [探路者]选题展示视频链接: http://v.youku.com/v_show/id_XMzIxMDM2MTQ1Ng==.html?spm=a2h3j.8428770.341605 ...

  6. 《Java学习笔记JDK8》学习总结

    chapter 6   继承与多态 6.1何谓继承 1.继承的定义:继承就是避免多个类间重复定义共同行为. 2.总结:教材中通过设计一款RPG游戏的部分代码向我们展示了“重复”程序代码的弊端,为了改进 ...

  7. 第一次spring冲刺第5天

    今天进行讨论基础功能的核心代码方面,还有简单的讨论继续关于界面的美化, 计算生成的答案功能 public class Core {// char[]h={'+','-','*','/'};int re ...

  8. 【图论】POJ-3723 最大生成树

    一.题目 Description Windy has a country, and he wants to build an army to protect his country. He has p ...

  9. JAVA自学日记——Part Ⅰ.

    和C++比较相似,Java同样是面向对象的设计语言,在基础的语句上有一些不大的差别,经过两天的学习,大概的了解了在eclipse中如何进行简单的编程,解决一些简单的问题,诸如在学习C时做过的“字符串倒 ...

  10. Linux操作系统(三)

    文件系统: boot sector:记录引导文件 sb: super block 每个文件系统只有一个sb,其余black group均是它的备份 读写文件会进入到inode operation vf ...