vs2015 c++数字图像处理基础编程(一)

目录

一、图像的概念:​​

图像:

图像的矩阵表示:

颜色表:

彩色空间有三种:

二、BMP文件结构及存取

三、GDI对象及GDI位图

1、从Resource View资源中装入GDI位图

对位图拉伸:使用CBitmap中的StretchBlt()

四、设备无关位图(DIB)

调色板:

一个窗口中显示一个DIB过程??:

DIB访问函数:

五、面向过程的DIB的读写及访问:

面向对象的DIB读写及访问-构建ImgCenterDib类:

使用ImgCenterDib类实现多文档应用程序图像的可视化编程

六、面向对象的图像处理算法实现



代码github地址:

https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git.

一、图像的概念:​​

图像:

图像是像素点的集合

图像的矩阵表示:

灰度图像用二维矩阵表示

颜色表:

图像的颜色记录靠颜色表来完成,颜色表是一个存储颜色信息的数据库,依靠索引获取颜色;

特别的,对于真彩色图像,每个像素占3个存储空间(24位)分别对于RGB,图像本身已包含颜色信息,无需颜色表

彩色空间有三种:

1、RGB:rgb以不同比例的线性运算构成彩色信息,有源物体一般采用RGB空间,rgb三基色相加模型

2、CMY:无源物体采用CMY(Cyan/Magenta/Yellow,青/洋红/黄)三减色模型表示,如油墨、颜料、彩色打印机

3、HSI:(Hue/Saturation/intensity,色调/饱和度/强度)模型

色调Hue:表示颜色信息,将红(0)、黄(60)、绿(120)、青(180)、蓝(240)、洋红(300)用角度表示;

饱和度Saturation:表示色的纯度,0%(灰色光或白光)-100%(纯色光);

强度Intensity:表示彩色光颜色的强弱程度,用亮度表示。

色调与饱和度称为色度,表示颜色的类别与深浅程度。

具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git

二、BMP文件结构及存取

bmp图像结构的构成:

位图文件头、位图信息头、颜色表、位图数据四部分构成。

在c++中,四部分分别由结构体进行实现。

注意:

1、bmp的扫描每行所占的字节数必须是4的倍数,不足4的倍数要进行扩充

2、bmp图像的坐标原点在左下角,读取方式是从下到上、从左到右

bmp文件的读取:

读入:readBmp()

存盘:saveBmp()

bmp位图数据访问:

bmp利用readBmp读入图像,又用saveBmp写入磁盘,在读入图像之后,写入磁盘之前这段时间,数据是存在在内存中的,我们可以利用这个时间来对图像中的数据进行修改(访问)

灰度图像是需要颜色表来获取颜色信息的,但是由于灰度图像的颜色表中RGB分量相等都是0-255,因此只有亮度信息,没有颜色信息;如果对灰度图像的颜色表进行改变,则灰度图像就有了颜色信息,但是这个颜色信息并非真实世界中的颜色。

win sdk BMP文件读写:

#include "stdio.h"
#include "Windows.h"
//几个全局变量,存放读入图像的位图数据、宽、高、颜色表及每像素所占位数(比特) 
//此处定义全局变量主要为了后面的图像数据访问及图像存储作准备
unsigned char *pBmpBuf;//读入图像数据的指针
int bmpWidth;//图像的宽
int bmpHeight;//图像的高
RGBQUAD *pColorTable;//颜色表指针
int biBitCount;//图像类型

/***********************************************************************
* 函数名称:
* readBmp()
*
*函数参数:
*  char *bmpName -文件名字及路径
*
*返回值:
*   0为失败,1为成功
*
*说明:给定一个图像文件名及其路径,读图像的位图数据、宽、高、颜色表及每像素
*      位数等数据进内存,存放在相应的全局变量中
***********************************************************************/
bool readBmp(char *bmpName)
{
	//二进制读方式打开指定的图像文件
    FILE *fp=fopen(bmpName,"rb");
	if(fp==0) return 0;
	
	
	//跳过位图文件头结构BITMAPFILEHEADER
	fseek(fp, sizeof(BITMAPFILEHEADER),0);
	
	
	//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
	BITMAPINFOHEADER head;  
	fread(&head, sizeof(BITMAPINFOHEADER), 1,fp); 
	
	//获取图像宽、高、每像素所占位数等信息
	bmpWidth = head.biWidth;
	bmpHeight = head.biHeight;
	biBitCount = head.biBitCount;
	
	//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
	int lineByte=(bmpWidth * biBitCount/8+3)/4*4;
	
	//灰度图像有颜色表,且颜色表表项为256
	if(biBitCount==8){
		//申请颜色表所需要的空间,读颜色表进内存
		pColorTable=new RGBQUAD[256];
		fread(pColorTable,sizeof(RGBQUAD),256,fp);
	}
	
	//申请位图数据所需要的空间,读位图数据进内存
	pBmpBuf=new unsigned char[lineByte * bmpHeight];
	fread(pBmpBuf,1,lineByte * bmpHeight,fp);
	
	//关闭文件
	fclose(fp);
	
	return 1;
}

