由於今年的疫情,已經太久沒有更新文章,作為湖北人,深知不給大家帶來麻煩的同時還要提升自己。奈何沒有帶電腦回家,家中電腦又是個老爺機,經過長達幾天的折騰終於可以使用。第一時間帶來【
Python
自動戴聖誕帽】系列的第二篇文章,另外,文末有抗疫彩蛋,別忘了翻過去看一看哦。
注:本文部分圖片來自百度百科,如有侵權請聯絡刪除。
OpenCV人臉關鍵點檢測
前面說到家中電腦的原因,實在是安裝不上face_recognition
庫,於是臨時更換為OpenCV
中的LBF
演算法方式。方法其實沒有什麼不同,都是通過檢測出人臉關鍵點,然後定位聖誕帽的位置。
上一篇文章詳細講了face_recognition
方式獲取人臉關鍵點,這裡就不詳細描述了,具體通過看程式碼和註釋瞭解。程式碼中的haarcascade_frontalface_alt2.xml
和lbfmodel.yaml
可以自行搜尋得到,或者在公眾號後臺回覆OpenCVFace
獲取下載地址。
# load image:
image = cv2.imread(image_path, 0)
# find faces:
cascade = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')
faces = cascade.detectMultiScale(image, 1.3, 5)
# create landmark detector and load lbf model:
facemark = cv2.face.createFacemarkLBF()
facemark.loadModel('lbfmodel.yaml')
# ok代表是否檢測出關鍵點, landmarks表示所有的關鍵座標
ok, landmarks = facemark.fit(image, faces)
複製程式碼
與face_recognition
不同的是,這種方式生成的關鍵點並不是按照器官分組的字典,而是一個列表檔案。下面是列表中各個點所屬器官的關係表:
landmarks---[0, 16]----Chin
landmarks---[17, 21]---Left eyebrow
landmarks---[22, 26]---Right eyebrow
landmarks---[27, 30]---Nose bridge
landmarks---[30, 35]---Lower nose
landmarks---[36, 41]---Left eye
landmarks---[42, 47]---Right Eye
landmarks---[48, 59]---Outer lip
landmarks---[60, 67]---Inner lip
複製程式碼
用OpenCV將關鍵點顯示出來:
points_face = copy.deepcopy(image)
for each in landmarks[0][0]:
points_face = cv2.circle(points_face, tuple(each), 3, (0, 0, 255), -1)
# Display the resulting image
cv2.imshow('points_face', points_face)
cv2.waitKey(0)
複製程式碼
效果如下:
與face_recognition
方式對比基本沒有什麼區別:
戴聖誕帽前的準備
合適的聖誕帽
首先需要找到一張合適的聖誕帽圖片,一定要是帶透明資訊的.png
格式的。
我找到的聖誕帽如下,還是比較漂亮的:
根據常識,頭的部分只是下圖中藍色框框住的地方,所以我們需要提前確定該部分的大小。
用windows自帶的畫圖簡單測量之後知道帽簷的部分寬為175px
,同時帽簷離圖片底部的距離為25px
.
確定聖誕帽的位置
有了聖誕帽,又知道了臉上各個關鍵點的位置,如何確定聖誕帽究竟該放在哪裡?
這時候就需要小學美術學到的“三庭五眼”的知識了:
從圖中可以看出從鼻翼下緣到下巴尖的距離與從眉心到髮際線的距離相等,而臉的寬度與兩頰的距離相等。
正常戴帽子時,帽簷一般會在髮際線處,可以根據這點推測聖誕帽的位置(不考慮帽子的各種花式戴法)。仔細觀察前文人臉關鍵點的圖片示例會發現,鼻樑最上面的關鍵點與眉心位置很接近,可以根據這些資訊確定聖誕帽在Y
軸方向的位置。
檢測人臉關鍵點時會得到兩側臉頰的具體座標,根據臉頰第一個點的座標和最後一個點的座標即可計算出人臉的實際寬度。根據比例可以推算出聖誕帽在X
軸方向的位置。
下面來進行細節的實現。
程式碼實現
首先聖誕帽要進行預處理:
# 聖誕帽相關引數
hat_img = Image.open("./hat.png")
hat_brim_length = 175.0
hat_height_buffer = 25.0
hat_img = hat_img.convert('RGBA')
複製程式碼
最後一句程式碼是將圖片轉換為RGB
和透明度形式,便於後面直接用於掩膜,這也是需要.png
格式圖片的原因。
獲取人臉關鍵點的方法已經介紹過,不再贅述。根據前面的分析,實現目標只需要目標人臉的臉頰關鍵點和鼻樑關鍵點,所以取出這些資料:
chin = landmarks[0][0][:17]
nose_bridge = landmarks[0][0][27:31]
複製程式碼
根據臉頰關鍵點計算實際臉寬,以及眉心到髮際線距離:
def get_distance(point1, point2):
return int(math.sqrt((point1[0] - point2[0])**2 + (point1[1] - point2[1])**2))
face_width = get_distance(chin[0], chin[-1])
hair_brim = get_distance(nose_bridge[-1], chin[int(len(chin)/2)])
複製程式碼
這裡是通過計算兩頰端點之間距離的方式確定實際臉寬的,簡便的方式的話,其實直接使用兩點的橫軸左邊相減也能得到大致結果。
下面根據臉寬來調整聖誕帽的大小:
resize_ratio = face_width / hat_brim_length
hat_width = int(hat_img.width * resize_ratio)
hat_height = int(hat_img.height * resize_ratio)
hat_buffer = int(hat_height_buffer * resize_ratio)
複製程式碼
聖誕帽的帽簷應該是在髮際線的位置,於是整張聖誕帽圖片的底部座標應該是鼻樑頂部縱座標減去“一庭”的位置:
hat_bottom = int(nose_bridge[0][1]) - hair_brim
複製程式碼
同理,下面是聖誕帽圖片的頂部、左側和右側座標:
hat_top = hat_bottom - hat_height
hat_left = int(chin[0][0])
hat_right = hat_left + hat_width
複製程式碼
接下來是最後一步,把縮放過的聖誕帽通過前面的掩膜貼到原始圖片上,然後顯示出來:
hat_region = hat_img
human_region = (hat_left, hat_top + hat_buffer, hat_right, hat_bottom + hat_buffer)
human_img.paste(hat_region, human_region, mask=hat_img)
human_img.show()
複製程式碼
效果如下:
注:這種方式沒有在女主的圖片中檢測出人臉,所以沒有放上結果。
觀察下來,整體效果還行,但感覺被範閒瞪著不是很舒服。細節有點粗糙,對臉部狀態有一定要求,臉部最好是處於正臉的位置。有興趣的同學可以對臉部稍微傾斜的情況做一些優化。
彩蛋
此次疫情極大的影響了大家的生活,以後生活中一定要注意飲食健康。希望此次疫情早日結束,武漢加油,湖北加油,中國加油!
大家出門一定要戴好口罩,保護自己,也保護他人。特意為大家準備了口罩一枚:
先幫範閒戴上:
最後,恭祝大家新的一年裡少病少災,實現心中所願。
結語
這個系列就到此為止了,如果有想要了解交流的可以在公眾號主頁聯絡我,這個系列的程式碼在這裡:
https://github.com/TitusWongCN/WeChatSubscriptionArticles/tree/master/WearChristmasHat
複製程式碼
大家有什麼想了解的,或者有什麼想做的也可以在文章後面留言,後面說不定就會做了哦~
後記
不管寫什麼,希望能跟更多人溝通,有問題或者需求隨時歡迎交流。
我所有的專案原始碼都會放在下面的github倉庫裡面,有需要可以參考,有問題歡迎指正,謝謝!
https://github.com/TitusWongCN/
複製程式碼
【Python自動戴聖誕帽】往期推薦:
第一期:【Python自動戴聖誕帽】01 熟悉face_recognition庫
下面是我的公眾號,有興趣可以掃一下: