sstutu 发表于 2018-7-30 15:12:02

深度学习AI如何实现美颜

本帖最后由 sstutu 于 2018-7-30 15:17 编辑

问题导读

1.深度学习分割算法有哪些?
2.换发色算法包含哪些流程?
3.Unet算法如何实现?
4.本文使用Unet算法如何实现美颜的?

static/image/hrline/line7.png


给照片或者视频中的人物头发换颜色,这个技术已经在手机app诸如天天P图,美图秀秀等应用中使用,并获得了不少用户的青睐。

如何给照片或者视频中的人物头发换发色?

换发色算法流程如下图所示:





基于深度学习的目标分割算法已经比较成熟,比较常用的有FCN,SegNet,UNet,PspNet,DenseNet等等。


1.详细介绍Unet

分割的方法有很多,CNN/FCN/UNet/DenseNet等等,这里我们使用UNet进行皮肤分割:
Unet做图像分割,它最开始的网络模型如下:



这是一个全卷积神经网络,输入和输出都是图像,没有全连接层,较浅的高分辨率层用来解决像素定位的问题,较深的层用来解决像素分类的问题;


左边进行卷积和下采样,同时保留当前结果,右边进行上采样时将上采样结果和左边对应结果进行融合,以此来提高分割效果;


这个网络中左右是不对称的,后来改进的Unet基本上在图像分辨率上呈现出对称的样式,本文这里使用Keras来实现,网络结构如下:
Layer (type)                  Output Shape         Param #   Connected to                     
==================================================================================================
input_1 (InputLayer)            (None, 256, 256, 3)0                                          
__________________________________________________________________________________________________
conv2d_1 (Conv2D)               (None, 256, 256, 32) 896         input_1                  
__________________________________________________________________________________________________
batch_normalization_1 (BatchNor (None, 256, 256, 32) 128         conv2d_1                  
__________________________________________________________________________________________________
activation_1 (Activation)       (None, 256, 256, 32) 0         batch_normalization_1      
__________________________________________________________________________________________________
conv2d_2 (Conv2D)               (None, 256, 256, 32) 9248      activation_1               
__________________________________________________________________________________________________
batch_normalization_2 (BatchNor (None, 256, 256, 32) 128         conv2d_2                  
__________________________________________________________________________________________________
activation_2 (Activation)       (None, 256, 256, 32) 0         batch_normalization_2      
__________________________________________________________________________________________________
max_pooling2d_1 (MaxPooling2D)(None, 128, 128, 32) 0         activation_2               
__________________________________________________________________________________________________
conv2d_3 (Conv2D)               (None, 128, 128, 64) 18496       max_pooling2d_1            
__________________________________________________________________________________________________
batch_normalization_3 (BatchNor (None, 128, 128, 64) 256         conv2d_3                  
__________________________________________________________________________________________________
activation_3 (Activation)       (None, 128, 128, 64) 0         batch_normalization_3      
__________________________________________________________________________________________________
conv2d_4 (Conv2D)               (None, 128, 128, 64) 36928       activation_3               
__________________________________________________________________________________________________
batch_normalization_4 (BatchNor (None, 128, 128, 64) 256         conv2d_4                  
__________________________________________________________________________________________________
activation_4 (Activation)       (None, 128, 128, 64) 0         batch_normalization_4      
__________________________________________________________________________________________________
max_pooling2d_2 (MaxPooling2D)(None, 64, 64, 64)   0         activation_4               
__________________________________________________________________________________________________
conv2d_5 (Conv2D)               (None, 64, 64, 128)73856       max_pooling2d_2            
__________________________________________________________________________________________________
batch_normalization_5 (BatchNor (None, 64, 64, 128)512         conv2d_5                  
__________________________________________________________________________________________________
activation_5 (Activation)       (None, 64, 64, 128)0         batch_normalization_5      
__________________________________________________________________________________________________
conv2d_6 (Conv2D)               (None, 64, 64, 128)147584      activation_5               
__________________________________________________________________________________________________
batch_normalization_6 (BatchNor (None, 64, 64, 128)512         conv2d_6                  
__________________________________________________________________________________________________
activation_6 (Activation)       (None, 64, 64, 128)0         batch_normalization_6      
__________________________________________________________________________________________________
max_pooling2d_3 (MaxPooling2D)(None, 32, 32, 128)0         activation_6               
__________________________________________________________________________________________________
conv2d_7 (Conv2D)               (None, 32, 32, 256)295168      max_pooling2d_3            
__________________________________________________________________________________________________
batch_normalization_7 (BatchNor (None, 32, 32, 256)1024      conv2d_7                  
__________________________________________________________________________________________________
activation_7 (Activation)       (None, 32, 32, 256)0         batch_normalization_7      
__________________________________________________________________________________________________
conv2d_8 (Conv2D)               (None, 32, 32, 256)590080      activation_7               
__________________________________________________________________________________________________
batch_normalization_8 (BatchNor (None, 32, 32, 256)1024      conv2d_8                  
__________________________________________________________________________________________________
activation_8 (Activation)       (None, 32, 32, 256)0         batch_normalization_8      
__________________________________________________________________________________________________
max_pooling2d_4 (MaxPooling2D)(None, 16, 16, 256)0         activation_8               
__________________________________________________________________________________________________
conv2d_9 (Conv2D)               (None, 16, 16, 512)1180160   max_pooling2d_4            
__________________________________________________________________________________________________
batch_normalization_9 (BatchNor (None, 16, 16, 512)2048      conv2d_9                  
__________________________________________________________________________________________________
activation_9 (Activation)       (None, 16, 16, 512)0         batch_normalization_9      
__________________________________________________________________________________________________
conv2d_10 (Conv2D)            (None, 16, 16, 512)2359808   activation_9               
__________________________________________________________________________________________________
batch_normalization_10 (BatchNo (None, 16, 16, 512)2048      conv2d_10                  
__________________________________________________________________________________________________
activation_10 (Activation)      (None, 16, 16, 512)0         batch_normalization_10   
__________________________________________________________________________________________________
max_pooling2d_5 (MaxPooling2D)(None, 8, 8, 512)    0         activation_10            
__________________________________________________________________________________________________
conv2d_11 (Conv2D)            (None, 8, 8, 1024)   4719616   max_pooling2d_5            
__________________________________________________________________________________________________
batch_normalization_11 (BatchNo (None, 8, 8, 1024)   4096      conv2d_11                  
__________________________________________________________________________________________________
activation_11 (Activation)      (None, 8, 8, 1024)   0         batch_normalization_11   
__________________________________________________________________________________________________
conv2d_12 (Conv2D)            (None, 8, 8, 1024)   9438208   activation_11            
__________________________________________________________________________________________________
batch_normalization_12 (BatchNo (None, 8, 8, 1024)   4096      conv2d_12                  
__________________________________________________________________________________________________
activation_12 (Activation)      (None, 8, 8, 1024)   0         batch_normalization_12   
__________________________________________________________________________________________________
up_sampling2d_1 (UpSampling2D)(None, 16, 16, 1024) 0         activation_12            
__________________________________________________________________________________________________
concatenate_1 (Concatenate)   (None, 16, 16, 1536) 0         activation_10            
                                                               up_sampling2d_1            
__________________________________________________________________________________________________
conv2d_13 (Conv2D)            (None, 16, 16, 512)7078400   concatenate_1            
__________________________________________________________________________________________________
batch_normalization_13 (BatchNo (None, 16, 16, 512)2048      conv2d_13                  
__________________________________________________________________________________________________
activation_13 (Activation)      (None, 16, 16, 512)0         batch_normalization_13   
__________________________________________________________________________________________________
conv2d_14 (Conv2D)            (None, 16, 16, 512)2359808   activation_13            
__________________________________________________________________________________________________
batch_normalization_14 (BatchNo (None, 16, 16, 512)2048      conv2d_14                  
__________________________________________________________________________________________________
activation_14 (Activation)      (None, 16, 16, 512)0         batch_normalization_14   
__________________________________________________________________________________________________
conv2d_15 (Conv2D)            (None, 16, 16, 512)2359808   activation_14            
__________________________________________________________________________________________________
batch_normalization_15 (BatchNo (None, 16, 16, 512)2048      conv2d_15                  
__________________________________________________________________________________________________
activation_15 (Activation)      (None, 16, 16, 512)0         batch_normalization_15   
__________________________________________________________________________________________________
up_sampling2d_2 (UpSampling2D)(None, 32, 32, 512)0         activation_15            
__________________________________________________________________________________________________
concatenate_2 (Concatenate)   (None, 32, 32, 768)0         activation_8               
                                                               up_sampling2d_2            
__________________________________________________________________________________________________
conv2d_16 (Conv2D)            (None, 32, 32, 256)1769728   concatenate_2            
__________________________________________________________________________________________________
batch_normalization_16 (BatchNo (None, 32, 32, 256)1024      conv2d_16                  
__________________________________________________________________________________________________
activation_16 (Activation)      (None, 32, 32, 256)0         batch_normalization_16   
__________________________________________________________________________________________________
conv2d_17 (Conv2D)            (None, 32, 32, 256)590080      activation_16            
__________________________________________________________________________________________________
batch_normalization_17 (BatchNo (None, 32, 32, 256)1024      conv2d_17                  
__________________________________________________________________________________________________
activation_17 (Activation)      (None, 32, 32, 256)0         batch_normalization_17   
__________________________________________________________________________________________________
conv2d_18 (Conv2D)            (None, 32, 32, 256)590080      activation_17            
__________________________________________________________________________________________________
batch_normalization_18 (BatchNo (None, 32, 32, 256)1024      conv2d_18                  
__________________________________________________________________________________________________
activation_18 (Activation)      (None, 32, 32, 256)0         batch_normalization_18   
__________________________________________________________________________________________________
up_sampling2d_3 (UpSampling2D)(None, 64, 64, 256)0         activation_18            
__________________________________________________________________________________________________
concatenate_3 (Concatenate)   (None, 64, 64, 384)0         activation_6               
                                                               up_sampling2d_3            
__________________________________________________________________________________________________
conv2d_19 (Conv2D)            (None, 64, 64, 128)442496      concatenate_3            
__________________________________________________________________________________________________
batch_normalization_19 (BatchNo (None, 64, 64, 128)512         conv2d_19                  
__________________________________________________________________________________________________
activation_19 (Activation)      (None, 64, 64, 128)0         batch_normalization_19   
__________________________________________________________________________________________________
conv2d_20 (Conv2D)            (None, 64, 64, 128)147584      activation_19            
__________________________________________________________________________________________________
batch_normalization_20 (BatchNo (None, 64, 64, 128)512         conv2d_20                  
__________________________________________________________________________________________________
activation_20 (Activation)      (None, 64, 64, 128)0         batch_normalization_20   
__________________________________________________________________________________________________
conv2d_21 (Conv2D)            (None, 64, 64, 128)147584      activation_20            
__________________________________________________________________________________________________
batch_normalization_21 (BatchNo (None, 64, 64, 128)512         conv2d_21                  
__________________________________________________________________________________________________
activation_21 (Activation)      (None, 64, 64, 128)0         batch_normalization_21   
__________________________________________________________________________________________________
up_sampling2d_4 (UpSampling2D)(None, 128, 128, 128 0         activation_21            
__________________________________________________________________________________________________
concatenate_4 (Concatenate)   (None, 128, 128, 192 0         activation_4               
                                                               up_sampling2d_4            
__________________________________________________________________________________________________
conv2d_22 (Conv2D)            (None, 128, 128, 64) 110656      concatenate_4            
__________________________________________________________________________________________________
batch_normalization_22 (BatchNo (None, 128, 128, 64) 256         conv2d_22                  
__________________________________________________________________________________________________
activation_22 (Activation)      (None, 128, 128, 64) 0         batch_normalization_22   
__________________________________________________________________________________________________
conv2d_23 (Conv2D)            (None, 128, 128, 64) 36928       activation_22            
__________________________________________________________________________________________________
batch_normalization_23 (BatchNo (None, 128, 128, 64) 256         conv2d_23                  
__________________________________________________________________________________________________
activation_23 (Activation)      (None, 128, 128, 64) 0         batch_normalization_23   
__________________________________________________________________________________________________
conv2d_24 (Conv2D)            (None, 128, 128, 64) 36928       activation_23            
__________________________________________________________________________________________________
batch_normalization_24 (BatchNo (None, 128, 128, 64) 256         conv2d_24                  
__________________________________________________________________________________________________
activation_24 (Activation)      (None, 128, 128, 64) 0         batch_normalization_24   
__________________________________________________________________________________________________
up_sampling2d_5 (UpSampling2D)(None, 256, 256, 64) 0         activation_24            
__________________________________________________________________________________________________
concatenate_5 (Concatenate)   (None, 256, 256, 96) 0         activation_2               
                                                               up_sampling2d_5            
__________________________________________________________________________________________________
conv2d_25 (Conv2D)            (None, 256, 256, 32) 27680       concatenate_5            
__________________________________________________________________________________________________
batch_normalization_25 (BatchNo (None, 256, 256, 32) 128         conv2d_25                  
__________________________________________________________________________________________________
activation_25 (Activation)      (None, 256, 256, 32) 0         batch_normalization_25   
__________________________________________________________________________________________________
conv2d_26 (Conv2D)            (None, 256, 256, 32) 9248      activation_25            
__________________________________________________________________________________________________
batch_normalization_26 (BatchNo (None, 256, 256, 32) 128         conv2d_26                  
__________________________________________________________________________________________________
activation_26 (Activation)      (None, 256, 256, 32) 0         batch_normalization_26   
__________________________________________________________________________________________________
conv2d_27 (Conv2D)            (None, 256, 256, 32) 9248      activation_26            
__________________________________________________________________________________________________
batch_normalization_27 (BatchNo (None, 256, 256, 32) 128         conv2d_27                  
__________________________________________________________________________________________________
activation_27 (Activation)      (None, 256, 256, 32) 0         batch_normalization_27   
__________________________________________________________________________________________________
conv2d_28 (Conv2D)            (None, 256, 256, 1)33          activation_27            
==================================================================================================


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 = UpSampling2D((2, 2))(center)
    up4 = concatenate(, 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)
    up4 = Conv2D(512, (3, 3), padding='same')(up4)
    up4 = BatchNormalization()(up4)
    up4 = Activation('relu')(up4)
    # 16

    up3 = UpSampling2D((2, 2))(up4)
    up3 = concatenate(, 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)
    up3 = Conv2D(256, (3, 3), padding='same')(up3)
    up3 = BatchNormalization()(up3)
    up3 = Activation('relu')(up3)
    # 32

    up2 = UpSampling2D((2, 2))(up3)
    up2 = concatenate(, 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)
    up2 = Conv2D(128, (3, 3), padding='same')(up2)
    up2 = BatchNormalization()(up2)
    up2 = Activation('relu')(up2)
    # 64

    up1 = UpSampling2D((2, 2))(up2)
    up1 = concatenate(, 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)
    up1 = Conv2D(64, (3, 3), padding='same')(up1)
    up1 = BatchNormalization()(up1)
    up1 = Activation('relu')(up1)
    # 128

    up0 = UpSampling2D((2, 2))(up1)
    up0 = concatenate(, 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)
    up0 = Conv2D(32, (3, 3), padding='same')(up0)
    up0 = BatchNormalization()(up0)
    up0 = Activation('relu')(up0)
    # 256

    classify = Conv2D(num_classes, (1, 1), activation='sigmoid')(up0)

    model = Model(inputs=inputs, outputs=classify)

    #model.compile(optimizer=RMSprop(lr=0.0001), loss=bce_dice_loss, metrics=)

return model


输入为256X256X3的彩色图,输出为256X256X1的MASK,训练参数如下:
model.compile(optimizer = "adam", loss = 'binary_crossentropy',metrics = ["accuracy"])

model.fit(image_train, label_train,epochs=100,verbose=1,validation_split=0.2, shuffle=True,batch_size=8)


效果图如下:





本人这里训练集中样本标定是把人脸区域都当作了肤色区域,因此没有排除五官区域,如果要得到不包含五官的皮肤区域,只需要替换相应样本就可以了。
拿到了精确的肤色区域,我们就可以更新磨皮算法,这里给出一组效果图:








大家可以看到,基于颜色空间的传统磨皮算法始终无法精确区分皮肤区域与类肤色区域,因此在头发的地方也做了磨皮操作,导致头发纹理细节丢失,而基于Unet皮肤分割的磨皮算法则可以很好的区分皮肤与头发这种类肤色区域,进而将头发的纹理细节保留,达到该磨皮的地方磨皮,不该磨皮的地方不磨,效果明显优于传统方法。


2.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 = UpSampling2D((2, 2))(center)
    up4 = concatenate(, 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)
    up4 = Conv2D(512, (3, 3), padding='same')(up4)
    up4 = BatchNormalization()(up4)
    up4 = Activation('relu')(up4)
    # 16

    up3 = UpSampling2D((2, 2))(up4)
    up3 = concatenate(, 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)
    up3 = Conv2D(256, (3, 3), padding='same')(up3)
    up3 = BatchNormalization()(up3)
    up3 = Activation('relu')(up3)
    # 32

    up2 = UpSampling2D((2, 2))(up3)
    up2 = concatenate(, 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)
    up2 = Conv2D(128, (3, 3), padding='same')(up2)
    up2 = BatchNormalization()(up2)
    up2 = Activation('relu')(up2)
    # 64

    up1 = UpSampling2D((2, 2))(up2)
    up1 = concatenate(, 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)
    up1 = Conv2D(64, (3, 3), padding='same')(up1)
    up1 = BatchNormalization()(up1)
    up1 = Activation('relu')(up1)
    # 128

    up0 = UpSampling2D((2, 2))(up1)
    up0 = concatenate(, 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)
    up0 = Conv2D(32, (3, 3), padding='same')(up0)
    up0 = BatchNormalization()(up0)
    up0 = Activation('relu')(up0)
    # 256

    classify = Conv2D(num_classes, (1, 1), activation='sigmoid')(up0)

    model = Model(inputs=inputs, outputs=classify)

    #model.compile(optimizer=RMSprop(lr=0.0001), loss=bce_dice_loss, metrics=)

return model


分割效果举例如下:





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

3.头发换色模块
这个模块看起来比较简单,实际上却并非如此。

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

①头发颜色增强与修正模块

为什么要颜色增强与修正?

先看下面一组图,我们直接使用HSV颜色空间对纯黑色的头发进行染色,目标色是紫色,结果如下:






大家可以看到,针对上面这张原图,头发比较黑,在HSV颜色空间进行头发换色之后,效果图中很不明显,只有轻微的颜色变化;

为什么会出现这种情况?原因如下:

我们以RGB和HSV颜色空间为例,首先来看下HSV和RGB之间的转换公式:

设 (r, g, b)分别是一个颜色的红、绿和蓝坐标,它们的值是在0到1之间的实数。设max等价于r, g和b中的最大者。设min等于这些值中的最小者。要找到在HSL空间中的 (h, s, l)值,这里的h ∈ 是饱和度和亮度,计算为:




我们假设头发为纯黑色,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 = , s = , v =
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 = 0;
    else
      *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 = 360 + 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;
      g = t;
      b = p;
      break;
    case 1:
      r = q;
      g = 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实现实时染发是没有问题的。



原文链接
https://blog.csdn.net/trent1985/article/details/80944942


页: [1]
查看完整版本: 深度学习AI如何实现美颜