1. 程式人生 > >圖像處理中幾個基本的處理方法c#代碼實現

圖像處理中幾個基本的處理方法c#代碼實現

位圖 edi windows系統 process 圖案 電視 間接 做了 同步

圖像是人類獲取和交換信息的主要來源,因此,圖像處理的應用領域必然涉及到人類生活和工作的方方面面。隨著人類活動範圍的不斷擴大,圖像處理的應用領域也將隨之不斷擴大。(1)航天和航空技術方面的應用 數字圖像處理技術在航天和航空技術方面的應用,除了上面介紹的JPL對月球、火星照片的處理之外,另一方面的應用是在飛機遙感和衛星遙感技術中。許多國家每天派出很多偵察飛機對地球上有興趣的地區進行大量的空中攝影。對由此得來的照片進行處理分析,以前需要雇用幾千人,而現在改用配備有高級計算機的圖像處理系統來判讀分析,既節省人力,又加快了速度,還可以從照片中提取人工所不能發現的大量有用情報。從60年代末以來,美國及一些國際組織發射了資源遙感衛星(如LANDSAT系列)和天空實驗室(如SKYLAB),由於成像條件受飛行器位置、姿態、環境條件等影響,圖像質量總不是很高。因此,以如此昂貴的代價進行簡單直觀的判讀來獲取圖像是不合算的,而必須采用數字圖像處理技術。如LANDSAT系列陸地衛星,采用多波段掃描器(MSS),在900km高空對地球每一個地區以18天為一周期進行掃描成像,其圖像分辨率大致相當於地面上十幾米或100米左右(如1983年發射的LANDSAT-4,分辨率為30m)。這些圖像在空中先處理(數字化,編碼)成數字信號存入磁帶中,在衛星經過地面站上空時,再高速傳送下來,然後由處理中心分析判讀。這些圖像無論是在成像、存儲、傳輸過程中,還是在判讀分析中,都必須采用很多數字圖像處理方法。現在世界各國都在利用陸地衛星所獲取的圖像進行資源調查(如森林調查、海洋泥沙和漁業調查、水資源調查等),災害檢測(如病蟲害檢測、水火檢測、環境汙染檢測等),資源勘察(如石油勘查、礦產量探測、大型工程地理位置勘探分析等),農業規劃(如土壤營養、水份和農作物生長、產量的估算等),城市規劃(如地質結構、水源及環境分析等)。我國也陸續開展了以上諸方面的一些實際應用,並獲得了良好的效果。在氣象預報和對太空其它星球研究方面,數字圖像處理技術也發揮了相當大的作用。(2)生物醫學工程方面的應用 數字圖像處理在生物醫學工程方面的應用十分廣泛,而且很有成效。除了上面介紹的CT技術之外,還有一類是對醫用顯微圖像的處理分析,如紅細胞、白細胞分類,染色體分析,癌細胞識別等。此外,在X光肺部圖像增晰、超聲波圖像處理、心電圖分析、立體定向放射治療等醫學診斷方面都廣泛地應用圖像處理技術。 (3)通信工程方面的應用 當前通信的主要發展方向是聲音、文字、圖像和數據結合的多媒體通信。具體地講是將電話、電視和計算機以三網合一的方式在數字通信網上傳輸。其中以圖像通信最為復雜和困難,因圖像的數據量十分巨大,如傳送彩色電視信號的速率達100Mbit/s以上。要將這樣高速率的數據實時傳送出去,必須采用編碼技術來壓縮信息的比特量。在一定意義上講,編碼壓縮是這些技術成敗的關鍵。除了已應用較廣泛的熵編碼、DPCM編碼、變換編碼外,目前國內外正在大力開發研究新的編碼方法,如分行編碼、自適應網絡編碼、小波變換圖像壓縮編碼等。(4)工業和工程方面的應用 在工業和工程領域中圖像處理技術有著廣泛的應用,如自動裝配線中檢測零件的質量、並對零件進行分類,印刷電路板疵病檢查,彈性力學照片的應力分析,流體力學圖片的阻力和升力分析,郵政信件的自動分揀,在一些有毒、放射性環境內識別工件及物體的形狀和排列狀態,先進的設計和制造技術中采用工業視覺等等。其中值得一提的是研制具備視覺、聽覺和觸覺功能的智能機器人,將會給工農業生產帶來新的激勵,目前已在工業生產中的噴漆、焊接、裝配中得到有效的利用。(5)軍事公安方面的應用 在軍事方面圖像處理和識別主要用於導彈的精確末制導,各種偵察照片的判讀,具有圖像傳輸、存儲和顯示的軍事自動化指揮系統,飛機、坦克和軍艦模擬訓練系統等;公安業務圖片的判讀分析,指紋識別,人臉鑒別,不完整圖片的復原,以及交通監控、事故分析等。目前已投入運行的高速公路不停車自動收費系統中的車輛和車牌的自動識別都是圖像處理技術成功應用的例子。(6)文化藝術方面的應用 目前這類應用有電視畫面的數字編輯,動畫的制作,電子圖像遊戲,紡織工藝品設計,服裝設計與制作,發型設計,文物資料照片的復制和修復,運動員動作分析和評分等等,現在已逐漸形成一門新的藝術--計算機美術。

數字圖像處理技術與圖象處理系統是七十年代末期形成一個獨立學科,當時只能處理靜止圖象,主要用於軍事、科研醫學等領域。圖象處理系統是為了加快處理速度而設計的專用系統,在中小型計算機控制下運行。這些系統的規模大,價格昂貴。面向PC機的圖象處理系統是八十年代中後期開始出現的。它價格便宜,易於擴充, 軟件豐富,因此很快得到推廣,帶動了圖象處理技術的普及。

