【數字影象處理】七.MFC影象增強之影象普通平滑、高斯平滑、Laplacian、Sobel、Prewitt銳化詳解

Eastmount發表於2015-06-08

       本文主要講述基於VC++6.0 MFC影象處理的應用知識,主要結合自己大三所學課程《數字影象處理》及課件進行講解,主要通過MFC單文件檢視實現顯示BMP影象增強處理,包括影象普通平滑、高斯平滑、不同運算元的影象銳化知識。希望該篇文章對你有所幫助,尤其是初學者和學習影象處理的學生。
       【數字影象處理】一.MFC詳解顯示BMP格式圖片
       【數字影象處理】二.MFC單文件分割視窗顯示圖片
       【數字影象處理】三.MFC實現影象灰度、取樣和量化功能詳解
       【數字影象處理】四.MFC對話方塊繪製灰度直方圖
       【數字影象處理】五.MFC影象點運算之灰度線性變化、灰度非線性變化、閾值化和均衡化處理詳解
       【數字影象處理】六.MFC空間幾何變換之影象平移、映象、旋轉、縮放詳解
        
免費資源下載地址:
        http://download.csdn.net/detail/eastmount/8785591

 

一. 影象增強簡介

        影象增強是對影象進行處理,使其比原始影象更適合於特定的應用,它需要與實際應用相結合。對於影象的某些特徵如邊緣、輪廓、對比度等,影象增強是進行強調或銳化,以便於顯示、觀察或進一步分析與處理。影象增強的方法是因應用不同而不同的,研究內容包括:(參考課件和左飛的《數字影象處理》)

 

二. 影象平滑

        影象平滑是一種區域增強的演算法,平滑演算法有鄰域平均法、中指濾波、邊界保持類濾波等。在影象產生、傳輸和複製過程中,常常會因為多方面原因而被噪聲干擾或出現資料丟失,降低了影象的質量(某一畫素,如果它與周圍畫素點相比有明顯的不同,則該點被噪聲所感染)。這就需要對影象進行一定的增強處理以減小這些缺陷帶來的影響。

       1.簡單平滑-鄰域平均法
        影象簡單平滑是指通過鄰域簡單平均對影象進行平滑處理的方法,用這種方法在一定程度上消除原始影象中的噪聲、降低原始影象對比度的作用。它利用卷積運算對影象鄰域的畫素灰度進行平均,從而達到減小影象中噪聲影響、降低影象對比度的目的。
        但鄰域平均值主要缺點是在降低噪聲的同時使影象變得模糊,特別在邊緣和細節處,而且鄰域越大,在去噪能力增強的同時模糊程度越嚴重。

        2.高斯平滑
       
為了克服簡單區域性平均法的弊端(影象模糊),目前已提出許多保持邊緣、細節的區域性平滑演算法。它們的出發點都集中在如何選擇鄰域的大小、形狀和方向、引數加平均及鄰域各店的權重係數等。
        影象高斯平滑也是鄰域平均的思想對影象進行平滑的一種方法,在影象高斯平滑中,對影象進行平均時,不同位置的畫素被賦予了不同的權重。
        在影象簡單平滑中,演算法利用卷積模板逐一處理影象中每個畫素,這一過程可以形象地比作對原始影象的畫素一一進行過濾整理,在影象處理中把鄰域畫素逐一處理的演算法過程稱為濾波器。平滑線性濾波器的工作原理是利用模板對鄰域內畫素灰度進行加權平均,也稱為均值濾波器。
        高斯平滑與簡單平滑不同,它在對鄰域內畫素進行平均時,給予不同位置的畫素不同的權值,下圖的所示的3*3和5*5領域的高斯模板。

        模板越靠近鄰域中心位置,其權值越高。在影象細節進行模糊時,可以更多的保留影象總體的灰度分佈特徵。下圖是常用的四個模板和matlab程式碼實現:

        程式碼如下:

