本文主要講述基於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分量:
//////////////////////////////////////////////////////////////////