在過去的二十年裏,C和C++已經成為在商業軟件的開發領域中使用最廣泛的語言。它們為程序員提供了十分靈活的操作,不過同時也犧牲了一定的效率。與諸如Microsoft,Visual Basic, 等語言相比,同等級別的C/C++應用程序往往需要更長時間來開發。由於C/C++語言的復雜性,許多程序員都試圖尋找一種新的語言,希望能在功能與效率之間找到一個更為理想的權衡點。

目前有些語言,以犧牲靈活性的代價來提高效率。可是這些靈活性正是C/C++程序員所需要的。這些解決方案對編程人員的限制過多(如屏蔽一些底層代碼控制的機制),其所提供的功能難以令人滿意。這些語言無法方便地同早先的系統交互,也無法很好地和當前的網絡編程相結合。

對於C/C++用戶來說,最理想的解決方案無疑是在快速開發的同時又可以調用底層平臺的所有功能。他們想要一種和最新的網絡標準保持同步並且能和已有的應用程序良好整合的環境。另外,一些C/C++開發人員還需要在必要的時候進行一些底層的編程。

微軟推出C# (C sharp)是微軟對這一問題的解決方案。C#是一種最新的、面向對象的編程語言。它使得程序員可以快速地編寫各種基於Microsoft .NET平臺的應用程序,Microsoft .NET提供了一系列的工具和服務來最大程度地開發利用計算與通訊領域。正是由於C#面向對象的卓越設計,使它成為構建各類組件的理想之選--無論是高級的商業對象還是系統級的應用程序。使用簡單的C#語言結構,這些組件可以方便的轉化為XML 網絡服務,從而使它們可以由任何語言在任何操作系統上通過Internet進行調用。最重要的是,C#使得C++程序員可以高效的開發程序,而絕不損失C/C++原有的強大的功能。因為這種繼承關系,C#與C/C++具有極大的相似性,熟悉類似語言的開發者可以很快的轉向C#。

效率與安全性新興的網絡經濟迫使商務企業必須更加迅速的應對競爭的威脅。開發者必須不斷縮短開發周期,不斷推出應用程序的新版本,而不僅僅是開發一個"標誌性"的版本。

C#在設計時就考慮了這些問題。它使開發者用更少的代碼做更多的事,同時也不易出錯。新的應用程序開發模型意味著越來越多地解決方案依賴於新出現的網絡標準,例如HTML,XML,SOAP等。現存的開發工具往往都是早於Internet出現的,或者是在我們所熟知的網絡還處於孕育期時出現的。所以,它們一般無法很好地支持最新的網絡技術。

C#程序員可以在Microsoft.NET平臺上事半功倍的構建應用程序的擴展框架。C#包含了內置的特性,使任何組件可以輕松轉化為XML網絡服務,通過Internet被任何操作系統上運行的任何程序調用。更突出的是,XML網絡服務框架可以使現有的XML網絡服務對程序員來說就和C#對象一樣。這樣,程序員就可以方便地使用他們已有的面向對象的編程技巧來開發利用現有的XML網絡服務。

還有一些精細的特性,使得C#成為一流的網絡編程工具。例如,XML正逐漸成為在網絡上傳輸結構化數據的標準。這種數據集合往往非常小。為提高性能,C#允許把XML數據直接映射到struct數據類型,而不是class。這樣對處理少量的數據非常有效。

即使是專家級的C++程序員也常會犯一些最簡單的小錯誤--比如忘了初始化變量,但往往就是這些小錯誤帶來了難以預料的問題,有些甚至需要很長時間來尋找和解決。一旦一個程序作為產品來使用,就算最簡單的錯誤糾正起來也可能要付出極其昂貴的代價。C#的現代化設計能夠消除很多常見的C++編程錯誤。 例如:資源回收減輕了程序員內存管理的負擔;C#中變量由環境自動初始化;變量是類型安全的。這樣,程序員編寫與維護那些解決復雜商業問題的程序就更方便了。

更新軟件組件是一項很容易出錯的工作,因為代碼的修改可能無意間改變原有程序的語義。為協助開發者進行這項工作,C#為版本的更新提供內在的支持。例如,方法重載必須顯式聲明。這樣可以防止編碼錯誤,保證版本更新的靈活性。還有一個相關的特性就是對接口和接口繼承的內在支持。這些特性使得C#可以開發復雜的框架並且隨著時間不斷發展更新它。

總體來說,這些特性使得開發程序項目的後續版本的過程更加健壯,從而減少後續版本的開發成本。

C#語言允許類型定義的,擴展的元數據。這些元數據可以應用於任何對象。項目構建者可以定義領域特有的屬性並把他們應用於任何語言元素-類,接口等等。然後,開發人員可以編程檢查每個元素的屬性。這樣,很多工作都變得方便多了,比如編寫一個小工具來自動檢查每個類或接口是否被正確定義為某個抽象商業對象的一部分,或者只是創建一份基於對象的領域特有屬性的報表。定制的元數據和程序代碼之間的緊密對應有助於加強程序的預期行為和實際實現的之間的對應關系。

作為一種自動管理的,類型安全的環境,C#適合於大多數企業應用程序。但實際的經驗表明有些應用程序仍然需要一些底層的代碼,要麽是因為基於性能的考慮,要麽是因為要與現有的應用程序接口兼容。這些情況可能會迫使開發者使用C++,即使他們本身寧願使用更高效的開發環境。C#采用以下對策來解決這一問題:內置對組建對象模型(COM)和基於Windows的API的支持;允許有限制地使用純指針(Native Pointer)。