I1 = imread('blood1.tif');
I=imnoise(I1,‘salt & pepper’,0.04);                %對影象加椒鹽噪聲
imshow(I);
h1= [0.1 0.1 0.1; 0.1 0.2 0.1; 0.1 0.1 0.1];        %定義4種模板
h2=1/16.*[1 2 1;2 4 2;1 2 1];
h3=1/8.*[1 1 1;1 0 1;1 1 1];
h4=1/2.*[0 1/4 0;1/4 1 1/4;0 1/4 0];
I2=filter2(h1,I);                                   %用4種模板進行濾波處理
I3=filter2(h2,I);
I4=filter2(h3,I);
I5=filter2(h4,I);
figure,imshow(I2,[])                                %顯示處理結果
figure,imshow(I3,[])
figure,imshow(I4,[])
figure,imshow(I5,[])

        執行效果如下圖所示:

        3.中值濾波
        在使用鄰域平均法去噪的同時也使得邊界變得模糊。而中值濾波是非線性的影象處理方法,在去噪的同時可以兼顧到邊界資訊的保留。
        選一個含有奇數點的視窗W,將這個視窗在影象上掃描,把視窗中所含的畫素點按灰度級的升或降序排列,取位於中間的灰度值來代替該點的灰度值。

        例如選擇濾波的視窗如下圖,是一個一維的視窗,待處理畫素的灰度取這個模板中灰度的中值,濾波過程如下:

        常用的視窗還有方形、十字形、圓形和環形。不同形狀的視窗產生不同的濾波效果,方形和圓形視窗適合外輪廓線較長的物體影象,而十字形視窗對有尖頂角狀的影象效果好。

        中值濾波對於消除孤立點和線段的干擾十分有用,尤其是對於二進噪聲,但對消除高斯噪聲的影響效果不佳。對於一些細節較多的複雜影象,可以多次使用不同的中值濾波。matlab實現參考:http://blog.csdn.net/timidsmile/article/details/6904381

        4.邊界保持類濾波
        K近鄰均值濾波器(KNNF)是指在m*m的視窗中,屬於同一集合類的畫素,它們的灰度值將高度相關。被處理的畫素(對應於視窗中心的畫素)可以用視窗內與中心畫素灰度最接近的k個近鄰畫素的平均灰度來替代。步驟如下:
        (1).作一個m*m的作用模板
        (2).在其中選擇K個與待處理畫素的灰度差為最小的畫素
        (3).用這K個畫素的灰度均值替換掉原來的值

        在K近旁均值濾波器(KNNMF)中,不選K個鄰近畫素的平均灰度來替代,而選K個鄰近畫素的中值灰度來替代,上圖中2,3,3中選擇3即可。
        下面介紹具體MFC VC++6.0程式碼實現過程。

 

三. 影象平滑程式碼實現

        第一步:在資源檢視的Menu中新增子選單“影象增強”,然後新增“影象平滑”四個選項如下圖所示:

        第二步:開啟類嚮導,在ImageProcessingView類中新增相應的四個實現函式:

        第三步:就是具體的平滑實現函式。
        1.普通平滑 模板一
        該演算法採用的模板如下:

        程式碼如下:

/**************************************************
  第九章--影象增強    
  影象平滑 普通平滑 模板
 
float H1[3][3]={{1.0/10,1.0/10,1.0/10}, //模板一:係數1/10
			   {1.0/10,2.0/10,1.0/10},
			   {1.0/10,1.0/10,1.0/10}};
		
float H2[3][3]={{1.0/16,2.0/16,1.0/16}, //模板二:係數1/16
			   {2.0/16,4.0/16,2.0/16},
			   {1.0/16,2.0/16,1.0/16}};

float H3[3][3]={{1.0/8,1.0/8,1.0/8},    //模板三:係數1/8,此種情況為把點轉為空心矩形
			   {1.0/8,0.0/8,1.0/8},
			   {1.0/8,1.0/8,1.0/8}};

float H4[3][3]={{0.0,1.0/8,0.0},        //模板四:係數乘資料後的矩陣
			   {1.0/8,1.0/2,1.0/8},
			   {0.0,1.0/8,0.0}};

/**************************************************/


