Matlab處理圖像后實(shí)現(xiàn)簡(jiǎn)單的人臉檢測(cè)
1.人臉檢測(cè)原理框圖
整體思路是尋找圖片中最大的連通域,將其認(rèn)定為人臉。
第一個(gè)環(huán)節(jié)均值濾波,是為了減弱圖像的相關(guān)細(xì)節(jié)部分,以免毛刺影響后期連通域的形成,二值化方便形態(tài)學(xué)處理,減少運(yùn)算量??紤]到人臉有黑人和白人黃種人,黑人膚色較深,在二值化之后面部區(qū)域不容易形成較大的連通域,如果采取形態(tài)學(xué)邊界提取的辦法,就可以避免這個(gè)問(wèn)題,形態(tài)學(xué)邊界提取,只要結(jié)構(gòu)元素夠大,也可以形成較大的封閉連通域。
然后就是縱向閉合操作,這一步我選擇采用豎向長(zhǎng)條狀的結(jié)構(gòu)元素進(jìn)行閉合運(yùn)算,因?yàn)槿说哪槻亢皖i部以及頭發(fā)和衣物等等都是縱向分布的,在進(jìn)行形態(tài)學(xué)邊界提取的時(shí)候,容易將這些靠近的成分割裂開(kāi)來(lái),這對(duì)連通域的判斷極為不利,所以用豎向長(zhǎng)條狀的結(jié)構(gòu)元素在在縱向進(jìn)行閉合運(yùn)算,將臉部上下部的區(qū)域重新連接起來(lái)。
緊接著我又用橫向長(zhǎng)條狀結(jié)構(gòu)元素進(jìn)行橫向腐蝕運(yùn)算,這是因?yàn)?,人的頭部以下的身體部分存在有大量連通域的時(shí)候,容易對(duì)最大連通域的判決產(chǎn)生干擾,又因?yàn)橄掳氩糠?,多半呈縱向分布,通過(guò)橫向腐蝕可以將這些大塊的連通域割裂開(kāi)來(lái),但是要注意的是,割裂程度不應(yīng)太大,否則會(huì)使得上一步閉合操作喪失意義。
接著,由于背景雜物等因素,同樣也會(huì)產(chǎn)生大量連通域,這會(huì)對(duì)最后結(jié)果的判決產(chǎn)生干擾,因此要予以剔除。
進(jìn)行了層層篩選之后,在剩下的連通域里面挑一個(gè)最大的連通域,并且尺寸形狀滿足要求的用矩形框框起來(lái)作為人臉檢測(cè)結(jié)果。
2 步驟
2.1 均值濾波
h = ones(9)/81; I = uint8(conv2(I,h)); figure,imshow(I),title('線性均值濾波')
采用9x9模板進(jìn)行線性均值濾波,因?yàn)楹竺嬲{(diào)用gpuArray()函數(shù)轉(zhuǎn)換對(duì)輸入數(shù)據(jù)有要求,所以在進(jìn)行了二維卷積之后重新將數(shù)據(jù)格式轉(zhuǎn)換成8位無(wú)符號(hào)整形數(shù)據(jù)。
2.2 二值化
BW = imbinarize(I); figure,imshow(BW),title('二值化')
直接調(diào)用imbinarize對(duì)圖像進(jìn)行二值化
2.3.形態(tài)學(xué)邊界提取
B = ones(21);%結(jié)構(gòu)元素 BW = -imerode(BW,B) + BW; figure,imshow(BW),title('形態(tài)學(xué)邊界提取') BW = bwmorph(BW,'thicken'); figure,imshow(BW),title('加粗邊界') BW = not(bwareaopen(not(BW), 300)); figure,imshow(BW),title('把空洞填了')
結(jié)構(gòu)元素采用21x21大小的全1矩陣,先調(diào)用imrode()進(jìn)行腐蝕,再用原圖減去腐蝕結(jié)果,得到邊界。為了讓邊界更加明顯,調(diào)用bmworph函數(shù),傳入'thicken'參數(shù),意在將邊界加粗加厚。最后為了把空洞就是連續(xù)的小黑色塊填滿,調(diào)用bwareaopen()函數(shù),該函數(shù)是去除面積小于300的白塊,為了去除黑塊,先調(diào)用not(BW)給原來(lái)的二值圖像取反,去掉取反后面積小于300的白塊,再次取反,達(dá)到去掉面積小于300的黑塊的目的。
2.4 縱向閉合與橫向腐蝕
%進(jìn)行形態(tài)學(xué)運(yùn)算 B = strel('line',50,90); BW = imdilate(BW,B); BW = imerode(BW,B); figure,imshow(BW),title('再閉操作之后') B = strel('line',10,0); BW = imerode(BW,B); figure,imshow(BW),title('閉操作之后再腐蝕') BW = gpuArray(BW);
調(diào)用strel()函數(shù)生成特定的結(jié)構(gòu)元素,第一步調(diào)用strel(‘line',50,90),意思是調(diào)用直線型結(jié)構(gòu)元素,長(zhǎng)度為50,角度90度,也就是豎直的長(zhǎng)條形結(jié)構(gòu)元素。接著利用B調(diào)用imdilate和imerode,先進(jìn)行膨脹再進(jìn)行腐蝕完成閉操作運(yùn)算。下一步,繼續(xù)生成橫向長(zhǎng)條形結(jié)構(gòu)元素,進(jìn)行腐蝕操作,注意這里的結(jié)構(gòu)元素不宜面積太大,長(zhǎng)度太長(zhǎng),否則會(huì)過(guò)度影響上一步的結(jié)果
最后為了循環(huán)過(guò)程中提升運(yùn)算速度,將數(shù)據(jù)類型更改為gpuArray(),可以在GPU上進(jìn)行計(jì)算,節(jié)省時(shí)間。
2.5 消除邊界多余連通域
%最小化背景 %細(xì)分 div = 10; r = floor(n1/div);%分成10塊 行 c = floor(n2/div);%分成10塊 列 x1 = 1;x2 = r;%對(duì)應(yīng)行初始化 s = r*c;%塊面積 %判斷人臉是否處于圖片四周,如果不是就全部弄黑 %figure for i=1:div y1 = 1;y2 = c;%對(duì)應(yīng)列初始化 for j=1:div loc = find(BW(x1:x2,y1:y2)==0);%統(tǒng)計(jì)這一塊黑色像素的位置 num = length(loc); rate = num*100/s;%統(tǒng)計(jì)黑色像素占比 if (y2<=0.2*div*c||y2>=0.8*div*c)||(x1<=r||x2>=r*div) if rate <=100 BW(x1:x2,y1:y2) = 0; end %imshow(BW) else if rate <=25 BW(x1:x2,y1:y2) = 1; end %imshow(BW) end%下一列 y1 = y1 + c; y2 = y2 + c; end%下一行 x1 = x1 + r; x2 = x2 + r; end
對(duì)于周圍多余的雜物產(chǎn)生的連通域,我選擇先將整幅圖像劃分為很多小塊,對(duì)一部分在圖像邊緣的小塊全部置成黑色,對(duì)不在邊緣的通過(guò)計(jì)算其中黑色像素比例來(lái)判定是否應(yīng)該全部給成白色。因?yàn)閳D片中央可能還會(huì)存在一些細(xì)小空洞,這些空洞在每一小塊占比不是很大但有可能影響連通性,如果一個(gè)小塊里面大部分是白色就全部給成白色,方便后期判定最大連通域。
Div是均分比例,我這里設(shè)置成10,也就是將整個(gè)圖像劃分成100個(gè)小塊。r就是在行方向上一個(gè)小塊占多少像素,c就是在列方向上一個(gè)小塊有多少像素。用兩層循環(huán)來(lái)遍歷每個(gè)小塊,通過(guò)find(x1:x2,y1:y2)==0返回所有滿足要求的像素的索引,索引長(zhǎng)度就是黑色像素的個(gè)數(shù)。
通過(guò)條件判斷是否在邊界。
2.6 尋找最大連通域并畫(huà)框
figure subplot(1,2,1) imshow(BW) title('最終處理') L = bwlabel(BW,8);%利用belabel函數(shù)對(duì)8連通域區(qū)間進(jìn)行標(biāo)號(hào) BB = regionprops(L,'BoundingBox');%得到矩形框,框柱每一個(gè)連通域 BB = cell2mat(struct2cell(BB)); [s1,s2] = size(BB); BB = reshape(BB,4,s1*s2/4)'; pickshape = BB(:,3)./BB(:,4);% shapeind = BB(0.3<pickshape&pickshape<3,:);%篩選掉尺寸比例不合格 [~,arealind] = max(shapeind(:,3).*shapeind(:,4)); subplot(1,2,2) imshow(rgb) hold on rectangle('Position',[shapeind(arealind,1),shapeind(arealind,2),shapeind(arealind,3),shapeind(arealind,3)],... 'EdgeColor','g','Linewidth',2) title('人臉檢測(cè)')
經(jīng)過(guò)消除邊界多余連通域后得到的BW中剩下的連通域內(nèi)存在著我們需要的那個(gè)對(duì)應(yīng)人臉的連通域。一般情況下,對(duì)應(yīng)人臉的那個(gè)連通域會(huì)是最大的連通域。
調(diào)用bwlabel(BW,8)函數(shù)給所有連通域標(biāo)記,其中鄰域規(guī)則采用8鄰域,返回一個(gè)被標(biāo)記過(guò)連通域的圖像,第k個(gè)被標(biāo)記的連通域所有像素值為k。
調(diào)用regionprops(L,'BoundingBox')函數(shù),傳入?yún)?shù)L和'BoundingBox',該函數(shù)用于獲取圖像的各種屬性,傳入'BoundingBox'返回的是一個(gè)結(jié)構(gòu)體,每一個(gè)結(jié)構(gòu)體內(nèi)都包含了一個(gè)能框柱其對(duì)應(yīng)連通域的最小方框。一個(gè)方框,用一個(gè)序列來(lái)描述[x,y,width,height],這個(gè)序列包含了方框左上角像素的坐標(biāo)以及長(zhǎng)和寬。
這三條語(yǔ)句,第一條用來(lái)將結(jié)構(gòu)體轉(zhuǎn)換成矩陣向量,方便計(jì)算。第二行獲取這個(gè)矩陣的維度。由于BB剛轉(zhuǎn)換成向量的時(shí)候,是一個(gè)行向量,每4個(gè)元素1組對(duì)應(yīng)一個(gè)方框。為了后續(xù)計(jì)算方便,使用reshape()函數(shù),將BB重構(gòu)成一個(gè)矩陣,這個(gè)矩陣有4列,每一列對(duì)應(yīng)方框的一個(gè)參數(shù),比如坐標(biāo),長(zhǎng)寬等等。每一行對(duì)應(yīng)一個(gè)方框。
第一行計(jì)算長(zhǎng)寬比,得到的pickshape向量的每一行對(duì)應(yīng)每個(gè)方框的長(zhǎng)寬比。由于有的方框明顯過(guò)于扁平或者過(guò)于狹長(zhǎng),這種方框應(yīng)該是要扔掉的。
所以第二行,通過(guò)邏輯表達(dá)式從BB內(nèi)篩選出尺寸比例合格的方框,存在shapeind里面。
剩下的尺寸符合要求的方框里面要選出面積最大的那個(gè),最后一行,得到面積最大的方框?qū)?yīng)的索引。
把方框畫(huà)出來(lái)。
3 檢測(cè)結(jié)果
圖 15rgb圖像轉(zhuǎn)換成灰度圖像 圖 16線性均值濾波結(jié)果
可以看到,均值濾波使得圖像變模糊了細(xì)節(jié)減少
圖 17二值化結(jié)果 圖 18形態(tài)學(xué)邊界提取結(jié)果
以看到邊界被成功提取了出來(lái),在人臉部形成了一個(gè)比較大的連通域
可以看到,進(jìn)行邊界加粗和空洞添補(bǔ)之后,眼睛部分的黑塊被消除了,這使得臉部連通域更大了
注意觀察圖片左邊的相分離的白塊,在縱向閉操作之后連在了一起,同時(shí)臉部連通域進(jìn)一步擴(kuò)大,然后橫向腐蝕在盡量維持臉部連通域大小的情況下減小了圖片下方連通域。
可以看到效果還可以。還有其他的測(cè)試結(jié)果
圖 24測(cè)試樣例
圖 25測(cè)試樣例
圖 26測(cè)試樣例
圖 27測(cè)試樣例
圖 28測(cè)試樣例
圖 29測(cè)試樣例
圖 30測(cè)試樣例
這里注意到,黑人同樣也被檢測(cè)到了
---%%%完整代碼 rgb = imread('f.jpg'); I = rgb2gray(rgb); [n1,n2] = size(I); %灰度圖 figure,imshow(I),title('灰度圖') tic h = ones(9)/81; I = uint8(conv2(I,h)); figure,imshow(I),title('線性均值濾波') BW = imbinarize(I); figure,imshow(BW),title('二值化') B = ones(21);%結(jié)構(gòu)元素 BW = -imerode(BW,B) + BW; figure,imshow(BW),title('形態(tài)學(xué)邊界提取') BW = bwmorph(BW,'thicken'); figure,imshow(BW),title('加粗邊界') BW = not(bwareaopen(not(BW), 300)); figure,imshow(BW),title('把空洞填了') %進(jìn)行形態(tài)學(xué)運(yùn)算 B = strel('line',50,90); BW = imdilate(BW,B); BW = imerode(BW,B); figure,imshow(BW),title('再閉操作之后') B = strel('line',10,0); BW = imerode(BW,B); figure,imshow(BW),title('閉操作之后再腐蝕') BW = gpuArray(BW); %最小化背景 %細(xì)分 div = 10; r = floor(n1/div);%分成10塊 行 c = floor(n2/div);%分成10塊 列 x1 = 1;x2 = r;%對(duì)應(yīng)行初始化 s = r*c;%塊面積 %判斷人臉是否處于圖片四周,如果不是就全部弄黑 %figure for i=1:div y1 = 1;y2 = c;%對(duì)應(yīng)列初始化 for j=1:div loc = find(BW(x1:x2,y1:y2)==0);%統(tǒng)計(jì)這一塊黑色像素的位置 num = length(loc); rate = num*100/s;%統(tǒng)計(jì)黑色像素占比 if (y2<=0.2*div*c||y2>=0.8*div*c)||(x1<=r||x2>=r*div) if rate <=100 BW(x1:x2,y1:y2) = 0; end %imshow(BW) else if rate <=25 BW(x1:x2,y1:y2) = 1; end %imshow(BW) end%下一列 y1 = y1 + c; y2 = y2 + c; end%下一行 x1 = x1 + r; x2 = x2 + r; end figure subplot(1,2,1) imshow(BW) title('最終處理') L = bwlabel(BW,8);%利用belabel函數(shù)對(duì)8連通域區(qū)間進(jìn)行標(biāo)號(hào) BB = regionprops(L,'BoundingBox');%得到矩形框,框柱每一個(gè)連通域 BB = cell2mat(struct2cell(BB)); [s1,s2] = size(BB); BB = reshape(BB,4,s1*s2/4)'; pickshape = BB(:,3)./BB(:,4);% shapeind = BB(0.3<pickshape&pickshape<3,:);%篩選掉尺寸比例不合格 [~,arealind] = max(shapeind(:,3).*shapeind(:,4)); subplot(1,2,2) imshow(rgb) hold on rectangle('Position',[shapeind(arealind,1),shapeind(arealind,2),shapeind(arealind,3),shapeind(arealind,3)],... 'EdgeColor','g','Linewidth',2) title('人臉檢測(cè)') toc
到此這篇關(guān)于MATLAB處理圖像后實(shí)現(xiàn)簡(jiǎn)單的人臉檢測(cè)的文章就介紹到這了,更多相關(guān)MATLAB實(shí)現(xiàn)人臉檢測(cè)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言內(nèi)存函數(shù)的使用及其模擬實(shí)現(xiàn)
這篇文章主要介紹了C語(yǔ)言內(nèi)存函數(shù)的使用及其模擬實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-10-10C語(yǔ)言連續(xù)子向量的最大和及時(shí)間度量實(shí)例
這篇文章主要介紹了C語(yǔ)言連續(xù)子向量的最大和及時(shí)間度量,需要的朋友可以參考下2014-09-09C++控制臺(tái)實(shí)現(xiàn)俄羅斯方塊游戲
這篇文章主要為大家詳細(xì)介紹了C++控制臺(tái)實(shí)現(xiàn)俄羅斯方塊游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06關(guān)于C++友元類的實(shí)現(xiàn)講解
今天小編就為大家分享一篇關(guān)于關(guān)于C++友元類的實(shí)現(xiàn)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12C++中結(jié)構(gòu)體和Json字符串互轉(zhuǎn)的問(wèn)題詳解
這篇文章主要給大家介紹了關(guān)于C++中結(jié)構(gòu)體和Json字符串互轉(zhuǎn)問(wèn)題的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03探討:用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列(我作為面試官的小結(jié))
作為面試官的我,經(jīng)常拿這道用兩個(gè)棧實(shí)現(xiàn)一個(gè)隊(duì)列的面試題來(lái)考面試者,通過(guò)對(duì)面試者的表現(xiàn)和反應(yīng),有一些統(tǒng)計(jì)和感受,在此做個(gè)小結(jié)2013-05-05使用c++實(shí)現(xiàn)OpenCV繪制旋轉(zhuǎn)矩形圖形
這篇文章主要給大家介紹了使用c++實(shí)現(xiàn)OpenCV繪制圖形旋轉(zhuǎn)矩形的方法案例,通過(guò)圖文及代碼形式進(jìn)行了詳細(xì)的描述,有需要的朋友可以參考下,希望可以有所幫助2021-08-08c語(yǔ)言?數(shù)據(jù)存儲(chǔ)與原碼?反碼?補(bǔ)碼詳細(xì)解析
不知道你是否和我一樣好奇,學(xué)習(xí)編程語(yǔ)言的同時(shí)想,各個(gè)數(shù)據(jù)類型是怎樣在我們的內(nèi)存中儲(chǔ)存的呢,如果你仔細(xì)深入了解的話,你會(huì)了解其中的樂(lè)趣,了解科學(xué)家們的偉大,了解c語(yǔ)言2022-02-02