在C#中,每個對象都自動生成為一個COM對象。開發者不再需要顯式的實現IUnknown和其他COM接口.這些功能都是內置的.類似的,C#可以調用現有的COM對象,無論它是由什麽語言編寫的。

  C#包含了一個特殊的功能,使程序可以調用任何純API。在一段特別標記的代碼中,開發者可以使用指針和傳統C/C++特性,如手工的內存管理和指針運算。這是其相對於其它環境的極大優勢。這意味著C#程序員可以在原有的C/C++代碼的基礎上編寫程序,而不是徹底放棄那些代碼。無論是支持COM還是純API的調用,都是為了使開發者在C#環境中直接擁有必要的強大功能。

所以C#是一種現代的面向對象語言。它使程序員快速便捷地創建基於Microsoft .NET平臺的解決方案。這種框架使C#組件可以方便地轉化為XML網絡服務,從而使任何平臺的應用程序都可以通過Internet調用它。C#增強了開發者的效率,同時也致力於消除編程中可能導致嚴重結果的錯誤。C#使C/C++程序員可以快速進行網絡開發,同時也保持了開發者所需要的強大性和靈活性。

C#中,Image為源自 Bitmap 和 Metafile 的類提供功能的抽象基類,使用Image可以操作各種支持的圖片,如GIF, BMP, JPG, Image.FromFile()返回的是某個繼承自Image的具體類的對象,在這裏,就是Bitmap或者Metafile其中之一。這Bitmap不僅僅對應於bmp,其實只要是像素式的圖片格式(矢量格式不行),理論上都可以用Bitmap。由於Bitmap是忽略圖像格式的,所以,在本圖像處理的源代碼中,並沒有給出不同圖像格式轉換的代碼,我們所做的僅僅是創建一個Bitmap對象,用 Image.FromFile()方法載入圖像並保存到我們所創建的Bitmap對象中即可。對載入圖像,我們可以使用SystemDrawing命名空間裏提供的Getpixel方法提取像素的RGB值來進行處理。

2.1 BMP圖像

2.1 BMP圖像的基本介紹

如今Windows(3.x以及95,98,NT)系列已經成為絕大多數用戶使用的操作系統,它比DOS成功的一個重要因素是它可視化的漂亮界面。那麽Windows是如何顯示圖象的呢?這就要談到位圖(bitmap)。

在 Windows 3.0 以前,Windows系統用的是DDB(設備有關位圖)。DDB沒有調色板,顯示的顏色依賴硬件,處理色彩很不方便。所以 Microsoft 在 Windows 3.0中 重新定義了BMP文件格式(BMP 3.0),使其支持設備無關位圖——也就是DIB。時至今日,BMP的版本號已升至5.0(Windows NT 4.0、Windows95 定義了 BMP 4.0,Windows 98、Windows 2000 定義了 BMP 5.0),但基本結構沒有變——仍是 BMP文件頭 和 DIB 組成。

Windows 3.1以上版本提供了對設備無關位圖DIB的支持。DIB位圖可以在不同的機器或系統中顯示位圖所固有的圖像。與DDB相比而言,DIB是一種外部的位圖格式,經常存儲為以BMP為後綴的位圖文件(有時也以DIB為後綴)。DIB位圖還支持圖像數據的壓縮。與Windows DIB結構相似,但不完全相同的另一種DIB是OS/2采用的DIB。

DIB位圖的位數據緊跟在顏色表後面。數據可以是不壓縮的,也可以是壓縮的。對4位和8位位圖,可以采用RLE(遊程長度編碼)壓縮,分別稱為RLE4和RLE8位圖。

位數據以行為單位存儲,每行都被填充到一個四字節邊界,即每行所占的存儲長度總是四字節(32位)的倍數,不足時將多余位用0填充。位圖行的存儲次序是顛倒的,即位圖文件中第一行數據對應的是位圖的最底行。對於像素位數為1的DIB位圖,其每個像素只占1位,每個字節存儲八個像素。字節的最高位對應於最左邊的像素。在沒有壓縮的像素位數為4的DIB位圖中,每個字節存儲兩個像素,高四位對應於最左邊的像素,每行填充到一個四字節邊界。采用RLE編碼壓縮的四位DIB由一系列組組成。有三種類型的組:重復組、文字組和特殊組。重復組由兩個字節組成,第一個字節表示像素個數,第二個字節表示一對像素的值。文字組由一個0字節、一個像素計數字節和文字像素字節組成。像素計數值必須至少為3(小於3時,可采用重復組編碼),文字像素應填充到一個偶數字節邊界。特殊組中,00 00表示一行的結束,00 01表示位圖的結束,00 02 xx yy表示位置增量,即圖像向右走xx個像素,向下走yy個像素。

在沒有壓縮的像素位數為8的DIB位圖中,每個字節存儲一個像素,每行填充到一個四字節邊界。采用R LE編碼壓縮的四位DIB由一系列組組成。有三種類型的組:重復組、文字組和特殊組。重復組內兩個字節組成,第一個字節表示像素個數,第二個字節表示像素值。文字組由一個0字節、一個像素計數字節和文字像素字節組成。像素計數值必須至少為3(小於3時,可采用重復組編碼),文字像素應填充到一個偶數字節邊界。特殊組中,00 00表示一行的結束,00 01表示位圖的結束,0002xx yy表示位置增量,即圖像向右走xx個像素, 向下走yy個像素。在像素位數為24的DIB位圖中,每個像素占三字節,從左到右的每一字節分別存儲藍、綠、紅的顏色值。每行用0填充到一個四字節邊界。

