博客專欄

EEPW首頁 > 博客 > 深度學(xué)習(xí)教你重建趙麗穎的三維人臉

深度學(xué)習(xí)教你重建趙麗穎的三維人臉

發(fā)布人:AI科技大本營 時(shí)間:2021-07-04 來源:工程師 發(fā)布文章

近年來,三維人臉重建成為計(jì)算機(jī)視覺、圖像識(shí)別等研究領(lǐng)域中的熱點(diǎn)問題。三維人臉重建技術(shù)分為基于不同視角的多幅圖像的重建和基于單幅圖像的三維人臉重建。

本項(xiàng)目通過使用PRNet算法,通過訓(xùn)練CNN網(wǎng)絡(luò)實(shí)現(xiàn)對(duì)二維圖像轉(zhuǎn)變?yōu)槿S空間圖像的轉(zhuǎn)換,其中二維使用UV坐標(biāo)作為2D的表達(dá),可以實(shí)現(xiàn)在UV空間保存完整的人臉3D形狀。實(shí)現(xiàn)的效果如下圖所示:

1.jpg2.gif

基本介紹

針對(duì)單幅圖像的三維人臉重建,傳統(tǒng)方法有基于模型的方法、基于明暗形狀恢復(fù)的方法等。

隨著深度學(xué)習(xí)被引入到三維人臉重建領(lǐng)域,并取得比傳統(tǒng)方法更優(yōu)異的效果,逐漸成為主流的重建方法。JacksonAS等人提出使用VRN(volumetric regression networks)從單個(gè)二維圖像直接進(jìn)行三維面部重建的方法。Feng Y等人設(shè)計(jì)了一個(gè)名為UV位置圖的二維表示方法,記錄UV空間中完整面部的三維形狀,然后訓(xùn)練一個(gè)簡單的卷積神經(jīng)網(wǎng)絡(luò),從單個(gè)二維圖像中回歸。

ChangFJ等人提出了直接應(yīng)用于人臉圖像強(qiáng)度,回歸3D表情系數(shù)的29D向量的ExpNet CNN模型。Tu等人針對(duì)3D標(biāo)注訓(xùn)練數(shù)據(jù)短缺問題,提出一種2D輔助自監(jiān)督學(xué)習(xí)方法,利用帶嘈雜地標(biāo)信息的無約束二維人臉圖像改善三維人臉模型的學(xué)習(xí),在密集人臉對(duì)齊和三維人臉重建方面取得了突出的效果。劉成攀等人提出一種基于自監(jiān)督深度學(xué)習(xí)的人臉表征及三維重建方法,將二維人臉的特征點(diǎn)信息映射到三維空間實(shí)現(xiàn)三維人臉重建。

3.jpg

環(huán)境要求

本次環(huán)境使用的是Python3.6.5+Windows平臺(tái)。主要用的庫有:

skimage模塊:skimage包的全稱是scikit-imageSciKit,它對(duì)scipy.ndimage進(jìn)行擴(kuò)展,提供更多的圖片處理功能。

opencv-python模塊:opencv-python是Python的綁定庫,解決計(jì)算機(jī)視覺問題。其使用Numpy,這是一個(gè)高度優(yōu)化的數(shù)據(jù)庫操作庫,具有MATLAB風(fēng)格的語法。所有Opencv數(shù)組結(jié)構(gòu)都轉(zhuǎn)換為Numpy數(shù)組。這也使得與使用Numpy的其他庫(如Scipy和Matplotlib)集成更容易。

Numpy模塊:Numpy是應(yīng)用Python進(jìn)行科學(xué)計(jì)算時(shí)的基礎(chǔ)模塊。它是一個(gè)提供多維數(shù)組對(duì)象的Python庫,包含多種衍生的對(duì)象以及一系列的為快速計(jì)算數(shù)組而生的例程,如數(shù)學(xué)運(yùn)算、邏輯運(yùn)算、形狀操作、排序、選擇、I/O、離散傅里葉變換、基本線性代數(shù)、基本統(tǒng)計(jì)運(yùn)算、隨機(jī)模擬等。

Matplotlib模塊:Matplotlib是Python的2D繪圖庫,以各種硬拷貝格式和跨平臺(tái)的交互式環(huán)境生成出版質(zhì)量級(jí)別的圖形。通過Matplotlib,開發(fā)者僅用幾行代碼便可生成繪圖。Matplot使用Numpy進(jìn)行數(shù)組運(yùn)算,調(diào)用其他Python庫來實(shí)現(xiàn)硬件交互。

模型介紹

模型使用的是端到端的PRN多任務(wù)方法,能完成稠密人臉對(duì)齊和3D人臉形狀重建。主要體現(xiàn)在以下幾個(gè)方面:

1、以端到端方式,實(shí)現(xiàn)了高分辨率的3D人臉重建和稠密對(duì)齊;

2、設(shè)計(jì)了UV位置圖,來記錄人臉的3D位置信息;

3、設(shè)計(jì)了權(quán)重掩模用于loss計(jì)算,loss中每個(gè)點(diǎn)權(quán)重不同,可明顯提高網(wǎng)絡(luò)性能

4、CNN采用輕量級(jí)模式,單張人臉任務(wù)可達(dá)到100FPS

5、在AFLW200-3D和Florence數(shù)據(jù)集上可達(dá)到25%的性能提升

PRNet通過輸入一張圖片,直接使用神經(jīng)網(wǎng)絡(luò)輸出一張稱為UV position map的 UV 位置映射圖。其中UV映射圖維度是一個(gè)三位矩陣,前面兩個(gè)維度上輸出的紋理圖的維度,最后一個(gè)維度表示紋理圖每個(gè)像素在 3D 空間中的位置信息。

其中網(wǎng)絡(luò)模型代碼如下:

class resfcn256(object):
    def __init__(self, resolution_inp = 256, resolution_op = 256, channel = 3, name = 'resfcn256'):
        self.name = name
        self.channel = channel
        self.resolution_inp = resolution_inp
        self.resolution_op = resolution_op
    def __call__(self, x, is_training = True):
        with tf.variable_scope(self.name) as scope:
            with arg_scope([tcl.batch_norm], is_training=is_training, scale=True):
                with arg_scope([tcl.conv2d,tcl.conv2d_transpose], activation_fn=tf.nn.relu,
                                     normalizer_fn=tcl.batch_norm,
                                     biases_initializer=None,
                                     padding='SAME',
                                     weights_regularizer=tcl.l2_regularizer(0.0002)):
                    size = 16 
                    # x: s x s x 3
                    se = tcl.conv2d(x, num_outputs=size, kernel_size=4, stride=1) # 256 x 256 x 16
                    se = resBlock(se, num_outputs=size * 2, kernel_size=4, stride=2) # 128 x 128 x 32
                    se = resBlock(se, num_outputs=size * 2, kernel_size=4, stride=1) # 128 x 128 x 32
                    se = resBlock(se, num_outputs=size * 4, kernel_size=4, stride=2) # 64 x 64 x 64
                    se = resBlock(se, num_outputs=size * 4, kernel_size=4, stride=1) # 64 x 64 x 64
                    se = resBlock(se, num_outputs=size * 8, kernel_size=4, stride=2) # 32 x 32 x 128
                    se = resBlock(se, num_outputs=size * 8, kernel_size=4, stride=1) # 32 x 32 x 128
                    se = resBlock(se, num_outputs=size * 16, kernel_size=4, stride=2) # 16 x 16 x 256
                    se = resBlock(se, num_outputs=size * 16, kernel_size=4, stride=1) # 16 x 16 x 256
                    se = resBlock(se, num_outputs=size * 32, kernel_size=4, stride=2) # 8 x 8 x 512
                    se = resBlock(se, num_outputs=size * 32, kernel_size=4, stride=1) # 8 x 8 x 512
                    pd =tcl.conv2d_transpose(se, size * 32, 4, stride=1) # 8 x 8 x 512
                    pd =tcl.conv2d_transpose(pd, size * 16, 4, stride=2) # 16 x 16 x 256
                    pd =tcl.conv2d_transpose(pd, size * 16, 4, stride=1) # 16 x 16 x 256
                    pd =tcl.conv2d_transpose(pd, size * 16, 4, stride=1) # 16 x 16 x 256
                    pd =tcl.conv2d_transpose(pd, size * 8, 4, stride=2) # 32 x 32 x 128
                    pd =tcl.conv2d_transpose(pd, size * 8, 4, stride=1) # 32 x 32 x 128
                    pd =tcl.conv2d_transpose(pd, size * 8, 4, stride=1) # 32 x 32 x 128
                    pd = tcl.conv2d_transpose(pd, size * 4, 4, stride=2) # 64 x 64 x 64
                    pd =tcl.conv2d_transpose(pd, size * 4, 4, stride=1) # 64 x 64 x 64
                    pd =tcl.conv2d_transpose(pd, size * 4, 4, stride=1) # 64 x 64 x 64
                    pd =tcl.conv2d_transpose(pd, size * 2, 4, stride=2) # 128 x 128 x 32
                    pd =tcl.conv2d_transpose(pd, size * 2, 4, stride=1) # 128 x 128 x 32
                    pd =tcl.conv2d_transpose(pd, size, 4, stride=2) # 256 x 256 x 16
                    pd =tcl.conv2d_transpose(pd, size, 4, stride=1) # 256 x 256 x 16
                    pd =tcl.conv2d_transpose(pd, 3, 4, stride=1) # 256 x 256 x 3
                    pd =tcl.conv2d_transpose(pd, 3, 4, stride=1) # 256 x 256 x 3
                    pos =tcl.conv2d_transpose(pd, 3, 4, stride=1, activation_fn = tf.nn.sigmoid)#, padding='SAME', weights_initializer=tf.random_normal_initializer(0,0.02))
                    return pos