void CImageProcessingView::OnTxzqPtph1() 
{
	if(numPicture==0) {
		AfxMessageBox("載入圖片後才能影象增強(平滑)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(平滑)!選取的模板為:普通平滑 模板一",MB_OK,0);

	/******************************************************************/
	/* 圖想平滑的演算法:                                               
	/*   1.定義常用的四個模板,它們的維數均為3,矩陣的個數均為9個資料   
	/*   2.它的思想是把一個點分散到這周圍的9個點上,這樣使影象更模糊   
	/*   3.通過卷積計算圍繞該點的矩陣畫素和,計算其平均值(除9)賦值給點 
	/*   4.模組不同,處理後的影象也各不相同                           
	/******************************************************************/

	/*第一步:先定義資料模板*/
	int HWS=3;                                //模板維數:此四個模板均為3維的
	float H1[3][3]={{1.0/10,1.0/10,1.0/10},   //模板一:係數1/10
					{1.0/10,2.0/10,1.0/10},
					{1.0/10,1.0/10,1.0/10}};
	
	//開啟臨時的圖片
	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);

	//new和delete有效的進行動態記憶體的分配和釋放
    unsigned char *ImageSize;        
    ImageSize = new unsigned char[m_nImage];    
	float red,green,blue;
	int X,Y;               //一維座標轉換為二維座標
	int TR,TG,TB;          //記錄紅綠藍座標位置

	//影象增強:平滑 它要獲取源影象周圍9個點的矩陣乘以模板9個點的矩陣,故一維影象轉二維
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		//原圖:一維矩陣轉換為二維矩陣
		X=(i/3)%m_nWidth;    //影象在X列
		Y=(i/3)/m_nWidth;    //影象在Y行

		//賦值為黑色,相當於清零
		red=green=blue=0;

		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )      //防止越界
				{			
					//模板一 進行模板平均,把該點畫素分散到四周
					TR=j*m_nWidth*3+k*3;	
					red+=H1[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H1[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H1[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值
		ImageSize[i]=(unsigned char)(red);
		ImageSize[i+1]=(unsigned char)(green);
		ImageSize[i+2]=(unsigned char)(blue);
	}
		
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;
	Invalidate();
}

        執行效果如圖所示,影象平滑模糊了,但效果不是很好。

        其中實現的具體原理如下:
        首先將影象畫素矩陣轉換為(X,Y)的二維矩陣進行操作,同時獲取(X,Y)座標為中心的3*3矩陣,再通過它與3*3模板進行畫素平均操作,就是兩個3*3矩陣互乘。需要注意的是矩陣一個格子是RGB三位元組(24位BMP),同時獲取該中心點位置時,通過兩層迴圈for(k=n-1;k<=n+1;k++)實現獲取矩陣中九個點的畫素。最後對該點(X,Y)的RGB進行賦值操作即可。


       2.普通平滑 模板二
        該演算法採用的模板如下:

        程式碼如下:

void CImageProcessingView::OnTxzqPtph2() 
{
	if(numPicture==0) {
		AfxMessageBox("載入圖片後才能影象增強(平滑)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(平滑)!選取的模板為:普通平滑 模板二",MB_OK,0);

	/*第一步:先定義資料模板*/
	int HWS=3;
	float H2[3][3]={{1.0/8,1.0/8,1.0/8},    //模板三:係數1/8 此種情況為把點轉為空心矩形
					{1.0/8,0.0/8,1.0/8},
					{1.0/8,1.0/8,1.0/8}};
	
	//開啟臨時的圖片
	FILE *fpo = fopen(BmpName,"rb");
	FILE *fpw = fopen(BmpNameLin,"wb+");
	fread(&bfh,sizeof(BITMAPFILEHEADER),1,fpo);  
    fread(&bih,sizeof(BITMAPINFOHEADER),1,fpo);
	
	//重點:影象的每行畫素都必須是4的倍數:1*1的影象為 r g b 00H   
	int num;            //記錄每行多餘的影象素數個數  
	int sfSize;         //補齊後的影象大小  
    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;  
    }  
  
    /*更改檔案頭資訊 定義臨時檔案頭結構變數*/  
    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);  

	//new和delete有效的進行動態記憶體的分配和釋放
    unsigned char *ImageSize;        
    ImageSize = new unsigned char[sfSize];    
	float red,green,blue;
	int X,Y;               //一維座標轉換為二維座標
	int TR,TG,TB;          //記錄紅綠藍座標位置  
    int countWidth=0;      //記錄每行的畫素個數,滿行時變回0  
	int place=0;           //建立臨時座標 記錄起始座標(0,0)平移過來的位置 

	//影象增強 平滑
	for(int i=0; i<m_nImage; )
	{
		//原圖一維矩陣轉換為二維矩陣
		X=(i/3)%m_nWidth;    //影象在X列
		Y=(i/3)/m_nWidth;    //影象在Y行

		//賦值為黑色,相當於清零
		red=green=blue=0;

		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )      //防止越界
				{			
					//模板二 進行模板平均,把該點畫素分散到四周
					TR=j*m_nWidth*3+k*3;	
					red+=H2[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H2[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H2[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值
		//通過變數place賦值變換後的影象 i始終指向原圖3的倍數 為了補0而新增place變數
		ImageSize[place]=(unsigned char)(red);
		i++; place++;
		ImageSize[place]=(unsigned char)(green);
		i++; place++;
		ImageSize[place]=(unsigned char)(blue);
		i++; place++;
		countWidth=countWidth+3;
		
		if(countWidth==m_nWidth*3)    
        {    
			if(num==0)  
            {  
                countWidth=0;    
                place=Y*m_nWidth*3;
            }  
            else //num為補0  
            {  
                for(int n=0;n<num;n++)  
                {    
                    ImageSize[place]=0;  
					place++;  
                }  
                countWidth=0;   
                place=Y*(m_nWidth*3+num); //重點 新增Num  
            }  
		} 
	}
		
	fwrite(ImageSize,sfSize,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture=2;
	level=400;
	Invalidate();
}

        你可能注意到了,在影象處理過程中,如果每行的位元組數不是4的倍數,可能會出現斜線之類的處理BUG,所以需要手動補0籌齊4的倍數,程式碼中補0後執行效果如下圖所示,我也一直沒找到原因,可能是思想和深度還沒有達到,以後有機會在解決吧!同時後面的演算法都不準備再進行補0處理,主要講述演算法的思想!

         3.高斯平滑
         採用的模板如下:

        程式碼如下圖所示:

//高斯平滑
void CImageProcessingView::OnTxzqGsph() 
{
	if(numPicture==0) {
		AfxMessageBox("載入圖片後才能影象增強(平滑)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(平滑)!選取的模板為:高斯平滑",MB_OK,0);

	/*第一步:先定義資料模板*/
	int HWS=3;                                //模板維數為3維
	float H[3][3]={{1.0/16,2.0/16,1.0/16},    //高斯模板 係數1/16
				   {2.0/16,4.0/16,2.0/16},
				   {1.0/16,2.0/16,1.0/16}};
	
	//開啟臨時的圖片
	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);

	//new和delete有效的進行動態記憶體的分配和釋放
    unsigned char *ImageSize;        
    ImageSize = new unsigned char[m_nImage];    
	float red,green,blue;
	int X,Y;               //一維座標轉換為二維座標
	int TR,TG,TB;          //記錄紅綠藍座標位置

	//影象增強:平滑 
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		//原圖:一維矩陣轉換為二維矩陣
		X=(i/3)%m_nWidth;    //影象在X列
		Y=(i/3)/m_nWidth;    //影象在Y行

		//賦值為黑色,相當於清零
		red=green=blue=0;

		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )      //防止越界
				{			
					//模板二 進行模板平均,把該點畫素分散到四周
					TR=j*m_nWidth*3+k*3;	
					red+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值
		ImageSize[i]=(unsigned char)(red);
		ImageSize[i+1]=(unsigned char)(green);
		ImageSize[i+2]=(unsigned char)(blue);
	}
		
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;
	Invalidate();
}

        執行效果如下圖所示:

        4.中值濾波
        中值濾波我的理解是:它不但可以去除孤點噪聲,而且可以保持影象的邊緣特性,不會產生顯著的模糊;它的方法是把區域性區域的畫素按灰度等級進行排序,再取該鄰域中灰度的中值作為當前畫素的灰度值。其步驟如下:
        (1).將濾波模板(含若干個點的滑動視窗)在影象中漫遊,並將模板中心與影象中的某個畫素位置重合;
        (2).讀取模板中各對應畫素的灰度值;
        (3).將這些灰度值從小到大排序;
        (4).取這一列資料的中間資料,將其賦值給對應模板中心位置的畫素。

        我採用的是3*3的模本,取矩陣中間位置畫素替代原畫素。程式碼如下:

//中值濾波
void CImageProcessingView::OnTxzqZzlb() 
{
	if(numPicture==0) {
		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);

	//new和delete有效的進行動態記憶體的分配和釋放
    unsigned char *ImageSize;        
    ImageSize = new unsigned char[m_nImage];    
	int X,Y;               //一維座標轉換為二維座標
	int TR,TG,TB;          //記錄紅綠藍座標位置

	//選取它為中心的周圍9個點畫素(注意一個點為RGB)
	int H[9]={0,0,0,0,0,0,0,0,0};    
	int HWS=3;             //維數為三維

	//影象增強:平滑 它要獲取源影象周圍9個點的矩陣乘以模板9個點的矩陣,故一維影象轉二維
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		//原圖:一維矩陣轉換為二維矩陣
		X=(i/3)%m_nWidth;    //影象在X列
		Y=(i/3)/m_nWidth;    //影象在Y行
		
		//第一行 第一列 最後一行 最後一列 直接複製
		if(X==0 || Y==0 || X==m_nWidth*3 || Y==m_nHeight) 
		{
			if(i+2>m_nImage) break;
			ImageSize[i] = m_pImage[i];
			ImageSize[i+1] = m_pImage[i+1];
			ImageSize[i+2] = m_pImage[i+2];
			continue;
		}

		//對影象進行畫素求和並取平均值 HWS維數
		int num=0;
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )      //防止越界
				{			
					//獲取當前位置Red畫素 k一次增加RGB三個畫素 R=G=B
					TR = j*m_nWidth*3+k*3;	
					H[num] = m_pImage[TR];
					num++;
				}
			}
		}
		//排序獲取中間值
		int temp=0;
		for(int x=0;x<9;x++)
		{
			for(int y=x;y<9;y++)
			{
				if(H[x]>=H[y])
				{
					temp=H[x];
					H[x]=H[y];
					H[y]=temp;
				}
			}
		}
		//CString str;
		//str.Format("矩陣:%d %d %d, %d %d %d, %d %d %d",H[0],H[1],H[2],H[3],H[4],H[5],H[6],H[7],H[8]);
		//AfxMessageBox(str);

		//對新影象賦值 灰度影象RGB相同
		ImageSize[i]=H[4];
		ImageSize[i+1]=H[4];
		ImageSize[i+2]=H[4];
	}
		
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;
	Invalidate();
}

        執行效果如下圖所示:

        PS:這部分總算講述完成,演算法都是根據自己的理解用底層程式碼實現的,而不是向其它的通過呼叫GDI+庫實現。可能存在因為理解不夠或其它的錯誤,歡迎提出修改~
        推薦資料:
        影象平滑處理——OpenCV         數字影象處理學習筆記——影象平滑銳化 
        中值濾波    

 

