# Face detection in Python (臉部識別)
## Introduction (簡介)
- Face-engine 是一個用於人臉識別和人臉特徵檢測的 Python 套件
- 它利用先進的機器學習算法來檢測和分析人臉屬性。此套件適用於需要生物識別身份驗證、人臉屬性識別和情感檢測的應用。
### face-engine 的功能
1. 人臉檢測:識別並定位圖像中的人臉。
2. 人臉標誌檢測:檢測關鍵的人臉特徵,如眼睛、鼻子和嘴巴。
3. 情感識別:分析面部表情以確定情感。
4. 人臉識別:根據面部特徵識別或驗證個人。
5. 年齡和性別預測:估計圖像中人物的年齡和性別。
## 環境設定 (Windows OS)
### 使用 conda 建立 Workspace 的環境
- face_engine 套件尚未支援 Python 3.11+
- 請用 conda 建立一個 Python 版本為 3.10 的環境
:::warning
使用 python 環境管理套件,按 + 號

:::
:::danger
選擇 Python 3.10

:::
:::success
打上此環境的名字,例如 FD_Project

:::
:::warning
將剛建立的 FD_Project 設定成開啟專案 (workspace) 自動使用這個環境

:::
:::info
透過 env 的終端機按鈕啟用終端機

:::
:::success
使用下列指令查看在環境下的 Python 版本及目前 conda 版本

:::
-----
### 安裝 face-engine 套件
:::warning
使用 `python -m pip install face_engine` 指令安裝 face_engine 套件

:::
:::info
寫一個簡單的程式先測試 face_engine 是否成功安裝,並且可以引用

:::
**「人臉辨識」已經有直接可用的套件可用,但是需要安裝 cmake 與 dlib,如果想要在辨識人臉後畫出相對應的人臉區域則會需要用到 matplotlib 套件**
:::success
使用 `python -m pip install matplotlib` 指令安裝 **matplotlib** 套件

:::
:::danger
安裝 cmake 有兩個部份
1. 一個是在 windows 系統裡安裝 cmake
- 方法 1: 使用 visual studio 2019 或更新版本安裝 cmake 工具集
- 方法 2: 網路尋找 cmake 並進行安裝
3. 在conda 的 env 裡安裝 cmake 套件
使用 Google 尋找 cmake download