OS/2 DIB和Windows DIB的主要區別是位圖信息結構(信息頭結構和顏色表結構)不同。而它們的圖像位數據的存儲方式是完全一樣的。

我們知道,普通的顯示器屏幕是由許許多多點構成的,我們稱之為象素。顯示時采用掃描的方法:電子槍每次從左到右掃描一行,為每個象素著色,然後從上到下這樣掃描若幹行,就掃過了一屏。為了防止閃爍,每秒要重復上述過程幾十次。例如我們常說的屏幕分辨率為640×480,刷新頻率為70Hz,意思是說每行要掃描640個象素,一共有480行,每秒重復掃描屏幕70次。我們稱這種顯示器為位映象設備。所謂位映象,就是指一個二維的象素矩陣,而位圖就是采用位映象方法顯示和存儲的圖象。舉個例子,圖1.1是一幅普通的黑白位圖,圖1.2是被放大後的圖,圖中每個方格代表了一個象素。我們可以看到:整個骷髏就是由這樣一些黑點和白點組成的。在設計中,我們也是對圖像的像元進行處理的。

而自然界中的所有顏色都可以由紅、綠、藍(R,G,B)組合而成。有的顏色含有紅色成分多一些,如深紅;有的含有紅色成分少一些,如淺紅。針對含有紅色成分的多少,可以分成0到255共256個等級,0級表示不含紅色成分;255級表示含有100%的紅色成分。同樣,綠色和藍色也被分成256級。這種分級概念稱為量化。這樣,根據紅、綠、藍各種不同的組合我們就能表示出256×256×256,約1600萬種顏色。這麽多顏色對於我們人眼來說已經足夠豐富了。

常見顏色的RGB組合值

顏色 R G B

紅 255 0 0

藍 0 255 0

綠 0 0 255

黃 255 255 0

紫 255 0 255

青 0 255 255

白 255 255 255

黑 0 0 0

灰 128 128 128

當一幅圖中每個象素賦予不同的RGB值時,能呈現出五彩繽紛的顏色了,這樣就形成了彩色圖。

圖象數據就是該象素顏在調色板中的索引值。對於真彩色圖,圖象數據就是實際的R、G、B值。對於2色位圖,用1位就可以表示該象素的顏色(一般0表示黑,1表示白),所以一個字節可以表示8個象素。對於16色位圖,用4位可以表示一個象素的顏色,所以一個字節可以表示2個象素。對於256色位圖,一個字節剛好可以表示1個象素。對於真彩色圖,三個字節才能表示1個象素。

要註意兩點:

(1)每一行的字節數必須是4的整倍數,如果不是,則需要補齊。這在前面介紹biSizeImage時已經提到了。

(2)一般來說,.bMP文件的數據從下到上,從左到右的。也就是說,從文件中最先讀到的是圖象最下面一行的左邊第一個象素,然後是左邊第二個象素……接下來是倒數第二行左邊第一個象素,左邊第二個象素……依次類推,最後得到的是最上面一行的最右一個象素。

Windows操作系統統一管理著諸如顯示,打印等操作,將它們看作是一個個的設備,每一個設備都有一個復雜的數據結構來維護。所謂設備上下文就是指這個數據結構。然而,我們不能直接和這些設備上下文打交道,只能通過引用標識它的句柄(實際上是一個整數),讓Windows去做相應的處理。

2.2 圖像的讀取

圖像讀取主要方法是利用OpenFileDialog(文件打開控件)獲得打開圖像的絕對路徑,用System.Drawing裏Bitmap.FromFile方法將所獲得的圖像轉換成DIB並加載到內存中,同時將加載的圖像復制到pictureBox1圖像框中。

private void menuItem2_Click(object sender, System.EventArgs e)

{

OpenFileDialog imageopen = new OpenFileDialog();

imageopen.Title = "請打開圖像文件" ;

imageopen.InitialDirectory = @"c:/" ;

imageopen.Filter = "(圖像文件)*.bmp;jepg;jpg;gif;png|*.bmp;*.jepg;*.jpg;*.gif;*.png" ;

imageopen.FilterIndex = 1 ;

imageopen.RestoreDirectory = true ;

if(imageopen.ShowDialog() == DialogResult.OK)

{

filepath=imageopen.FileName;

this.progressBar1.Value=20;

this.pictureBox1.Image=System.Drawing.Bitmap.FromFile(filepath);

}

this.pictureBox2.Image=null;

}

3 灰度直方圖

3.1灰度直方圖的基本概念

從某種角度上來講,圖像處理是基於統計學概念上的,所以,為了能夠將圖像變成計算機所能夠識別並處理的數據,我們必須對圖像進行量化,使得我們能從數值概念上獲得對圖像的映像。這裏,我們引入灰度圖像的概念:灰度圖像是一種具有從黑到白256級灰度色域或等級的單色圖像。該圖像中的每個像素用8位數據表示,因此像素點值介於黑白間的256種灰度中的一種。該圖像只有灰度等級,而沒有顏色的變化。這樣,我們可以將圖像的RGB屬性歸一為灰度屬性,由此就可以方便我們對圖像進行處理。

3.2 如何獲取灰度直方圖

首先我們應該清楚的是灰度直方圖是一個從0-255範圍變化的步長為一的數組,數組的每一個元素對應的是每一個灰度值。在這裏,我們首先定義了一個int型數組,數組的大小為256,數組名為Histogram。其中,Histogram[i]對應得是灰度為i的像素的個數。

