资源描述
图像处理实验部分
打开图像
1. 创建MFC Application工程: 文件名称:MyImage
2. 添加cdib.h和cdib.cpp到工程项目中(也可以在工程项目中创建这两个文件,需要编写程序)
3. 在MyImageDoc.h文件中增加如下语句
public:
CDib *m_pDib; (此类中增加成员变量表示Cdib对象的指针)
4. 在MyImageDoc.cpp文件中的构造函数中添加:
m_pDib = new CDib;
在析构函数中添加
delete m_pDib;
5. 在MyImageDoc.h文件中添加#include "cdib.h"
6. 在Doc.cpp中CMyImageDoc::Serialize(CArchive& ar)函数中添加:
m_pDib->Serialize(ar);
用CDib的串行化来实现CDisplayBmpImDoc文档的串行化。
7. 在CMyImageView.h中添加变量声明
18
public:
int lWidth;
int lHeight;
long int lLineBytes;
CPoint StartPoint;
CPoint EndPoint;
bool m_bTwoValue;
8. CMyImageView.cpp文件中的OnDraw()函数中添加代码
void CMyImageView::OnDraw(CDC* pDC)
{
…
CSize size;
int lHeight,lWidth;
if (pDoc->m_pDib->m_lpBMIH!=NULL)
{
lWidth=pDoc->m_pDib->m_lpBMIH->biWidth;
lHeight=pDoc->m_pDib->m_lpBMIH->biHeight;
size.cx=lWidth;
size.cy=lHeight;
pDoc->m_pDib->Draw(pDC,CPoint(0,0),size);
}
}
运行,即可打开一幅BMP格式的图像
一、灰度化与直方图
图1 初始图片 图2 灰度化图片(作为原图片使用)
图3 灰度化图片直方图
灰度化图片直方图生成流程图
二、图像滤波
(一)均值滤波(3*3模板)
基本思想:
基本原理是用均值代替原图像中的各个像素值,即对待处理的当前像素点(x,y),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度值g(x,y)。
步骤如下:
1.在资源菜单menu中Hist Modify中添加菜单按钮Mean Filter
2.ID名称可以随意修改,也可以默认。
3.中CMyImage点击右键,选择属性
在下拉菜单中找到ID_IMGSMOOTHMEANFILTER,添加代码
4.在MyImageView.cpp中出现
void CMyImageView::OnImgsmoothMeanfilter()函数
5.在其中添加代码
void CMyImageView::OnImgsmoothMeanfilter()
{
CMyImageDoc* pDoc = GetDocument();
int lHeight=pDoc->m_pDib->m_lpBMIH->biHeight;
int lWidth=pDoc->m_pDib->m_lpBMIH->biWidth;
int lLineBytes=(lWidth+3)/4*4;
unsigned char *lpSrc;
unsigned char *lpDst;
unsigned char *lpNewDib;
lpNewDib=new unsigned char[lLineBytes*lHeight];
memcpy(lpNewDib,pDoc->m_pDib->m_lpImage,lLineBytes*lHeight);
for (int i = 0; i <lHeight; i ++)
{
lpSrc = pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i) ;
lpDst = lpNewDib + lLineBytes* (lHeight-1-i) ;
for (int j =0; j <lWidth; j ++)
{
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)
{
*lpDst=(*(lpSrc)+*(lpSrc+1)+*(lpSrc-1)+
*(lpSrc+lLineBytes)+*(lpSrc-lLineBytes)+
*(lpSrc+lLineBytes+1)+*(lpSrc-lLineBytes+1)+
*(lpSrc+lLineBytes-1)+*(lpSrc-lLineBytes-1))/9;
}
lpSrc++;lpDst++;
}
}
memcpy(pDoc->m_pDib->m_lpImage,lpNewDib,lLineBytes*lHeight);
Invalidate(true);
MessageBox(L"均值滤波");
}
(二)中值滤波(3*3模板)
基本思想:
中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点。方法是去某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序,生成单调上升的二维数据序列,并取出序列中位于中间位置的灰度作为中心像素的灰度。
步骤如下:
1.在资源菜单menu中Hist Modify中添加菜单按钮Median Filter
2.中CMyImage点击右键,选择属性
在下拉菜单中找到ID_IMGSMOOTHMEDIANFILTER,添加代码
3.在MyImageView.cpp中出现
void CMyImageView::OnImgsmoothMedianfilter()函数
4.在其中添加代码
void CMyImageView::OnImgsmoothMedianfilter()
{
CMyImageDoc* pDoc = GetDocument();
int lHeight=pDoc->m_pDib->m_lpBMIH->biHeight;
int lWidth=pDoc->m_pDib->m_lpBMIH->biWidth;
int lLineBytes=(lWidth+3)/4*4;
unsigned char *lpSrc;
unsigned char *lpDst;
unsigned char *lpNewDib;
lpNewDib=new unsigned char[lLineBytes*lHeight]; memcpy(lpNewDib,pDoc->m_pDib->m_lpImage,lLineBytes*lHeight);
for (int i = 0; i <lHeight; i ++)
{
lpSrc = pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i) ;
lpDst = lpNewDib + lLineBytes* (lHeight-1-i) ;
for (int j =0; j <lWidth; j ++)
{
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)
{
int a[9]={0};int m,n,temp;
a[0]=*(lpSrc);a[1]=*(lpSrc+1);a[2]=*(lpSrc+lLineBytes); a[3]=*(lpSrc-lLineBytes);a[4]=*(lpSrc+lLineBytes+1);a[5]=*(lpSrc-lLineBytes+1); a[6]=*(lpSrc-1);a[7]=*(lpSrc+lLineBytes-1);a[8]=*(lpSrc-lLineBytes-1);
for(n=0;n<9;n++)
{
for(m=n+1;m<9;m++)
if(a[n]<a[m])
{
temp=a[n];
a[n]=a[m];
a[m]=temp;
}
}
*lpDst=a[4];
}
lpSrc++;lpDst++;
}
}
memcpy(pDoc->m_pDib->m_lpImage,lpNewDib,lLineBytes*lHeight);
Invalidate(true);
MessageBox(L"中值滤波");
}
(三)均值滤波(5*5模板)
1.在资源菜单menu中Hist Modify中添加菜单按钮Median Filter5
2.中CMyImage点击右键,选择属性
在下拉菜单中找到ID_IMGSMOOTHMEDIANFILTER5,添加代码
3.在MyImageView.cpp中出现
void CMyImageView::OnImgsmoothMedianfilter5()函数
4.在其中添加代码
void CMyImageView::OnImgsmoothMeanfilter5()
{
…
for (int i = 0; i <lHeight; i ++)
{
lpSrc = pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i) ;
lpDst = lpNewDib + lLineBytes* (lHeight-1-i) ;
for (int j =0; j <lWidth; j ++)
{
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)
{
*lpDst=(*(lpSrc)+*(lpSrc+1)+*(lpSrc-1)+
*(lpSrc+lLineBytes)+*(lpSrc-lLineBytes)+
*(lpSrc+lLineBytes+1)+*(lpSrc-lLineBytes+1)+
*(lpSrc+lLineBytes-1)+*(lpSrc-lLineBytes-1)+
*(lpSrc-2*lLineBytes)+ *(lpSrc+2*lLineBytes)+ *(lpSrc-2*lLineBytes-1)+
*(lpSrc-2*lLineBytes-2)+ *(lpSrc-2*lLineBytes+1)+ *(lpSrc-2*lLineBytes+2)+
*(lpSrc+2*lLineBytes-1)+ *(lpSrc+2*lLineBytes-2)+ *(lpSrc+2*lLineBytes+1)+
*(lpSrc+2*lLineBytes+2)+ *(lpSrc+2)+*(lpSrc-2)+
*(lpSrc+lLineBytes+2)+*(lpSrc-lLineBytes+2)+
*(lpSrc+lLineBytes-2)+*(lpSrc-lLineBytes-2))/15;
}
lpSrc++;lpDst++;
}
}
memcpy(pDoc->m_pDib->m_lpImage,lpNewDib,lLineBytes*lHeight);
Invalidate(true);
MessageBox(L"均值滤波5*5");
}
(四)中值滤波(5*5模板)
1.在资源菜单menu中Hist Modify中添加菜单按钮Median Filter5
2.中CMyImage点击右键,选择属性
在下拉菜单中找到ID_IMGSMOOTHMEDIANFILTER5,添加代码
3.在MyImageView.cpp中出现
void CMyImageView::OnImgsmoothMedianfilter()函数
4.在其中添加代码
void CMyImageView::OnImgsmoothMedianfilter5()
{
…
for (int i = 0; i <lHeight; i ++)
{
lpSrc = pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i) ;
lpDst = lpNewDib + lLineBytes* (lHeight-1-i) ;
for (int j =0; j <lWidth; j ++)
{
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)
{
int a[25]={0};int m,n,temp;
a[0]=*(lpSrc);a[1]=*(lpSrc+1);a[2]=*(lpSrc+lLineBytes);
a[3]=*(lpSrc-lLineBytes);a[4]=*(lpSrc+lLineBytes+1);
a[5]=*(lpSrc-lLineBytes+1);a[6]=*(lpSrc-1);
a[7]=*(lpSrc+lLineBytes-1);a[8]=*(lpSrc-lLineBytes-1);
a[9]=*(lpSrc-2*lLineBytes);a[10]=*(lpSrc+2*lLineBytes);
a[11]=*(lpSrc-2*lLineBytes-1);a[12]=*(lpSrc-2*lLineBytes-2);
a[13]= *(lpSrc-2*lLineBytes+1);a[14]= *(lpSrc-2*lLineBytes+2);
a[15]=*(lpSrc+2*lLineBytes-1);a[16]=*(lpSrc+2*lLineBytes-2);
a[17] =*(lpSrc+2*lLineBytes+1); a[18]=*(lpSrc+2*lLineBytes+2);
a[19]= *(lpSrc+2);a[20]=*(lpSrc-2);a[21]=*(lpSrc+lLineBytes+2);
a[22]=*(lpSrc-lLineBytes+2);a[23]=*(lpSrc+lLineBytes-2);
a[24]=*(lpSrc-lLineBytes-2);
for(n=0;n<25;n++)
{
for(m=n+1;m<25;m++)
if(a[n]<a[m])
{
temp=a[n];
a[n]=a[m];
a[m]=temp;
}
}
*lpDst=a[12];
}
lpSrc++;lpDst++;
}
}
memcpy(pDoc->m_pDib->m_lpImage,lpNewDib,lLineBytes*lHeight);
Invalidate(true);
MessageBox(L"中值滤波");
}
(五)运行结果比较和分析
结果分析:
这个取均值的模板其实是一个低通滤波器。因图像细节信息主要分布在高频区域,因此均值滤波的过程会导致图像变模糊。如果模板选取过大,则这种模糊会加剧;模板选择越小,去噪能力会下降。即3*3模板去噪能力没有5*5模板强,但5*5模板的处理室图像更模糊,如图1,2,3,4,5所示。
用3*3的模板和5*5处理图像,结果不一样。5*5的模板产生的图形边缘更模糊,亮点更少。同时使用中值滤波算法,可以在保护图像边缘同时去除处噪声,而均值滤波不能对图像边缘保护,如图1,2,3,4,5所示。
对于椒盐噪声,中值滤波能在去除噪声的同时较好的保持图像边缘,而均值滤波效果不佳。如图6,7,8所示。
原图像均值与中值滤波流程图
图1 原图像
图2 3*3均值滤波 图3 3*3中值滤波
图4 5*5均值滤波 图5 5*5中值滤波
图6 添加椒盐噪声图像 图7 3*3均值滤波 图8 3*3中值滤波
三、边缘检测
(一)梯度算子(以Priwitt为例)
基本思想:
利用梯度幅值在边缘处达到极值检测边缘。该法不受施加运算方向限制,同时能获得边缘方向信息,定位精度高,但对噪声较为敏感。
主要代码:
void CMyImageView::OnEdgePriwitt()
{
CMyImageDoc* pDoc = GetDocument();
int lHeight=pDoc->m_pDib->m_lpBMIH->biHeight;
int lWidth=pDoc->m_pDib->m_lpBMIH->biWidth;
int lLineBytes=(lWidth+3)/4*4;
unsigned char *lpSrc; unsigned char *lpDst;
unsigned char *lpNewDib;
lpNewDib=new unsigned char[lLineBytes*lHeight]; memcpy(lpNewDib,pDoc->m_pDib->m_lpImage,lLineBytes*lHeight);
for (int i = 0; i <lHeight; i ++)
{
lpSrc = pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i) ;
lpDst = lpNewDib + lLineBytes* (lHeight-1-i) ;
for (int j =0; j <lWidth; j ++)
{
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)//二阶微分,梯度算子
{
int tmp1=*(lpSrc+lLineBytes-1)+1**(lpSrc-1)+*(lpSrc-lLineBytes-1)
-*(lpSrc+lLineBytes+1)-1**(lpSrc+1)-*(lpSrc-lLineBytes+1);
int tmp2=*(lpSrc+lLineBytes-1)+1**(lpSrc+lLineBytes)+*(lpSrc+lLineBytes+1)
-*(lpSrc-lLineBytes-1)-1**(lpSrc-lLineBytes)-*(lpSrc-lLineBytes+1);
*lpDst=min((*lpSrc+(unsigned
char )(min( sqrt(float(tmp1*tmp1+tmp2*tmp2)),255))/2),255);
}
lpSrc++;lpDst++;
}
}
memcpy(pDoc->m_pDib->m_lpImage,lpNewDib,lLineBytes*lHeight);
Invalidate(true);
MessageBox(L"Prewitt边缘提取");
}
(二)Sobel算子
基本思想:
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
主要代码:、
void CMyImageView::OnEdgeSobel()
{
CMyImageDoc* pDoc = GetDocument();
unsigned char* lpSrc; unsigned char* lpDst;
unsigned char* lpNewDIBBits;
lpNewDIBBits=new unsigned char [lLineBytes*lHeight];
if (lpNewDIBBits == NULL)
{
return ;
}
BeginWaitCursor();
lpSrc=(unsigned char *)pDoc->m_pDib->m_lpImage;
memcpy( lpNewDIBBits,lpSrc, lLineBytes * lHeight);
int pixel[8];
for (int i = 0; i <=lHeight; i ++)
{
lpDst = (unsigned char *)pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i)-1;
lpSrc =lpNewDIBBits + lLineBytes * (lHeight - 1 - i)-1;
for (int j =0; j <lWidth; j ++)
{
lpSrc++;lpDst++;
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1){
pixel[0] = (int)*(lpSrc+lLineBytes-1);
pixel[1] = (int)*(lpSrc+lLineBytes);
pixel[2] = (int)*(lpSrc+lLineBytes+1);
pixel[3] = (int)*(lpSrc-1);
pixel[4] = (int)*(lpSrc+1);
pixel[5] = (int)*(lpSrc - lLineBytes-1);
pixel[6] = (int)*(lpSrc - lLineBytes);
pixel[7] = (int)*(lpSrc - lLineBytes+1);
int tmp1=pixel[0]+2*pixel[1]+pixel[2]-pixel[5]-2*pixel[6]-pixel[7];
int tmp2=pixel[2]+2*pixel[4]+pixel[7]-pixel[0]-2*pixel[3]-pixel[5];
int result =(int)(sqrt(double(tmp1*tmp1+tmp2*tmp2))/2+0.5);//>>255
if(result>255) result=255;
*lpDst = (unsigned char)result; *lpDst&=0xfc;
if(tmp2==0) tmp2=1;
double tha=atan((double(tmp1))/tmp2);
if(abs(tha)<Pi/8) *lpDst=*lpDst;
else
if(tha>Pi/8 && tha<Pi*3/8)
*lpDst=*lpDst+1;
else
if(tha<-Pi/8 && tha>-Pi*3/8) *lpDst=*lpDst+3;
else
*lpDst=*lpDst+2;
}else *lpDst=0;
}
}
CString str=pDoc->GetTitle();
pDoc->SetTitle(str+L"--Sobel");
InvalidateRect(CRect(0,0,lHeight,lWidth),true);
EndWaitCursor();
delete []lpNewDIBBits;
MessageBox(L"Sobel 边缘提取");
}
(三)拉普拉斯算子
基本思想:
Laplace算子是二阶微分算子,利用边缘点处二阶导函数出现零交叉原理检测边缘。不具方向性,对灰度突变敏感,定位精度高,同时对噪声敏感,且不能获得边缘方向等信息。
主要代码:
void CMyImageView::OnEdgeLaplace()
{
CMyImageDoc* pDoc = GetDocument();
unsigned char* lpSrc; unsigned char* lpDst;
unsigned char* lpNewDIBBits;
lpNewDIBBits=new unsigned char [lLineBytes*lHeight];
if (lpNewDIBBits == NULL)
{
return ;
}
BeginWaitCursor();
lpSrc=(unsigned char *)pDoc->m_pDib->m_lpImage;
memcpy( lpNewDIBBits,lpSrc, lLineBytes * lHeight);
int pixel[8];
for (int i = 0; i <=lHeight; i ++)
{
lpDst = (unsigned char *)pDoc->m_pDib->m_lpImage + lLineBytes* (lHeight-1-i)-1;
lpSrc =lpNewDIBBits + lLineBytes * (lHeight - 1 - i)-1;
for (int j =0; j <lWidth; j ++)
{
lpSrc++;lpDst++;
if(i>0 && i<lHeight-1 && j>0 && j<lWidth-1)
{
pixel[0] = (int)*(lpSrc+lLineBytes-1);
pixel[1] = (int)*(lpSrc+lLineBytes);
pixel[2] = (int)*(lpSrc+lLineBytes+1);
pixel[3] = (int)*(lpSrc-1);
pixel[4] = (int)*(lpSrc+1);
pixel[5] = (int)*(lpSrc - lLineBytes-1);
pixel[6] = (int)*(lpSrc - lLineBytes);
pixel[7] = (int)*(lpSrc - lLineBytes+1);
int Tmp=0;
for(int k=0;k<8;k++)
Tmp+=pixel[k]; Tmp=8**lpSrc-Tmp; Tmp+=128;
if(Tmp>255) Tmp=255;
if(Tmp<0) Tmp=0; *lpDst=Tmp;
}
else *lpDst=128;
}
}
CString str=pDoc->GetTitle();
pDoc->SetTitle(str+L"--Laplace");
InvalidateRect(CRect(0,0,lHeight,lWidth),true);
EndWaitCursor();
delete []lpNewDIBBits;
MessageBox(L"Laplace 边缘提取");
}
(四)运行结果及分析
由图1-4可知,前两种算子的检测结果基本相同,而拉普拉斯算子能提取对比度弱的边缘,边缘定位精确度高。
梯度算子得到的图像边缘更陡峭、清晰。Sobel算子具有平滑作用,能滤除一些噪声,去掉部分伪边缘,但同时也平滑了真正的边缘;定位精度不高。Laplace算子只能获得边缘位置信息,不能得到边缘的方向等信息。
图1 原图像 图2 Prewitt边缘提取
图3 sobel边缘提取 图4 拉普拉斯边缘提取
四、固定阀值法、最大熵分割方法、最大类间方差法
(一)固定阈值分割
基本思想:
利用图像的灰度特征来选择一个最佳阈值,使前景和背景的两个灰度级分布的有效信息为最大。
主要代码:
void CMyImageView::OnSegmentationMaxlikelihood()
{
CMyImageDoc* pDoc = GetDocument();
BeginWaitCursor();
int lHistogram[256]={0};
GetHistgram(lHistogram,true);
int meanBack,meanObj;
int ABack,AObj;
ABack=0;
for (int i = 0; i < 256;i++)
{ if(ABack<lHistogram[i])
{
ABack=(int)lHistogram[i];//max
meanBack=i;
}
}
int minH=0;
while(lHistogram[minH]<ABack/20) minH++;
int maxH=255;
while(lHistogram[maxH]<ABack/20) maxH--;
AObj=0;
int pos=meanBack-(maxH-minH)/3;
int min=lHistogram[meanBack];
if(pos<0) pos=0;
if(meanBack>10){
for (int i =pos ; i>=0 ;i--){
if(min>lHistogram[i])
min=lHistogram[i];
else
{
pos=i;break;
}
if(min<10) {
pos=i;break;
}
}
for (int i = pos; i >=0 ;i--){
if(AObj<lHistogram[i]) {
AObj=(int)lHistogram[i];
meanObj=i;
}
}
}
min=lHistogram[meanBack]; pos=meanBack+(maxH-minH)/3;
if(pos>255) pos=255;
if(meanBack<250){
for (int i = pos; i<256 ;i++){
if(min>lHistogram[i])
min=lHistogram[i];
else
{
pos=i;break;
}
if(min<10) {
pos=i;break;
}
}
for (int i = pos; i <256 ;i++){
if(AObj<lHistogram[i]) {
AObj=(int)lHistogram[i];//max
meanObj=i;
}
}
}
if(meanObj<meanBack){
int tmp=meanBack; meanBack=meanObj; meanObj=tmp;
int dtmp=ABack; ABack=AObj; AObj=dtmp;
}
TRACE("\nmeanBack=%d,(%d),meanObj=%d,(%d) posStep=%d\n\n",meanBack,ABack,meanObj,AObj,(maxH-minH)/3);
double segmBack;
pos=meanBack;
bool bfind=false;
if(meanBack>10){
while(pos>0 && !bfind){
pos--;
double dtmp=((double)lHistogram[pos])/ABack;
if(dtmp<exp(-0.5))
bfind=true;
}
}
else{
while(pos<255 && !bfind){
pos++;
double dtmp=((double)lHistogram[pos])/ABack;
if(dtmp<exp(-0.5)) bfind=true;
}
}
segmBack=abs(pos-meanBack);
if(segmBack<3.0)segmBack=3.0;//不连续直方图
else if(segmBack>meanObj-meanBack)
segmBack=(meanObj-meanBack)/3;
double segmObj; pos=meanObj;
bfind=false;
if(meanObj>255-10){
while(pos>0 && !bfind){
pos--;
double dtmp=((double)lHistogram[pos])/AObj;
if(dtmp<exp(-0.5)) bfind=true;
}
}
else
{while(pos<255 && !bfind){
pos++;
double dtmp=((double)lHistogram[pos])/AObj;
if(dtmp<exp(-0.5)) bfind=true;
}
}
segmObj=abs(pos-meanObj);
if(segmObj<3.0)segmObj=3.0;//不连续直方图
else if(segmObj>meanObj-meanBack)
segmObj=(meanObj-meanBack)/3;
TRACE("\nmeanBack=%d,segmBack=%3.1f,meanObj=%d,segmObj=%3
展开阅读全文