R>95
G>40
B>20
R>G
R>B
Max(R,G,B)-Min(R,G,B)>15
Abs(R-G)>15
滿足上述公式即可認爲,是膚色。這是經驗得到的~
我們還可有轉換顏色空間,通過把R,G,B各分量轉換成對數值,並進一步得到膚色的色度和飽和度以進行膚色分割.本文則對膚色色調在YHV和YIO顏色空間中的分佈作線性化處理.RGB空間到YUV空間的轉換用矩陣表示如下
其中,U和V是平面上兩個相互正交的矢量,色度信號(即U與V之和)是一個二維矢量,稱之爲色度信號矢量.每一種顏色對應一個色度信號矢量,它的飽和度由模值Ch表示,色調
由相位角θ表示
圖2是YUV色彩空間中的色度信號矢量圖.白色和黑色都由原點(0,0)表示,模值爲0,相位角任意.在YUV空間的UV平面上,膚色的色調介於紅與黃之間.根據對大量圖像的彩色分析,可以確定人的膚色色調6的變化範圍,如圖3所示.把彩色圖像的象素p由RGB空間變換到YUV空間,如果滿足條件,θp在[100,150]內,則p是膚色點.
YIQ表示法的定義是基於這樣一個原則:對顏色編碼所得信號的冗餘信息最少或者說人眼看不見的就無需傳送和重現.其中1色度分量選在相位角爲173的紅色和303的青藍色色調位置Q色度,分量選在色分解力弱的紫紅色和綠黃色色調位置上充分利用了人眼的色分辨力.將YUV色空間的UV平面逆時針方向旋轉33就得到了YIQ空間的10平面.從RGB空間到YIQ空間的矩陣轉換如下:
在YIQ空間中1分量代表從桔黃到藍綠的色調I值越小包含的黃色越多藍綠色越小.所以膚色在YIQ空間內的I值在一個範圍裏變化通過試驗和統計分析可確定其範
圍爲[20,90]。
此外,在YCbCr顏色空間的簡單閾值膚色識別算法則更爲簡單,將圖像轉換到YCbCr顏色空間,然後按下述計算式判斷是否屬於皮膚區域:
Cb > 77 And Cb < 127
Cr > 133 And Cr < 173
YCbCr顏色空間與RGB的轉換如下:Y = 0.257*R+0.564*G+0.098*B+16
Cb = -0.148*R-0.291*G+0.439*B+128
Cr = 0.439*R-0.368*G-0.071*B+128
好了,原理說完了,先給出RGB空間的膚色檢測算法:
public Image RGBskinDetection(){
if(gray)
return this;
Image copy = this.clone();
int[] d = new int[h*w];
for(int i =0;i<this.h;i++){
for(int j=0;j<this.w;j++){
if(red[j+i*w] >95)
if(green[j+i*w]>40)
if(blue[j+i*w]>20)
if(red[j+i*w] >green[j+i*w])
if(red[j+i*w] >blue[j+i*w])
if(check(red[j+i*w], green[j+i*w], blue[j+i*w]))
if(Math.abs(red[j+i*w]-green[j+i*w])>15)
d[j+i*w] = 255;
}
}
copy.data = d;
copy.gray = true;
return copy;
}
原圖:
效果如下:
在在YCbCr顏色空間檢測如下:
public Image skinDet(){
if(gray)
return this;
Image copy = this.clone();
int[] d = new int[h*w];
for(int i =0;i<this.h;i++){
for(int j=0;j<this.w;j++){
int R = red[j+i*w];
int G = green[j+i*w];
int B = blue[j+i*w];
//double Y = 0.299*R+0.587*G+0.114*B;
double Cb = 128 - 0.168736*R - 0.331264*G + 0.5*B;
double Cr = 128 + 0.5*R - 0.418688*G - 0.081312*B;
if(Cb > 77 && Cb<127 && Cr>133 && Cr<173)
d[j+i*w] = 255;
}
}
copy.data = d;
copy.gray = true;
return copy;
}
效果如下:
其它的兩種方法,就交由讀者自己實現了,還是一樣簡單的~
以上