4.png

人臉生成

檢測人臉關(guān)鍵點(diǎn):

通過調(diào)用人臉模型監(jiān)測人臉并將人臉裁剪分出:

cas =cv2.CascadeClassifier('./Data/cv-data/haarcascade_frontalface_alt2.xml')

img = plt.imread('./images/zly.jpg')

img_gray= cv2.cvtColor(img,cv2.COLOR_BGR2RGB)

faces = cas.detectMultiScale(img_gray,2,3,0,(30,30))

bbox = np.array([faces[0,0],faces[0,1],faces[0,0]+faces[0,2],faces[0,1]+faces[0,3]])

plt.imshow(cv2.rectangle(img.copy(),(bbox[0],bbox[1]),(bbox[2],bbox[3]),(0,255,0),2))

plt.axis('off')

plt.show()

left = bbox[0]; top = bbox[1]; right = bbox[2]; bottom = bbox[3]

old_size = (right - left + bottom - top)/2

center = np.array([right- (right - left) / 2.0, bottom - (bottom -top) / 2.0])

size = int(old_size*1.6)

src_pts = np.array([[center[0]-size/2, center[1]-size/2],

                    [center[0] - size/2, center[1]+size/2],

                    [center[0]+size/2, center[1]-size/2]])

DST_PTS = np.array([[0,0], [0,255], [255, 0]]) #圖像大小256*256

tform =estimate_transform('similarity', src_pts, DST_PTS)

img = img/255.

cropped_img =warp(img, tform.inverse, output_shape=(256, 256))

5.png

獲取人臉點(diǎn)云:

三維點(diǎn)云是一個(gè)在三維空間關(guān)聯(lián)系統(tǒng)中包含點(diǎn)的數(shù)據(jù)庫,即事物或空間的精確數(shù)字記錄,主要關(guān)于表面點(diǎn)的集合。與相片不同,三維圖像是對(duì)一類信息的統(tǒng)稱,信息還需要有具體的表現(xiàn)形式。

其表現(xiàn)形式包括:深度圖(以灰度表達(dá)物體與相機(jī)的距離),幾何模型(由CAD軟件建立),點(diǎn)云模型(所有逆向工程設(shè)備都將物體采樣成點(diǎn)云)。可見,點(diǎn)云數(shù)據(jù)是最為常見也是最基礎(chǔ)的三維模型。點(diǎn)云模型往往由測量直接得到每個(gè)點(diǎn)對(duì)應(yīng)一個(gè)測量點(diǎn),未經(jīng)過其他處理手段,故包含了最大的信息量。

對(duì)應(yīng)代碼如下:

face_ind =np.loadtxt('./Data/uv-data/face_ind.txt').astype(np.int32)
all_vertices = np.reshape(pos, [256*256, -1])
vertices = all_vertices[face_ind, :]
plt.figure(figsize=(8,8))
plt.imshow(draw_kps(img.copy(),vertices[:,:2],1))
plt.axis('off')
plt.show()
texture = cv2.remap(img, pos[:,:,:2].astype(np.float32), None, interpolation=cv2.INTER_NEAREST, borderMode=cv2.BORDER_CONSTANT,borderValue=(0))

6.png

生成三維人臉:

首先是獲取三角形每個(gè)頂點(diǎn)的depth,平均值作為三角形高度。接著獲取三角形每個(gè)頂點(diǎn)的color,平均值作為三角形顏色。對(duì)應(yīng)代碼如下:

tri_depth = (vertices[triangles[:,0],2 ] +vertices[triangles[:,1],2] +vertices[triangles[:,2],2])/3.
# tri_tex =(colors[triangles[:,0] ,:] +colors[triangles[:,1],:] +colors[triangles[:,2],:])/3.
tri_tex = tri_tex*255
img_3D =np.zeros_like(img,dtype=np.uint8)
for i in range(triangles.shape[0]):
    cnt =np.array([(vertices[triangles[i,0],0],vertices[triangles[i,0],1]),
           (vertices[triangles[i,1],0],vertices[triangles[i,1],1]),
           (vertices[triangles[i,2],0],vertices[triangles[i,2],1])],dtype=np.int32)
    img_3D =cv2.drawContours(img_3D,[cnt],0,tri_tex[i],-1)

7.png

代碼鏈接:

https://pan.baidu.com/s/1s3NjgxuuCnB238GpzN-6Pw

提取碼:ucqj

*博客內(nèi)容為網(wǎng)友個(gè)人發(fā)布,僅代表博主個(gè)人觀點(diǎn),如有侵權(quán)請(qǐng)聯(lián)系工作人員刪除。



關(guān)鍵詞: 深度學(xué)習(xí)

相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