数字图像处理——灰度变换与空间滤波(使用MFC)

一、直方图均衡化

1. 如何用在MFC中画直方图:

这是一个对话框类的成员函数,IDC_PIC2是对话框中picture control的控件ID,目标直方图计划要画在picture control中。

source_image是CImage类型的一个对象,因为是对灰度级为256的图片进行直方图均衡化,所以默认数据数组大小256.

plot_hist()函数得到该图像各点的频数,并把频数的数组存到data数组中。

	source_image.plot_hist();
	LONG* data = source_image.data;

 
  
void IDD_GRAPH::OnDraw(){
	CRect rectClient;
	CWnd *pWnd = GetDlgItem(IDC_PIC2);
	pWnd->GetClientRect(&rectClient);
	CDC *pDC = pWnd->GetDC();
	HDC hDC= pDC->GetSafeHdc();

	SIZE sz;
	sz.cx = rectClient.Width()-30;
	sz.cy = rectClient.Height()-70;
	int nLength = 256;
	source_image.plot_hist();
	LONG* data = source_image.data;

	CPoint origin(10,sz.cy+10);//原点坐标(10, sz高度+20)
	CString str;
	pDC->Rectangle(0,0,sz.cx+30,sz.cy+70);//轮廓大小,上空10,下空20

	CPen cyPen(PS_SOLID,2,RGB(0,0,0));  
 	CPen *oldPen=pDC->SelectObject(&cyPen);
	pDC->MoveTo(origin); // 移到原点
	pDC->LineTo(origin.x+sz.cx+5,origin.y);//画X轴
	pDC->LineTo(origin.x+sz.cx,origin.y+5);
	pDC->MoveTo(origin.x+sz.cx+5,origin.y);//画X轴箭头
	pDC->LineTo(origin.x+sz.cx,origin.y-5);
	pDC->MoveTo(origin); // 移到原点
	pDC->LineTo(origin.x,origin.y-sz.cy);
	pDC->MoveTo(origin.x-5,origin.y-sz.cy+5);//画Y轴箭头
	pDC->LineTo(origin.x,origin.y-sz.cy);
	pDC->LineTo(origin.x+5,origin.y-sz.cy+5);
	pDC->TextOut(origin.x+10,origin.y+20,"Histogram of source image");//图名
	for(int i=0;iTextOut(origin.x+i,origin.y+3,str);
	}

 		CPen redPen(PS_SOLID,1,RGB(255,0,0)); //红
 		oldPen=pDC->SelectObject(&redPen);
        long total=0;
		long lMaxCount=0;
		int idMax;
		lMaxCount=Max(data,nLength,idMax);
 		if(lMaxCount>0)
		{
		    for(int i=0;iMoveTo(origin.x+i,origin.y);
				pDC->LineTo(origin.x+i,origin.y+1-(int)(data[i]*sz.cy/lMaxCount));
				total+=data[i];
			}			
		}
		CString strmax;
		strmax.Format("最值为%d,数目为%d",idMax,lMaxCount);
		pDC->TextOut(origin.x+10,origin.y+40,strmax);
		str.Format("总像元数%d",total);
		pDC->TextOut(0,0,str);
		pDC->SelectObject(oldPen);

	ReleaseDC( pDC );
}



2. 统计灰度图像各像素值的频数

·对于函数void plot_hist(),它的主要作用是计算每个像素值的频数,并把值保存到LONG data[256]的数组中。

    1 初始化数组LONGdata[256],把里面所有值都置零。

    2 用x,y遍历图片的每一个像素,统计0-255像素值的频数。

void MYCImage::plot_hist(){//画直方图函数
	int maxY=this->GetHeight();  
    int maxX=this->GetWidth();  
    int r,g,b,gray;
	for(int i = 0;i<256;i++)//initialize
		data[i] = 0;
    for(int x=0;xGetPixel(x,y);
			r=GetRValue(pixel);
			g=GetGValue(pixel);
			b=GetBValue(pixel);
			gray = (int)(0.28965 * r + 0.60581 * g + 0.10454 * b + 0.5);//四舍五入
			data[gray]++;
		}         
	}
}