開啟網頁後,往下滑動找到符合系統的版本進行下載
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
**安裝好 cmake 後關掉「終端機 (command prompt)」再重開,然後以 `cmake --version` 指令測試 cmake 版本資訊**
<kbd></kbd>
:::
:::info
**在 conda 環境裡要安裝 cmake,操作如下**
<kbd></kbd>
:::
:::success
**接下來,在 環境裡安裝 dlib 套件**
<kbd></kbd>
開啟 https://anaconda.org/ 網頁,如果沒有帳號可以註冊一個 (免費),有帳號者按 login 登入
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
:::
### 使用 face_engine 套件進行臉部偵測
1. 先從網路上找一張有人臉的影像 (我將下載的影像放在 imgs 子目錄下,影像檔名以 family02.jpg 為例)
<kbd></kbd>
2. 建立一個 test.py 的程式檔,程式碼如下
```python=
from face_engine import FaceEngine
import matplotlib.pyplot as plt
if __name__ == "__main__":
face_engine = FaceEngine()
imgName = "imgs/family02.jpg"
plt.imshow(plt.imread(imgName))
plt.show()
boxes, extra = face_engine.find_faces( imgName )
print( boxes )
print( extra )
```
<kbd></kbd>
<kbd></kbd>
:::success
**接下來,將找到的人臉範圍資訊以 PIL 套件的 ImageDraw 工具畫上紅色的矩形**
```python=
from face_engine import FaceEngine
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw
if __name__ == "__main__":
face_engine = FaceEngine()
imgName = "imgs/family02.jpg"
try:
boxes, extra = face_engine.find_faces( imgName )
print( boxes )
img = Image.open(imgName)
drawImg = ImageDraw.Draw(img)
for i in range( len( boxes ) ):
shape =[(boxes[i][0], boxes[i][1]),
(boxes[i][2], boxes[i][3])]
drawImg.rectangle( shape, outline='red',
width=2)
plt.imshow(img)
plt.show()
except:
print("Error loading image or no faces found in image")
```
<kbd></kbd>
:::
## 環境設定 Mac OS
在 Mac 的終端機下建立一個 資料匣,在進入資料匣後以 `code .` 指令開啟 VS Code,如果沒出 VS Code 可能是沒安裝 VS Code 或者 VS Code 在環境變數裡的 Path 設定未設定好
<kbd>
</kbd>
<kbd>
</kbd>
<kbd></kbd>
<kbd></kbd>
:::info
將剛剛建立的 env 指定為開啟此 workspace 的預設環境
<kbd></kbd>
:::
:::success
檢查 conda 與 Python版本
<kbd></kbd>
:::
:::info
在終端機以 `python3 -m pip install face_engine` 指令安裝 face_engine 套件
<kbd></kbd>
如果安裝過程出現無法安裝,查看是不是有被防毒軟體阻擋
<kbd>
</kbd>
選擇阻擋事件,然後 點擊 Unblock 放行
<kbd>
</kbd>
再安裝一次看看
<kbd>
</kbd>
寫個測試檔試看看
<kbd>
</kbd>
:::
:::warning
在終端機以 `brew install cmake` 在 Mac 作業系統下安裝 cmake
<kbd>
</kbd>
:::
:::info
在 terminal 裡使用 conda 安裝 cmake
<kbd>
</kbd>
接著再以 `python3 -m pip install cmake` 用 python 的方式安裝 cmake
<kbd>
</kbd>
:::
:::success
在終端機下安裝 `conda install dlib`
<kbd>
</kbd>
<kbd>
</kbd>
<kbd>
</kbd>
<kbd>
</kbd>
<kbd>
</kbd>
:::
:::info
準備一張 測試影像 放在 imgs 子目錄裡,撰寫 Python 程式,
<kbd>
</kbd>
執行結果
<kbd>
</kbd>
```python=
from face_engine import FaceEngine
if __name__ == '__main__':
face_engine = FaceEngine()
imgFileName = 'imgs/Family_02.jpg'
try:
boxes, extra = face_engine.find_faces(imgFileName)
if boxes is not None:
print('Detected face')
for box in boxes:
print(box)
else:
print('Failed to detect face')
except Exception as e:
print(e)
```
:::
:::warning
測試影像 中的人臉識別,並用 PIL套件及 matplotlib 套件對 找到的人臉 物件畫上紅色矩形
<kbd>