四. 影象銳化

       有時還需要加強影象中景物的邊緣和輪廓,邊緣和輪廓通常位於影象中灰度突出的地方,因而可以直觀的想到用灰度的差分對邊緣和輪廓進行提取,通常可以通過梯度運算元進行提取。影象銳化的目的是提高影象的對比度,從而使影象更清晰,通過提高鄰域內畫素的灰度差來提高影象的對比度。
       下面介紹影象銳化的幾種運算元及效果。
        1.拉普拉斯運算元(Laplacian)
        拉普拉斯運算元是影象鄰域內畫素灰度差分計算的基礎,通過二階微分推匯出的一種影象鄰域增強演算法。它的基本思想是當鄰域的中心畫素灰度低於它所在鄰域內的其他畫素的平均灰度時,此中心畫素的灰度應該被進一步降低;當高於時進一步提高中心畫素的灰度,從而實現影象銳化處理。
        在演算法實現過程中,通過對鄰域中心畫素的四方向或八方向求梯度,並將梯度和相加來判斷中心畫素灰度與鄰域內其他畫素灰度的關係,並用梯度運算的結果對畫素灰度進行調整。
        一個連續的二元函式f(x,y),其拉普拉斯運算定義為:

        對於數字影象,拉普拉斯運算元可以簡化為:

        也可以表示為卷積的形式:

        其中K=1,I=1時H(r,s)取下式,四方向模板:

        通過模板可以發現,當鄰域內畫素灰度相同時,模板的卷積運算結果為0;當中心畫素灰度高於鄰域內其他畫素的平均灰度時,模板的卷積運算結果為正數;當中心畫素的灰度低於鄰域內其他畫素的平均灰度時,模板的卷積為負數。對卷積運算的結果用適當的衰弱因子處理並加在原中心畫素上,就可以實現影象的銳化處理。
        其中實現過程步驟如下:
        新增子選單和類嚮導新增實現函式

        程式碼如下:

/*****************************************/
/* 影象銳化:我在menu中建立5個子的menu    */
/* 防止出現平滑錯誤,一次只呼叫一個下拉單 */
/* ID_RH_Laplacian Laplacian拉普拉斯運算元 */
/* ID_RH_Sobel Sobel運算元                 */
/* ID_RH_Prewitt Prewitt運算元             */
/* ID_RH_Isotropic Isotropic運算元         */
/* ID_RH_GTMB 高通模板H2                 */
/*****************************************/

void CImageProcessingView::OnRHLaplacian() 
{
	if(numPicture==0) 
	{
		AfxMessageBox("載入圖片後才能影象增強(銳化)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(銳化):採用拉普拉斯(Laplacian)運算元!");

	//模板維數:此四個模板均為3維的
	int HWS=3;  
	int H[3][3]={{0,-1,0},    //模板為拉普拉斯運算元(中心為4的Laplacian)
				{-1,4,-1},
				{0,-1,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);

	//new和delete有效的進行動態記憶體的分配和釋放
	unsigned char *ImageSize;      
	ImageSize=new unsigned char[m_nImage];  
	int red,green,blue;
	int X,Y;       //一維座標轉換為二維座標
	int TR,TG,TB;  //記錄紅綠藍座標位置
	
	//影象增強 銳化
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		X=(i/3)%m_nWidth;    //X列
		Y=(i/3)/m_nWidth;    //Y行
		red=green=blue=0;
		
		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )
				{			
					
					TR=j*m_nWidth*3+k*3;	
					red+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值
		if(red>=0 && red<256) ImageSize[i]=red;
		else if(red<0) ImageSize[i]=0;      //ImageSize[i]=-red;
		else ImageSize[i]=0;
		
		if(green>=0 && green<256) ImageSize[i+1]=green;
		else if(green<0) ImageSize[i+1]=0;  //ImageSize[i+1]=-green;
		else ImageSize[i+1]=0;
		
		if(blue>=0 && blue<256) ImageSize[i+2]=blue;
		else if(blue<0) ImageSize[i+2]=0;   //ImageSize[i+2]=-blue;
		else ImageSize[i+2]=0;
	}
	
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;	
	Invalidate();
}

        執行效果如下圖所示:


        2.高通濾波
        常用的高通模板如下所示,其中H2有的書又稱為拉普拉斯八方向的銳化模板。

        選取H2模板,程式碼如下所示:

//高通模板
void CImageProcessingView::OnRhGtmb() 
{
	if(numPicture==0)
	{
		AfxMessageBox("載入圖片後才能影象增強(銳化)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(銳化):採用高通模板!");

	int HWS=3;                                
	int H[3][3]={{-1,-1,-1},    
				{-1,8,-1},
				{-1,-1,-1}};

	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);

	unsigned char *ImageSize;      
	ImageSize=new unsigned char[m_nImage];  
	int red,green,blue;
	int X,Y;       
	int TR,TG,TB; 
	
	//影象增強 銳化
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		X=(i/3)%m_nWidth;    //X列
		Y=(i/3)/m_nWidth;    //Y行
		red=green=blue=0;
		
		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )
				{			
					
					TR=j*m_nWidth*3+k*3;	
					red+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TB]);
				}
			}
		}
		
		//對新影象賦值
		if(red>=0 && red<256) ImageSize[i]=red;
		else if(red<0) ImageSize[i]=0;      //ImageSize[i]=-red;
		else ImageSize[i]=0;
		
		if(green>=0 && green<256) ImageSize[i+1]=green;
		else if(green<0) ImageSize[i+1]=0;  //ImageSize[i+1]=-green;
		else ImageSize[i+1]=0;
		
		if(blue>=0 && blue<256) ImageSize[i+2]=blue;
		else if(blue<0) ImageSize[i+2]=0;   //ImageSize[i+2]=-blue;
		else ImageSize[i+2]=0;
	}
	
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;	
	Invalidate();	
}

        執行效果如下圖所示,該效果相對較好:

 


       3.Sobel運算元

        程式碼如下所示,需要注意X和Y兩個方向的模板處理:

//Sobel運算元採用PPT上的d(x)d(y)模板
void CImageProcessingView::OnRHSobel() 
{
	if(numPicture==0)
	{
		AfxMessageBox("載入圖片後才能影象增強(銳化)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(銳化):採用Sobel運算元!");

	int HWS=3;                                
	//模板為Sobel運算元
	int HX[3][3]={{1,0,-1},{2,0,-2},{1,0,-1}};
	int HY[3][3]={{-1,-2,-1},{0,0,0},{1,2,1}};

	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);

	unsigned char *ImageSize;      
	ImageSize=new unsigned char[m_nImage];  
	int redX,greenX,blueX;
	int redY,greenY,blueY;
	int X,Y;       
	int TR,TG,TB;  
	
	//影象增強 銳化
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		X=(i/3)%m_nWidth;    //X列
		Y=(i/3)/m_nWidth;    //Y行
		redX=greenX=blueX=0;
		redY=greenY=blueY=0;
		
		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )
				{			
					
					TR=j*m_nWidth*3+k*3;	
					redX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TR]);
					redY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					greenX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TG]);
					greenY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blueX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TB]);
					blueY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TB]);
				}
			}
		}
		//s=(d(x)*d(x)+d(y)*d(y))開根號
		int R,G,B;
		R=(int)(sqrt(redX*redX*1.0+redY*redY*1.0));
		G=(int)(sqrt(greenX*greenX*1.0+greenY*greenY*1.0));
		B=(int)(sqrt(blueX*blueX*1.0+blueY*blueY*1.0));

		if(redX<0 && redY<0) ImageSize[i]=0;
		else if(R>255) ImageSize[i]=255;
		else ImageSize[i]=R;
		
		if(greenX<0 && greenY<0) ImageSize[i+1]=0;
		else if(G>255) ImageSize[i+1]=255;
		else ImageSize[i+1]=G;

		if(blueX<0 && blueY<0) ImageSize[i+2]=0;
		else if(B>255) ImageSize[i+2]=255;
		else ImageSize[i+2]=B;
	}
	
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;	
	Invalidate();
}

        執行效果如下圖所示:

        如果採用Sobel邊緣細化,建議二值化(0和255閾值化)處理後再銳化,彩色圖建議先灰度處理再進行其他處理。


      4.Isotropic運算元
 

        程式碼實現如下:

//Isotropic運算元採用PPT上的d(x)模板 d(y)
void CImageProcessingView::OnRHIsotropic() 
{
	if(numPicture==0)
	{
		AfxMessageBox("載入圖片後才能影象增強(銳化)!",MB_OK,0);
		return;
	}

	AfxMessageBox("影象增強(銳化):採用Isotropic運算元!");

	int HWS=3;                               
	//模板為Isotropic運算元
	float HX[3][3]={{1,0,-1},
					{sqrt(2.0),0,-sqrt(2.0)}, 
					{1,0,-1} };
	float HY[3][3]={{-1,-sqrt(2.0),-1},
					{0,0,0}, 
					{1,sqrt(2.0),1} };

	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);

	unsigned char *ImageSize;      
	ImageSize=new unsigned char[m_nImage];  
	float redX,greenX,blueX;
	float redY,greenY,blueY;
	int X,Y;      
	int TR,TG,TB;  
	
	//影象增強
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		X=(i/3)%m_nWidth;    //X列
		Y=(i/3)/m_nWidth;    //Y行
		redX=greenX=blueX=0;
		redY=greenY=blueY=0;
		
		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )
				{			
					
					TR=j*m_nWidth*3+k*3;	
					redX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TR]);
					redY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					greenX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TG]);
					greenY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blueX+=HX[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TB]);
					blueY+=HY[(j-Y+HWS/2)][(k-X+HWS/2)]*(float)(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值 s=(d(x)*d(x)+d(y)*d(y))開根號
		int R,G,B;
		R=(int)(sqrt(redX*redX*1.0+redY*redY*1.0));
		G=(int)(sqrt(greenX*greenX*1.0+greenY*greenY*1.0));
		B=(int)(sqrt(blueX*blueX*1.0+blueY*blueY*1.0));

		if(redX<0 && redY<0) ImageSize[i]=0;
		else if(R>255) ImageSize[i]=255;
		else ImageSize[i]=R;
		
		if(greenX<0 && greenY<0) ImageSize[i+1]=0;
		else if(G>255) ImageSize[i+1]=255;
		else ImageSize[i+1]=G;

		if(blueX<0 && blueY<0) ImageSize[i+2]=0;
		else if(B>255) ImageSize[i+2]=255;
		else ImageSize[i+2]=B;
	}
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;	
	Invalidate();
}

        執行效果如下圖所示,效果與上面的Sobel類似:


       5.Prewitt運算元

        程式碼如下圖所示:

//Prewitt運算元採用PPT上的d(x)模板,不是d(y)
void CImageProcessingView::OnRHPrewitt() 
{
	
	if(numPicture==0)
	{
		AfxMessageBox("載入圖片後才能影象增強(銳化)!",MB_OK,0);
		return;
	}
	AfxMessageBox("影象增強(銳化):採用Prewitt運算元!");

	int HWS=3;                               
	int H[3][3]={{1,0,-1},    //模板為Prewitt運算元
				{1,0,-1},
				{1,0,-1}};

	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);

	unsigned char *ImageSize;      
	ImageSize=new unsigned char[m_nImage];  
	int red,green,blue;
	int X,Y;      
	int TR,TG,TB; 
	
	//影象增強:平滑
	for(int i=0; i<m_nImage ; i=i+3 )
	{
		X=(i/3)%m_nWidth;    //X列
		Y=(i/3)/m_nWidth;    //Y行
		red=green=blue=0;
		
		//對影象進行畫素求和並取平均值 HWS維數
		for(int j=Y-HWS/2 ; j<Y+HWS/2+1 ; j++ )                      //第j行
		{
			for(int k=X-HWS/2 ; k<X+HWS/2+1 ; k++ )                  //第k列
			{
				if( j>=0 && k>=0 && k<m_nWidth && j<m_nHeight )
				{			
					
					TR=j*m_nWidth*3+k*3;	
					red+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TR]);
					TG=j*m_nWidth*3+k*3+1;
					green+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TG]);
					TB=j*m_nWidth*3+k*3+2;
					blue+=H[(j-Y+HWS/2)][(k-X+HWS/2)]*(m_pImage[TB]);
				}
			}
		}
		//對新影象賦值
		if(red>=0 && red<256) ImageSize[i]=red;
		else if(red<0) ImageSize[i]=0;      //ImageSize[i]=-red;
		else ImageSize[i]=0;
		
		if(green>=0 && green<256) ImageSize[i+1]=green;
		else if(green<0) ImageSize[i+1]=0;  //ImageSize[i+1]=-green;
		else ImageSize[i+1]=0;
		
		if(blue>=0 && blue<256) ImageSize[i+2]=blue;
		else if(blue<0) ImageSize[i+2]=0;   //ImageSize[i+2]=-blue;
		else ImageSize[i+2]=0;	
	}
	fwrite(ImageSize,m_nImage,1,fpw);  
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level=400;	
	Invalidate();
}

        執行效果如下圖所示,只選取了X分量:

        最後還是希望文章對你有所幫助,如果文章有不足或錯誤之處,請海涵。自己給自己點個贊,挺不容易的,但還會繼續寫完~同時後面的影象處理準備研究些感興趣的東西,而不是這樣的長篇大論了,例如怎樣實現驗證碼提取、如何實現影象恢復、DICOM影象等知識吧!

      (By:Eastmount 2015-06-08 下午6點   http://blog.csdn.net/eastmount/

相關文章