王偉家
(蘭州大學信息科學與工程學院,蘭州730000)
邊緣是一幅圖像中最重要的特征之一,它會攜帶著一幅圖片中的絕大多數(shù)信息,例如,它可以反映圖像的面積、圖像的形狀、圖像的周長、圖像之間的包含關系等最基本的特征。那么邊緣又是如何定義的?所謂邊緣是指其周圍像素灰度值有明顯變化的像素點的集合,而邊緣檢測算法的目的便是準確的找到這些像素點。當前存在的大部分邊緣檢測算法即是根據像素點在邊緣區(qū)域的跳躍性變化來檢測,反映在數(shù)學上即是根據邊緣鄰近的一階或二階方向導數(shù)的變換規(guī)律,來判斷圖像邊緣點。
基于一階和二階導數(shù)來進行圖像邊緣檢測的算法也已經存在多種,例如:結合了高斯平滑和微分求導,并采用了卷積核來檢測圖像邊緣的Sobel 算子;使用圖像梯度以及對圖像進行二階求導的Laplace 算子;利用高斯平滑濾波器去除噪音,并采用一階求導的有限差分來檢測圖像邊緣的Canny 算子等。上述的各種算子已經存在相應的集成函數(shù),但是,上述的邊緣檢測技術,對圖像中各區(qū)域像素灰度對比度大的邊緣提取效果較好,也就是所謂的邊緣明顯的圖像,而對對比度較小的邊緣,其檢測效果則不理想,有的模糊圖像的邊緣甚至根本無法檢測出來。
針對于上述模糊圖像的邊緣檢測效果差的情況,設計了一種利用邊緣像素點與周圍像素點的差值與閾值之間的關系來檢測邊緣的算法。這種算法主要經過四個核心的步驟:
圖1
由于一幅圖像中可能存在著多個圖形,或者可能存在多個圖形嵌套的情況,那么這便會使圖像中存在多個圖像的邊緣,而對于要檢測的某一個圖形來說,其
他多余的圖形的邊緣信息便會對要檢測的圖形的邊緣產生干擾。因此,第一部分所要做的工作便是將圖片中所要檢測的圖形的外接矩形區(qū)域切割出來,可以利用圖像查看器將要檢測圖像的左上角以及圖像的右下角信息記錄下來,利用這兩個信息確定出包含要檢測圖像的外接矩形。
由于圖像分為彩色圖像和灰度圖像,所以在相應的外接矩形區(qū)域劃分出來之后,利用OpenCV 中的cvt-Color 方法將相應的彩色圖像轉換為灰度圖像,針對轉變?yōu)榈幕叶葓D像,設置適當?shù)拈撝禐門,對灰度圖像的每一行選取適當?shù)南袼攸c為邊緣像素點。
下面主要為實現(xiàn)的思想:
假設處理的圖片為img,利用Python 中的len(img[0])求出像素點的列數(shù),利用len(img)求出像素點的行數(shù)。由于要求模糊圖像的邊緣,而邊緣分為封閉圖形的邊緣和未封閉圖形的邊緣,封閉圖形的邊緣例如圓形、正方形等,未封閉圖形的邊緣例如半圓、半正方形等??梢岳蒙鲜鰣D形邊緣的性質,在檢測模糊圖像邊緣時,對于每一行分別從左向右、從右向左進行檢測。從左向右檢測時,當檢測到第一組符合邊緣性質的像素點時,將符合要求的像素點的位置記錄下來,并停止從左向右檢測此行的邊緣。從右向左檢測時,當檢測到第一組符合邊緣性質的像素點時,將符合要求的像素點的位置記錄下來,并停止從右向左檢測此行的邊緣。上述步驟完成后,對于封閉圖形來說,每一行像素點中都已經暫時確定了左邊緣和右邊緣,對于未封閉圖形來說,每一行像素點都會集中到一組邊緣像素點。
圖2 封閉模糊圖形檢測邊緣示意圖
圖3 未封閉模糊圖形檢測邊緣示意圖
具體的實現(xiàn)細節(jié)如下:
設置當前行的下標為i,從左向右檢測邊緣時,在每一行像素點中從第五個像素點開始,依次進行判斷,設為列像素點j。當在當前行i 下,某一個j 開始的像素點與j-5 的像素點的差值超過設置的閾值時,判斷j接下來的連續(xù)k 個像素點是否與j+k-5 的像素點的差值超過閾值。如果從j 開始的k 個像素點與前面的像素點都超過了設定的閾值,就將其中的img[i][j]像素點暫定為邊緣像素點,即滿足下列的公式:
上述公式中j 的范圍為[5,col],i 的范圍為[1,row](col 為圖像像素點的列數(shù),row 為圖像像素點的行數(shù)),并且上述公式實現(xiàn)從左向右檢測左邊緣,下述公式實現(xiàn)的是從右向左檢測右邊緣:
上述公式中j 是從col 開始,到5 結束,i 的范圍為[1,row]。
上述閾值選取和邊緣檢測步驟完成之后,模糊圖像的大體邊緣已經可以呈現(xiàn)出來,但是,還是存在一個問題:圖中檢測的邊緣中會存在著部分的噪音點,這些噪音點組成的像素點的集合也會被上述的步驟誤判為邊緣,那么,這就會導致這一塊的邊緣檢測出現(xiàn)錯誤,這一部分所要做的就是去除圖片中的噪音點。
實現(xiàn)的思想如下:
當1.2 步驟完成之后,對于每一行像素點來說,依靠步驟2 已經找到了疑似邊緣的像素點,并且這個像素點的橫縱坐標已經記錄下來了。設定一個閾值T,假定此時的行下標為i,找到的像素點的縱坐標為pixel[i],這時,求出[i,pixel[i]]這個像素點與[i-1,pixel[i-1]]、[i-2,pixel[i-2]]、[i+1,pixel[i+1]]、[i+2,pixel[i+2]]像素點的距離總和,當距離總和的值超過設定的閾值T 時,判定此像素點為噪音點,當求得的像素點的距離總和小于設定的閾值T 時,判定此像素點為邊緣點。
假設檢測出來的噪音點的坐標為(i,pixel[i]),噪音點的坐標(i,pixel[i])與(i-1,pixel[i-1])、(i+1,pixel[i+1])的距離超過了設定的閾值,那么此時我們可以使用(i-1,pixel[i-1])以及(i+1,pixel[i+1])來對噪音點進行糾正,取pixel[i-1]和pixel[i+1]的中值來替代pixel[i],就可以有效地將噪音點去除,使得檢測模糊圖片的邊緣達到較好的效果。
經過上述的三個步驟之后,已經可以十分準確地確定出模糊圖片的邊緣,此時,圖片邊緣點的坐標已經記錄了下來,接下來要做的工作便是將這些邊緣在圖片中表示出來,這里我們采用的是像素點值替換的方法:確定圖片的邊緣點之后,重新遍歷圖片的邊緣點,將圖片邊緣點的像素值全部替換為255,那么此時,再次顯示圖片的時候便可以直觀地觀察到模糊圖像的邊緣。
(1)利用OpenCV 中的imread()函數(shù)讀入需要檢測的模糊圖像,并使用cvtColor 函數(shù)將圖像統(tǒng)一轉變?yōu)榛叶葓D像,之后利用Python 中的len()函數(shù)求出圖像的橫縱像素的總數(shù),分別記為col 和row,用于表示行數(shù)和列數(shù)。
(2)利用open()函數(shù)來讀取存放檢測圖像的外接矩形的左上角和右下角的坐標,使用數(shù)組存放起來。
(3)依靠步驟2 所存放外接矩形的左上角和右下角的坐標,指明了一張圖片在哪一個區(qū)域開始進行檢測,此時,循環(huán)的上界和下界便已經確定下來。對圖像檢測區(qū)域的每一行分別從左到右、從右到左檢測邊緣,并提前設置好適當?shù)拈撝?。先?zhí)行從左向右檢測,當此行下存在一組像素點符合行檢測邊緣的要求時,記錄下此像素點的行坐標和列坐標,跳出循環(huán),如果此行下所有的像素點都無法滿足邊緣點的要求,將此行對應的數(shù)組值設置為-1。從左向右檢測完畢之后,此時圖像的左邊部分的邊緣點已經找出并存儲,此時,執(zhí)行從右向左的邊緣檢測,同樣,當檢測到符合要求的像素點時,記錄下此像素點的坐標,如果沒有找到符合要求的像素點,將相應的數(shù)組值設置為-1。
(4)步驟3 求出每一行的邊緣點,此時求出的邊緣點中還存在著噪音點,之后利用每一行的邊緣點與此行的前兩行與后兩行之間的距離與設置的閾值之間的大小來判斷是否是噪音點。如果檢測的是噪音點,利用此行的前一行和后一行的中值來糾正噪音點,并存放其糾正后的像素點的坐標,最后達到去除噪音點的目的。
(5)此時精確的邊緣點已經全部存在相應的數(shù)組中,最后利用一個循環(huán),將每一行的邊緣像素點的值替換為255,利用OpenCV 中的imwrite()函數(shù)將處理好的圖片保存在特定的文件夾下,至此,模糊圖像的邊緣檢測已經完成。
由于要對模糊圖像中的每一個像素進行分析與判斷,所以需要遍歷圖像中的每一行和每一列,所以該算法的時間復雜度應該為O(col×row),col 表示的是檢測圖像的列數(shù),row 表示的是檢測圖像的行數(shù)。
空間復雜度:由于該算法是對圖像的每一行分別從左到右、從右向左尋找邊緣點,所以每一行需要2 個位置來存儲像素點,而圖像中一共有row 行,則算法的空間復雜度為O(2×row)。
實驗過程均運行在VS code 的Python 環(huán)境下,需要依賴Python 下的OpenCV 函數(shù)庫以及numpy 函數(shù)庫,圖像數(shù)據為1 組模糊圖像,在附件中保存。
下述的實驗過程將會通過本篇文章中介紹的算法與傳統(tǒng)的邊緣檢測算法Sobel、Canny、Laplacian 進行比較來分析模糊圖像邊緣檢測的優(yōu)劣。實驗中的圖片都是事先經過了OpenCV 的灰度處理,并且經過了區(qū)域劃分的操作。
圖中的a 表示的是二氧化硅在589 攝氏度下的灰度圖,圖中的邊緣表現(xiàn)的非常模糊。
C.1 圖片和C.2 的圖片都是利用Sobel 算子檢驗邊緣的效果圖,C.1 和C.2 都是使用了大小為5 的卷積核進行的操作,不過C.1 是分別從x 和y 方向分別進行了求導,最后通過addWeighted()方法將兩個方向的結果合并到了一起,C.2 是直接運行Sobel 函數(shù)對x 和y 方向一起進行了求導,圖中展示的即為效果圖。從C.1的效果圖可以看出,對x 和y 方向分別進行求導,最終通過addWeighted 函數(shù)合并在一起的邊緣檢測效果較好,但是,有一些模糊的邊緣以及細節(jié)處,它未能有效的提取出邊緣,其次,圖中除去邊緣之外,其他部分摻雜的噪音過多,這將會對邊緣周長的計算或者圖形面積的計算產生較大的干擾,C.2 的圖片中對模糊圖像的邊緣檢測效果較差,甚至無法辨認出模糊圖像的邊緣。
D 圖片是利用了Canny 算子檢驗邊緣的效果圖,Canny 是通過使用兩種不同的閾值分別用來檢測圖片中的強邊緣和弱邊緣,它的實現(xiàn)方法相對簡單,Canny算子是利用了高斯模糊去除噪音,但這樣會存在一個缺點,在去除噪音的過程中也同時會平滑邊緣,這樣做的結果是使邊緣信息減弱,可能會在后面的步驟中漏掉一些需要的邊緣,特別是一些孤立的邊緣和弱邊緣,可能在雙閾值和聯(lián)通計算中被剔除??梢灶A想到,如果加大高斯模糊的半徑,就會對噪音的平滑力度進一步增強,也會使得后面得到的圖像邊緣圖中的邊緣明顯減少。雖然噪音會有效地降低,但是,有效的邊緣信息也消失了很多。圖D 就是很好的證明,雖然D 圖中的噪音大部分被平滑掉了,但是圖中的邊緣也同樣被平滑掉了太多,導致檢測的邊緣十分不完整,只有一部分很明顯的邊緣被檢測了出來。
E 圖片是采用Laplacian 算子對模糊圖片邊緣檢測的效果圖,Laplacian 算法對圖片中存在的噪音比較敏感,因此如果圖片中存在大量的噪音,就導致模糊圖片的邊緣檢測效果過差,從效果圖可以看出,已經達到無法辨認邊緣的層次,但實際中Laplacian 算子很少用來檢測邊緣,它大多數(shù)是用來判定邊緣像素為圖像的亮區(qū)還是暗區(qū)。
圖4
經過C 圖、D 圖、E 圖的比較,可以看到Sobel 邊緣檢測算子計算速度快,邊緣連續(xù)性較好,但細節(jié)完整效果粗略,一些模糊的邊緣以及一些細節(jié)之處,它未能有效地提取出邊緣。而Canny 算子利用兩種不同的閾值來見測強邊緣和弱邊緣,實現(xiàn)簡單,但噪音平滑的過程中會使部分的邊緣點減少,從而使檢測邊緣的無法較大。Laplacian 算子對噪音的敏感度太高,導致圖片中的絕大多數(shù)噪音無法去除,對邊緣的檢測效果過差。從H 圖可以看出,本文提出的邊緣檢測算法在選擇合適的閾值的前提下的得到的圖像邊緣在邊緣的定位精度。完整程度、連續(xù)性以及噪音去除方面都比Sobel 算子的效果強很多,雖然時間復雜度高一點,但是這種方法的抗噪能力比較強,當對模糊圖片進行邊緣檢測時,此種方法的效果比較好,并且,由于噪音去除效果比較好,在計算模糊圖形的面積和周長等方便也有很大的便利之處,但是,此種方法也有一些缺點,一方面實現(xiàn)起來需要寫的代碼比較繁雜,沒有集成的函數(shù),另一方面,如果圖像的上下邊緣較為平滑,那么檢測上下邊緣時的效果就比較差,從圖中的H 中就可以觀察到這一點,在圖片的上邊緣和下邊緣的邊緣檢測相對于中間的邊緣檢測來說比較差。
本文通過先對模糊圖像的每一行像素點進行邊緣檢測達到檢測行邊緣的效果,但是,上述產生的邊緣像素點存在大量的噪音,此時,再依據邊緣的連續(xù)性,利用列像素點的關系達到去除噪音的目的。上述陳述的算法都是運用基本的數(shù)學運行來實現(xiàn)的,有思想簡單的特點,其次,該算法基于的是灰度圖像的檢驗,因為灰度圖像與彩色圖像的閾值選取差別較大,如果要對模糊的彩色圖像進行邊緣檢測,可以先將其轉變?yōu)榛叶葓D像,之后,再對轉變成的灰度圖像進行邊緣檢測。