Python實(shí)戰(zhàn)之基于OpenCV的美顏掛件制作
基于 Snapchat 的增強(qiáng)現(xiàn)實(shí)
胡子掛件融合
第一個(gè)項(xiàng)目中,我們將在檢測到的臉上覆蓋了一個(gè)小胡子。我們可以使用從攝像頭捕獲的連續(xù)視頻幀,也可以使用單張測試圖像。在進(jìn)行實(shí)際講解程序的關(guān)鍵步驟前,首先查看應(yīng)用程序預(yù)期輸出的結(jié)果圖像:
項(xiàng)目的第一步是檢測圖像中的人臉。如上圖所示,使用青色矩形繪制圖像中檢測到的人臉的位置和大??;接下來迭代圖像中所有檢測到的人臉,在其區(qū)域內(nèi)搜索鼻子,粉紅色矩形表示圖像中檢測到的鼻子;檢測到鼻子之后,就要根據(jù)之前計(jì)算出的鼻子的位置和大小來調(diào)整我們想要覆蓋“胡子”掛件的區(qū)域,藍(lán)色矩形表示計(jì)算獲得的胡須將被覆蓋的區(qū)域位置。如果處理的目標(biāo)是連續(xù)視頻幀,在處理完成所有檢測到的人臉后,將繼續(xù)分析下一幀。
根據(jù)上述描述,程序應(yīng)當(dāng)首先檢測圖像中的人臉和鼻子。為了檢測這些對(duì)象,創(chuàng)建了兩個(gè)分類器,一個(gè)用于檢測人臉,另一個(gè)用于檢測鼻子:
# 用于人臉和鼻子檢測的級(jí)聯(lián)分類器 face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") nose_cascade = cv2.CascadeClassifier("haarcascade_mcs_nose.xml")
一旦創(chuàng)建了分類器,下一步就是使用 cv2.detectMultiScale()
函數(shù)檢測圖像中的這些對(duì)象。cv2.detectMultiScale()
函數(shù)檢測輸入灰度圖像中不同大小的對(duì)象,并將檢測到的對(duì)象作為矩形列表返回。例如,檢測人臉時(shí)使用以下代碼:
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
接下來,遍歷檢測到的人臉,嘗試檢測鼻子:
# 遍歷檢測到的人臉 for (x, y, w, h) in faces: # 根據(jù)檢測到的面大小創(chuàng)建感興趣區(qū)域(ROI) roi_gray = gray[y:y + h, x:x + w] roi_color = frame[y:y + h, x:x + w] # 在檢測到的人臉內(nèi)檢測鼻子 noses = nose_cascade.detectMultiScale(roi_gray)
檢測到鼻子后,遍歷所有檢測到的鼻子,并計(jì)算將被“胡子”掛件覆蓋的區(qū)域。過濾掉錯(cuò)誤的鼻子位置后,將“胡子”掛件根據(jù)先前計(jì)算的區(qū)域覆蓋在圖像上:
for (nx, ny, nw, nh) in noses: # 計(jì)算將被“胡子”掛件覆蓋的區(qū)域坐標(biāo) x1 = int(nx - nw / 2) x2 = int(nx + nw / 2 + nw) y1 = int(ny + nh / 2 + nh / 8) y2 = int(ny + nh + nh / 4 + nh / 6) if x1 < 0 or x2 < 0 or x2 > w or y2 > h: continue # 計(jì)算將被“胡子”掛件覆蓋的區(qū)域尺寸 img_moustache_res_width = int(x2 - x1) img_moustache_res_height = int(y2 - y1) # 調(diào)整掩膜大小,使其與放置“胡子”掛件的區(qū)域相等 mask = cv2.resize(img_moustache_mask, (img_moustache_res_width, img_moustache_res_height)) # 翻轉(zhuǎn)掩膜 mask_inv = cv2.bitwise_not(mask) # 將“胡子”掛件調(diào)整為所需區(qū)域 img = cv2.resize(img_moustache, (img_moustache_res_width, img_moustache_res_height)) # 獲取原始圖像的ROI roi = roi_color[y1:y2, x1:x2] # 創(chuàng)建ROI背景和ROI前景 roi_bakground = cv2.bitwise_and(roi, roi, mask=mask_inv) roi_foreground = cv2.bitwise_and(img, img, mask=mask) # 獲取結(jié)果 res = cv2.add(roi_bakground, roi_foreground) # 將res置于原始圖像中 roi_color[y1:y2, x1:x2] = res break
上述程序的關(guān)鍵在于 img_mustache_mask
圖像。此圖像是使用要融合的“胡子”圖像的 Alpha 通道創(chuàng)建的,使用此圖像,將可以只繪制疊加圖像的前景,基于融合圖像的 alpha
通道創(chuàng)建的“胡子”蒙版如下:
img_moustache = cv2.imread('moustache.png', -1) img_moustache_mask = img_moustache[:, :, 3] cv2.imshow("img moustache mask", img_moustache_mask)
獲得的胡子蒙版,如下所示:
增強(qiáng)現(xiàn)實(shí)融合“胡子”掛件后的結(jié)果如下所示:
這里不再針對(duì)視頻幀的處理進(jìn)行講解,因?yàn)槠浞椒ㄅc單個(gè)圖像相同,在接下來的完整代碼中,給出對(duì)連續(xù)視頻幀進(jìn)行增強(qiáng)現(xiàn)實(shí)的代碼。
完整代碼
import cv2 # 用于人臉和鼻子檢測的級(jí)聯(lián)分類器 face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") nose_cascade = cv2.CascadeClassifier("haarcascade_mcs_nose.xml") # 加載胡子圖像 img_moustache = cv2.imread('moustache.png', -1) # 創(chuàng)建胡子蒙版 img_moustache_mask = img_moustache[:, :, 3] # 將胡子圖像轉(zhuǎn)換為 BGR 圖像 img_moustache = img_moustache[:, :, 0:3] # 創(chuàng)建 VideoCapture 對(duì)象 video_capture = cv2.VideoCapture(0) while True: # 從 VideoCapture 對(duì)象捕獲幀 ret, frame = video_capture.read() # 將 frame 轉(zhuǎn)換為灰度圖像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 檢測人臉 faces = face_cascade.detectMultiScale(gray, 1.3, 5) # 迭代檢測到的人臉 for (x, y, w, h) in faces: # 根據(jù)檢測到的人臉大小創(chuàng)建ROI roi_gray = gray[y:y + h, x:x + w] roi_color = frame[y:y + h, x:x + w] # 在檢測到的人臉中檢測鼻子 noses = nose_cascade.detectMultiScale(roi_gray) for (nx, ny, nw, nh) in noses: # 計(jì)算將放置 “胡子” 掛件的坐標(biāo) x1 = int(nx - nw / 2) x2 = int(nx + nw / 2 + nw) y1 = int(ny + nh / 2 + nh / 8) y2 = int(ny + nh + nh / 4 + nh / 6) if x1 < 0 or x2 < 0 or x2 > w or y2 > h: continue # 計(jì)算“胡子”掛件區(qū)域的尺寸 img_moustache_res_width = int(x2 - x1) img_moustache_res_height = int(y2 - y1) # 根據(jù)掛件區(qū)域縮放“胡子”蒙版 mask = cv2.resize(img_moustache_mask, (img_moustache_res_width, img_moustache_res_height)) # 翻轉(zhuǎn)蒙版 mask_inv = cv2.bitwise_not(mask) # 縮放“胡子”掛件 img = cv2.resize(img_moustache, (img_moustache_res_width, img_moustache_res_height)) # 從原始圖像中獲取ROI roi = roi_color[y1:y2, x1:x2] # 創(chuàng)建ROI前景和背景 roi_bakground = cv2.bitwise_and(roi, roi, mask=mask_inv) roi_foreground = cv2.bitwise_and(img, img, mask=mask) # roi_bakground 與 roi_foreground 加和獲取結(jié)果 res = cv2.add(roi_bakground, roi_foreground) roi_color[y1:y2, x1:x2] = res break # 顯示結(jié)果 cv2.imshow('Snapchat-based OpenCV moustache overlay', frame) # 按下 “q” 鍵退出 if cv2.waitKey(1) & 0xFF == ord('q'): break # 釋放資源 video_capture.release() cv2.destroyAllWindows()
眼鏡掛件融合
在這一實(shí)戰(zhàn)程序中,我們將學(xué)習(xí)在檢測到的面部眼睛區(qū)域上融合眼鏡掛件。我們通過下圖首次查看程序預(yù)期結(jié)果:
同樣為了實(shí)現(xiàn)眼鏡掛件融合,需要首先使用眼睛檢測器檢測圖像中的眼睛:
eyepair_cascade= cv2.CascadeClassifier("haarcascade_mcs_eyepair_big.xml")
上圖中青色矩形表示檢測到的人臉在圖像中的位置和大??;粉紅色矩形表示圖像中檢測到的眼睛;黃色矩形表示眼鏡將被覆蓋的位置,其根據(jù)眼睛所在區(qū)域的位置和大小進(jìn)行計(jì)算。
同時(shí),為融合的眼鏡眼鏡掛件圖像增加一些透明度,以使它們更逼真,眼鏡圖像蒙版如下所示:
融合后的結(jié)果圖像如下圖所示:
完整代碼
基本代碼與上例相同,因此不再進(jìn)行贅述,需要注意的是“眼鏡”掛件的融合區(qū)域計(jì)算。
import cv2 face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") eyepair_cascade = cv2.CascadeClassifier("haarcascade_mcs_eyepair_big.xml") img_glasses = cv2.imread('glasses.png', -1) img_glasses_mask = img_glasses[:, :, 3] img_glasses = img_glasses[:, :, 0:3] video_capture = cv2.VideoCapture(0) while True: ret, frame = video_capture.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 檢測人臉 faces = face_cascade.detectMultiScale(gray, 1.3, 5) for (x, y, w, h) in faces: roi_gray = gray[y:y + h, x:x + w] roi_color = frame[y:y + h, x:x + w] # 在檢測到的人臉中檢測眼睛 eyepairs = eyepair_cascade.detectMultiScale(roi_gray) for (ex, ey, ew, eh) in eyepairs: # 計(jì)算“眼睛”掛件放置的坐標(biāo) x1 = int(ex - ew / 10) x2 = int((ex + ew) + ew / 10) y1 = int(ey) y2 = int(ey + eh + eh / 2) if x1 < 0 or x2 < 0 or x2 > w or y2 > h: continue # 計(jì)算“眼睛”掛件放置區(qū)域大小 img_glasses_res_width = int(x2 - x1) img_glasses_res_height = int(y2 - y1) mask = cv2.resize(img_glasses_mask, (img_glasses_res_width, img_glasses_res_height)) mask_inv = cv2.bitwise_not(mask) img = cv2.resize(img_glasses, (img_glasses_res_width, img_glasses_res_height)) roi = roi_color[y1:y2, x1:x2] roi_bakground = cv2.bitwise_and(roi, roi, mask=mask_inv) roi_foreground = cv2.bitwise_and(img, img, mask=mask) res = cv2.add(roi_bakground, roi_foreground): roi_color[y1:y2, x1:x2] = res break # 顯示結(jié)果畫面 cv2.imshow('Snapchat-based OpenCV glasses filter', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break video_capture.release() cv2.destroyAllWindows()
以上就是Python實(shí)戰(zhàn)之基于OpenCV的美顏掛件制作的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV的內(nèi)容請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python 用NumPy創(chuàng)建二維數(shù)組的案例
這篇文章主要介紹了Python 用NumPy創(chuàng)建二維數(shù)組的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-03-03Python用requests-html爬取網(wǎng)頁的實(shí)現(xiàn)
本文主要介紹了Python用requests-html爬取網(wǎng)頁的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07python 實(shí)現(xiàn)簡單的計(jì)算器(gui界面)
這篇文章主要介紹了python 如何實(shí)現(xiàn)簡單的計(jì)算器,幫助大家更好的理解和使用python,感興趣的朋友可以了解下2020-11-11python通過pil將圖片轉(zhuǎn)換成黑白效果的方法
這篇文章主要介紹了python通過pil將圖片轉(zhuǎn)換成黑白效果的方法,實(shí)例分析了Python中pil庫的使用技巧,需要的朋友可以參考下2015-03-03python?中?關(guān)于reverse()?和?reversed()的用法詳解
這篇文章主要介紹了python?中?關(guān)于reverse()?和?reversed()的用法介紹,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01