形態學,即數學形態學(mathematical Morphology),是圖像處理中應用最爲普遍的技術之一,主要用於從圖像中提取對錶達和描繪區域形狀有意義的圖像份量,使後續的識別工做可以抓住目標對象最爲本質〈最具區分能力-most discriminative)的形狀特徵,如邊界和連通區域等。同時像細化、像素化和修剪毛刺等技術也常應用於圖像的預處理和後處理中,成爲圖像加強技術的有力補充。算法
本文主要包括如下內容 markdown
預備知識
在數字圖像處理中, 形態學是藉助集合論的語言來描述的, 本章後面的各節內容均以本集合論爲基礎。
函數
結構元素(structure element)
設有兩幅圖像A, S。若A是被處理的對象, 而S是用來處理A的, 則稱S爲結構元素。結構元素一般都是一些比較小的圖像, A與S的關係相似於濾波中圖像和模板的關係. ui
本節介紹幾種二值圖像的基本形態學運算, 包括腐蝕、膨脹, 以及開、閉運算。因爲全部形態學運算都是針對圖像中的前景物體進行的, 於是首先對圖像前景和背景的認定給出必要的說明. spa
注意: 大多數圖像,通常相對於背景而言物體的顏色(灰度)更深, 二值化以後物體會成爲黑色, 而背景則成爲白色, 所以咱們一般是習慣於將物體用黑色(灰度值0)表示, 而背景用白色(灰度值255)表示,本章全部的算法示意圖以及全部的Visual C++的程序實例都聽從這種約定;但Matlab 在二位圖像形態學處理中,默認狀況下白色的(二位圖像中灰度值爲1的像素,或灰度圖像中灰度值爲255的像素)
是前景(物體),黑色的爲背景, 於是本章涉及Matlab 的全部程序實例又都聽從Matlab自己的這種前景認定習慣. 3d
實際上, 不管以什麼灰度值爲前景和背景都只是一種處理上的習慣, 與形態學算法自己無關。例如對於上面兩幅圖片, 只須要在形態學處理以前先對圖像反色就能夠在兩種認定習慣之間自由切換。 code
腐蝕和膨脹是兩種最基本也是最重要的形態學運算, 它們是後續要介紹的不少高級形態學處理的基礎, 不少其餘的形態學算法都是由這兩種基本運算複合而成
對象
matlab實現
Matlab中與腐蝕相關的兩個經常使用函數爲imerode和strel。
imerode函數用於完成圖像腐蝕.其經常使用調用形式以下:I2 = imrode(I,SE)
I爲原始圖像,能夠是二位或灰度圖像(對應於灰度腐蝕).
SE是由strel函數返回的自定義或預設的結構元素對象.blog
strel函數能夠爲各類常見形態學運算生成結構元素SE, 當生成供二值形態學使用的結構元素肘, 其調用形式爲:SE=strl(shape,parameter)
shape指定告終構元素的形狀, 其經常使用合法取值如在8.1所示.
索引
腐蝕的做用「 顧名思義,腐蝕可以消融物體的邊界,而具體的腐蝕結果與圖像自己和結構元素的形狀有關。若是物體總體上大於結構元素,腐蝕的結構是使物體變「 瘦」一圈,而
這一圈到底有多大是由結構元素決定的:若是物體自己小於結構元素, 則在腐蝕後的圖像中物體將徹底消失:如物體僅有部分區域小於結構元素〈如細小的連通3,則腐蝕後物體會在細
連通處斷裂,分離爲兩部分。
I = imread('erode_dilate.bmp');
se = strel('square',3);
Ib = imerode(I,se);
se = strel([0 1 0;1 1 1;0 1 0]);
Ic = imerode(I,se);
se = strel('square',5);
Id = imerode(I,se);
figure;
subplot(2,2,1);
imshow(I);
subplot(2,2,2);
imshow(Ib);
subplot(2,2,3);
imshow(Ic);
subplot(2,2,4);
imshow(Id);
隨着腐蝕結構元素的逐步增大,小於結構元素的物體相繼消失。因爲腐蝕運算具備上述的特色,能夠用於濾波。選擇適當大小和形狀的結構元素,能夠濾除掉全部不能 徹底包含結構元素的噪聲點。然而,利用腐蝕濾除噪聲有一個缺點,即在去除噪聲點的同時,對圖像中前景物體的形狀也會有影響,但當咱們只關心物體的位置或者個數時,則影響不大
實際上, 膨脹和腐蝕對子集合求補和反射運算是彼此對偶的.
這裏值得注意的是定義中要求和A有公共交集的不是結構元素S自己, 而是S的反射集, 以爲熟悉嗎?這在形式上彷佛容易讓咱們回憶起卷積運算, 而腐蝕在形式上則更像相關運算。因爲圖8.8 中使用的是對稱的結構元素, 故使用S 和S^' 的膨脹結果相同:但對於圖8.9中非對稱結構元素的膨脹示例, 則會產生徹底不一樣的結果, 所以在實現膨脹運算時必定要先計算S^'
matlab實現
imdilate函數用於完成圖像膨脹, 其經常使用調用形式以下:
I2 = imdilate(I,SE);
I爲原始圖像, 能夠是二位或灰度圖像(對應於灰度膨脹).
SE是由strel函數返回的自定義或預設的結構元素對象
膨脹的做用和腐蝕相反, 膨脹能使物體邊界擴大, 具體的膨脹結果與圖像自己和結構元素的形狀有關。膨脹經常使用於將圖像中本來斷裂開來的同一物體橋接起來, 對圖像進行二值化以後, 很容易使一個連通的物體斷裂爲兩個部分, 而這會給後續的圖像分析(如要基於連通區域的分析統計物體的個數〉形成困擾,此時就可藉助膨脹橋接斷裂的縫隙
I = imread('starcraft.bmp');
Ie1 = imerode(I,[1 1 1;1 1 1;1 1 1]);
Ie2 = imerode(Ie1,[0 1 0;1 1 1;0 1 0]);
Id1 = imdilate(Ie2,[1 1 1;1 1 1;1 1 1]);
Id2 = imdilate(Id1,[0 1 0;1 1 1;0 1 0]);
figure;
subplot(2,2,1);
imshow(Ie1);
subplot(2,2,2);
imshow(Ie2);
subplot(2,2,3);
imshow(Id1);
subplot(2,2,4);
imshow(Id2);
開運算和閉運算都由腐蝕和膨脹複合而成, 開運算是先腐蝕後膨脹, 而閉運算是先膨脹後腐蝕。
通常來講, 開運算可使圖像的輪廓變得光滑, 還能使狹窄的鏈接斷開和消除細毛刺。
如圖8.11所示, 開運算斷開了團中兩個小區域間兩個像素寬的鏈接〈斷開了狹窄鏈接〉,而且去除了右側物體上部突出的一個小於結構元素的2×2的區域〈去除細小毛刺〉: 但與腐蝕不一樣的是, 圖像大的輪廓並無發生總體的收縮, 物體位置也沒有發生任何變化。
根據圖8.12 的開運算示意圖, 能夠幫助你們更好地理解開運算的特色。爲了比較, 圖中也標示出了相應的腐蝕運算的結果:
matlab實現
根據定義,以相同的結構元素前後調用imerode和imdilate便可實現開操做。此外,Matlab 中也直接提供了開運算函數imopen, 其調用形式以下:
I2 = imopen(I,SE);
I = imread('erode_dilate.bmp');
Io = imopen(I,ones(6,6));
figure;
subplot(1,2,1);
imshow(I);
subplot(1,2,2);
imshow(Io);
從圖8.13中能夠看到同腐蝕相比,開運算在過濾噪聲的同時並無對物體的形狀、輪廓形成明顯的影響,這是一大優點。但當咱們只關心物體的位置或者個數時,物體形狀的改變不會給咱們帶來困擾,此時用腐蝕濾波具備處理速度上的優點〈同開運算相比節省了一次膨脹運算〉。
閉運算一樣可使輪廓變得光滑, 但與開運算相反, 它一般可以彌合狹窄的間斷, 填充小的孔洞。
Matlab實現
根據定義,以相同的結構元素前後調用imdilate 和imerode 便可實現閉操做。此外,Matlab中也直接提供了閉運算函數imclose, 其用法同imopen 相似
I = zeros(120,180);
I(11:80,16:75)=1;
I(56:105,86:135)=1;
I(26:55,141:170)=1;
se = zeros(58,58);
se(5:54,5:54)=1;
Ie1 = imerode(I,se);
Ic = 1 - I;
S2 = 1 - se;
Ie2 = imerode(Ic,S2);
Ihm = Ie1&Ie2;
figure;
subplot(2,2,1);
imshow(I);
subplot(2,2,2);
imshow(Ie1);
subplot(2,2,3);
imshow(Ie2);
subplot(2,2,4);
imshow(Ihm);
圖中給出了變換的最終結果。爲便於觀察在顯示時每幅圖像周圍都環繞着一圈黑色邊框, 注意該邊框並非圖像自己的一部分。
注意: 注意對於結構元素s,咱們感興趣的物體S1以外的背景S2不能選擇得太寬,由於使得S包含背景S2的目的僅僅是定義出物體S1的外輪廓,以便在圖像中可以
找到準確的徹底匹配位置. 從這個意義上說, 物體S1周圍有一個像素寬的背景環繞就足夠了, 例8.3中選擇了4個像素寬的背景,是爲了使結構元素背景部分應
看起來比較明顯, 但若是背景部分過大, 則會影響擊中/擊不中變換的計算結果.在上例中, 中間的正方形Y與右上的正方形Z之間的水平距離爲6,若是在定義S時, S2的寬度超過6個像素, 則最終的計算結果將是空集.
輪廓是對物體形狀的有力描述, 對圖像分析和識別十分有用。經過邊界提取算法能夠獲得物體的邊界輪廓:而邊界跟蹤算法在提取邊界的同時還能依次記錄下邊界像素的位置信息,下面分別介紹.
邊界提取
要在二值圖像中提取物體的邊界,容易想到的一個方法是將全部物體內部的點刪除(置爲背景色〉。具體地說,能夠逐行掃描原圖像,若是發現一個黑點〈圖8.17 中黑點爲前景點)的8個鄰域都是黑點, 則該點爲內部點, 在目標圖像中將它刪除。實際上這至關於採用一個3*3的結構元素對原圖像進行腐蝕, 使得只有那些8個鄰域都有黑點的內部點被保留,再用原圖像減去腐蝕後的圖像, 剛好刪除了這些內部點, 留下了邊界像素。這一過程可參
見圖8.17 。
I = imread('head_portrait.bmp');
se = strel('square',3);
Ie = imerode(I,se);
Iout = I - Ie;
figure;
subplot(1,2,1);
imshow(I);
subplot(1,2,2);
imshow(Iout);
邊界跟蹤
區域填充可視爲邊界提取的反過程, 它是在邊界已知的狀況下獲得邊界包圍的整個區域的形態學技術。
理論基礎
問題的描述以下: 己知某-8連通邊界和邊界內部的某個點, 要求從該點開始填充整個邊界包圍的區域, 這一過程稱爲種子填充, 填充的開始點被稱爲種子.
如圖8.20 所示, 對於4 連通的邊界, 其圍成的內部區域是8 連通的, 而8連通的邊界圍成的內部區域倒是4連通的.
爲了填充4 連通的邊界應選擇圖8.20 (b )中的3 × 3 結構元素, 但若是想在8 連通邊界內從種子點獲得區域則需選用圖8.20 (d)的十字結構元素S 對初始時僅爲種子點的圖像B進行膨脹,十字結構元素S可以保證只要B在邊界A的內部〈不包括邊界自己〉,每次膨脹都不會產生邊界以外的點(新膨脹出來的點或者在邊界內部, 或者落在邊界上〉, 這樣只需把每次膨脹的結果圖像和邊界的補圖像
不斷膨脹, B的區域不斷生長, 但每次膨脹後與
連通份量的概念在0.3.1小節中曾介紹過。在二值圖像中提取連通份量是許多自動圖像分析應用中的核心任務。提取連通份量的過程實際上也是標註連通份量的過程, 一般的作法是給原圖像中的每一個連通區分配一個惟一表明該區域的編號, 在輸出圖像中該連通區內的全部像素的像素值就賦值爲該區域的編號, 咱們將這樣的輸出圖像稱爲標註圖像。
matlab實現
在Matlab中, 連通份量的相關操做主要藉助IPT函數bwlabel實現. 其調用語法爲
[L num]= bwlabel(Ibw,conn);
Ibw爲一幅輸入二位圖像.
conn爲可選參數, 指明要提取的連通份量是4連邊仍是8連通, 默認值爲8.
L爲相似於圖8.23 ( b)的標註圖像.
num爲二維圖像Ibw中連通份量的個數.
提取連通份量的應用十分普遍, 利用標註圖像能夠方便地進行不少基於連通區的操做。例如要計算某一連通份量的大小, 只需掃描一遍標註圖像, 對像素值爲該區編號的像素進行計數: 又如要計算某一連通份量的質心, 只需掃描一遍標註圖像, 找出全部像素值爲該區編號的像素的x、y座標, 而後計算其平均值.
在人臉局部圖像中定位嘴的中心
咱們但願在如圖8.24 (a )所示的圖像中定位嘴的中心,假定已經掌握了輸入圖像中的某些先驗知識,嘴部佔據了圖像的大部分區域且從灰度上易於與周圍皮膚分離開來. 因而針
對性地擬定了在二位化圖像中尋找最大連通區域中心的解決方案, 具體步驟爲:
(1)對輸入圖像進行二位化處理.
(2)標註二值圖像中的連通份量.
(3)找出最大的連通份量.
(4)計算最大連通份量的中心.
% locateMouth.m
I = imread('mouth.bmp'); %讀入圖像
Id = im2double(I);
figure, imshow(Id) % 獲得8.24(a)
Ibw = im2bw(Id, 0.38); % 以0.38爲閾值二值化
Ibw = 1 - Ibw; %爲在Matlab中進行處理,將圖像反色
figure, imshow(Ibw) % 獲得8.24(b)
hold on
[L, num] = bwlabel(Ibw, 8); % 標註連通份量
disp(['圖中共有' num2str(num) '個連通份量'])
% 找出最大的連通份量(嘴)
max = 0; % 當前最大連通份量的大小
indMax = 0; % 當前最大連通份量的索引
for k = 1:num
[y x] = find(L == k); % 找出編號爲k的連通區的行索引集合y和列索引集合x
nSize = length(y); %計算該連通區中的像素數目
if(nSize > max)
max = nSize;
indMax = k;
end
end
if indMax == 0
disp('沒有找到連通份量')
return
end
% 計算並顯示最大連通份量(嘴)的中心
[y x] = find(L == indMax);
yMean = mean(y);
xMean = mean(x);
plot(xMean, yMean, 'Marker', 'o', 'MarkerSize', 14, 'MarkerEdgeColor', 'w', 'MarkerFaceColor', 'w');
plot(xMean, yMean, 'Marker', '*', 'MarkerSize', 12, 'MarkerEdgeColor', 'k'); % 獲得8.24(c)
細菌計數
I = imread('bw_bacteria.bmp');
[L,num]=bwlabel(I,8);
Idil = imdilate(I,ones(3,3));
[L,num] = bwlabel(Idil,8);
figure;
subplot(1,2,1);
imshow(I);
subplot(1,2,2);
imshow(Idil);