染发模拟相机(「AI超级美发师」深度学习算法打造染发特效(附代码))

Posted

篇首语:自由的生活方式是借知识和洞察获得的。本文由小常识网(cha138.com)小编为大家整理,主要介绍了染发模拟相机(「AI超级美发师」深度学习算法打造染发特效(附代码))相关的知识,希望对你有一定的参考价值。

染发模拟相机(「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

  • 活动行购票二维码:

相关参考

昆布染发膏(紫药水真能染头发?皮肤科医生告诉你:别闹啦)

...获得比美发店更好的效果!而且价格便宜量又足,还没有染发剂的化学成分!介绍这个传说的妹子们还细心地分享了操作步骤!图源|小红书截图,侵删只需要打开某宝,花费十几到几十元不等的价格,就能把头发染成最飒的紫...

染发剂拿到理发店染多少钱(染发剂致癌吗?几十块和几百块的染发剂,究竟有什么区别?)

...十年代,就开始有研究者展开研究:1963年,有研究发现美发师易患膀胱癌;1971年,英国学者提出染发剂能使妇女患乳癌的机会增加;1977年,美国学者证实,美发师患肺癌的机会较高;1978年,专家报告说时常染发的妇女淋巴球...

烫发成本多少钱啊(花一个月工资染一次头发,资深理发师揭幕,染发68和2000其实没有区别)

理发师可能是与你生活联系最紧密的职业了,每个月总要见他们一两次。他们隐藏在写字楼里、街边的门店、胡同里面,每天应对形形色色的客人,兵来将挡水来土掩。他们是你又爱又恨的角色:我们都知道一个好看的发型能让...

烫发成本多少钱啊(花一个月工资染一次头发,资深理发师揭幕,染发68和2000其实没有区别)

理发师可能是与你生活联系最紧密的职业了,每个月总要见他们一两次。他们隐藏在写字楼里、街边的门店、胡同里面,每天应对形形色色的客人,兵来将挡水来土掩。他们是你又爱又恨的角色:我们都知道一个好看的发型能让...

构建算法模型(AI算法模型之应用部署概述)

...ver会开专题介绍。边缘端模型训练:通过pytorch、tensorflow等深度学习框架进行训练算法模型,得到模型权重文件,模型训练部分今天不着重介绍,后续专题会展开讨论训练tricks、模型调优、模型剪枝、蒸

染发梳比染发剂好吗(烫头发和染头发哪个更伤发?科普这三个烫染知识点,看完就有答案)

...一个最容易被忽略,但又不得不说的话题,那就是烫发和染发哪个对发质的伤害更大。之所以要说这个话题,是因为有一位粉丝私信我,说她的头发经常染发有点干燥,心血来潮去烫了头发,结果头发被烫焦了。理发师说是因为...

染发染膏怎么搭配(染发公式与药水计算方法技术)

作为一个优异的美发师,有必要对头发的布局,成长规则功用,性质以及它们的化学功能和物理、功能的基本知识,有所晓得A的色素量*A的份数+B的色素量*B的份数A的份数+B的份数6度的赤色想要染棕色,就要用6度蓝绿色来染。而...

没有漂粉怎么褪色(染发必修课:如何快速染“渐变色”,退“底色”详细解说,速收藏)

...有还是比较容易掌控。重点我们还是来说说发尾,大部分美发师应该在实战中,常常会遇见染生活色出现发尾颜色比上面两段的颜色偏暗,这是因为发尾染发的次数偏多,颗粒沉淀在发尾,时间久后变得偏硬,导致再次染色或者...

染膏比例是怎么算的(美发师课堂:染发篇(染发显色为何会不均匀))

在染发的过程中设计师常常会有发生顾客染完后,发现染出来的颜色会有不均匀的状况,一般会发生深、浅度不同、颜色的饱和度不同、色相有些差异、、、这些的状况,通常会有哪些因素的形成,下列的探讨可以帮助美发设计...

探色染发剂黑色有黑油吗(染发省钱小妙招)

头条的朋友,大家好,我是一名美发师。从业已经快七年了……今天给大家分享一下自己在家也能染的颜色,很多人去理发店染头发少则几百。多则几千。每个地方价格都不一样。一年下来在头发上也是一笔不小的开支。下面给...