/***********************************************************************
* 函数名称:
* saveBmp()
*
*函数参数:
*  char *bmpName -文件名字及路径
*  unsigned char *imgBuf  -待存盘的位图数据
*  int width   -像素为单位待存盘位图的宽
*  int  height  -像素为单位待存盘位图高
*  int biBitCount   -每像素所占位数
*  RGBQUAD *pColorTable  -颜色表指针

*返回值:
*   0为失败,1为成功
*
*说明:给定一个图像位图数据、宽、高、颜色表指针及每像素所占的位数等信息,
*      将其写到指定文件中
***********************************************************************/
bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, 
			 int biBitCount, RGBQUAD *pColorTable)
{
	//如果位图数据指针为0,则没有数据传入,函数返回
	if(!imgBuf)
		return 0;
	
	//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
	int colorTablesize=0;
	if(biBitCount==8)
		colorTablesize=1024;

	//待存储图像数据每行字节数为4的倍数
	int lineByte=(width * biBitCount/8+3)/4*4;
	
	//以二进制写的方式打开文件
	FILE *fp=fopen(bmpName,"wb");
	if(fp==0) return 0;
	
	//申请位图文件头结构变量,填写文件头信息
	BITMAPFILEHEADER fileHead;
	fileHead.bfType = 0x4D42;//bmp类型
	
	//bfSize是图像文件4个组成部分之和
	fileHead.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
		+ colorTablesize + lineByte*height;
	fileHead.bfReserved1 = 0;
	fileHead.bfReserved2 = 0;
	
	//bfOffBits是图像文件前三个部分所需空间之和
	fileHead.bfOffBits=54+colorTablesize;
	
	//写文件头进文件
	fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);
	
	//申请位图信息头结构变量,填写信息头信息
	BITMAPINFOHEADER head; 
	head.biBitCount=biBitCount;
	head.biClrImportant=0;
	head.biClrUsed=0;
	head.biCompression=0;
	head.biHeight=height;
	head.biPlanes=1;
	head.biSize=40;
	head.biSizeImage=lineByte*height;
	head.biWidth=width;
	head.biXPelsPerMeter=0;
	head.biYPelsPerMeter=0;
	//写位图信息头进内存
	fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);
	
	//如果灰度图像,有颜色表,写入文件 
	if(biBitCount==8)
		fwrite(pColorTable, sizeof(RGBQUAD),256, fp);
	
	//写位图数据进文件
	fwrite(imgBuf, height*lineByte, 1, fp);
	
	//关闭文件
	fclose(fp);
	
	return 1;
}

/*
//文件存取函数调用的例子
void main()
{
	//读入指定BMP文件进内存
	char readPath[]="dog.BMP";
	readBmp(readPath);

	//输出图像的信息
	printf("width=%d,height=%d,biBitCount=%d\n",bmpWidth,bmpHeight,biBitCount);

	//将图像数据存盘
	char writePath[]="dogcpy.BMP";
	saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);

	//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
	delete []pBmpBuf;
	if(biBitCount==8)
		delete []pColorTable;
}
*/
/*
//文件数据进行修改的例子
void main()
{
	//读入指定BMP文件进内存
	char readPath[]="dog.BMP";
	readBmp(readPath);
	
	//输出图像的信息
	printf("width=%d,height=%d,biBitCount=%d\n",bmpWidth,bmpHeight,biBitCount);
	
	
	//循环变量,图像的坐标
	int i,j;
	//每行字节数
	int lineByte=(bmpWidth*biBitCount/8+3)/4*4;

	//循环变量,针对彩色图像,遍历每像素的三个分量
	int k;
	
	//将图像左下角1/4部分置成黑色
	if(biBitCount==8){//对于灰度图像
		for(i=0;i<bmpHeight/2;i++){
			for(j=0;j<bmpWidth/2;j++){
				*(pBmpBuf+i*lineByte+j)=0;
			}
		}
	}
	else if(biBitCount==24){//彩色图像
		for(i=0;i<bmpHeight/2;i++){
			for(j=0;j<bmpWidth/2;j++){
				for(k=0;k<3;k++)//每像素RGB三个分量分别置0才变成黑色
					*(pBmpBuf+i*lineByte+j*3+k)=0;
			}
		}
	}
	
    //将图像数据存盘
	char writePath[]="dogcpy.BMP";
	saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
		  
	//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
	delete []pBmpBuf;
	if(biBitCount==8)
		delete []pColorTable;
}
 */

//调色板与灰度图像的关系
void main()
{
	//读入指定BMP文件进内存
	char readPath[]="dog.BMP";
	readBmp(readPath);

	//输出图像的信息
	printf("width=%d,height=%d,biBitCount=%d\n",bmpWidth,bmpHeight,biBitCount);

	//改变灰度图像的颜色表蓝色分量的值,察看前后变化
	if(biBitCount==8){
		for(int i=0; i<256;i++){
			pColorTable[i].rgbBlue = 255-pColorTable[i].rgbBlue;
		}
	}


	//将图像数据存盘
	char writePath[]="dogcpy.BMP";
	saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);

	//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
	delete []pBmpBuf;
	if(biBitCount==8)
		delete []pColorTable;
}

 

 源码下载:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git

三、GDI对象及GDI位图

GDI(graphics device interface)图像设备接口的缩写,

GDI对象类型在windows中是通过MFC(Microsoft基础类库)中的类表示的

CGdiobject是所有GDI对象类的抽象基类,

即GDI对象类型在windows中是通过CGdiobject派生类的c++对象实现的。

CGdiobject派生类的c++对象有:

  • CBitmap:位图,此类封装了windows GDI位图,提供了一些操作位图的成员函数,是GDI对象的一种
  • CBrush:画刷,是GDI对象的一种
  • CFont:字体,是GDI对象的一种
  • CPallete:调色板,是GDI对象的一种
  • CPen:笔,是GDI对象的一种
  • CRgn:区域,是GDI对象的一种

使用CBitmap对象步骤:

  • 1、必须创建一个CBitmap对象,
  • 2、把对象选进设备环境中,
  • 3、调用该类中的成员对位图进行操作,
  • 4、对象使用完毕后,必须从设备环境中选出来并删除

1、从Resource View资源中装入GDI位图

从资源中直接获取位图是使用位图最简单的方法。

从Workspace窗口里的Resource Viewz中,打开工程位图资源列表

其中,IDB_building是位图资源ID,打开IDB_building位图资源管理器对话框,

看到对应的位图文件位于工程文件res目录下的building.bmp,

由于资源位图不支持24位真彩色,所以此图是256色的索引位图。

将资源的DIB转换为GFI位图:使用CBitmap中的LoadBitmap()函数

将内存中图像数据复制到显示器设备环境中:使用CBitmap中的BitBlt()函数

/
// CChap1_2View drawing

void CChap1_2View::OnDraw(CDC* pDC)
{
	//CBitmap对象
	CBitmap bitmap;

	//设备环境类对象
	CDC dcMemory;

	//加载资源位图
	bitmap.LoadBitmap(IDB_building);

	//创建内存设备环境
	dcMemory.CreateCompatibleDC(pDC);

	//把位图选进内存设备环境,并保存旧的GDI位图对象,
	CBitmap *oldbitmap=dcMemory.SelectObject(&bitmap);

	//显示
	pDC->BitBlt(0,0,400,300, &dcMemory,0,0,SRCCOPY);

	//释放bitmap,恢复原GDI位图
	dcMemory.SelectObject(oldbitmap);
}

结果显示:

对位图拉伸:使用CBitmap中的StretchBlt()

// CChap1_3View drawing

void CChap1_3View::OnDraw(CDC* pDC)
{
	//CBitmap对象
	CBitmap bitmap;

	//设备环境类对象
	CDC dcMemory;

	//加载资源位图
	bitmap.LoadBitmap(IDB_building);

	//创建内存设备环境
	dcMemory.CreateCompatibleDC(pDC);

	//把位图选进内存设备环境,并保存旧的GDI位图对象,
	CBitmap *oldbitmap=dcMemory.SelectObject(&bitmap);

	//将128*128的图像伸缩显示在以(0,0)点开始的450*250的一个区域内
	pDC->StretchBlt(0,0,450,250, &dcMemory,0,0,400,300,SRCCOPY);

	//释放bitmap,恢复原GDI位图
	dcMemory.SelectObject(oldbitmap);
}

拉伸结果

具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git

四、设备无关位图(DIB)

BMP文件包括DIB格式和DDB格式。

DDB(device-dependent bitmap):设备相关位图,存储与某个硬件设备(显示设备或打印设备)内存兼容的不压缩图像,依赖于硬件,不常用。

DIB(device-independent bitmap):设备无关位图,与设备无关,自带颜色信息、颜色表管理容易,具有很强通用性。

如果调用内存中的DIB位图?:

DIB位图有位图信息头、颜色表、位图数据三部分构成,没有位图文件头,是脱离文件存在的。

调色板:

一种GDI对象,是颜色映射接口;

将颜色映射到显示器或打印机;

可以使应用程序在不干扰其他应用程序前提下,充分利用输出设备的颜色描述能力

当我们需要显示DIB位图时,需要根据DIB位图的颜色表生产调色板。

一个窗口中显示一个DIB过程??:

  • 创建逻辑调色板(包含了DIB位图中颜色的GDI);
  • 实现逻辑调色板(使它进入硬件的系统的调色板);
  • 只有当用户在8-bpp(每像素8位)模式下运行才需要处理调色板

DIB访问函数:

window支持一些重要的DIB访问函数,但是没有被封装到MFC中,可以利用一下函数实现:

  • SetDIBitsToDevice
  • StretchDIBits
  • GetDIBits
  • CreateDIBitmap
  • CreateDIBSection

五、面向过程的DIB的读写及访问:

利用上面的windows自带的DIB访问函数,可以实现位图的可视化编程。

现在我们编写一个基于单文档、面向过程的位图显示与存储程序。

在基于MFC的文档-视图结果应用程序中,需要遵守两个原则:

  • 1、有关文件的操作(文件的读写)在文档类中完成
  • 2、有关显示部分的操作在视图类中完成具体见: 

原始图显示:

void CChap1_4View::OnDraw(CDC* pDC)
{
	//获取文档类句柄
	CChap1_4Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if(pDoc->m_pDib==NULL)
		return;

	//定义infoHead变量指向DIB中的BITMAPINFOHEADER结构
	BITMAPINFOHEADER *infoHead=(BITMAPINFOHEADER*)pDoc->m_pDib;

	//获取DIB的宽、高、所占位数
	int width=infoHead->biWidth;
	int height=infoHead->biHeight;
	int biBitCount=infoHead->biBitCount;

	//求颜色表的长度,彩色图像颜色表长度为0,非彩色图像(灰度图像)
	// 颜色表长度为pow(2,biBitCount)
	int colorTableLng;
	if(biBitCount!=24)
		colorTableLng=pow(2,biBitCount);
	else
		colorTableLng=0;

	//如果有颜色表,则创建调色板,hPalette为新创建的调色板句柄,
	//hOldPal旧的调色板句柄
	HPALETTE hPalette=0, hOldPal;
	if(colorTableLng!=0){

		//定义颜色表指针pColorTable,指向DIB的颜色表
		RGBQUAD *pColorTable=(RGBQUAD *)(pDoc->m_pDib+
			sizeof(BITMAPINFOHEADER));

		//申请空间,生成LOGPALETTE结构
		LPLOGPALETTE pLogPal = (LPLOGPALETTE)new char[2*sizeof(WORD)
			+colorTableLng * sizeof(PALETTEENTRY)];
		pLogPal->palVersion = 0x300;
		pLogPal->palNumEntries =colorTableLng;
		for(int i = 0; i < colorTableLng; i++) {
			pLogPal->palPalEntry[i].peRed = pColorTable[i].rgbRed;
			pLogPal->palPalEntry[i].peGreen =pColorTable[i].rgbGreen;
			pLogPal->palPalEntry[i].peBlue = pColorTable[i].rgbBlue;
			pLogPal->palPalEntry[i].peFlags = 0;
		}

		//创建逻辑调色板
		hPalette = ::CreatePalette(pLogPal);

		//将调色板选入系统
		hOldPal=::SelectPalette(pDC->GetSafeHdc(), hPalette, TRUE);

		//实现调色板
		pDC->RealizePalette();

		//清理空间
		delete []pLogPal;
	}

	//DIB显示所需要的模式
	pDC->SetStretchBltMode(COLORONCOLOR);

	//pImgData指向DIB的位图数据
	unsigned char* pImgData=(unsigned char*)(pDoc->m_pDib+
		sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD) * colorTableLng);

	//显示DIB到显示器
	::StretchDIBits(pDC->GetSafeHdc(), 0, 0, width, height,
		0, 0,  width, height, pImgData,
		 (LPBITMAPINFO)(pDoc->m_pDib), DIB_RGB_COLORS, SRCCOPY);

	//恢复原调色板
	if(hOldPal!=NULL)
		::SelectPalette(pDC->GetSafeHdc(), hOldPal, TRUE);
}

 位图访问数据后修改显示:

void CChap1_4View::OnDataAccess() 
{
		//获取文档类句柄
	CChap1_4Doc* pDoc = GetDocument();

	//如果DIB为空,则返回
	if(pDoc->m_pDib==NULL)
		return;

	//定义infoHead变量指向DIB中的BITMAPINFOHEADER结构
	BITMAPINFOHEADER *infoHead=(BITMAPINFOHEADER*)pDoc->m_pDib;

	//获取DIB的宽、高、所占位数
	int width=infoHead->biWidth;
	int height=infoHead->biHeight;
	int biBitCount=infoHead->biBitCount;

	//每行像素所占字节数,必须是4的倍数
	int lineByte=(width*biBitCount/8+3)/4*4;

	//求颜色表的长度,彩色图像颜色表长度为0,非彩色图像(灰度图像)
	// 颜色表长度为pow(2,biBitCount)
	int colorTableLng;
	if(biBitCount!=24)
		colorTableLng=pow(2,biBitCount);
	else
		colorTableLng=0;

	//pImgData指向DIB的位图数据
	unsigned char* pImgData=(unsigned char*)(pDoc->m_pDib+
		sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD) * colorTableLng);

	//以下将图像数据左下角1/4置成黑色
	//循环变量,图像的坐标
	int i, j;
	if(biBitCount==8){//灰度图像
		for(i=0;i<height/2;i++){
			for(j=0;j<width/2;j++){
				*(pImgData+i*lineByte+j)=0;
			}
		}
	}
	else{//彩色图像
		int k;
		for(i=0;i<height/2;i++){
			for(j=0;j<width/2;j++){
				for(k=0;k<3;k++)//彩色图像,每像素三个分量都置0
					*(pImgData+i*lineByte+j*3+k)=0;
			}
		}
	}
	
	//刷新显示
	Invalidate();
}

 

源码:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git

面向对象的DIB读写及访问-构建ImgCenterDib类:

上面利用面向过程的方式实现了图像的可视化编程;

由于MFC类中没有封装DIB类,我们可以自己封装类来实现面向对象的DIB读写及访问。

我们声明的类是ImgCenterDib(Image Center Dib,图像处理中心编写的DIB);

ImgCenterDib(里面封装了DIB位图处理所需要的基本的成员变量和成员函数);

以后的算法实现都依该类为基类派生(充分利用了面向对象的封装、继承特点,有使代码易于维护和移植)

  • ImgCenterDib.h中实现类的定义
  • 构造函数与析构函数
  • DIB显示函数-Dram()
  • DIB文件读入函数-Read()
  • DIB文件存储函数-Write()
  • DIB替换函数-ReplaceDib()
  • 其他帮助性的中间函数,如ComputerColorTabaLength()、MakePalette()、Empty()等,都是DIB读写及显示中的中间函数。
  • DIB尺寸获取函数-GetDimensions()

使用ImgCenterDib类实现多文档应用程序图像的可视化编程

原始图像进行数据访问:

void CChap1_5View::OnDataAccess1() 
{
	
	//获取文档类指针
	CChap1_5Doc *pDoc=GetDocument();
	
	//获取ImgCenterDib类对象的指针,访问打开文件的数据
	ImgCenterDib *pDib=pDoc->GetPDib();
	
	//位图数据的指针
	unsigned char *imgData=pDib->m_pImgData;
	
	//位图阵列的大小
	CSize imgSize=pDib->GetDimensions();
	
	//每像素的位数
	int nBitCount=pDib->m_nBitCount;
	
	//循环变量,图像坐标
	int i,j;
	
	//每像素占字节数
	int bytePerPixel=nBitCount/8;
	
	//每行像素所占字节数,必须是4的倍数
	int lineByte=(imgSize.cx*nBitCount/8+3)/4*4;
	
	//循环变量,每像素各字节访问的循环变量
	int k;
	
	//将图像左下角1/4部分置黑色
	for(i=0;i<imgSize.cy/2;i++){
		for(j=0;j<imgSize.cx/2;j++){
			for(k=0;k<bytePerPixel;k++){
				*(imgData+i*lineByte+j*bytePerPixel+k)=0;
			}
		}
	}
	
	//刷新显示
	Invalidate();
}

新建视图进行数据访问:

void CChap1_5View::OnDataAccess2() 
{
	//获取文档类指针
	CChap1_5Doc *pDoc=GetDocument();
	
	//获取ImgCenterDib类对象的指针,访问打开文件的数据
	ImgCenterDib *pDib=pDoc->GetPDib();
	
	//位图数据的指针
	unsigned char *imgData=pDib->m_pImgData;
	
	//位图阵列的大小
	CSize imgSize=pDib->GetDimensions();
	
	//每像素的位数
	int nBitCount=pDib->m_nBitCount;
	
	//每像素占字节数
	int bytePerPixel=nBitCount/8;
	
	//每行像素所占字节数,必须是4的倍数
	int lineByte=(imgSize.cx*nBitCount/8+3)/4*4;

	//申请缓冲区pBuf
	unsigned char* pBuf=new unsigned char[lineByte*imgSize.cy];

	//将原DIB位图数据拷贝至pBuf
	memcpy(pBuf, imgData, lineByte*imgSize.cy);
	
	//循环变量,图像坐标
	int i,j;
	//循环变量,每像素各字节访问的循环变量
	int k;
	
	//将pBuf左下角1/4部分置黑色
	for(i=0;i<imgSize.cy/2;i++){
		for(j=0;j<imgSize.cx/2;j++){
			for(k=0;k<bytePerPixel;k++){
				*(pBuf+i*lineByte+j*bytePerPixel+k)=0;
			}
		}
	}
	
	//打开新视图,显示分割结果
  CMainFrame* pFrame = (CMainFrame *)(AfxGetApp()->m_pMainWnd);
  //发送一个新建文件的消息,创建一个新的文档-视图
  pFrame->SendMessage(WM_COMMAND, ID_FILE_NEW);

  //获取新建视图指针
  CChap1_5View* pView=(CChap1_5View*)pFrame->MDIGetActive()->GetActiveView();

  //获取相关联的新的文档类指针
  CChap1_5Doc* pDocNew=pView->GetDocument();

  //获取新文档中的ImgCenterDib类对象指针
  ImgCenterDib *dibNew=pDocNew->GetPDib();
  
  //调用ReplaceDib,用改变以后的位图数据替换原位图,
  dibNew->ReplaceDib(imgSize,nBitCount,pDib->m_lpColorTable, pBuf);

  //文档数据置脏,提示存盘信息
  pDocNew->SetModifiedFlag(TRUE);

  //各视图刷新显示
  pDocNew->UpdateAllViews(pView);
}

结果:

六、面向对象的图像处理算法实现

以ImgCenterDib类为基类派生一个具有特效显示功能的特效显示类,并实现四种图像特效显示效果。

  • 图像扫描
  • 图像滑动
  • 图像渐进
  • 图像马赛克
void SpecialEffectShow::Scan(CDC *pDC) //扫描显示一幅图象
{
	int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
	int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位

	CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
	CBrush brush(RGB(255,255,255));	//设置画刷为白色
	pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果

	LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
	//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型

	for(int j=0;j<bitmapHeight;j++)//扫描特效显示的具体算法
	{
		::StretchDIBits(pDC->GetSafeHdc(),
			0, j, bitmapWidth, 1,
			0, bitmapHeight-j, bitmapWidth, 1,
			m_pImgData, pBitmapInfo,
			DIB_RGB_COLORS, SRCCOPY);
		Sleep(3);//设置延时时间
	}
}

void SpecialEffectShow::Slide(CDC *pDC)
{
	int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
	int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位

	CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
	CBrush brush(RGB(255,255,255));	//设置画刷为白色
	pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果

	LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
	//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型

	for(int i=0;i<=bitmapWidth;i++)//滑动特效显示的具体算法
	{
		for(int j=0;j<=bitmapHeight;j=j+8)
		{
			::StretchDIBits(pDC->GetSafeHdc(),
				0,  j-8 ,  i+1, 8,
				bitmapWidth-i, bitmapHeight-j, i+1, 8,
				m_pImgData, pBitmapInfo,
				DIB_RGB_COLORS, SRCCOPY);
		}
		Sleep(3);//设置延时时间
	}
}

void SpecialEffectShow::FadeIn(CDC *pDC)
{
	int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
	int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位

	CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
	CBrush brush(RGB(0,0,0));	//设置画刷为黑色
	pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成黑色,达到刷新屏幕的效果

	LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
	//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型

		
	int lineByte=(m_imgWidth*m_nBitCount/8+3)/4*4;//每行像素占字节数,必须为4的倍数
	LPBYTE temp =new BYTE[bitmapHeight*lineByte];//在堆上分配内存存储临时图象数据
	memset (temp,0,bitmapHeight*lineByte);//初始置零

	for(int m=0;m<256;m++)
	{
		for(int j = 0; j < bitmapHeight; j++)
		{
			for(int i = 0; i < lineByte; i ++)
				temp[j*lineByte+i]=m_pImgData[j*lineByte+i]*m/256;
		}
		::StretchDIBits(pDC->GetSafeHdc(),
			0, 0, bitmapWidth, bitmapHeight,
			0, 0, bitmapWidth, bitmapHeight,
			temp, pBitmapInfo,DIB_RGB_COLORS, SRCCOPY);
		Sleep(3);//设置延时时间
	}
	delete [] temp;//释放堆上分配的内存
}

void SpecialEffectShow::Mosaik(CDC *pDC)
{
	int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
	int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位

	CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
	CBrush brush(RGB(255,255,255));	//设置画刷为白色
	pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果

	LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
	//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型


	//马赛克的大小设置为宽高都是12个像素
	int WithFalg=0; //图象宽是12的整数倍的标志
	int HeightFlag=0;//图象高是12的整数倍的标志
	if((bitmapWidth%12)!=0)WithFalg=1;
	if((bitmapHeight%12)!=0)HeightFlag=1;
	long ArrayLength=(bitmapWidth/12+WithFalg)*(bitmapHeight/12+HeightFlag);
	//将图象宽高都延拓至12的整数倍,然后将图象分成12X12的小块,按行顺序排列成一个数组

	struct Square
	{
		CPoint pt;//记录每个12X12的小块的左上角坐标
		bool HitFlag;//当随机扫描所有小块的时候,记录是否曾经被扫描过
	};//自己定义一个结构,由一个Cpoint类型和一个击中标志构成

	Square* SquareArray=new Square[ArrayLength];
	int x=0;
	int y=0;

	for(int i=0; i<ArrayLength; i++)
	{
		//初始化每个小块的坐标点
		SquareArray[i].pt.x=x;
		SquareArray[i].pt.y=y;
		x=x+12;
		if(x>bitmapWidth)
		{x=0;y=y+12;}

		//初始所有小块都未被击中过
		SquareArray[i].HitFlag=FALSE;
	}
	
	long RandNum;//随即变量
	srand( (unsigned)time( NULL ) );//生成随机种子
	int i;
	for(i=0;i<=ArrayLength;i++)
	{	
		do
		{
			RandNum=(long)( ( (double)ArrayLength )*rand()/RAND_MAX );//随机变量在0到ArrayLength-1之间取值
			x=SquareArray[RandNum].pt.x;
			y=SquareArray[RandNum].pt.y;		
		}while(SquareArray[RandNum].HitFlag==TRUE);//检查小块以前是否被击中过,如果是,重新计算一个随机数
		SquareArray[RandNum].HitFlag=TRUE;//设置击中标志

		::StretchDIBits(pDC->GetSafeHdc(),
			x, y, 12,12,
			x,bitmapHeight-y-12, 12,12,
			m_pImgData, pBitmapInfo,DIB_RGB_COLORS, SRCCOPY);
		Sleep(1);//设置延时时间
	}
	delete [] SquareArray;
}

具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git

  • 0
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DLANDML

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值