在這裏,我們使用的計算灰度的算法為Gray=(int)(0.3*r+0.59*g+0.11*b)。其中,r,g,b分別為所處理像素的RGB值。算法的源代碼如下:

private void menuItem24_Click(object sender, System.EventArgs e)

{

int height=this.pictureBox1.Image.Height;

int width=this.pictureBox1.Image.Width;

Bitmap process=(Bitmap)this.pictureBox1.Image;

Color pixel;

int [] Histogram=new int[256];

int Times,Gray,r,g,b;

for (i=0;i<=255;i++)

{

Histogram[i]=0;

}

for(i=0;i<width;i++)

{

for(j=0;j<height;j++)

{

pixel=process.GetPixel(i,j);

r=pixel.R;

g=pixel.G;

b=pixel.B;

Gray=(int)(0.3*r+0.59*g+0.11*b);

Histogram[Gray]=Histogram[Gray]+1;

}

}

}

下圖為對某一圖像處理後所獲得的灰度直方圖,從該圖中我們可以獲得一個圖像的灰度分布的直觀映像。

4 圖象的幾何變換

4.1平移

平移(translation)變換大概是幾何變換中最簡單的一種了。如下圖,初始坐標為(x0,y0)的點經過平移(tx,ty)(以向右,向下為正方向)後,坐標變為(x1,y1)。這兩點之間的關系是x1=x0+tx ,y1=y0+ty。

如下圖所示

以矩陣的形式表示為

我們更關心的是它的逆變換:

這是因為:我們想知道的是平移後的圖象中每個象素的顏色。例如我們想知道,新圖中左上角點的RGB值是多少?很顯然,該點是原圖的某點經過平移後得到的,這兩點的顏色肯定是一樣的,所以只要知道了原圖那點的RGB值即可。那麽到底新圖中的左上角點對應原圖中的哪一點呢?將左上角點的坐標(0,0)入公式(2.2),得到x0=-tx ,y0=-ty;所以新圖中的(0,0)點的顏色和原圖中(-tx , -ty)的一樣。這樣就存在一個問題:如果新圖中有一點(x1,y1),按照公式(2.2)得到的(x0,y0)不在原圖中該怎麽辦?通常的做法是,把該點的RGB值統一設成(0,0,0)或者(255,255,255)。

4.2旋轉

旋轉(rotation)有一個繞著什麽轉的問題,通常的做法是以圖象的中心為圓心旋轉。在我們熟悉的坐標系中,將一個點順時針旋轉a角後的坐標變換公式,如下圖所示,r為該點到原點的距離,在旋轉過程中,r保持不變;b為r與x軸之間的夾角。

旋轉前:x0=rcosb;y0=rsinb

旋轉a角度後:

x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina;

y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa;

以矩陣的形式表示:

上面的公式中,坐標系xoy是以圖象的中心為原點,向右為x軸正方向,向上為y軸正方向。它和以圖象左上角點為原點o’,向右為x’軸正方向,向下為y’軸正方向的坐標系x’o’y’之間的轉換關系如何呢

設圖象的寬為w,高為h,容易得到:

逆變換為:

理解了上述理論基礎,其實在C#中我們有現成的方法函數進行操作,我們可以利用Graphics對象所生成的g.RotateTransform方法函數來對圖像進行旋轉操作。圖像旋轉後我們還需要將旋轉所得到的圖像填充到指定的矩形區域中,在這裏我們使用了g.FillRectangle方法函數來進行填充。

private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

{

this.panel2.Refresh();

Graphics g = e.Graphics;

int angel=Convert.ToInt16(this.numericUpDown4.Value);

System.Drawing.Bitmap temp=new Bitmap(filepath);

TextureBrush brush=new TextureBrush(temp);

g.RotateTransform(flaot(angel)); g.FillRectangle(brush,0,0,this.ClientRectangle.Width,this.ClientRectangle.Height);

return;

}

4.3縮放

假設放大因子為ratio,(為了避免新圖過大或過小,我們在程序中限制0.25≤ratio≤4),縮放(zoom)的變換矩陣很簡單:

由於放大圖象時產生了新的象素,以及浮點數的操作,得到的坐標可能並不是整數,這一點我們在介紹旋轉時就提到了。我們采用的做法是找與之最臨近的點。實際上,更精確的做法是采用插值(interpolation),即利用鄰域的象素來估計新的象素值。其實我們前面的做法也是一種插值,稱為最鄰近插值(Nearest Neighbour Interpolation)。下面先介紹線形插值(Linear Interpolation)。

線形插值使用原圖中兩個值來構造所求坐標處的值。舉一個一維的例子。下圖所示,如果已經知道了兩點x0,x2處的函數值f(x0),f(x2),現在要求x1處的函數值f(x1)。我們假設函數是線形的,利用幾何知識可以知道

f(x1)=(f(x2)-f(x0))(x1-x0)/(x2-x0)+f(x0)

在圖象處理中需要將線形插值擴展到二維的情況,即采用雙線形插值(Bilinear Intrepolation),

線形插值的示意圖

雙線形插值的示意圖

已知a、b、c、d四點的灰度,要求e點的灰度,可以先在水平方向上由a,b線形插值求出g、c、d線形插值求出f,然後在垂直方向上由g,f線形插值求出e。

