文章目錄
- dlib換臉介紹
- 仿射變換
- 在 dlib 換臉中的應用
- 換臉操作
dlib換臉介紹
- dlib 換臉是基于 dlib 庫實現的一種人臉替換技術,以下是關于它的詳細介紹:
- 原理
- 人臉檢測:dlib 庫中包含先進的人臉檢測器,如基于 HOG(方向梯度直方圖)特征和線性分類器的方法,能夠在圖像或視頻幀中快速定位人臉的位置,確定人臉的邊界框。
- 關鍵點檢測:利用 dlib 預訓練的形狀預測模型,如shape_predictor_68_face_landmarks.dat,可以精準定位出人臉的 68 個關鍵點,這些點分布在眼睛、眉毛、鼻子、嘴巴、下巴等部位,精確描述了人臉的形狀和表情。
- 圖像變換與融合:通過計算源人臉和目標人臉關鍵點之間的變換關系,如仿射變換、Delaunay 三角剖分等,將源人臉的形狀和姿態調整到與目標人臉匹配。然后,采用圖像融合技術,將調整后的源人臉與目標圖像進行融合,使換臉效果更加自然。
- 實現步驟
- 安裝相關庫:主要安裝 dlib 庫,還可能需要 opencv、numpy 等庫配合。在 Python 中可以使用pip install dlib等命令安裝。
- 人臉檢測與關鍵點提取:使用 dlib 的人臉檢測器和形狀預測器,加載圖像或視頻幀,檢測其中的人臉并提取關鍵點。
- 計算變換關系:根據源人臉和目標人臉的關鍵點,計算出兩者之間的變換矩陣,確定如何將源人臉變換到目標人臉的位置和姿態。
- 人臉替換:將源人臉按照計算出的變換關系進行變形和調整,然后將其融合到目標圖像的對應位置上。
- 后處理:對換臉后的圖像進行一些后處理操作,如調整顏色、亮度、對比度等,使換臉效果更加自然逼真。
- 優缺點
- 優點:具有較高的人臉檢測和關鍵點定位精度,能夠處理不同姿態、表情和光照條件下的人臉;提供了豐富的函數和工具,方便開發者進行二次開發和集成;在處理速度上相對較快,能夠滿足一些實時性要求不高的應用場景。
- 缺點:對圖像和視頻的質量要求較高,如果輸入的圖像分辨率低、模糊或存在遮擋,可能會影響換臉效果;對于復雜的場景和特殊的光照條件,換臉效果可能不夠自然;在實時性要求較高的場景中,如實時視頻通話換臉,可能需要進一步優化才能滿足性能要求。
- 原理
仿射變換
仿射變換(Affine Transformation)是一種在二維或三維空間中廣泛應用的線性變換,它能夠保持圖像的 “平直性” 和 “平行性”,在計算機視覺、圖形學等領域有諸多應用,比如在 dlib 換臉中就用于調整源人臉的形狀和姿態以匹配目標人臉。
在 dlib 換臉中的應用
圖片:
仿射變換代碼:
import cv2
import numpy as np# 讀取圖像,這里讀取的圖像文件名為 'c_luo.png'
# cv2.imread 函數用于從指定路徑讀取圖像,返回一個表示圖像的 NumPy 數組
img = cv2.imread('c_luo.png')# 獲取圖像的高度和寬度
# img.shape 返回一個包含圖像維度信息的元組,元組的前兩個元素分別是圖像的高度和寬度
height, width = img.shape[:2]# 定義源圖像的三個關鍵點
# 這里使用 np.float32 創建一個 3x2 的浮點型 NumPy 數組
# 三個點分別是圖像的左上角 (0, 0)、左下角 (0, height - 1) 和右上角 (width - 1, 0)
mat_src = np.float32([[0, 0], [0, height - 1], [width - 1, 0]])# 定義目標圖像對應的三個關鍵點
# 同樣是一個 3x2 的浮點型 NumPy 數組
# 這里的三個點是經過變換后源圖像三個關鍵點對應的目標位置
mat_dst = np.float32([[0, 0], [100, height - 100], [width - 100, 100]])# 計算仿射變換矩陣
# cv2.getAffineTransform 函數根據源圖像和目標圖像的三個對應關鍵點,計算仿射變換矩陣
# 該矩陣用于描述從源圖像到目標圖像的仿射變換關系
M = cv2.getAffineTransform(mat_src, mat_dst)# 應用仿射變換
# cv2.warpAffine 函數將仿射變換應用到輸入圖像 img 上
# M 是前面計算得到的仿射變換矩陣
# (width, height) 是輸出圖像的大小
dst = cv2.warpAffine(img, M, (width, height))# 將原始圖像和經過仿射變換后的圖像水平拼接在一起
# np.hstack 函數用于將多個數組水平堆疊,方便同時顯示原始圖像和變換后的圖像
imgs = np.hstack([img, dst])# 創建一個可調整大小的窗口
# cv2.namedWindow 函數用于創建一個指定名稱的窗口,cv2.WINDOW_NORMAL 表示窗口可以調整大小
cv2.namedWindow('imgs', cv2.WINDOW_NORMAL)# 在創建的窗口中顯示拼接后的圖像
cv2.imshow('imgs', imgs)# 等待用戶按下任意按鍵
# cv2.waitKey(0) 表示無限期等待用戶按鍵,按鍵后程序繼續執行
cv2.waitKey(0)
這段代碼的主要功能是對一張圖像進行仿射變換,并將原始圖像和變換后的圖像拼接在一起顯示。通過指定源圖像和目標圖像的三個對應關鍵點,計算出仿射變換矩陣,然后將該變換應用到圖像上。
結果:
換臉操作
對下方圖片進行換臉操作:
實現代碼:
import cv2
import dlib
import numpy as np# 定義人臉關鍵點的索引范圍
# 下巴關鍵點索引
JAW_POINTS = list(range(0, 17))
# 右眉毛關鍵點索引
RIGHT_BROW_POINTS = list(range(17, 22))
# 左眉毛關鍵點索引
LEFT_BROW_POINTS = list(range(22, 27))
# 鼻子關鍵點索引
NOSE_POINTS = list(range(27, 35))
# 右眼關鍵點索引
RIGHT_EYE_POINTS = list(range(36, 42))
# 左眼關鍵點索引
LEFT_EYE_POINTS = list(range(42, 48))
# 嘴巴關鍵點索引
MOUTH_POINTS = list(range(48, 61))
# 臉部關鍵點索引(不包括下巴)
FACE_POINTS = list(range(17, 68))# 選取用于變換的關鍵點集合
POINTS = [LEFT_BROW_POINTS + RIGHT_EYE_POINTS + LEFT_EYE_POINTS + RIGHT_BROW_POINTS + NOSE_POINTS + MOUTH_POINTS]
# 將關鍵點集合轉換為元組
POINTStuple = tuple(POINTS)def getFaceMask(im, keyPoints):"""生成人臉掩碼:param im: 輸入圖像:param keyPoints: 人臉關鍵點:return: 人臉掩碼圖像"""# 創建一個與輸入圖像高度和寬度相同的零矩陣im = np.zeros(im.shape[:2], dtype=np.float64)for p in POINTS:# 計算關鍵點的凸包points = cv2.convexHull(keyPoints[p])# 填充凸包區域為 1cv2.fillConvexPoly(im, points, color=1)# 將單通道掩碼擴展為三通道im = np.array([im, im, im]).transpose((1, 2, 0))# 對掩碼進行高斯模糊處理im = cv2.GaussianBlur(im, (25, 25), 0)return imdef getM(points1, points2):"""計算仿射變換矩陣:param points1: 源圖像的關鍵點:param points2: 目標圖像的關鍵點:return: 仿射變換矩陣"""points1 = points1.astype(np.float64)points2 = points2.astype(np.float64)# 計算關鍵點的均值c1 = np.mean(points1, axis=0)c2 = np.mean(points2, axis=0)# 減去均值進行中心化points1 -= c1points2 -= c2# 計算關鍵點的標準差s1 = np.std(points1)s2 = np.std(points2)# 歸一化關鍵點points1 /= s1points2 /= s2# 進行奇異值分解U, S, Vt = np.linalg.svd(points1.T * points2)# 計算旋轉矩陣R = (U * Vt).Treturn np.hstack(((s2 / s1) * R, c2.T - (s2 / s1) * R * c1.T))def getKeyPoints(im):"""檢測圖像中的人臉關鍵點:param im: 輸入圖像:return: 人臉關鍵點矩陣"""# 使用 dlib 的人臉檢測器檢測人臉rects = detector(im, 1)# 使用 dlib 的形狀預測器預測關鍵點shape = predictor(im, rects[0])# 將關鍵點轉換為矩陣形式s = np.matrix([[p.x, p.y] for p in shape.parts()])return sdef normalColor(a, b):"""顏色校正:param a: 源圖像:param b: 目標圖像:return: 顏色校正后的目標圖像"""# 定義高斯核大小ksize = (71, 71)# 對源圖像和目標圖像進行高斯模糊aGauss = cv2.GaussianBlur(a, ksize, 0)bGauss = cv2.GaussianBlur(b, ksize, 0)# 避免除以零bGauss = np.where(bGauss == 0, 1e-8, bGauss)# 計算權重weight = aGauss / bGaussreturn b * weight# 讀取源圖像和目標圖像
a = cv2.imread('c_luo.png')
b = cv2.imread('mu_ba_pei.png')# 初始化 dlib 的人臉檢測器
detector = dlib.get_frontal_face_detector()
# 初始化 dlib 的形狀預測器,需要提前下載 shape_predictor_68_face_landmarks.dat 文件
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')# 檢測源圖像和目標圖像的人臉關鍵點
aKeyPoints = getKeyPoints(a)
bKeyPoints = getKeyPoints(b)# 保存目標圖像的原始副本
bOriginal = b.copy()# 生成源圖像的人臉掩碼
aMask = getFaceMask(a, aKeyPoints)
# 顯示源圖像的人臉掩碼
cv2.imshow('aMask', aMask)
cv2.waitKey()# 生成目標圖像的人臉掩碼(使用源圖像的關鍵點)
bMask = getFaceMask(b, aKeyPoints)
# 顯示目標圖像的人臉掩碼
cv2.imshow('bMask', bMask)
cv2.waitKey()# 計算仿射變換矩陣
M = getM(aKeyPoints[POINTStuple], bKeyPoints[POINTStuple])# 獲取源圖像的尺寸(寬度和高度)
dsize = a.shape[:2][::-1]# 對目標圖像的掩碼進行仿射變換
bMaskWarp = cv2.warpAffine(bMask, M, dsize,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)
# 顯示變換后的目標圖像掩碼
cv2.imshow('bMaskWarp', bMaskWarp)
cv2.waitKey()# 合并源圖像掩碼和變換后的目標圖像掩碼
mask = np.max([aMask, bMaskWarp], axis=0)
# 顯示合并后的掩碼
cv2.imshow('mask', mask)
cv2.waitKey()# 對目標圖像進行仿射變換
bWrap = cv2.warpAffine(b, M, dsize,borderMode=cv2.BORDER_TRANSPARENT,flags=cv2.WARP_INVERSE_MAP)
# 顯示變換后的目標圖像
cv2.imshow('bWrap', bWrap)
cv2.waitKey()# 對變換后的目標圖像進行顏色校正
bcolor = normalColor(a, bWrap)
# 顯示顏色校正后的目標圖像
cv2.imshow('bcolor', bcolor)
cv2.waitKey()# 融合源圖像和顏色校正后的目標圖像
out = a * (1.0 - mask) + bcolor * mask
# 顯示源圖像
cv2.imshow('a', a)
# 顯示目標圖像的原始副本
cv2.imshow('b', bOriginal)
# 顯示換臉后的結果圖像
cv2.imshow('out', out / 255)
cv2.waitKey()
# 關閉所有 OpenCV 窗口
cv2.destroyAllWindows()
結果:
可通過結果看出換臉成功。