【AI超级美发师】深度学习算法打造染发特效(附代码)

简介: 如今,在类似天天P图、美图秀秀等手机APP中,给指定照片或视频中的人物更换头发颜色已经是再正常不过的事情了。那么本文便介绍了该功能背后如AI头发分割模块、头发换色、颜色增强与修正模块等技术原理(附代码)。

【新智元导读】如今,在类似天天P图、美图秀秀等手机APP中,给指定照片或视频中的人物更换头发颜色已经是再正常不过的事情了。那么本文便介绍了该功能背后如AI头发分割模块、头发换色、颜色增强与修正模块等技术原理(附代码)。

首先,为照片或视频中人物换发色的算法流程如下图所示:

image

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

分割效果举例如下:

image

使用的训练和测试数据集合大家自己准备即可。

发色更换模块

这个模块看起来比较简单,实际上却并非如此。 这个模块要细分为:

①头发颜色增强与修正模块;
②颜色空间染色模块;
③头发细节增强;

发色增强与修正模块

为什么要对头发的颜色进行增强与修正? 先看下面一组图,我们直接使用HSV颜色空间对纯黑色的头发进行染色,目标色是紫色,结果如下:

image

大家可以看到,针对上面这张原图,头发比较黑,在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]是饱和度和亮度,计算为:


image

我们假设头发为纯黑色,R=G=B=0,那么按照HSV计算公式可以得到H = S = V = 0;

假设我们要把头发颜色替换为红色(r=255,g=0,b=0);

那么,我们先将红色转换为对应的hsv,然后保留原始黑色头发的V,红色头发的hs,重新组合新的hsV,在转换为RGB颜色空间,即为头发换色之后的效果(hs是颜色属性,v是明度属性,保留原始黑色头发的明度,替换颜色属性以达到换色目的);

HSV转换为RGB的公式如下:

image

对于黑色,我们计算的结果是H=S=V=0,由于V=0,因此,p=q=t=0,不管目标颜色的hs值是多少,rgb始终都是0,也就是黑色;

这样,虽然我们使用了红色,来替换黑色头发,但是,结果却依旧是黑色,结论也就是hsv/hsl颜色空间,无法对黑色换色。

下面,我们给出天天P图和美妆相机对应紫色的换发色效果:

image

与之前HSV颜色空间的结果对比,我们明显可以看到,天天P图和美妆相机的效果要更浓,更好看,而且对近乎黑色的头发进行了完美的换色;

由于上述原因,我们这里需要对图像中的头发区域进行一定的增强处理:提亮,轻微改变色调;

这一步通常可以在PS上进行提亮调色,然后使用LUT来处理;

经过提亮之后的上色效果如下图所示:


image

可以看到,基本与美妆相机和天天P图类似了。

HSV/HSL/YCbCr颜色空间换色

这一步比较简单,保留明度分量不变,将其他颜色、色调分量替换为目标发色就可以了。

这里以HSV颜色空间为例:

假如我们要将头发染发为一半青色,一般粉红色,那么我们构建如下图所示的颜色MAP:

image

对于头发区域的每一个像素点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);
};

效果图如下:

image

本文算法对比美妆相机效果如下:


image

头发区域增强

这一步主要是为了突出头发丝的细节,可以使用锐化算法,如Laplace锐化,USM锐化等等。上述过程基本是模拟美妆相机染发算法的过程,给大家参考一下,最后给出本文算法的一些效果举例:


image

本文效果除了实现正常的单色染发,混合色染发之外,还实现了挑染,如最下方一组效果图所示。

对于挑染的算法原理:

计算头发纹理,根据头发纹理选取需要挑染的头发束,然后对这些头发束与其他头发分开染色即可,具体逻辑这里不再累赘,大家自行研究,这里给出解决思路供大家参考。

最后,本文算法理论上实时处理是没有问题的,头发分割已经可以实时处理,所以后面基本没有什么耗时操作,使用opengl实现实时染发是没有问题的。

原文发布时间为:2018-08-02
本文来自云栖社区合作伙伴新智元,了解相关信息可以关注“AI_era”。
原文链接:【AI超级美发师】深度学习算法打造染发特效(附代码)