線形插值基於這樣的假設:原圖的灰度在兩個象素之間是線形變化的。一般情況下,這種插值的效果還不錯。更精確的方法是采用曲線插值(Curvilinear Interpolation),即認為象素之間的灰度變化規律符合某種曲線,但這種處理的計算量是很大的。

同樣的,我們可以利用Graphics對象所生成的g.FillRectangle方法函數來對圖像進行縮放操作。圖像縮放後我們還需要將縮放所得到的圖像填充到指定的矩形區域中,同樣在這裏我們使用了g.FillRectangle方法函數來進行填充。

private void panel2_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

{

this.panel2.Refresh();

Graphics g = e.Graphics;

float fx=(float)(this.numericUpDown1.Value/10);

float fy=(float)(this.numericUpDown2.Value/10);

System.Drawing.Bitmap temp=new Bitmap(filepath);

TextureBrush brush=new TextureBrush(temp);

g.ScaleTransform(fx,fy);

g.FillRectangle(brush,0,0,this.ClientRectangle.Width,this.ClientRectangle.Height);

checkscale=0;

}

5 簡單圖像處理

5.1 黑白處理:

彩色圖像黑白化處理通常有三種方法:最大值法、平均值法、加權平均值法
三種方法的原理
最大值法:最大值法是每個像素點的RGB值等於原像素點的RGB值中最大的一個,即R=G=B=MAX( R,G,B ); 效果,最大值發產生亮度很高的黑白圖像。
平均值法:平均值法使每個像素點的RGB值等於原像素點的RGB值的平均值,即R=G=B=(R+G+B)/3
加權平均法:加權平均法根據需要指定每個像素點RGB的權數,並取其加權平均值,即R=G=B=(Wr*R+Wg*G+Wb*B )/3 。Wr、Wg、Wb表示RGB的權數,均大於零,通過取不同的權數可實現不同的效果。

本程序中采用的是平均值法來處理圖像:

private void menuItem20_Click(object sender, System.EventArgs e)

{

if(this.pictureBox1.Image!=null)

{

this.pictureBox2.Visible=true;

int height=this.pictureBox1.Image.Height;

int width=this.pictureBox1.Image.Width;

Bitmap temp=new Bitmap(width,height);

Bitmap process=(Bitmap)this.pictureBox1.Image;

Color pixel;

for( int x=0;x<width;x++)

{

for(int y=0;y<height;y++)

{

int r,g,b,t;

pixel=process.GetPixel(x,y);

t=(pixel.R+pixel.G+pixel.B)/3;

r=t;

g=t;

b=t;

temp.SetPixel(x,y,Color.FromArgb(r,g,b));

}

}

this.pictureBox2.Image=temp;

}

}

5.2 浮雕處理

浮雕效果就是只將圖像的變化部分突出出來,而相同顏色部分則被淡化,使圖像出現縱深感,從而達到浮雕效果,這裏采用的算法是將要處理的像素取值為與處於對角線上的另一個像素間的差值,這樣只有顏色變化區才會出現色彩,而顏色平淡區因差值幾乎為零則變成黑色。

private void menuItem21_Click(object sender, System.EventArgs e)