</kbd>
```python=
from face_engine import FaceEngine
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == '__main__':
face_engine = FaceEngine()
imgFileName = 'imgs/Family_02.jpg'
try:
boxes, extra = face_engine.find_faces(imgFileName)
print(boxes)
if boxes is not None:
img = Image.open(imgFileName)
drawing = ImageDraw.Draw(img)
for i in range( len( boxes )):
shape = [ (boxes[i][0], boxes[i][1]),
(boxes[i][2], boxes[i][3]) ]
drawing.rectangle( shape, outline ="red", width=3)
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
except Exception as e:
print(e)
```
:::
## 人臉特徵學習、佈署與運用
### 人臉特徵學習
:::success
<kbd>
</kbd>
輸出結果:
<kbd>
</kbd>
<kbd>
</kbd>
```python=
from face_engine import FaceEngine
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == '__main__':
face_engine = FaceEngine()
imgNames = ['imgs/Andy_Lau_01.jpeg', 'imgs/Andy_Lau_02.jpeg',
'imgs/Keanu_Reeves_02.jpeg', 'imgs/Keanu_Reeves_05.jpeg',
'imgs/Yi_Fei_03.jpeg', 'imgs/Yi_Fei_04.jpeg']
try:
face_engine.fit( imgNames,
['Andy', 'Andy', 'Keanu', 'Keanu', 'YiFei', 'YiFei'] )
testImg = 'imgs/Andy_Lau_04.jpeg'
boxes, names = face_engine.make_prediction( testImg )
if boxes is not None:
print (names, boxes )
img = Image.open( testImg )
drawing = ImageDraw.Draw(img)
for i in range( len( boxes )):
shape = [ (boxes[i][0], boxes[i][1]),
(boxes[i][2], boxes[i][3]) ]
drawing.rectangle( shape, outline ="red", width=3)
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
except Exception as e:
print(e)
```
:::
### 將人臉特徵儲存成特徵檔
:::warning
<kbd></kbd>
執行結果:
<kbd></kbd>
範例程式碼
```python
from face_engine import FaceEngine
if __name__ == '__main__':
face_engine = FaceEngine()
imgNames = ['imgs/Andy_Lau_01.jpeg', 'imgs/Andy_Lau_02.jpeg',
'imgs/Keanu_Reeves_02.jpeg', 'imgs/Keanu_Reeves_05.jpeg',
'imgs/Yi_Fei_03.jpeg', 'imgs/Yi_Fei_04.jpeg']
try:
face_engine.fit( imgNames,
['Andy', 'Andy', 'Keanu', 'Keanu', 'YiFei', 'YiFei'] )
face_engine.save( 'face_feature.p' )
except Exception as e:
print(e)
```
:::
### 讀入人臉特徵檔進行運用
:::info
<kbd></kbd>
執行結果:
<kbd>
</kbd>
<kbd>
</kbd>
範例程式碼:
```python=
from face_engine import load_engine
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == '__main__':
face_engine = load_engine( 'face_feature.p' )
testImg = 'imgs/Yi_Fei_01.jpeg'
boxes, names = face_engine.make_prediction( testImg )
if boxes is not None:
print (names, boxes )
img = Image.open( testImg )
drawing = ImageDraw.Draw(img)
for i in range( len( boxes )):
shape = [ (boxes[i][0], boxes[i][1]),
(boxes[i][2], boxes[i][3]) ]
drawing.rectangle( shape, outline ="red", width=3)
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
```
:::
## 使用 face-recognition 模組分析人臉特徵
**`face_recognition`** 模組的主要用途:
1. **人臉識別**: `face_recognition` 可以從圖片或視訊中檢測並識別人臉。它能夠定位測試影像或視訊中的人臉與已知的人臉進行比對
2. **人臉位置檢測**:此模組可以檢測圖像或視頻中人臉的位置。它能夠回傳每張人臉在圖像中的座標,進而可以提供後續的處理和分析
3. **人臉特徵提取**:`face_recognition` 可以提取人臉的特徵碼,這些特徵碼是128維的向量,能夠用於比較和辨識不同的人臉
4. **人臉比對**:利用提取到的特徵碼,可以進行人臉之間的比對和配對。這使得可以驗證圖像中的人臉是否屬於已知的人
5. **人臉分群**:模組還可以將多張人臉影像分群,依特徵碼將相似的人臉分為一組
`face_recognition` 模組的主要目的是提供簡單且強大的工具,用於**人臉識別**和**分析**,適用於**安全驗證**、**監控系統**、**照片管理**等多種應用場景。
face_recognition 模組是基於深度學習的人臉識別庫,提供了多種功能來檢測和識別影像中的人臉。以下是主要函數:
1. **load_image_file(file, mode='RGB')**
- 功能:從文件中載入影像並轉換為 numpy 陣列。
- 參數:
- **file**:影像檔案的路徑與檔名
- **mod**e:影像模式(預設為'RGB')
2. **face_locations(img, number_of_times_to_upsample=1, model='hog')**
- 功能:檢測影像中人臉的位置
- 參數:
- **img**:影像的 numpy 陣列
- **number_of_times_to_upsample**:影像上採樣的次數以檢測更多小型人臉
- **model**:檢測模型('hog' 或 'cnn')
3. **face_encodings(face_image, known_face_locations=None, num_jitters=1, model='small')**
- 功能:計算影像中每張人臉的128維特徵向量
- 參數:
- **face_image**:包含人臉的影像 numpy 陣列
- **known_face_locations**:已知的人臉位置(可選)
- **num_jitters**:抖動次數,增加特徵碼的準確性
- **model**:特徵碼模型('small' 或 ‘large')
- 補充說明:
- **num_jitter**s 參數指定了在計算人臉特徵碼時,對每張人臉進行多少次隨機變換(抖動)。每次抖動會稍微改變影像中人臉的角度、位置或比例,然後重新計算特徵碼。最終的特徵碼是所有這些抖動結果的平均值。
- **預設值**:預設值為1,表示不進行額外的抖動。這適用於影像品質較高且人臉特徵明顯的情況。
### <span style="background-color: #99ddff">安裝 face_recognition 套件</span>
:::warning
#### **Mac OS**
在終端機使用 **`conda install face_recognition`** 指令安裝 **face_recognition** 套件
<kbd></kbd>
在終端機使用 **`python3 -m pip install face_recognition`** 指令安裝 **face_recognition** 套件
<kbd>
</kbd>
:::
### <span style="background-color: #99ddff">使用 face_recognition 人臉識別</span>
#### 範例一
:::success
<kbd>
</kbd>
執行結果:
<kbd>
</kbd>
<kbd>
</kbd>
```python=
import face_recognition
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == "__main__":
imgX = 'imgs/Yi_Fei_05.jpeg'
img_load = face_recognition.load_image_file( imgX )
boxes = face_recognition.face_locations( img_load )
if boxes is not None:
print( boxes )
img = Image.open( imgX )
drawing = ImageDraw.Draw(img)
for i in range( len( boxes )):
shape = [ (boxes[i][3], boxes[i][0]),
(boxes[i][1], boxes[i][2]) ]
drawing.rectangle( shape, outline ="red", width=3)
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
```
:::
#### 範例二
:::info
使用多人照片測試看看
<kbd></kbd>
<kbd></kbd>
<kbd></kbd>
```python=
import face_recognition
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == "__main__":
imgX = 'imgs/Family_02.jpg'
img_load = face_recognition.load_image_file( imgX )
boxes = face_recognition.face_locations( img_load )
if boxes is not None:
img = Image.open( imgX )
drawing = ImageDraw.Draw(img)
for i in range( len( boxes )):
print( f'{i} => {boxes[i]}' )
shape = [ (boxes[i][3], boxes[i][0]),
(boxes[i][1], boxes[i][2]) ]
drawing.rectangle( shape, outline ="red", width=3)
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
```
:::
## 臉部特徵資訊萃取
:::success
<kbd></kbd>
#### 執行結果
<kbd></kbd>
face landmark 的項目如下:
- **chin (臉頰)**
- **left_eyebrow (左眉毛)**
- **right_eyebrow (右眉毛)**
- **nose_bridge (鼻樑)**
- **nose_tip (鼻尖)**
- **left_eye (左眼睛)**
- **right_eye (右眼睛)**
- **top_lip (上唇)**
- **bottom_lip (下唇)**
<kbd></kbd>
```python=
import face_recognition
if __name__ == "__main__":
imgX = 'imgs/Andy_Lau_04.jpeg'
img_load = face_recognition.load_image_file( imgX )
landmarks = face_recognition.face_landmarks( img_load )
if landmarks is not None:
for i in range( len( landmarks )):
print( f'{i}-th object ')
for key, value in landmarks[i].items():
print( f'{key} => {value}' )
else:
print('Failed to detect face')
```
:::
:::warning
#### **尋找完臉部 特徵 後畫出特徵位置**
<kbd>
</kbd>
**執行結果**
<kbd>
</kbd>
<kbd>
</kbd>
**範例程式碼**
```python=
import face_recognition
from PIL import Image, ImageDraw
import matplotlib.pyplot as plt
if __name__ == "__main__":
imgX = 'imgs/Andy_Lau_04.jpeg'
img_load = face_recognition.load_image_file( imgX )
landmarks = face_recognition.face_landmarks( img_load )
if landmarks is not None:
img = Image.open( imgX )
drawing = ImageDraw.Draw(img)
for i in range( len( landmarks )):
print( f'{i}-th object ')
for key, value in landmarks[i].items():
drawing.line( value, fill ="blue", width=5)
print( f'{key} => {value}' )
plt.imshow(img)
plt.show()
else:
print('Failed to detect face')
```
:::
:::info
#### **比對臉部特徵判斷是何人**
<kbd></kbd>
**執行結果**
<kbd></kbd>
<kbd></kbd>
```python=
import face_recognition
from PIL import Image
import matplotlib.pyplot as plt
if __name__ == "__main__":
imgNames = ['imgs/Andy_Lau_01.jpeg', 'imgs/Andy_Lau_02.jpeg',
'imgs/Andy_Lau_03.jpeg',
'imgs/Keanu_Reeves_01.jpeg', 'imgs/Keanu_Reeves_02.jpeg',
'imgs/Keanu_Reeves_03.jpeg',
'imgs/Yi_Fei_01.jpeg', 'imgs/Yi_Fei_02.jpeg',
'imgs/Yi_Fei_02.jpeg']
load_imgs = []
for img in imgNames:
img_load = face_recognition.load_image_file( img )
load_imgs.append( img_load )
face_codes = []
for img in load_imgs:
face_codes.append( face_recognition.face_encodings( img )[0] )
names = ['Andy', 'Andy', 'Andy', 'Keanu', 'Keanu', 'Keanu', 'YiFei', 'YiFei', 'YiFei']
testImg = 'imgs/Keanu_Reeves_04.jpeg'
testImg_load = face_recognition.load_image_file( testImg )
test_face_code = face_recognition.face_encodings( testImg_load )[0]
results = face_recognition.compare_faces( face_codes, test_face_code )
print( results )
img = Image.open( testImg )
plt.imshow(img)
plt.show()
if True in results:
print( f'The person <<{names[ results.index( True )]}>> is in the test image' )
else:
print('The test image does not contain any of the persons in the database')
```
:::
:::warning
#### **使用 face_recognition 中的 face_distance( ) 計算測試影像裡的人物臉部特徵與 "已知" 人物的臉部特徵距離**
<kbd></kbd>
**執行結果**
<kbd></kbd>
<kbd></kbd>
**範例程式碼**
```python=
import face_recognition
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
if __name__ == "__main__":
imgNames = ['imgs/Andy_Lau_01.jpeg', 'imgs/Andy_Lau_02.jpeg',
'imgs/Andy_Lau_03.jpeg',
'imgs/Keanu_Reeves_01.jpeg', 'imgs/Keanu_Reeves_02.jpeg',
'imgs/Keanu_Reeves_03.jpeg',
'imgs/Yi_Fei_01.jpeg', 'imgs/Yi_Fei_02.jpeg',
'imgs/Yi_Fei_03.jpeg']
load_imgs = []
for img in imgNames:
img_load = face_recognition.load_image_file( img )
load_imgs.append( img_load )
face_codes = []
for img in load_imgs:
face_codes.append( face_recognition.face_encodings( img )[0] )
names = ['Andy', 'Andy', 'Andy', 'Keanu', 'Keanu', 'Keanu', 'YiFei', 'YiFei', 'YiFei']
testImg = 'imgs/Yi_Fei_05.jpeg'
testImg_load = face_recognition.load_image_file( testImg )
test_face_code = face_recognition.face_encodings( testImg_load )[0]
distance = face_recognition.face_distance( face_codes, test_face_code )
print( distance )
if min( distance ) < 0.6:
print( f'The person <<{names[ np.argmin(distance) ]}>> is in the test image with feature distance {min(distance)}' )
else:
print('The test image does not contain any of the persons in the database')
img = Image.open( testImg )
plt.imshow(img)
plt.show()
```
:::