相关文章
|
23天前
|
机器学习/深度学习 人工智能 算法
猫狗宠物识别系统Python+TensorFlow+人工智能+深度学习+卷积网络算法
宠物识别系统使用Python和TensorFlow搭建卷积神经网络,基于37种常见猫狗数据集训练高精度模型,并保存为h5格式。通过Django框架搭建Web平台,用户上传宠物图片即可识别其名称,提供便捷的宠物识别服务。
232 55
|
1天前
|
机器学习/深度学习 人工智能 算法
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
眼疾识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了4种常见的眼疾图像数据集(白内障、糖尿病性视网膜病变、青光眼和正常眼睛) 再使用通过搭建的算法模型对数据集进行训练得到一个识别精度较高的模型,然后保存为为本地h5格式文件。最后使用Django框架搭建了一个Web网页平台可视化操作界面,实现用户上传一张眼疾图片识别其名称。
14 4
基于Python深度学习的眼疾识别系统实现~人工智能+卷积网络算法
|
8天前
|
机器学习/深度学习 人工智能 算法
Enhance-A-Video:上海 AI Lab 推出视频生成质量增强算法,显著提升 AI 视频生成的真实度和细节表现
Enhance-A-Video 是由上海人工智能实验室、新加坡国立大学和德克萨斯大学奥斯汀分校联合推出的视频生成质量增强算法,能够显著提升视频的对比度、清晰度和细节真实性。
36 8
Enhance-A-Video:上海 AI Lab 推出视频生成质量增强算法,显著提升 AI 视频生成的真实度和细节表现
|
1月前
|
机器学习/深度学习 人工智能 算法
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
宠物识别系统,本系统使用Python作为主要开发语言,基于TensorFlow搭建卷积神经网络算法,并收集了37种常见的猫狗宠物种类数据集【'阿比西尼亚猫(Abyssinian)', '孟加拉猫(Bengal)', '暹罗猫(Birman)', '孟买猫(Bombay)', '英国短毛猫(British Shorthair)', '埃及猫(Egyptian Mau)', '缅因猫(Maine Coon)', '波斯猫(Persian)', '布偶猫(Ragdoll)', '俄罗斯蓝猫(Russian Blue)', '暹罗猫(Siamese)', '斯芬克斯猫(Sphynx)', '美国斗牛犬
167 29
【宠物识别系统】Python+卷积神经网络算法+深度学习+人工智能+TensorFlow+图像识别
|
30天前
|
机器学习/深度学习 缓存 人工智能
【AI系统】QNNPack 算法
QNNPACK是Marat Dukhan开发的量化神经网络计算加速库,专为移动端优化,性能卓越。本文介绍QNNPACK的实现,包括间接卷积算法、内存重排和间接缓冲区等关键技术,有效解决了传统Im2Col+GEMM方法存在的空间消耗大、缓存效率低等问题,显著提升了量化神经网络的计算效率。
40 6
【AI系统】QNNPack 算法
|
30天前
|
存储 人工智能 缓存
【AI系统】Im2Col 算法
Caffe 作为早期的 AI 框架,采用 Im2Col 方法优化卷积计算。Im2Col 将卷积操作转换为矩阵乘法,通过将输入数据重排为连续内存中的矩阵,减少内存访问次数,提高计算效率。该方法首先将输入图像转换为矩阵,然后利用 GEMM 库加速计算,最后将结果转换回原格式。这种方式显著提升了卷积计算的速度,尤其适用于通道数较多的卷积层。
53 5
【AI系统】Im2Col 算法
|
30天前
|
存储 机器学习/深度学习 人工智能
【AI系统】Winograd 算法
本文详细介绍Winograd优化算法,该算法通过增加加法操作来减少乘法操作,从而加速卷积计算。文章首先回顾Im2Col技术和空间组合优化,然后深入讲解Winograd算法原理及其在一维和二维卷积中的应用,最后讨论算法的局限性和实现步骤。Winograd算法在特定卷积参数下表现优异,但其应用范围受限。
37 2
【AI系统】Winograd 算法
|
19天前
|
人工智能 算法
AI+脱口秀,笑点能靠算法创造吗
脱口秀是一种通过幽默诙谐的语言、夸张的表情与动作引发观众笑声的表演艺术。每位演员独具风格,内容涵盖个人情感、家庭琐事及社会热点。尽管我尝试用AI生成脱口秀段子,但AI缺乏真实的情感共鸣和即兴创作能力,生成的内容显得不够自然生动,难以触及人心深处的笑点。例如,AI生成的段子虽然流畅,却少了那份不期而遇的惊喜和激情,无法真正打动观众。 简介:脱口秀是通过幽默语言和夸张表演引发笑声的艺术形式,AI生成的段子虽流畅但缺乏情感共鸣和即兴创作力,难以达到真人表演的效果。
|
26天前
|
机器学习/深度学习 算法 信息无障碍
基于GoogleNet深度学习网络的手语识别算法matlab仿真
本项目展示了基于GoogleNet的深度学习手语识别算法,使用Matlab2022a实现。通过卷积神经网络(CNN)识别手语手势,如&quot;How are you&quot;、&quot;I am fine&quot;、&quot;I love you&quot;等。核心在于Inception模块,通过多尺度处理和1x1卷积减少计算量,提高效率。项目附带完整代码及操作视频。
|
29天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于深度学习网络的宝石类型识别算法matlab仿真
本项目利用GoogLeNet深度学习网络进行宝石类型识别,实验包括收集多类宝石图像数据集并按7:1:2比例划分。使用Matlab2022a实现算法,提供含中文注释的完整代码及操作视频。GoogLeNet通过其独特的Inception模块,结合数据增强、学习率调整和正则化等优化手段,有效提升了宝石识别的准确性和效率。