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

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

        程式碼如下:
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+庫實現。可能存在因為理解不夠或其它的錯誤,歡迎提出修改~