### 使用 OpenCV 進行臉部識別
:::success
1. 要進行特定圖像辨識最重要的是要有『識別對象特徵檔』。
2. OpenCV 已內建臉部識別特徵檔,只要使用 OpenCV 的CascadeClassifier 類別即可識別臉部。
:::
<font color='red'>記得安裝相關套件: </font>
pip install opencv-contrib-python
---
### 建立CascadeClassifier 物件
:::success
識別物件變數 = cv2.CascadeClassifier(識別檔路徑)
:::
<font color='red'>可將套件cv2下data目錄複製到您的專案中,比較不會出錯</font>
---
加入識別檔路徑
```python=1
import cv2
# 識別檔路徑: 當未在專案中加入識別檔路徑要在各自電腦中找出其位置
xmlFile = '''
C:\\Users\\電腦名稱\\anaconda3\\Lib\\site-packages\\cv2\\data\\haarcascade_frontalface_default.xml
'''
# 當已在專案中加入識別檔路徑
xmlFile = 'data/haarcascade_frontalface_default.xml'
# 建立識別物件
faceCascade = cv2.CascadeClassifier(xmlFile)
```
---
### 偵測識別結果物件
:::success
識別結果變數 = 識別物件變數.detectMultiScale(圖片, 參數1, ....)
:::
1. 使用識別物件的 detectMultiScale()就能取得識別結果,如臉部識別
2. detectMultiScale()傳回串列,包含識別圖片中多張臉部資訊
3. 串列元素是由臉部圖形左上角(x座標, y座標, 臉部寬度, 臉部高度)所組成
for (x,y,w,h) in faces:
---
## 標識人臉及眼睛位置(一)
:::success
1. 標識出圖片中臉部位置
2. 在左下角顯示辨識出臉部的數量
:::
----
設定辨識檔路徑與要識別的檔案
```python=1
import cv2
# 設定人臉識別檔路徑
facePath = "data/haarcascade_frontalface_default.xml"
# 設定眼睛識別檔路徑
eyePath = "data/haarcascade_eye.xml"
# 建立人臉識別CascadeClassifier 物件
face_cascade = cv2.CascadeClassifier(facePath)
# 建立眼睛識別CascadeClassifier 物件
eye_cascade = cv2.CascadeClassifier(eyePath)
# 讀取要識別的圖片
#frame = cv2.imread("images/students.jpg")
frame = cv2.imread("images/obamafamily.jpg")
#frame = cv2.imread("images/face.jpg")
```
----
##### 標識人臉及眼睛位置(二)
```python=1
# 取得人臉識別結果變數
faces = face_cascade.detectMultiScale(
frame, # 要識別的圖檔
scaleFactor=1.1, # 區塊的改變倍數為1.1倍
minNeighbors=5, # 成功取得特徵須達到5次
minSize=(10,10), # 最小辨識區為10x10
flags=cv2.CASCADE_SCALE_IMAGE # 按比例正常檢測
)
```
----
##### 標識人臉及眼睛位置(三)
```python=1
# 在圖片中顯示相關文字
# frame.shape[0]為圖片高度、frame.shape[1]為圖片寬度
cv2.putText(
frame, # 要顯示文字的圖片
"Find " + str(len(faces)) + " face!", # 要顯示的文字
(10, frame.shape[0] - 5), # 要顯示文字的位置
cv2.FONT_HERSHEY_SIMPLEX, # 要使用的字型
0.5, # 字體尺寸
(255,255,255), # 文字顏色
2 # 文字外框粗細
)
```
----
##### 標識人臉及眼睛位置(四)
```python=1
# 依序取得人臉識別結果變數內容
# (x, y): 左上角座標、(w, h): 寬度與高度
for (x, y, w, h) in faces:
# 計算中心點座標
center = (x + w//2, y + h//2)
# 以圖片中心點為圓心在圖片中繪製橢圓形
#cv2.ellipse(frame, center, (w//2, h//2), 0, 0, 360, (255, 0, 255), 4)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 取得圖片中Region Of Interest(有興趣的區域: 人臉)後儲存為圖片faceROI
faceROI = frame[y : y+h, x : x+w]
# 取得眼睛識別結果變數
eyes = eye_cascade.detectMultiScale(faceROI)
# 依序取得眼睛識別結果變數內容
# (x, y): 左上角座標、(w, h): 寬度與高度
for (x2, y2, w2, h2) in eyes:
# 計算眼睛中心點座標
eye_center = (x + x2 + w2//2, y + y2 + h2//2)
# 計算半徑
radius = int(round((w2 + h2)*0.25))
# 以眼睛中心點繪製圓形
frame = cv2.circle(frame, eye_center, radius, (255, 0, 0 ), 4)
```
----
##### 標識人臉及眼睛位置(五)
```python=1
# 在視窗中顯示frame圖片
cv2.imshow("FaceDetect", frame)
# 等待使用者按下任意鍵後繼續
cv2.waitKey(0)
# 關閉圖形顯示視窗
cv2.destroyWindow("FaceDetect")
```
<!--
## Tello Face Tracking(一)
```python=1
import cv2
import numpy as np
import TelloActions as action
import threading
import time
##### Video Stream UDP Address #####
VS_UDP_IP = '0.0.0.0'
VS_UDP_PORT = 11111
tello_cap_address = f'udp://{VS_UDP_IP}:{VS_UDP_PORT}'
#print(tello_cap_address)
```
## Tello Face Tracking(二)
```python=1
##### Other Parameters #######
# 圖片大小
w, h = 360, 240
# 圖片 大小 範圍(最小為6200, 最大6800)
fbRange = [6200, 6800]
# PID 控制
pid = [0.4, 0.4, 0]
# PID 的 Error
pError = 0
```
## Tello Face Tracking(三)
```python=1
#### 建立讀取影像的類別,採用多執行緒的方式
class TelloCapture:
def __init__(self, address):
self.frame = []
self.status = False
self.stop = False
self.cap = cv2.VideoCapture(address)
def start(self):
print('Tello Capture Started!')
threading.Thread(target=self.queryFrame, daemon=True, args=()).start()
def stop(self):
self.stop=True
print('Tello Capture Stopped!')
def queryFrame(self):
while not self.stop:
self.status, self.frame = self.cap.read()
self.cap.release()
def getFrame(self):
return self.frame.copy()
```
## Tello Face Tracking(四)
```python=1
def findFace(img):
faceCascade = cv2.CascadeClassifier('data/haarcascade_frontalface_alt2.xml')
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(imgGray, 1.1, 5)
# 紀錄 圖片中心點位置
faceListCenter = []
# 紀錄 圖片 大小
faceListSize = []
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cx = x+w//2; cy = y+h//2; area = w*h
faceListCenter.append([cx, cy])
faceListSize.append(area)
cv2.circle(img, (cx, cy), 5, (0, 255, 0), cv2.FILLED)
if len(faceListSize)!=0:
# 取出最大圖片的索引
idx = faceListSize.index(max(faceListSize))
# 回傳 繪製的圖, 最大圖的中心點座標, 最大圖的大小
return img, [faceListCenter[idx], faceListSize[idx]]
else: return img, [[0, 0], 0]
```
## Tello Face Tracking(五)
```python=1
def trackFace(info, w, pid, pError):
# 圖片大小
area = info[1]
# 中心點
x, y = info[0]
# 前進後退的飛行值
fb = 0
# 中心點與目前位置的差距
error = x- w//2
# PID 控制 yaw 角度
speed = pid[0]*error + pid[1]*(error-pError)
# 將 speed 控制在 -100 到 100 之間的整數
speed = int(np.clip(speed, -100, 100))
# 當 圖片大小在範圍內不調整
if area > fbRange[0] and area < fbRange[1]:
fb = 0
# 距離太近,往後
elif area > fbRange[1]:
fb = -20
# 距離太遠,往前
elif area < fbRange[0] and area!=0:
fb = 20
# 當x在中心時,維持原狀態
if x==0:
speed = 0; error=0
# 送出控制信號
action.send_rc_control(0, fb, 0, speed)
# 目前的error,作為下次PID運算的pError
return error
```
## Tello Face Tracking(六)
```python=1
def main():
global pError
action.connect(1)
action.streamon()
cap = TelloCapture(tello_cap_address)
cap.start()
time.sleep(1)
action.takeoff(5)
while True:
frame = cap.getFrame()
frame = cv2.resize(frame, (w, h))
frame, info = findFace(frame)
pError = trackFace(info, w, pid, pError)
cv2.imshow('Output', frame)
if cv2.waitKey(1) & 0xff == ord('q'):
action.land()
break
cap.stop()
cv2.destroyAllWindows()
action.land()
action.end()
if __name__ == '__main__':
main()
```
-->
{"metaMigratedAt":"2023-06-16T06:29:39.068Z","metaMigratedFrom":"YAML","title":"OpenCV 人臉識別","breaks":true,"contributors":"[{\"id\":\"240c1e7c-1eda-461f-8d55-5a4f00e12992\",\"add\":8974,\"del\":2325}]"}