本文主要讲述基于VC++6.0 MFC图像处理的应用知识,主要结合自己大三所学课程《数字图像处理》及课件进行解说,主要通过MFC单文档视图实现显示BMP图片空间几何变换。包含图像平移、图形旋转、图像反转倒置镜像和图像缩放的知识。
http://download.csdn.net/detail/eastmount/8772951
同一时候文章比較具体基础。没有採用GDI+获取矩阵。而是通过读取BMP图片信息头和矩阵像素实现变换,希望该篇文章对你有所帮助,尤其是刚開始学习的人和学习图像处理的学生。
【数字图像处理】一.MFC具体解释显示BMP格式图片
【数字图像处理】二.MFC单文档切割窗体显示图片
【数字图像处理】三.MFC实现图像灰度、採样和量化功能具体解释
【数字图像处理】四.MFC对话框绘制灰度直方图
【数字图像处理】五.MFC图像点运算之灰度线性变化、灰度非线性变化、阈值化和均衡化处理具体解释
http://download.csdn.net/detail/eastmount/8772951
一. 图像平移
前一篇文章讲述了图像点运算(基于像素的图像变换),这篇文章讲述的是图像几何变换:在不改变图像内容的情况下对图像像素进行空间几何变换的处理方式。
点运算对单幅图像做处理,不改变像素的空间位置;代数运算对多幅图像做处理。也不改变像素的空间位置;几何运算对单幅图像做处理,改变像素的空间位置,几何运算包含两个独立的算法:空间变换算法和灰度级插值算法。
点运算对单幅图像做处理,不改变像素的空间位置;代数运算对多幅图像做处理。也不改变像素的空间位置;几何运算对单幅图像做处理,改变像素的空间位置,几何运算包含两个独立的算法:空间变换算法和灰度级插值算法。
空间变换操作包含简单空间变换、多项式卷绕和几何校正、控制栅格插值和图像卷绕,这里主要讲述简单的空间变换。如图像平移、镜像、缩放和旋转。主要是通过线性代数中的齐次坐标变换。
图像平移坐标变换例如以下:
执行效果例如以下图所看到的,当中BMP图片(0,0)像素点为左下角。
其代码核心算法:
1.在对话框中输入平移坐标(x,y) m_xPY=x。m_yPY=y
2.定义Place=dlg.m_yPY*m_nWidth*3 表示当前m_yPY行须要填充为黑色
3.新建一个像素矩阵 ImageSize=new unsigned char[m_nImage]
4.循环整个像素矩阵处理
for(int i=0 ; i<m_nImage ; i++ ){
if(i<Place) {ImageSize[i]=black; continue;} //黑色填充底部 从小往上画图
else if(i>=Place && countWidth<dlg.m_xPY*3) {//黑色填充左部分
ImageSize[i]=black; countWidth++; continue;
}
else if(i>=Place && countWidth>=dlg.m_xPY*3) {//图像像素平移区域
ImageSize[i]=m_pImage[m_pImagePlace];//原(0,0)像素赋值过去
m_pImagePlace++; countWidth++;
if(countWidth==m_nWidth*3) { //一行填满 m_pImagePlace走到(0,1)
number++; m_pImagePlace=number*m_nWidth*3;
}
}
}
5.写文件画图fwrite(ImageSize,m_nImage,1,fpw)
第一步:在ResourceView资源视图中,加入Menu子菜单例如以下:(注意ID号)
其代码核心算法:
1.在对话框中输入平移坐标(x,y) m_xPY=x。m_yPY=y
2.定义Place=dlg.m_yPY*m_nWidth*3 表示当前m_yPY行须要填充为黑色
3.新建一个像素矩阵 ImageSize=new unsigned char[m_nImage]
4.循环整个像素矩阵处理
for(int i=0 ; i<m_nImage ; i++ ){
if(i<Place) {ImageSize[i]=black; continue;} //黑色填充底部 从小往上画图
else if(i>=Place && countWidth<dlg.m_xPY*3) {//黑色填充左部分
ImageSize[i]=black; countWidth++; continue;
}
else if(i>=Place && countWidth>=dlg.m_xPY*3) {//图像像素平移区域
ImageSize[i]=m_pImage[m_pImagePlace];//原(0,0)像素赋值过去
m_pImagePlace++; countWidth++;
if(countWidth==m_nWidth*3) { //一行填满 m_pImagePlace走到(0,1)
number++; m_pImagePlace=number*m_nWidth*3;
}
}
}
5.写文件画图fwrite(ImageSize,m_nImage,1,fpw)
第一步:在ResourceView资源视图中,加入Menu子菜单例如以下:(注意ID号)
第二步:设置平移对话框。将试图切换到ResourceView界面--选中Dialog,右键鼠标新建一个Dialog,并新建一个名为IDD_DIALOG_PY。编辑框(X)IDC_EDIT_PYX
和 (Y)IDC_EDIT_PYY。确定为默认button。设置成下图对话框:
第三步:在对话框资源模板空白区域双击鼠标—Create a new class创建一个新类--命名为CImagePYDlg。
会自己主动生成它的.h和.cpp文件。
打开
类向导(Ctrl W),选择类名:CImagePYDlg加入成员变量例如以下图所看到的。同一时候在Message Maps中生成ID_JHBH_PY实现函数。 第四步:在CImageProcessingView.cpp中加入头文件#include "ImagePYDlg.h",并实现平移。
/********************************************************/ /* 图像空间几何变换:图像平移 ID_JHBH_PY(几何变换-平移) /* 使用平移对话框:CImagePYDlg dlg /* 算法:f(x,y)=f(x+x0,y+y0)图像全部点平移,空的补黑'0' /* 注意该图像平移方法仅仅是从左上角(0,0)处開始平移 /* 其它方向原理同样 自己去实现 /********************************************************/ void CImageProcessingView::OnJhbhPy() { if(numPicture==0) { AfxMessageBox("加载图片后才干空间平移!",MB_OK,0); return; } //定义採样对话框也是用来空间变换平移的坐标 CImagePYDlg dlg; if( dlg.DoModal()==IDOK ) //显示对话框 { //採样坐标最初为图片的自身像素 if( dlg.m_xPY>m_nWidth || dlg.m_yPY>m_nHeight ) { AfxMessageBox("图片平移不能为超过原图长宽!",MB_OK,0); return; } AfxMessageBox("图片空间变换-平移!",MB_OK,0); //打开暂时的图片 读写文件 FILE *fpo = fopen(BmpName,"rb"); FILE *fpw = fopen(BmpNameLin,"wb+"); fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo); fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo); fwrite(&bfh,sizeof(BITMAPFILEHEADER),1,fpw); fwrite(&bih,sizeof(BITMAPINFOHEADER),1,fpw); fread(m_pImage,m_nImage,1,fpo); /************************************************************/ /* 图片空间变换-平移 /* 坐标(dlg.m_xPY,dlg.m_yPY)表示图像平移的坐标 /* 先用Plave计算出平移后的起始坐标。其它的坐标赋值为'0'黑色 /* 然后依次平移坐标,空的赋为黑色,否则填充 /************************************************************/ /******************************************************************/ /* 严重错误1:数组变量赋值相等 /* 在View.h中定义变量 BYTE *m_pImage 读入图片数据后的指针 /* 建立暂时变量数组,让它平移变换 unsigned char *ImageSize /* ImageSize=m_pImage(错误) /* 会导致ImageSize赋值变换时m_pImage也产生了变换,所以输出全为黑色 /* 由于它俩指向了同样的数组地址 /* 解决方法:使用以下C++的new方法动态分配或for循环i=m_nImage赋值 /******************************************************************/ /*暂时变量存储的像素与m_pImage同样。便于处理图像*/ unsigned char *ImageSize; ImageSize=new unsigned char[m_nImage]; //new和delete有效的进行动态内存的分配和释放 int Place; //建立暂时坐标 记录起始坐标(0,0)平移过来的位置 int m_pImagePlace; //原始图像平移为(0,0) 图像把它平移到Place位置 unsigned char black; //填充黑色='0' /************************************************************/ /* for(int i=0 ; i<m_nHeight ; i++ ) /* for(int j=0 ; j<m_nWidth ; j++ ) /* 不能使用的上面的由于可能图像的最后一行没有完整的一行像素 /* 这样会出现exe报错,使用m_nImage读写全部像素比較正确 /************************************************************/ Place=dlg.m_yPY*m_nWidth*3; //前m_yPY行都要填充为黑色 black=0; //颜色为黑色 m_pImagePlace=0; //图像处事位置为(0,0),把该点像素平移过去 int countWidth=0; //记录每行的像素个数,满行时变回0 int number=0; //数字记录使用的像素行数,平移时使用 for(int i=0 ; i<m_nImage ; i++ ) { /*假设每行的像素填满时清为0*/ if(countWidth==m_nWidth*3) { countWidth=0; } /*第一部分:到平移后像素位置前面的全部像素点赋值为黑色*/ if(i<Place) { ImageSize[i]=black; //赋值为黑色 continue; } /*第二部分:平移区域的左边部分赋值为黑色*/ else if(i>=Place && countWidth<dlg.m_xPY*3) { //RGB乘3 ImageSize[i]=black; //赋值为黑色 countWidth++; continue; } /****************************/ /* 各部分如图所看到的: /* 000000000000000000000000 /* 000000000000000000000000 /* 0000000................. /* 0000000................. /* 0000000................. /* 0000000................. /* 点表示像素部分,0为黑色 /****************************/ /* 重点错误提示:由于bmp图像显示是从左下角開始存储(0,0)点所以输出图像为 */ /* bmp图像是从左下角到右上角排列的 */ /****************************/ /* 各部分如图所看到的: /* 0000000................. /* 0000000................. /* 0000000................. /* 0000000................. /* 000000000000000000000000 /* 000000000000000000000000 /* 点表示像素部分,0为黑色 /****************************/ /*第三部分:图像像素平移区域*/ else if(i>=Place && countWidth>=dlg.m_xPY*3) { ImageSize[i]=m_pImage[m_pImagePlace]; m_pImagePlace++; countWidth++; if(countWidth==m_nWidth*3) { number++; m_pImagePlace=number*m_nWidth*3; } } } fwrite(ImageSize,m_nImage,1,fpw); fclose(fpo); fclose(fpw); numPicture = 2; level=200; //200表示几何变换 Invalidate(); } }同一时候在ShowBitmap中加入level标记又一次绘制图片,代码例如以下:
else //图像几何变换 if(level=200) { m_hBitmapChange = (HBITMAP) LoadImage(NULL,BmpNameLin,IMAGE_BITMAP,0,0, LR_LOADFROMFILE|LR_DEFAULTSIZE|LR_CREATEDIBSECTION); }执行时须要注意一点:BMP图像在处理过程中可能会出现一些斜线,而平移(40,60)位移量时可能出现例如以下。他是由于BMP格式有个很重要的规定,要求每一扫描的字节数据必须能被4整除,也就是Dword对齐(长度4字节),假设图像的一行字节数不能被4整除,就须要在每行末尾不起0达到标准。
比如一行像素为97字节。我们就须要补3个字节吗,数值能够是0,可是我们在BMP格式的信息头里说明了其宽度。所以补齐后对我们没有影响,所以后面补若干个字节的0就可以直到被4整除。
通过后面的图像缩放后,我从学做了一遍这个补齐的缩放。
代码例如以下,可以实现完美平移。nice啊~
void CImageProcessingView::OnJhbhPy() { if(numPicture==0) { AfxMessageBox("加载图片后才干空间平移!",MB_OK,0); return; } //定义採样对话框也是用来空间变换平移的坐标 CImagePYDlg dlg; if( dlg.DoModal()==IDOK ) //显示对话框 { //採样坐标最初为图片的自身像素 if( dlg.m_xPY>m_nWidth || dlg.m_yPY>m_nHeight ) { AfxMessageBox("图片平移不能为超过原图长宽!",MB_OK,0); return; } AfxMessageBox("图片空间变换-平移!",MB_OK,0); //打开暂时的图片 读写文件 FILE *fpo = fopen(BmpName,"rb"); FILE *fpw = fopen(BmpNameLin,"wb+"); fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo); fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo); int num; //记录每行多余的图像素数个数 int sfSize; //补齐后的图像大小 //重点:图像的每行像素都必须是4的倍数:1*1的图像为 r g b 00H if(m_nWidth*3%4!=0) { num=(4-m_nWidth*3%4); sfSize=(m_nWidth*3+num)*m_nHeight; //每行多number个 } else { num=0; sfSize=m_nWidth*m_nHeight*3; } //注意:假如最后一行像素不足,我默认处理为完整的一行,不足补00H //总之处理后的图像总是m*n且为4倍数,每行都完整存在 /*更改文件头信息 定义暂时文件头结构变量*/ BITMAPFILEHEADER bfhsf; BITMAPINFOHEADER bihsf; bfhsf=bfh; bihsf=bih; bfhsf.bfSize=sfSize+54; fwrite(&bfhsf,sizeof(BITMAPFILEHEADER),1,fpw); fwrite(&bihsf,sizeof(BITMAPINFOHEADER),1,fpw); fread(m_pImage,m_nImage,1,fpo); CString str; str.Format("补齐=%d",num); AfxMessageBox(str); /*暂时变量存储的像素与sfSize同样 new和delete有效的进行动态内存的分配和释放*/ unsigned char *ImageSize; ImageSize=new unsigned char[sfSize]; int Place; //建立暂时坐标 记录起始坐标(0,0)平移过来的位置 int m_pImagePlace; //原始图像平移为(0,0) 图像把它平移到Place位置 unsigned char black=0; //填充黑色='0' unsigned char other=0; //补码00H='