數(shù)字圖像處理實習(xí)報告
一、實習(xí)的目的和意義
實習(xí)目的:本實習(xí)內(nèi)容旨在讓學(xué)生通過用VC等高級語言編寫數(shù)字圖像處理的一些基本算法程序,來鞏固和掌握圖像處理技術(shù)的基本技能,提高實際動手能力,并通過實際編程了解圖像處理軟件的實現(xiàn)的基本原理。為學(xué)生進(jìn)一步學(xué)習(xí)數(shù)字?jǐn)z影測量、遙感和地理信息系統(tǒng)等專業(yè)課程以及應(yīng)用圖像處理解決實際問題奠定基礎(chǔ)。
二、實習(xí)原理和方法
(1)
Raw格式:Raw格式文件是按照數(shù)字圖像組成的二維矩陣,將像素按行列號順序存儲在文件中。這種文件只含有圖像像素數(shù)據(jù),不含有信息頭,因此,在讀圖像時,需要根據(jù)文件大小,計算圖像所包含的行列號,或者需要事先知道圖像大?。ň仃嚧笮。?。RAW文件按圖像上行到下行、左列到右列順序存儲。
BMP格式:BMP文件數(shù)據(jù)區(qū)按圖像上下行到上行、左列列到右列順序存儲到數(shù)據(jù)區(qū)。BMP文件由文件頭、信息頭、顏色表、數(shù)據(jù)區(qū)四個部分組成。
做Raw格式文件到BMP格式文件的轉(zhuǎn)化,先要為BMP格式文件申請四部分的內(nèi)存:文件頭,位圖信息頭,顏色表,圖象數(shù)據(jù),然后根據(jù)輸入值以及Raw文件信息,BMP格式文件信息計算出這幾部分的值,賦給他們,寫到BMP文件中去。
(2)
灰度的線性變換就是指圖像的中所有點(diǎn)的灰度按照線性灰度變換函數(shù)進(jìn)行變換?;叶茸儞Q方程如下:
該方程為線性方程。式中參數(shù)
灰度值。
設(shè)原圖象的灰度范圍為[a,b],變化之后的范圍為[a’,b’],則:
如果算出來的值大于255,則讓它等于255,小于0則讓其等于0。
(3)
局部處理在處理某一像素時,利用與該像素相鄰的一組像素,經(jīng)過某種變換得到處理后圖像中某一點(diǎn)的像素值。目標(biāo)像素的鄰域一般是由像素組成的二維矩陣,該矩陣的大小為奇數(shù),目標(biāo)像素位于該矩陣的中央,即目標(biāo)像素就是區(qū)域的中心像素。經(jīng)過處理后,目標(biāo)像素的值為經(jīng)過特定算法計算后所得的結(jié)果。
實際上都是利用卷積來實現(xiàn)的,卷積往往用一個矩陣表示,將矩陣的中心對齊某個像素,矩陣中的值乘到相應(yīng)的像素中去,然后將所有乘積加起來就得到中心像素的灰度值。邊界像素不做處理,仍為原來的灰度值。求出的像素灰度值若超過[0~255],則向離其最近的屬于該范圍的像素值靠攏。
3*3低通濾波的算子見表1。
3*3高通濾波的算子見表2。
表格 1
|
1/9 |
1/9 |
1/9 |
|
1/9 |
1/9 |
1/9 |
|
1/9 |
1/9 |
1/9 |
表格 2
|
-1 |
-1 |
-1 |
|
-1 |
9 |
-1 |
|
-1 |
-1 |
-1 |
(4)
對于圖像平移來說,若平移量是(tx,ty),像素在原圖像中的坐標(biāo)為(x0,y0),則變化后的坐標(biāo)為(x1,y1),x1=x0+tx,y1=y0+ty。平移只需改變像素的灰度值,不必改變位圖信息頭和調(diào)色板內(nèi)容。
對于圖像縮放,假設(shè)放大因子為ratio,縮放的變換矩陣為:
(5)
中值濾波也屬于局部處理的一種,將窗口中的各個像素排序之后排序,取中值賦給模板中心的像素,所以窗口中個數(shù)一般是基數(shù)。
我用的中值濾波窗口是十字絲的9個數(shù)的窗口。
(6)
(7)
旋轉(zhuǎn)矩陣如下:
同樣要求各個像素在原圖像中的坐標(biāo),先將新圖像的坐標(biāo)系平移到圖像中心,做逆時針旋轉(zhuǎn),然后再平移到屏幕左上角,然后將原圖像對應(yīng)坐標(biāo)的值賦給新圖像。
(8)
判斷分析法:假定圖像的灰度區(qū)間為[0,L-1],則選擇一閾值T 將圖像的像素分為兩組。
為最大值所對應(yīng)的T,就是所求判斷分析法的分割閾值。
搜尋到閾值之后,灰度值小于閾值的像素賦0,其他的賦1,修改文件信息頭,調(diào)色板,申請新內(nèi)存。
(9)
統(tǒng)計各灰度值出現(xiàn)的頻數(shù),以及像素的總個數(shù),用頻數(shù)除以總個數(shù)作為頻率,以灰度值作為橫坐標(biāo),頻率作為縱坐標(biāo)繪圖。
三、實習(xí)過程和步驟
首先要建立一個基于MFC的多文檔工程,將視圖基類改為滾動視圖,以自己的學(xué)號命名。
我用的是書上給的CDib類,類里面有獲取BMP寬度,高度的函數(shù),有指向位圖信息頭的指針,指向圖象數(shù)據(jù)的指針,因此我在文檔類(Doc類)里定義了一個CDib類的對象,打開以及保存文件的時候利用這個對象去調(diào)用CDib里讀取與存儲文件的函數(shù),并且可以利用這個對象的兩個指針對打開的圖象進(jìn)行各種操作。
1.Raw格式到BMP格式的轉(zhuǎn)換:
首先建立一個RawToBMP的對話框,在上面加上四個編輯框(一個輸入打開文件的路徑一個輸入保存文件的路徑,另兩個),兩個按鈕,以及默認(rèn)的確認(rèn),取消按鈕。利用類向?qū)Р迦氪藢υ捒蝾悾⑶覟榍皟蓚€編輯框定義CString的兩個變量,用來存儲打開與保存文件的路徑。同時為兩個瀏覽按鈕添加消息響應(yīng)函數(shù),在消息函數(shù)里創(chuàng)建CFileDialog對象,利用此對象的函數(shù)將兩個路徑值賦給前兩個編輯框的成員變量。再為OK鍵添加消息響應(yīng)函數(shù),分別定義 BMP格式文件前三部分?jǐn)?shù)據(jù)變量,計算出各變量的值,并且利用一個CFile對象獲取Raw圖象的數(shù)據(jù),利用另一個CFile對象將數(shù)據(jù)存儲到所輸入的路徑的文件中去,CFile對象的Read函數(shù)會自動創(chuàng)建一個文件。
然后在菜單上新建一個菜單,為菜單添加消息響應(yīng)函數(shù),在其消息響應(yīng)函數(shù)里創(chuàng)建RowToBMP對話框。這樣點(diǎn)擊菜單后就會彈出一個對話框,按確定鍵之后就可以讀取Raw文件并且存儲BMP文件,完成整個消息循環(huán)。
2.灰度圖象的線性拉伸:
創(chuàng)建一個對話框來輸入變化后的灰度值,為對話框的兩個編輯框定義成員變量,在文檔類中添加處理函數(shù),按照對話框輸入值計算出fA與fB,做一個循環(huán),將0 到255的灰度值,計算出拉伸后的灰度值(超限情況特殊處理),存放在下標(biāo)為此值的一個數(shù)組中,然后利用文檔類的中定義的CDib類的成員變量 m_DIB,獲得當(dāng)前打開的圖像指向圖像數(shù)據(jù)部分的指針m_DIB.m_pBits,在數(shù)組中查出每個像素變化后的灰度值,并將此值賦給指針 m_pBits指向的內(nèi)存。刷新視圖。
然后在菜單中加上線性拉伸的菜單,為該菜單的ID添加消息響應(yīng)函數(shù),在該函數(shù)中創(chuàng)建對話框,并調(diào)用文檔類線性拉伸的函數(shù),將對話框的兩個成員變量傳給此函數(shù)。
3.局部處理:
在文檔類里添加低通濾波和高通濾波的成員函數(shù),在函數(shù)中使用m_DIB對象中指向圖像數(shù)據(jù)部分的指針m_pBits,首先申請一個新內(nèi)存,將原來圖像的灰度值存儲起來,然后定義9個BYTE類型的指針,利用雙重嵌套循環(huán),在循環(huán)中每次用這9個指針指向復(fù)制圖像對應(yīng)模板中的9個數(shù),然后按照模板中的數(shù)值計算出中心像素的灰度值,判斷是否超過范圍,如果超過范圍則做相應(yīng)的處理,否則將此值直接賦給m_pBits中對應(yīng)的中心像素。循環(huán)之后刷新視圖。
添加局部處理的菜單,為菜單設(shè)置消息響應(yīng)函數(shù),在菜單消息響應(yīng)函數(shù)中調(diào)用文檔類的函數(shù),完成對m_DIB的處理。
建立平移對話框,定義兩個成員變量,分別存儲輸入的水平位移和垂直位移。
在文檔類里添加平移函數(shù),申請一塊新內(nèi)存復(fù)制原圖像的信息,在函數(shù)中將
外層循環(huán)變量i視為縱坐標(biāo),內(nèi)層循環(huán)變量j視為橫坐標(biāo),通過雙重循環(huán),對每個像素,求出其在原圖像中的坐標(biāo)(i0,j0),將復(fù)制圖像中的對應(yīng)(i0,j0)的像素灰度值賦給m_DIB.m_pBits指針中的圖像。如果在原圖像中找不到該像素,置為背景色。刷新視圖。
在菜單中添加圖像平移菜單,并為該菜單添加消息響應(yīng)函數(shù),在此函數(shù)中創(chuàng)建平移對話框,調(diào)用文檔類的平移函數(shù),將對話框的成員變量傳入該函數(shù)。
建立縮放對話框類,為此類定義兩個成員變量,存儲輸入的水平縮放因子和垂直縮放因子。
再在文檔類中添加縮放函數(shù),利用m_DIB.m_pBMI(指向位圖信息頭的指針),修改位圖信息頭中的寬度,高度,圖像大小。計算出新圖像的大小,申請一塊新內(nèi)存存儲新圖像,同平移函數(shù)一樣,計算出每個像素在原圖像中的坐標(biāo),i0=i/PRatio,j0=j/VRatio,PRatio與VRatio 分別為水平縮放因子和垂直縮放因子。將原圖像中對應(yīng)坐標(biāo)的灰度值賦給新內(nèi)存,然后將m_DIB.m_pBits(指向圖像數(shù)據(jù)的指針)指向新內(nèi)存,刷新視圖。
5.中值濾波:
在文檔類中添加兩個成員函數(shù)。一個用來把傳入的指針里的內(nèi)容排序,一個用來做中值濾波。也要申請一塊新內(nèi)存來復(fù)制原圖像的信息,雙重嵌套循環(huán),邊界像素不處理,對每個像素,使用一個大小為9個字節(jié)的數(shù)組來存放復(fù)制圖像窗口中各像素值,然后將數(shù)組首地址傳入排序的函數(shù)中,將中間的值賦給當(dāng)前圖像窗口中心的像素。排序函數(shù)我用的是快速排序法。
在菜單中添加中值濾波菜單項,為其添加消息響應(yīng)函數(shù),調(diào)用文檔類的中值濾波函數(shù)。
6.邊緣檢測:
在文檔類中定義三個函數(shù),分別為Roberts,Prewit,Sobel算子處理函數(shù),處理時,先申請新內(nèi)存復(fù)制原來圖像信息,邊界像素不作處理,對每個像素值,求出其在復(fù)制圖像中的梯度,判斷,若梯度值大于150(這個是我自己定的),則將灰度值賦為255,否則置零。
菜單中添加邊緣檢測菜單,置屬性為Pop—up,添加三個下一級菜單,分別為Roberts,Prewit,Sobel,各個菜單的消息響應(yīng)函數(shù)中調(diào)用文檔類中各自的處理函數(shù)。
7.圖像旋轉(zhuǎn):
創(chuàng)建一個對話框輸入旋轉(zhuǎn)角度,在文檔類中添加成員函數(shù)。
先將角度化為弧度值。
計算原圖像四個角點(diǎn)的坐標(biāo),以及新圖像四個角點(diǎn)的坐標(biāo)。
根據(jù)新圖像四個角點(diǎn)的坐標(biāo),取對角線上兩個點(diǎn)橫坐標(biāo)差值較大值作為寬度,縱坐標(biāo)差值較大值作為高度。
根據(jù)計算出來的高度和寬度修改文件信息頭,并且申請內(nèi)存存儲新圖像。
計算每點(diǎn)的像素在原來圖像中的坐標(biāo)從而獲取其灰度值,寫入新內(nèi)存。
將m_DIB.m_pBits指向該新內(nèi)存。刷新視圖。
添加圖像旋轉(zhuǎn)菜單,在菜單響應(yīng)函數(shù)中創(chuàng)建對話框,調(diào)用文檔類中旋轉(zhuǎn)函數(shù),將對話框中獲取的角度傳給旋轉(zhuǎn)函數(shù)。
8.圖像二值化:
在文檔類添加一個成員函數(shù),根據(jù)傳人的圖像和閾值返回組間方差和組內(nèi)方差的比值。
再添加一個成員函數(shù),進(jìn)行二值化。
在函數(shù)中:
計算新BMP文件的大小,申請一塊新內(nèi)存,存儲新的整個BMP文件的信息,將位圖信息頭中biBitCount置為1,調(diào)色板數(shù)組只有兩個兩個元素,下標(biāo)為0的三個灰度值都為0,下標(biāo)為1的三個灰度值為255。
從最大灰度值到最小灰度值之間搜尋上述函數(shù)返回值最大的值,作為閾值。
對每個像素,若其原來灰度值小于閾值,賦1,否則賦0。
將m_DIB,m_pBits指向新內(nèi)存的圖像數(shù)據(jù)部分,m_DIB.m_pBMI指向位圖信息頭。
9.圖像直方圖:
為文檔類添加一個int型指針成員變量m_pGray,在構(gòu)造函數(shù)中將該指針賦空,在文檔類中定義了一個函數(shù),統(tǒng)計各個灰度值出現(xiàn)的頻數(shù),申請一個內(nèi)存,存儲在這個內(nèi)存中,并將m_pGray指向它。
創(chuàng)建一個畫直方圖的對話框,添加Picture控件,在控件里調(diào)用文檔類成員變量,畫直方圖。添加一個滾動條,用來確定閾值,為滾動條添加消息響應(yīng)函數(shù),按照滾動條的值進(jìn)行二值化。
在菜單中添加直方圖菜單,添加消息響應(yīng)函數(shù),在響應(yīng)函數(shù)中創(chuàng)建直方圖對話框?qū)ο蟆?/p>
最后,因為我開始做工程的時候沒有把菜單設(shè)計好,做得有點(diǎn)亂,所以,我又在View里添加WM_CONTEXTMENU消息響應(yīng)函數(shù),在函數(shù)體內(nèi)用CMenu類來實現(xiàn)彈出菜單。
四、結(jié)果分析與評價
(1)Raw格式到BMP格式的轉(zhuǎn)換:效果見圖1。
圖表 1
老師說在轉(zhuǎn)化的時候后面用一個循環(huán)會降低效率,但是實際上只要寬度是4的整數(shù)倍,后面的循環(huán)就不會做了。所以這個算法效率我覺得還行吧。
(2)線性變化:輸入線性變化范圍10~20,效果見圖2。
圖表 2
用了線性查找表之后,這個算法的效率應(yīng)該會高很多,但是我的算法里是線性表從0~255都有變化之后的值,實際上,如果圖片的灰度范圍小一些的話,做了很多無用的計算,而且前面已經(jīng)搜尋過原圖像的最大最小灰度值了,所以線性表的生成循環(huán)可以只從最小灰度做到最大灰度。另外,我設(shè)計的算法里,如果最大值和最小值輸反了的話,程序會自動交換他們的值,做這個可能就會多算一些東西了。
(3)低通濾波:效果見圖3。
圖表 3
取的是8鄰域內(nèi)的平均值,效果不是很好。
高通濾波:效果見圖4。
圖表 4
基本上我覺得邊緣還是有突出了吧。
中值濾波:效果見圖5。
圖表 5
這個中值濾波的效果我還是比較滿意的,因為排序所以要調(diào)用其他函數(shù),我用了快速排序,而且用的是9個數(shù)的十字絲窗口,所以速度要比25個數(shù)的窗口快一些。平滑的效果出來還可以。
(4)邊緣檢測:
Roberts算子:效果見圖6。
圖表 6
Prewit算子:效果見圖7。
圖表 7
Sobel算子:效果見圖8。
圖表 8
由于Prewit算子和Sobel算子都用了8個數(shù)去做,所以效果要好一些,相比之下,Sobel算子對這幅圖又要效果好些,應(yīng)該是對4鄰域賦予了更大權(quán)的緣故。但是后兩種算法計算量也要大一些。
(5)圖像平移:效果見圖9。
圖表 9
這個圖像平移量比較大,所以被裁切的也顯得不真實了。主要是因為我的圖像大小和坐標(biāo)都沒有變化,所以只在原來的圖像坐標(biāo)范圍內(nèi)顯示平移后的圖像,實際上,我既可以改變圖像的大小,并且為了節(jié)省計算,可以讓循環(huán)變量i和j從一個新的值開始做計算,前面的全都賦背景色。
圖像縮放:水平比例0.4,垂直比例0.5,效果見圖10。
圖表 10
在此基礎(chǔ)上旋轉(zhuǎn):效果見圖11。
圖表 11
這幾種算法主要的計算量都在for循環(huán)內(nèi),所以要想優(yōu)化算法的話,必須簡化循環(huán)里的計算。不過我的想法差不多跟書上的差不多,還沒有什么優(yōu)化。也許,這種優(yōu)化的算法需要看很多別人做的好程序才能慢慢自己學(xué)會吧。
(6)二值化(判斷分析法):效果見圖12。
圖表 12
實際上,我用直方圖看的最佳閾值應(yīng)該在100多左右,而我做的程序閾值好像偏小一些,所以效果不太好,我計算組間方差和組內(nèi)方差的時候調(diào)用了一個函數(shù)專門求閾值,可能這里的計算還是有一點(diǎn)問題。而且在我的函數(shù)里,要256次調(diào)用這個函數(shù),又因為計算機(jī)是按字節(jié)處理數(shù)據(jù)的,因此寫圖像數(shù)據(jù)的時候要用每8個寫到一個數(shù)組中,然后通過計算得到字節(jié)類型的值,這些都使得我的算法效率比較低,最后一個問題,我覺得如果使用位運(yùn)算會快一些,但是前面的問題還沒有想到比較好的解決方法。
(7)直方圖:效果見圖13。
這個圖像255的像素太多,如果我沒算錯的話,量化應(yīng)該不是很好吧。
圖表 13
五、實習(xí)總結(jié)與體會
這次實習(xí)學(xué)到最大的東西,是自己總算有MFC編程的概念了,雖然自己VC++考試的分?jǐn)?shù)還不錯,但是里面的很多東西,不通過自己的編程時絕對不能真正理解。比如說封裝性,這次用CDib的方便,很好地利用了類的封裝性。另外,比如MFC是基于消息響應(yīng)機(jī)制的,這就決定了,要利用鼠標(biāo)或者菜單響應(yīng)函數(shù)去實現(xiàn)功能,而用c語言編寫程序的時候,完全是按主函數(shù)的線程來的。
另外,我也學(xué)會了調(diào)試的真正含義。以前都只知道那幾個按鍵是做什么用的,調(diào)試的真正目的,是根據(jù)自己的算法來檢驗程序計算的各個值是否符合,從而可以很快速方便地查到自己的錯誤。
自學(xué)也是很重要的一方面。實際上,在現(xiàn)在來說,用MSDN也不是很難的事了,我們不應(yīng)該被英文打到,而且現(xiàn)在,隨著對一些專有名詞熟悉了之后,看MSDN也容易一些了,萬一不懂的函數(shù),也可以利用網(wǎng)絡(luò)查到很多函數(shù)功能用法的解釋。
剛開始的時候做的是位圖的讀取和顯示,實在是不知從哪里做起,所以就照著實習(xí)書上敲了前面的部分,但是慢慢地也看懂了代碼的意思。所以后來的基本上都是自己做的了,但是算法還是基本上和書上差不多。不過自己編的時候還是有很多細(xì)節(jié)的部分沒有注意到,比如說,強(qiáng)制數(shù)據(jù)類型轉(zhuǎn)換,我自己編的時候沒有注意這個問題,結(jié)果出了很多錯,有些事由于函數(shù)調(diào)用引起的,有些是由于不等號兩邊數(shù)據(jù)的匹配問題,還有的是由于指針的移動,直到這個時候,才真正明白實習(xí)書上程序為什么那么多強(qiáng)制類型轉(zhuǎn)換,雖然書上很多東西不是盡善盡美,但是對于我這種剛開始學(xué)會編程的人還是有很多可以學(xué)習(xí)的地方的。
如老師所說,算法的效率是很重要的。要提高算法的效率,一個是要簡化計算(不得不說,這需要數(shù)學(xué)基礎(chǔ)),另外一個就是要避免許多重復(fù)的計算。在參考書上的程序里,很多時候,為了避免這種重復(fù)的計算(在循環(huán)中表現(xiàn)尤其明顯),會把某些數(shù)當(dāng)常數(shù)算出來,只要后來加上這個常數(shù)就可以,這樣,效率高很多。
另外,對許多出錯的情況,我的程序里也沒有做好。比如,如果打開的不是8位圖像,我的程序不會提示錯誤,正常結(jié)束,而可能做錯,所以,這也是我應(yīng)該向別人程序?qū)W習(xí)的地方。
最后一個,自己菜單的布局也是很亂的。要從一開始就布局好。

- [思想?yún)R報]入黨積極分子學(xué)習(xí)黨章后的思想?yún)R報
- [思想?yún)R報]電子專業(yè)大學(xué)生的思想?yún)R報
- [賀詞致辭]我們都是追夢人——習(xí)近平主席2019年新年賀詞啟示錄
- [辭職報告]十年老員工的辭職報告
- [辭職須知]懷孕后要不要辭職養(yǎng)胎?經(jīng)驗告訴我們
- [辭職須知]勞動者注意:2019年辭職原因?qū)憽皞€人”, 賠償金你拿不到!
- [實習(xí)報告]醫(yī)院門診部實習(xí)報告范文
- [實習(xí)報告]大學(xué)生假期實踐報告檔案館實習(xí)報告
- [工作總結(jié)]小學(xué)畢業(yè)班班主任工作總結(jié)
- [自查報告]糾四風(fēng)問題自查報告