3. 进行直方图均衡化

·对于函数void equalize_hist(CStringPathName),它的主要作用是找到直方图均衡化后的对应像素值,并对图片的像素点进行赋值。

    1 传入参数是当前页面打开的图片的路径PathName,目的是为了避免CImage的一些接口句柄问题。

    2 调用plot_hist()函数,用于计算每个像素值的频数,得到记录每个像素值频数的数组data。

    3 一个双层的嵌套循环。对于0-255的每个像素值,运用公式 sk = (L - 1) * (p0 + p1+ … + pk)进行计算,得到原像素值 r 经过直方图均衡化后对应的像素值 s ,贮存于数组result中,result[r] = s。如直方图均衡化前值为4的像素值,经过均衡化后,像素值变成了0。就写作result[4]= 0.

    4 用x,y嵌套循环遍历图片的每一个像素,为其赋上直方图均衡化变化之后的像素值。

void MYCImage::equalize_hist(CString PathName){
	MYCImage src;
	src.Load(PathName);
	if(IsNull())  
         return;
	this->Destroy();
	this->Create(src.GetWidth(),src.GetHeight(),src.GetBPP());
	int result[256] = {0};
	src.plot_hist();
	int total_pixel = src.GetWidth() * src.GetHeight();
	for(int i = 0;i<256;i++){//直方图均衡化
		for(int j = 0;j <= i;j++){
			result[i] += src.data[j];
		}
		result[i] = (int)((double)result[i] * 255 / (double)total_pixel + 0.5 );//要四舍五入
	}
	for(int x = 0;x < src.GetWidth();x++){
		for(int y = 0;y < src.GetHeight();y++){
				COLORREF pixel;
				int r,g,b,gray;
				pixel=src.GetPixel(x,y);
				r=GetRValue(pixel);
				g=GetGValue(pixel);
				b=GetBValue(pixel);
				gray = (int)(0.28965 * r + 0.60581 * g + 0.10454 * b + 0.5);//四舍五入
				this->SetPixelRGB(x,y,result[gray],result[gray],result[gray]);
		}
	}
}



二、空间滤波SpatialFiltering

1. 均值滤波

均值滤波降低了图像灰度的尖锐变化,常见的均值滤波应用就是降低噪声。

对于那些为了对感兴趣的物体得到一个粗略的描述而模糊的图像,经过均值滤波之后,可以使较小的物体的灰度与背景混合在一起,而较大的物体变得明显而易于检测。


具体均值滤波函数在void MYCImage::averaging_filter(CStringPathName,int width,intheight)中。传入的参数是原图片的路径,目的是为了避免CImage句柄的一些问题。Width和height是滤波盒子的大小。

       首先设置x,y的一个二层嵌套循环,是针对目标图像的每一个像素点经行赋值的。

在这个循环里,还有一个I,j的二层循环,是用来遍历滤波盒子里的所有像素值的。

其中,关于i,j的范围:

for(int i = -width/2;i<=width/2;i++)

for(int j = -height/2;j<=height/2;j++)

       在这里,我设置了一个判断条件解决边界问题:

       if(x + i < 0 || x + i > src.GetWidth()-1 || y+ j < 0 || y + j > src.GetHeight()-1)

其中(x + i ,y + j)表示在滤波盒子(i ,j)位置上的坐标点对应于原图像的坐标位置。所以显然,如果它们小于零或者超出了宽度或者高度,这是不合法的。所以对于这样的点,我对其进行舍弃,即不对其进行累加。

为了记录到实际上参加累加的像素点的个数,便于最后取平均值,我声明了一个变量count,每次对目标值result进行累加时,count都会自增一。最后只需要result = result / count 即可,然后再把result值赋给新的图像的对应坐标点。this->SetPixelRGB(x,y,result,result,result);


void MYCImage::averaging_filter(CString PathName,int width,int height){
	MYCImage src;
	src.Load(PathName);
	if(IsNull())  
         return ;
	this->Destroy();
	this->Create(src.GetWidth(),src.GetHeight(),src.GetBPP());
	for(int x = 0;x src.GetWidth()-1 || y + j < 0 || y + j > src.GetHeight()-1){
						}
						else{
							COLORREF pixel;
							pixel=src.GetPixel(x + i,y + j);
							int gray;
							gray = GetRValue(pixel);
							result += gray;
							count ++;
						}
					}
				}
				result = result / count;
				this->SetPixelRGB(x,y,result,result,result);
				}
			}
		}




2.Laplacian filter拉普拉斯滤波:


拉普拉斯算子滤波属于二阶微分对图像进行锐化的一种方法。强调图像中灰度的突变,所以比较适用于突出图像细节部分的处理。

拉普拉斯滤波的函数处理方法与均值滤波有异曲同工之妙,它们的函数结构相似。而我对他们的边界点处理也是一样的,所以在此我就不详细说明。

需要说明的一点是,对于我选取的拉普拉斯算子的3*3矩阵,它的中心点值是-8,其余值是1,所以在循环中特地判断找出中心点对其进行* (-8)的特殊处理,其他点和均值滤波方法一样直接累加即可。

最后还涉及到像素值的标定,如果像素值大于255,我令其为255;如果像素值小于0,我令其为0;

void MYCImage::Laplacian_filter(CString PathName,int width,int height){
	MYCImage src;
	src.Load(PathName);
	if(IsNull())  
         return ;
	this->Destroy();
	this->Create(src.GetWidth(),src.GetHeight(),src.GetBPP());
	for(int x = 0;x src.GetWidth()-1 || y + j < 0 || y + j > src.GetHeight()-1){
						}
						else if(i == 0 && j == 0){		
							COLORREF pixel;
							pixel=src.GetPixel(x + i,y + j);
							int gray;
							gray = GetRValue(pixel);
							result += gray * (-8);
						}
						else{
							COLORREF pixel;
							pixel=src.GetPixel(x + i,y + j);
							int gray;
							gray = GetRValue(pixel);
							result += gray;
						}
					}
				}
				if(result < 0)
					result = 0;
				else if(result > 255)
					result = 255;
				this->SetPixelRGB(x,y,result,result,result);
			}
		}
}



3. Soble滤波

利用Soble算子滤波属于一阶微分图像锐化的一种方法(梯度处理)。梯度处理常用于工业检测,可以辅助人工检测产品的缺陷,也可以作为自动检测的预处理。

Soble滤波的方法的函数处理与拉普拉斯滤波也很像。

需要说明的一点是,对于我选取的Soble算子的3*3矩阵,当取的值是滤波模板的最左边一列的时候,它们都是负数;当取的值是滤波模板的中间一列的时候,它们都是0;当取的值是右边一列的时候,它们都是整数。而且第一行与第三行的数绝对值是1,而第二行的绝对值是2。对于这点,我在函数中作出了如下判断:

void MYCImage::Soble_filter(CString PathName,int width,int height){
	MYCImage src;
	src.Load(PathName);
	if(IsNull())  
         return ;
	this->Destroy();
	this->Create(src.GetWidth(),src.GetHeight(),src.GetBPP());
	for(int x = 0;x src.GetWidth()-1 || y + j < 0 || y + j > src.GetHeight()-1){
						}
						else if(j == -height/2 || j == height/2){
							COLORREF pixel;
							pixel=src.GetPixel(x + i,y + j);
							int gray;
							gray = GetRValue(pixel);
							result += gray * 1 * i;
						}
						else if(j==0){
							COLORREF pixel;
							pixel=src.GetPixel(x + i,y + j);
							int gray;
							gray = GetRValue(pixel);
							result += gray * 2 * i;
						}
					}
				}
				if(result < 0)
					result = 0;
				else if(result > 255)
					result = 255;
				this->SetPixelRGB(x,y,result,result,result);
			}
		}
}


你可能感兴趣的:(数字图像处理,图像处理,mfc)