{

if(this.pictureBox1.Image!=null)

{

this.pictureBox2.Visible=true;

int height=this.pictureBox1.Image.Height;

int width=this.pictureBox1.Image.Width;

Bitmap temp=new Bitmap(width,height);

Bitmap process=(Bitmap)this.pictureBox1.Image;

Color pixel,pixelnext;

for( int x=0;x<width-1;x++)

{

this.progressBar1.Value=(int)(((float)x/(float)width)*100);

for(int y=0;y<height-1;y++)

{

int r,g,b;

pixel=process.GetPixel(x,y);

pixelnext=process.GetPixel(x+1,y+1);

r=pixel.R-pixelnext.R+128;

b=pixel.B-pixelnext.B+128;

g=pixel.G-pixelnext.G+128;

r=Judge(r);

g=Judge(g);

b=Judge(b);

temp.SetPixel(x,y,Color.FromArgb(r,g,b));

}

}

this.pictureBox2.Image=temp;

}

5.2 反色處理

反色的實際含義是將R、G、B值反轉。若顏色的量化級別是256,則新圖的R、G、B值為255減去原圖的R、G、B值。這裏針對的是所有圖,包括真彩圖、帶調色板的彩色圖(又稱為偽彩色圖)、和灰度圖。

private void menuItem22_Click(object sender, System.EventArgs e)

{

if(this.pictureBox1.Image!=null)

{

this.pictureBox2.Visible=true;

int height=this.pictureBox1.Image.Height;

int width=this.pictureBox1.Image.Width;

Bitmap temp=new Bitmap(width,height);

Bitmap process=(Bitmap)this.pictureBox1.Image;

Color pixel;

for( int x=0;x<width;x++)

{

this.progressBar1.Value=(int)(((float)x/(float)width)*100);

for(int y=0;y<height;y++)

{

int r,g,b;

pixel=process.GetPixel(x,y);

r=255-pixel.R;

g=255-pixel.G;

b=255-pixel.B;

temp.SetPixel(x,y,Color.FromArgb(r,g,b));

}

}

this.pictureBox2.Image=temp;

}

}

6圖象的輪廓提取

圖像的邊緣(輪廓)是圖像最基本的特征。所謂邊緣(或邊沿)是指其周圍象素灰度有階躍 變化或“屋頂”變化的那些象素的集合。邊緣廣泛存在於物體與背景之間、物體與物體之間、基元與基元之間。因此,它是圖像分割依賴的重要特征。

物體的邊緣是由灰度不連續性形成的。經典的邊緣提取方法是考察圖像的每個象素在某個鄰域內灰度的變化,利用邊緣鄰近一階或二階方向導數變化規律,用簡單的方法檢測邊緣。這種方法稱為邊緣檢測局部算子法。如果一個象素落在圖像中某一個物體的邊界上,那麽他 的鄰域將成為一個灰度級的變化帶。對這種變化最有用的2個特征:灰度的變化率和方向, 他們分別以梯度向量的幅度和方向來表示。邊緣檢測算子檢查每個象素的鄰域並對灰度變化率進行量化,也包括方向的確定。常用的檢測算子有Roerts算子、Sobel算子、Prewitt 算子和 Kirsh 算子等。

我們給出一個模板 和一幅圖象 。不難發現原圖中左邊暗,右邊亮,中間存在著一條明顯的邊界。進行模板操作後的結果如下: 。

可以看出,第3、4列比其他列的灰度值高很多,人眼觀察時,就能發現一條很明顯的亮邊,其它區域都很暗,這樣就起到了邊沿檢測的作用。為什麽會這樣呢?仔細看看那個模板就明白了,它的意思是將右鄰點的灰度值減左鄰點的灰度值作為該點的灰度值。在灰度相近的區域內,這麽做的結果使得該點的灰度值接近於0;而在邊界附近,灰度值有明顯的跳變,這麽做的結果使得該點的灰度值很大,這樣就出現了上面的結果。這種模板就是一種邊沿檢測器,它在數學上的涵義是一種基於梯度的濾波器,又稱邊沿算子,你沒有必要知道梯度的確切涵義,只要有這個概念就可以了。梯度是有方向的,和邊沿的方向總是正交(垂直)的,例如,對於上面那幅圖象的轉置圖象,邊是水平方向的,我們可以用梯度是垂直方向的模板檢測它的邊沿。

例如,一個梯度為45度方向模板 ,可以檢測出135度方向的邊沿。

6 .1Sobel算子輪廓提取:

在邊沿檢測中,常用的一種模板是Sobel 算子。Sobel 算子有兩個,一個是檢測水平邊沿的 ;另一個是檢測垂直平邊沿的。與 和 相比,Sobel算子對於象素的位置的影響做了加權,因此效果更好。

Sobel算子另一種形式是各向同性Sobel(Isotropic Sobel)算子,也有兩個,一個是檢測水平邊沿的 ,另一個是檢測垂直平邊沿的 。各向同性Sobel算子和普通Sobel算子相比,它的位置加權系數更為準確,在檢測不同方向的邊沿時梯度的幅度一致。由於建築物圖像的特殊性,我們可以發現,處理該類型圖像輪廓時,並不需要對梯度方向進行運算,所以程序並沒有給出各向同性Sobel算子的處理方法。下面的兩幅圖中,下左圖為原圖;下右圖為Sobel算子處理後的結果圖。可以看出Sobel算子確實把圖象中的邊沿提取了出來。

原圖 Sobel算子處理後的結果圖

由於Sobel算子是濾波算子的形式,用於提取邊緣,可以利用快速卷積函數, 簡單有效,因此應用廣泛。美中不足的是,Sobel算子並沒有將圖像的主體與背景嚴格地區分開來,換言之就是Sobel算子沒有基於圖像灰度進行處理,由於Sobel算子沒有嚴格地模擬人的視覺生理特征,所以提取的圖像輪廓有時並不能令人滿意。 在觀測一幅圖像的時候,我們往往首先註意的是圖像與背景不同的部分,正是這個部分將主體突出顯示,基於該理論,我們給出了下面閾值化輪廓提取算法,該算法已在數學上證明當像素點滿足正態分布時所求解是最優的。

6 .2閾值化提取:

閾值化(thresholding)可以看作是削波的一個特例,我們用下圖說明閾值化的原理。

閾值化的原理

不難看出,只要令削波中的g1old=g2old就實現了閾值化。閾值就象個門檻,比它大就是白,比它小就是黑。經過閾值化處理後的圖象變成了黑白二值圖,所以說閾值化是灰度圖轉二值圖的一種常用方法(我們以前介紹過圖案化和抖動的方法)。進行閾值化只需給出閾值點g1old即可。一般情況下,閾值的選取對程序執行結果有很大的影響,在本程序中采取的是直方圖求門限,叠代法求最佳閾值的方法,這個方法在數學上已被證明當圖像像素概率分布逼近正態分布時是最佳的。閾值化處理後的結果,是一幅二值圖像

閾值化處理後的結果

在這裏,閾值的選取是很重要的,閾值如果過高,就會產生將圖像主體誤認為是背景的情況;閾值如果過低,則會將背景部分誤認為是主體部分。因此,我們應該仔細選擇閾值,我認為閾值不應該是一個固定的值,它的值應該隨著圖像的不同而不同。在本程序中,采取了動態求取閾值的方法。首先我們先求得被處理圖像的灰度直方圖,根據直方圖求得最大灰度與最小灰度。我們設置閾值的初值為最大灰度與最小灰度值之和的一半。然後求得小於閾值部分灰度的平均值iMean1Gray和大於閾值部分的平均值iMean2Gray,將閾值重新設為這兩個灰度均值 的一半,如此叠代,最後求得最佳閾值iThreshold。實驗結果證明該閾值是可信的,基本上可以把主體與背景區分開來。求閾值的代碼如下:

for(Times=0;Times<128&&iThreshold!=iNewThreshold;Times++)

{

iThreshold=iNewThreshold;

lP1=0;

lP2=0;

lS1=0;

lS2=0;

for(i=iMinGray;i<iThreshold;i++)

{

lP1+=Histogram[i]*i;

lS1+=Histogram[i];

}

iMean1Gray=lP1/lS1;

for(i=iThreshold;i<iMaxGray;i++)

{

lP2+=Histogram[i]*i;

lS2+=Histogram[i];

}

iMean2Gray=lP2/lS2;

iNewThreshold=(iMean1Gray+iMean2Gray)/2;

}

6 .3 Robert算子輪廓提取

Roberts邊緣檢測算子是一種利用局部差分算子尋找邊緣的算子

Robert算子輪廓提取結果

可以看出,圖像處理後結果邊緣不是很平滑。經分析,由於Robert算子通常會在圖像邊緣附近的區域內 產生較寬的響應,故采用上述算子檢測的邊緣圖像常需做細化處理,邊緣定位的精度不是很高。

private void menuItem23_Click(object sender, System.EventArgs e)

{

if(this.pictureBox1.Image!=null)

{

this.pictureBox2.Visible=true;

int height=this.pictureBox1.Image.Height;

int width=this.pictureBox1.Image.Width;

Bitmap temp=new Bitmap(width,height);

Bitmap process=(Bitmap)this.pictureBox1.Image;

int i,j,p0,p1,p2,p3;

Color [] pixel=new Color[4];

int result;

for(j=height-2;j>0;j--)

{

for(i=0;i<width-2;i++)

{

pixel[0]=process.GetPixel(i,j);

pixel[1]=process.GetPixel(i,j+1);

pixel[2]=process.GetPixel(i+1,j);

pixel[3]=process.GetPixel(i+1,j+1);

p0=(int)(0.3*pixel[0].R+0.59*pixel[0].G+0.11*pixel[0].B);

p1=(int)(0.3*pixel[1].R+0.59*pixel[1].G+0.11*pixel[1].B);

p2=(int)(0.3*pixel[2].R+0.59*pixel[2].G+0.11*pixel[2].B);

p3=(int)(0.3*pixel[3].R+0.59*pixel[3].G+0.11*pixel[3].B);

result=(int)Math.Sqrt((p0-p3)*(p0-p3)+(p1-p2)*(p1-p2));

if (result>255)

result=255;

if (result<0)

result=0;

temp.SetPixel(i,j,Color.FromArgb(result,result,result));

}

}

this.pictureBox2.Image=temp;

}

}

6程序運行

6 .1 程序運行主界面

6 .2 相關功能介紹

在文件菜單下,可以實現圖像的打開,保存,程序的退出等功能。

編輯菜單下可以將操作結果欄的圖像復制到緩存中

視圖菜單下可實現程序的所有圖像處理功能,並可以將被處理圖像轉換為源圖像。

窗口欄可將操作結果的圖像關閉。

程序處理圖像的結果如下圖所示,左邊為被處理圖像,右邊為處理結果圖像。

結 論

數字圖像處理主要研究的內容有以下幾個方面: (1) 圖像變換 由於圖像陣列很大,直接在空間域中進行處理,涉及計算量很大。因此,往往采用各種圖像變換的方法,如傅立葉變換、沃爾什變換、離散余弦變換等間接處理技術,將空間域的處理轉換為變換域處理,不僅可減少計算量,而且可獲得更有效的處理(如傅立葉變換可在頻域中進行數字濾波處理)。(2) 圖像編碼壓縮 圖像編碼壓縮技術可減少描述圖像的數據量(即比特數),以便節省圖像傳輸、處理時間和減少所占用的存儲器容量。壓縮可以在不失真的前提下獲得,也可以在允許的失真條件下進行。編碼是壓縮技術中最重要的方法,它在圖像處理技術中是發展最早且比較成熟的技術。(3) 圖像增強和復原圖像增強和復原的目的是為了提高圖像的質量,如去除噪聲,提高圖像的清晰度等。圖像增強不考慮圖像降質的原因,突出圖像中所感興趣的部分。如強化圖像高頻分量,可使圖像中物體輪廓清晰,細節明顯;如強化低頻分量可減少圖像中噪聲影響。圖像復原要求對圖像降質的原因有一定的了解,一般講應根據降質過程建立"降質模型",再采用某種濾波方法,恢復或重建原來的圖像。(4) 圖像分割 圖像分割是數字圖像處理中的關鍵技術之一。圖像分割是將圖像中有意義的特征部分提取出來,其有意義的特征有圖像中的邊緣、區域等,這是進一步進行圖像識別、分析和理解的基礎。雖然目前已研究出不少邊緣提取、區域分割的方法,但還沒有一種普遍適用於各種圖像的有效方法。因此,對圖像分割的研究還在不斷深入之中,是目前圖像處理中研究的熱點之一,這個同時也是本設計的重中之重。(5) 圖像描述 圖像描述是圖像識別和理解的必要前提。作為最簡單的二值圖像可采用其幾何特性描述物體的特性,一般圖像的描述方法采用二維形狀描述,它有邊界描述和區域描述兩類方法。對於特殊的紋理圖像可采用二維紋理特征描述.(6) 圖像分類(識別)屬於模式識別的範疇,其主要內容是圖像經過某些預處理(增強、復原、壓縮)後,進行圖像分割和特征提取,從而進行判決分類。圖像分類常采用經典的模式識別方法,有統計模式分類和句法(結構)模式分類,近年來新發展起來的模糊模式識別和人工神經網絡模式分類在圖像識別中也越來越受到重視

圖像處理中幾個基本的處理方法c#代碼實現