「AI超級美髮師」深度學習演算法打造染髮特效(附代碼)
新智元推薦
來源:OpenCV學堂
作者:胡耀武
【新智元導讀】如今,在類似天天P圖、美圖秀秀等手機APP中,給指定照片或視頻中的人物更換頭髮顏色已經是再正常不過的事情了。那麼本文便介紹了該功能背後如AI頭髮分割模塊、頭髮換色、顏色增強與修正模塊等技術原理(附代碼)。
首先,為照片或視頻中人物換髮色的演算法流程如下圖所示:
AI頭髮分割模塊
基於深度學習的目標分割演算法已經比較成熟,比較常用的有FCN,SegNet,UNet,PspNet,DenseNet等等。這裡我們使用Unet網路來進行頭髮分割,具體可以參考如下鏈接:點擊打開鏈接Unet頭髮分割代碼如下:
def get_unet_256(input _shape=(256,256,3),
num_classes=1):
inputs=Input(shape=input_shape)
#256
down0 = Conv2D(32,(3,3), padding="same")(inputs)
down0 = BatchNormalization(down0)
down0 = Activation("relu")(down0)
down0 = Conv2D(32,(3,3), padding="same")(down0)
down0 = BatchNormalization(down0)
down0 = Activation("relu")(down0)
down0_pool = MaxPooling2D((2,2),strides=(2,2))(down0)
#128
down1 = Conv2D(64,(3,3), padding="same")(down0_pool)
down1 = BatchNormalization(down1)
down1 = Activation("relu")(down1)
down1 = Conv2D(64,(3,3), padding="same")(down1)
down1 = BatchNormalization(down1)
down1 = Activation("relu")(down1)
down1_pool = MaxPooling2D((2,2),strides=(2,2))(down1)
#64
down2 = Conv2D(128,(3,3), padding="same")(down1_pool)
down2 = BatchNormalization(down2)
down2 = Activation("relu")(down2)
down2 = Conv2D(128,(3,3), padding="same")(down2)
down2 = BatchNormalization(down2)
down2 = Activation("relu")(down2)
down2_pool = MaxPooling2D((2,2),strides=(2,2))(down2)
#32
down3 = Conv2D(256,(3,3), padding="same")(down2_pool)
down3 = BatchNormalization(down3)
down3 = Activation("relu")(down3)
down3 = Conv2D(256,(3,3), padding="same")(down3)
down3 = BatchNormalization(down3)
down3 = Activation("relu")(down3)
down3_pool = MaxPooling2D((2,2),strides=(2,2))(down3)
#16
down4 = Conv2D(512,(3,3), padding="same")(down3_pool)
down4 = BatchNormalization(down4)
down4 = Activation("relu")(down4)
down4 = Conv2D(512,(3,3), padding="same")(down4)
down4 = BatchNormalization(down4)
down4 = Activation("relu")(down4)
down4_pool = MaxPooling2D((2,2),strides=(2,2))(down4)
#8
center = Conv2D(1024,(3,3), padding="same")(down4_pool)
center = BatchNormalization(center)
center = Activation("relu")(center)
center = Conv2D(1024,(3,3), padding="same")(center)
center = BatchNormalization(center)
center = Activation("relu")(center)
#center
up4 = UpSamepling2D((2,2))(center)
up4 = Concatenate([down4,up4],axis=3)
up4 = Conv2D(512,(3,3),padding="same")(up4)
up4 = BatchNormalization(up4)
up4 = Activation("relu")(up4)
up4 = Conv2d(512,(3,3),padding="same")(up4)
up4 = BatchNormalization(up4)
up4 = Activation("relu")(up4)
#16
up3 = UpSamepling2D((2,2))(up4)
up3 = Concatenate([down4,up4],axis=3)
up3 = Conv2D(256,(3,3),padding="same")(up3)
up3 = BatchNormalization(up3)
up3 = Activation("relu")(up3)
up3 = Conv2d(256,(3,3),padding="same")(up3)
up3 = BatchNormalization(up3)
up3 = Activation("relu")(up3)
#32
up2 = UpSamepling2D((2,2))(up3)
up2 = Concatenate([down4,up4],axis=3)
up2 = Conv2D(128,(3,3),padding="same")(up2)
up2 = BatchNormalization(up2)
up2 = Activation("relu")(up2)
up2 = Conv2d(128,(3,3),padding="same")(up2)
up2 = BatchNormalization(up2)
up2 = Activation("relu")(up2)
#64
up1 = UpSamepling2D((2,2))(up2)
up1 = Concatenate([down4,up4],axis=3)
up1 = Conv2D(64,(3,3),padding="same")(up1)
up1 = BatchNormalization(up1)
up1 = Activation("relu")(up1)
up1 = Conv2d(64,(3,3),padding="same")(up1)
up1 = BatchNormalization(up1)
up1 = Activation("relu")(up1)
#128
up0 = UpSamepling2D((2,2))(up1)
up0 = Concatenate([down4,up4],axis=3)
up0 = Conv2D(32,(3,3),padding="same")(up0)
up0 = BatchNormalization(up0)
up0 = Activation("relu")(up0)
up0 = Conv2d(32,(3,3),padding="same")(up0)
up0 = BatchNormalization(up0)
up0 = Activation("relu")(up0)
#256
classify = Con2D(num_classes,(1,1)),activation="sigmoid")(up0)
model = Model(input=inputs,outputs=classify)
#model.compile(optimizer=RMSprop(lr=0.0001),loss=bce_dice_loss,metrices=[dice_coeff])
return model
分割效果舉例如下:
使用的訓練和測試數據集合大家自己準備即可。
發色更換模塊
這個模塊看起來比較簡單,實際上卻並非如此。 這個模塊要細分為:
①頭髮顏色增強與修正模塊;
②顏色空間染色模塊;
③頭髮細節增強;
發色增強與修正模塊
為什麼要對頭髮的顏色進行增強與修正? 先看下面一組圖,我們直接使用HSV顏色空間對純黑色的頭髮進行染色,目標色是紫色,結果如下:
大家可以看到,針對上面這張原圖,頭髮比較黑,在HSV顏色空間進行頭髮換色之後,效果圖中很不明顯,只有輕微的顏色變化。
為什麼會出現這種情況?原因如下: 我們以RGB和HSV顏色空間為例,首先來看下HSV和RGB之間的轉換公式:
設 (r, g, b)分別是一個顏色的紅、綠和藍坐標,它們的值是在0到1之間的實數。設max等價於r, g和b中的最大者。設min等於這些值中的最小者。要找到在HSL空間中的 (h, s, l)值,這裡的h ∈ [0, 360)度是角度的色相角,而s, l ∈ [0,1]是飽和度和亮度,計算為:
我們假設頭髮為純黑色,R=G=B=0,那麼按照HSV計算公式可以得到H = S = V = 0;
假設我們要把頭髮顏色替換為紅色(r=255,g=0,b=0);
那麼,我們先將紅色轉換為對應的hsv,然後保留原始黑色頭髮的V,紅色頭髮的hs,重新組合新的hsV,在轉換為RGB顏色空間,即為頭髮換色之後的效果(hs是顏色屬性,v是明度屬性,保留原始黑色頭髮的明度,替換顏色屬性以達到換色目的);
HSV轉換為RGB的公式如下:
對於黑色,我們計算的結果是H=S=V=0,由於V=0,因此,p=q=t=0,不管目標顏色的hs值是多少,rgb始終都是0,也就是黑色;
這樣,雖然我們使用了紅色,來替換黑色頭髮,但是,結果卻依舊是黑色,結論也就是hsv/hsl顏色空間,無法對黑色換色。
下面,我們給出天天P圖和美妝相機對應紫色的換髮色效果:
與之前HSV顏色空間的結果對比,我們明顯可以看到,天天P圖和美妝相機的效果要更濃,更好看,而且對近乎黑色的頭髮進行了完美的換色;
由於上述原因,我們這裡需要對圖像中的頭髮區域進行一定的增強處理:提亮,輕微改變色調;
這一步通常可以在PS上進行提亮調色,然後使用LUT來處理;
經過提亮之後的上色效果如下圖所示:
可以看到,基本與美妝相機和天天P圖類似了。
HSV/HSL/YCbCr顏色空間換色這一步比較簡單,保留明度分量不變,將其他顏色、色調分量替換為目標發色就可以了。
這裡以HSV顏色空間為例:
假如我們要將頭髮染髮為一半青色,一般粉紅色,那麼我們構建如下圖所示的顏色MAP:
對於頭髮區域的每一個像素點P,我們將P的RGB轉換為HSV顏色空間,得到H/S/V;
根據P在原圖頭髮區域的位置比例關係,我們在顏色MAP中找到對應位置的像素點D,將D的RGB轉換為HSV顏色空間,得到目標顏色的h/s/v;
根據目標顏色重組hsV,然後轉為RGB即可;
這一模塊代碼如下:
#h=[0,360],s=[0,1],v=[0,1]
void RGBToHSV(int R, int G, int B, float* h, float* s,float* v)
{
float min,max;
float r = R/255.0f;
float g = G/255.0f;
float b = B/255.0f;
min = MIN2(r,MIN2(g,b));
max = MAX2(r,MAX2(g,b));
if(max == min)
*h=0;
if(max == r && g >= b)
*h = 60.0f * (g-b) / (max-min);
if(max == r && g < b)
*h = 60.0f * (g-b) / (max-min) + 360.0f;
if(max == g)
*h = 60.0f * (b-r) / (max-min) + 120.0f;
if(max == b)
*h = 60.0f * (r-g) / (max-min) + 240.0f;
if(max == 0)
*s = (max-min) / max;
*v = max;
};
void HSVToRGB(float h, float s,float v, int* R,int *G,int *B)
{
float q=0,p=0,t=0,r=0,g=0,b=0;
int hN=0;
if(h<0)
h=260+h;
hN=(int)(h/60);
p=v*(1.0f-s);
q=v*(1.0f-(h/60.0f-hN)*s);
t=v*(1.0f-(1.0f-(h/60.0f-hN))*s);
switch(hN)
{
case 0:
r=v;
q=t;
b=p;
break;
case 1:
r=q;
q=v;
b=p;
break;
case 2:
r=p;
g=v;
b=t;
break;
case 3:
r=p;
g=q;
b=v;
break;
case 4:
r=t;
g=p;
b=v;
break;
case 5:
r=v;
g=p;
b=q;
break;
default:
break;
}
*R=(int)CLIP3((r*255.0f),0,255);
*G=(int)CLIP3((g*255.0f),0,255);
*B=(int)CLIP3((b*255.0f),0,255);
};
效果圖如下:
本文演算法對比美妝相機效果如下:
頭髮區域增強
這一步主要是為了突出頭髮絲的細節,可以使用銳化演算法,如Laplace銳化,USM銳化等等。上述過程基本是模擬美妝相機染髮演算法的過程,給大家參考一下,最後給出本文演算法的一些效果舉例:
本文效果除了實現正常的單色染髮,混合色染髮之外,還實現了挑染,如最下方一組效果圖所示。
對於挑染的演算法原理:
計算頭髮紋理,根據頭髮紋理選取需要挑染的頭髮束,然後對這些頭髮束與其他頭髮分開染色即可,具體邏輯這裡不再累贅,大家自行研究,這裡給出解決思路供大家參考。
最後,本文演算法理論上實時處理是沒有問題的,頭髮分割已經可以實時處理,所以後面基本沒有什麼耗時操作,使用opengl實現實時染髮是沒有問題的。
(本文經授權轉載自OpenCV學堂,點擊閱讀原文了解更多)
新智元AI WORLD 2018大會【早鳥票】開售!
新智元將於9月20日在北京國家會議中心舉辦AI WORLD 2018 大會,邀請機器學習教父、CMU教授 Tom Mitchell,邁克思·泰格馬克,周志華,陶大程,陳怡然等AI領袖一起關注機器智能與人類命運。
大會官網:
http://www.aiworld2018.com/
即日起到8月19日,新智元限量發售若干早鳥票,與全球AI領袖近距離交流,見證全球人工智慧產業跨越發展。
活動行購票鏈接:
http://www.huodongxing.com/event/6449053775000
活動行購票二維碼:


※Facebook挖角谷歌視覺晶元負責人,加入AI晶元大戰
※DeepMind無監督表示學習重大突破:語音、圖像、文本、強化學習全能冠軍!
TAG:新智元 |