opencv
python
dlib
基於python語言使用OpenCV搭配dlib實作人臉偵測與辨識
2018/10/15 09:00:00
8
69004
基於python語言使用OpenCV搭配dlib實作人臉偵測與辨識
簡介 |
OpenCV的全名是Open Source Computer Vision Library,是一個跨平台的影像函式庫,OpenCV可用於開發即時影像處理,本文章使用opencv並搭配dlib Machine learning函式庫,可實作出一個簡單的人臉偵測與辨識功能。 |
作者 |
張鈞名 |
什麼是OpenCV?
OpenCV全名為Open Source Computer Vision Library,是一個跨平台的影像函式庫。OpenCV為BSD授權條款,可以免費用於商業和研究領域。
OpenCV可處理領域很多,如擴增實境、人臉辨識、動作與物體辨識等等。
OpenCV主要使用C++語言所編寫,但它有提供不同語言的API介面如Python、Java、C#,和Ruby,本文章使用Python語言來做進一步的實作介紹。
安裝OpenCV
由於本文章環境於Mac執行,所以我們使用brew安裝套件來安裝OpenCV。
打開終端機執行以下指令:
# add opencv
brew tap homebrew/science
# install opencv
# 安装2.4
brew install opencv
# 安装opencv3
brew install opencv3
在使用opencv前我們必須要安裝python的numpy和matplotlib函式庫。
pip install numpy
pip install matplotlib
接下來我們要設定環境變數,我先進到Pyhton的所在目錄底下。
cd /Library/Python/2.7/site-packages/
然後我們必須設定軟連結,而何謂軟連結?在Linux/Unix 檔案系統中,軟連結(symbolic link)的意思是指產生一個特殊的檔案,而將這個檔案的內容位置指向到另一個檔案的位置。
sudo ln -s /usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/cv2.so cv2.so
sudo ln -s /usr/local/Cellar/opencv3/3.2.0/lib/python2.7/site-packages/cv.py cv.py
最後寫入環境變數。
vim ~/.bash_profile
#加入環境變數
export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.7/site-packages
#使其環境變數生效
source ~/.bash_profile
最後打開終端機,鍵入python進入其環境,鍵入以下指令,若沒錯誤就代表有安裝成功。
import cv2
什麼是Dlib?
Dlib是一套使用C++語言所編寫的函式庫,主要可應用在機器學習、影像處理,以及影像辨識等等,他開源而且免費,基於BSD授權條款。
Dlib官網還提供相當完整的文檔,每個類別與每個函示都有詳細的說明,也有提供大量範例程式碼,另外也有
提供Python API。
安裝Dlib
安裝python依賴包
#使用pip進行安裝 pip install numpy pip install scipy pip install scikit-image
安裝Dlib
pip install dlib
接下來
驗證安裝是否正確,
鍵入Python進入其環境,
鍵入以下指令,若沒錯誤就代表有安裝成功。
import dlib
Python基本介紹
Python是一種物件導向的高階程式語言,它是一種簡單優雅、容易上手的直譯式語言,它主要使用空格縮排劃分程式碼區塊,而不像C#使用大括號做區分,
而且Python擁有眾多的開源且免費的第三方函式庫使得Python異常的強大。
Python中預設編碼格式為ASCII 格式,所以在讀取中文字的編碼時會出錯,所以必須要在文件一開頭加入以下編碼。
# -*- coding: utf-8 -*-
Hello World
>>> print("Hello, world!")
Hello, world!
程式碼範圍
if age > 18
print("我已經成年")
print("我在if裡面")
print("我在if外面")
基本數據類型
類型是不需要聲明的。
a = 1 # 整數
b = 1.2 # 浮點數
c = True # 布林值
d = "False" # 字串
e = None # NoneType
運算式
Python運算式跟C語言是差不多的
a = 2
b = 2.3
c = 3
a + b # 2 + 2.3 = 4.3
c – a # 3 - 2 = 1
a / b # 整數除以浮點數,運算以浮點數為準,2 / 2.3 = 0.8695652173913044
a / c # 整數除法,向下取整 2 / 3 = 0
a ** c # a的c次方,结果為8
a += 1 # Python中没有i++的用法,自增用+=
c -= 3 # c等於0
d = 'Hello'
d + ' world!' # 字串相加,結果為'Hello world!'
d += ' "world"!'# 相當於把字串接在當前字串尾,d變為'Hello "world"!'
e = r'\n\t\\'
print(e) # '\\n\\t\\\\'
模塊導入
# 直接導入Python的內建數學函式庫
import math
print(math.cos(math.pi))
for循環
a = ['This', 'is', 'a', 'list', '!']
# 依序輸出:'This', 'is', 'a', 'list', '!'
for x in a:
print(x)
if判斷式
其中elif就是else if的意思
pets =['dog', 'cat', 'droid', 'fly']
for pet in pets:
if pet == 'dog':
food = 'steak'
elif pet == 'cat':
food = 'milk'
elif pet == 'droid':
food = 'oil'
elif pet == 'fly':
food = 'sh*t'
else:
pass
print(food)
while循環
while True: # 一直笑
print("ha")
函式寫法
def say_hello():
print('Hello!')
say_hello() # Hello!
def create_a_list(x, y=2, z=3): # default值必須放後面
return [x, y, z]
b = create_a_list(1) # [1, 2, 3]
c = create_a_list(3, 3) # [3, 3, 3]
d = create_a_list(6, 7, 8) # [6, 7, 8]
人臉偵測實作範例
我們將實作人臉偵測如下圖所示影像
本範例就是用OpenCV搭配Dlib這套Machine learning函式庫來實作人臉辨識,我們可以使用Dlib所提供的人臉辨識演算法來實作,如上圖左邊的數字為偵測到的分數,分數越高判斷為人臉的機率越大,而右邊括號內的數字為子偵測器的編號,也可以解釋為人臉的方向,0就為正面,其他數字則為不同方向的編號。
另外dlib也提供訓練好的模型,可以辨識出人臉的68的特徵點,68
特徵點包括鼻子、眼睛、眉毛,以及嘴巴等等,如上圖紅點就是偵測出人臉的68個特徵點。
下載完成後解壓縮到程式所在根目錄底下即可。
程式實作
# -*- coding: utf-8 -*-
import dlib
import cv2
import imutils
#選擇第一隻攝影機
cap
= cv2.VideoCapture(
0)
#調整預設影像大小,預設值很大,很吃效能
cap.set(cv2.
CAP_PROP_FRAME_WIDTH,
650)
cap.set(cv2.
CAP_PROP_FRAME_HEIGHT,
500)
#取得預設的臉部偵測器
detector
= dlib.get_frontal_face_detector()
#根據shape_predictor方法載入68個特徵點模型,此方法為人臉表情識別的偵測器
predictor
= dlib.shape_predictor(
'shape_predictor_68_face_landmarks.dat')
#當攝影機打開時,對每個frame進行偵測
while(cap.isOpened()):
#讀出frame資訊
ret, frame
= cap.read()
#偵測人臉
face_rects, scores, idx
= detector.run(frame,
0)
#取出偵測的結果
for i, d
in
enumerate(face_rects):
x1
= d.left()
y1
= d.top()
x2
= d.right()
y2
= d.bottom()
text
=
"
%2.2f
(
%d
)"
% (scores[i], idx[i])
#繪製出偵測人臉的矩形範圍
cv2.rectangle(frame, (x1, y1), (x2, y2), (
0,
255,
0),
4, cv2.
LINE_AA)
#標上人臉偵測分數與人臉方向子偵測器編號
cv2.putText(frame, text, (x1, y1), cv2.
FONT_HERSHEY_DUPLEX,
0.7, (
255,
255,
255),
1, cv2.
LINE_AA)
#給68特徵點辨識取得一個轉換顏色的frame
landmarks_frame
= cv2.cvtColor(frame, cv2.
COLOR_BGR2RGB)
#找出特徵點位置
shape
= predictor(landmarks_frame, d)
#繪製68個特徵點
for i
in
range(
68):
cv2.circle(frame,(shape.part(i).x,shape.part(i).y),
3,(
0,
0,
255),
2)
cv2.putText(frame,
str(i),(shape.part(i).x,shape.part(i).y),cv2.
FONT_HERSHEY_COMPLEX,
0.5,(
255,
0,
0),
1)
#輸出到畫面
cv2.imshow(
"Face Detection", frame)
#如果按下ESC键,就退出
if cv2.waitKey(
10)
==
27:
break
#釋放記憶體
cap.release()
#關閉所有視窗
cv2.destroyAllWindows()
人臉辨識
接下來我們要來實作人臉辨識,一樣我們使用Dlib這套函式庫來實現,因為它有訓練好的特徵點模型與人臉辨識模型。
事前準備
我們必須準備人臉68特徵點訓練模型shape_predictor_68_face_landmarks.dat,這在前面人臉偵測已經有下載過了,再來是dlib_face_recognition_resnet_model_v1.dat,這是訓練好的ResNet人臉辨識模型,ResNet為用於影像辨識深度殘差學習,是一個可以用來訓練非常深的深度網絡又簡潔的框架。
下載完成後解壓縮到程式所在根目錄底下即可。
再來準備數張辨識的人臉照片,最好是正面的臉,並且是大張一點與解析度清楚的,在辨識上比較不會有錯誤。
我們的範例準備四張的人臉照片,並標示其人名為檔案名稱,以供識別,
並放在命名為rec的資料夾中。
再準備幾張需要辨識的照片,可以選擇幾張正臉與側臉的照片來測試。
辨識流程
1. 先對比對的人臉進行檢測,取得特徵點,並產生描述子。
2. 對要辨識的人臉進行人臉檢測,取得特徵點,一樣產生描述子。
3. 最後比對辨識的人臉與這四張比對人臉描述子之間的歐氏距離,判斷距離最小的為同一個人臉。
而歐式距離為一種計算距離的方法,源自於歐式空間計算兩點之間的演算法,我們從dlib訓練好的模型取得人臉的128維特徵向量,然後計算特徵向量之間的距離來得到人臉的相似度。
程式實作
# -*- coding: UTF-8 -*-
import sys,os,dlib,glob,numpy
from skimage
import io
import cv2
import imutils
if
len(sys.argv)
!=
2:
print
"缺少要辨識的圖片名稱"
exit()
# 人臉68特徵點模型路徑
predictor_path
=
"shape_predictor_68_face_landmarks.dat"
# 人臉辨識模型路徑
face_rec_model_path
=
"dlib_face_recognition_resnet_model_v1.dat"
# 比對人臉圖片資料夾名稱
faces_folder_path
=
"./rec"
# 需要辨識的人臉圖片名稱
img_path
= sys.argv[
1]
# 載入人臉檢測器
detector
= dlib.get_frontal_face_detector()
# 載入人臉特徵點檢測器
sp
= dlib.shape_predictor(predictor_path)
# 載入人臉辨識檢測器
facerec
= dlib.face_recognition_model_v1(face_rec_model_path)
# 比對人臉描述子列表
descriptors
= []
# 比對人臉名稱列表
candidate
= []
# 針對比對資料夾裡每張圖片做比對:
# 1.人臉偵測
# 2.特徵點偵測
# 3.取得描述子
for f
in glob.glob(os.path.join(faces_folder_path,
"*.jpg")):
base
= os.path.basename(f)
# 依序取得圖片檔案人名
candidate.append(os.path.splitext(base)[
0])
img
= io.imread(f)
# 1.人臉偵測
dets
= detector(img,
1)
for k, d
in
enumerate(dets):
# 2.特徵點偵測
shape
= sp(img, d)
# 3.取得描述子,128維特徵向量
face_descriptor
= facerec.compute_face_descriptor(img, shape)
# 轉換numpy array格式
v
= numpy.array(face_descriptor)
descriptors.append(v)
# 針對需要辨識的人臉同樣進行處理
img
= io.imread(img_path)
dets
= detector(img,
1)
dist
= []
for k, d
in
enumerate(dets):
shape
= sp(img, d)
face_descriptor
= facerec.compute_face_descriptor(img, shape)
d_test
= numpy.array(face_descriptor)
x1
= d.left()
y1
= d.top()
x2
= d.right()
y2
= d.bottom()
# 以方框標示偵測的人臉
cv2.rectangle(img, (x1, y1), (x2, y2), (
0,
255,
0),
4, cv2.
LINE_AA)
# 計算歐式距離
for i
in descriptors:
dist_
= numpy.linalg.norm(i
-d_test)
dist.append(dist_)
# 將比對人名和比對出來的歐式距離組成一個dict
c_d
=
dict(
zip(candidate,dist))
# 根據歐式距離由小到大排序
cd_sorted
=
sorted(c_d.iteritems(),
key
=
lambda
d:d[
1])
# 取得最短距離就為辨識出的人名
rec_name
= cd_sorted[
0][
0]
# 將辨識出的人名印到圖片上面
cv2.putText(img, rec_name, (x1, y1), cv2.
FONT_HERSHEY_SIMPLEX ,
1, (
255,
255,
255),
2, cv2.
LINE_AA)
img
= imutils.resize(img,
width
=
600)
img
= cv2.cvtColor(img,cv2.
COLOR_BGR2RGB)
cv2.imshow(
"Face Recognition", img)
#隨意Key一鍵結束程式
cv2.waitKey(
0)
cv2.destroyAllWindows()
進入到程式所在路徑,在終端機鍵入以下指令
python rec.py 要辨識的圖片檔名.jpg
接下來程式執行完畢就會跳出辨識出來結果的圖片,再隨意鍵入一鍵即可結束程式。
實作結果
根據這幾張辨識人臉的照片,不管是正臉或是側臉,都能完整的辨識出來,目前測試的結果,辨識率可以說達到百分之百了。
1099317C960A41719558BFEE0F40C3F5
2020/03/16 22:09:12
想請問一下在img path=sys.arg[1]這邊是什麼意思
在實際運行的 print img path會出現'-f'
726B99D25DF0EBC196BAF4F8A8BEFB04
2020/03/27 23:55:21
想請問出現這樣的Error是甚麼問題?
Traceback (most recent call last):
File "rec.py", line 90, in <module>
cd_sorted = sorted(c_d.iteritems(), key = lambda d:d[ 1])
AttributeError: 'dict' object has no attribute 'iteritems'
FD1938A1A53B4D9943053D8CB36F6EE5
2020/05/27 23:18:28
請問這是甚麼問題呢?
Traceback (most recent call last):
File "rec.py", line 90, in <module>
cd_sorted = sorted(c_d.iteritems(), key = lambda d:d[ 1])
AttributeError: 'dict' object has no attribute 'iteritems'
8E7227C6008DB64A27C5D3CE2B3D74EA
2021/06/16 17:29:17
你好! 我在程式跑到face_rects, scores, idx = detector.run(frame, 0)的時候,會出現TypeError: run(): incompatible function arguments. The following argument types are supported:
1. (self: _dlib_pybind11.fhog_object_detector, image: array, upsample_num_times: int=0, adjust_threshold: float=0.0) -> tuple Invoked with: <_dlib_pybind11.fhog_object_detector object at 0x000001981067B030>, None, 0
這一串字,我查了很久,也修改了很多種可能,但都沒有成功,想請問一下,這個可能是甚麼原因造成的?謝謝!
D01F6498B6D54B11B89800BF0956E88C
2021/07/20 18:49:05
請問1.如何印出68個特徵點? 2.如何68個特徵點存到CSV檔? 謝謝