###### tags: `OpenCV`,`臉部偵測` # OpenCV 實作篇-臉部偵測 目標偵測與識別 影像分類是將㇐張圖片識別為既定類別的某㇐種,但如果要知道分類的物體在影像中的位置,就是目標偵測要解決的問題  目標偵測即檢測出圖片中主體所在的位置Bounding Box(簡稱BBox),矩形框的左上角座標與右下角座標或是左上角座標與矩形框⾧寬 分成3類: * 傳統的目標檢測算法:Cascade + Harr / SVM + HOG /DPM 以及上述的諸多改進、優化 * 候選窗+深度學習分類:通過提取候選區域,並對相應區域進行以深度學習方法為主的分類的方案,如:RCNN /Fast-RCNN / Faster-RCNN / SPP-net / R-FCN 等系列方法 * 基於深度學習的回歸方法:YOLO / SSD / DenseBox等方法;以及最近出現的結合RNN算法的RRC detection;結合DPM的Deformable CNN等 認識Haar特徵分類器 **Haar features-哈爾特徵** Haar特徵值反映了圖像的灰階變化情況,以人臉為例,將任意㇐個矩形放到人臉區域上,然後,將白色區域的畫素和減去黑色區域的畫素和,得到的值我們暫且稱之為人臉特徵值,如果你把這個矩形放到㇐個非人臉區域,那麼計算出的特徵值應該和人臉特徵值是不㇐樣的,而且越不㇐樣越好,所以這些方塊的目的就是把人臉特徵量化,以區分人臉和非人臉。 ![](https://i.imgur.com/DW2at1E.jpg) 使用Haar特徵分類器模型時,系統會在圖片左上角產生㇐個檢測矩形,檢查此矩形是否符合Haar特徵分類模型特徵,接著將此矩形向右移動檢測,到最右方後移到左側下方檢測,直到圖片右下角為止。 示意圖如下~ ![](https://i.imgur.com/e0PBRwp.jpg) :star:當我們提到Haar cascades,指的就是用Haar features做成的cascade classifiers Haar-based face detector的理論概念 1.你可以去注意任何一張臉,眼睛的部分一定會比額頭和臉頰的部分暗。 2.嘴巴四周一定也會比臉頰部分暗。 Boost的想法 我們不需要絕對無敵的特徵點或是巨大複雜的模型,我們只需要很多表現高於平均的辨識器(weak learner),把它們串聯起來,就可以變超強。 ![](https://i.imgur.com/A03ePjX.jpg) 導入模型-就用別人訓練好的 Rerference: <https://github.com/opencv/opencv/tree/master/data/haarcascades> ```python= import cv2 import numpy as np #導入模型 haarCascadeFace = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") ``` detectMultiScale語法 detectedObjects = cv2.CascadeClassifier.detectMultiScale(image,[scaleFactor,minNeighbors,flags,minSize,maxSize]) output: detectedObjects-找出來的物件 input的部分: 1.image-目標照片-大家都說轉灰階照片比較好,但是彩色照片好像也沒差! 2.scaleFactor-每一次搜尋的時候,縮小的比例。這樣才可以避免尺寸的問題,做大大小小的偵測。這個數字越大,代表縮小比例越大,所以很快就看不到臉了! 3.minNeighbors-這是一個很妙的參數,簡單的說就是"人臉聚集的地方必定有人臉",所以區塊附近的搜尋區域應該也要發現有人臉的訊號。 4.minSize,maxSize-偵測區域尺寸的上限和下限 ![](https://i.imgur.com/CdsP2cv.jpg) ```python= import cv2 #依照圖片格式,把圖片讀入 image = cv2.imread('child_resize.jpg') #gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) haarCascadeFace = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") detectedObjects = haarCascadeFace.detectMultiScale(image, 1.2 ,2) for face in detectedObjects: x, y, w, h = face cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 2) cv2.imshow('Find Face image haarcascade', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 哈哈 結果不太好 = = ![](https://i.imgur.com/1d1Sx3H.jpg) ```python= #測試scaleFactor的差異 import cv2 haarCascadeFace = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") image = cv2.imread('child_resize.jpg') # gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) detectedObjects = haarCascadeFace.detectMultiScale(image, 1.1,3) #detectedObjects這是一個list資料結構 for face in detectedObjects: x, y, w, h = face cv2.rectangle(image, (x, y), (x+w, y+h), (0, 0, 255), 2) cv2.imshow(f'scaleFactor 1.1', image) image1 = cv2.imread('child_resize.jpg') gray_image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY) detectedObjects1 = haarCascadeFace.detectMultiScale(gray_image1, 1.5,3) for face in detectedObjects1: x, y, w, h = face cv2.rectangle(image1, (x, y), (x+w, y+h), (0, 255, 0), 2) cv2.imshow(f'scaleFactor 1.5', image1) image2 = cv2.imread('child_resize.jpg') gray_image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY) detectedObjects2 = haarCascadeFace.detectMultiScale(gray_image1, 2,3) for face in detectedObjects2: x, y, w, h = face cv2.rectangle(image2, (x, y), (x+w, y+h), (255, 0, 0), 2) cv2.imshow(f'scaleFactor 2', image2) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/4645Xhi.jpg) 來試試其他deep learning face detector models 深度學習的架構有很多種...Caffe, TensorFlow, Torch, and Darknet, 接下來我們使用Caffe, TensorFlow兩種範例 **Caffe版** 需要有兩個檔案 res10_300x300_ssd_iter_140000_fp16.caffemodel :這就是權重系數檔案 deploy.prototxt :這是模型結構說明檔 **cv2.dnn.blobFromImage()** cv2.dnn.blobFromImage(image[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]]) image:輸入圖像(1、3或者4通道) scalefactor:圖像各通道數值的縮放比例 size:輸出圖像的空間尺寸,如size=(200,300)表示寬w=200,高h=300 mean:用於各通道減去的值,以降低亮度的影響(e.g. image為 BGR 3通道的圖像,mean=[104.0, 177.0, 123.0],表示b通道的值-104,g-177,r-123) swapRB:交換RB通道,默認為False.(cv2.imread讀取的是彩圖是BGR通道) crop:圖像裁剪,默認為False.當值為True時,先按比例縮放,然後從中心裁剪成size尺寸 ddepth:輸出的圖像深度,可選CV_32F 或者 CV_8U. ```python= import cv2 import numpy as np from matplotlib import pyplot as plt #模型結構 跟 參數 net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000_fp16.caffemodel") #依照圖片格式,把圖片讀入 image = cv2.imread('child_resize.jpg') (h, w) = image.shape[:2] print(h,w) #605 454 ``` ```python= #模型要求是BGR格式 #圖片尺寸用的是我們圖片的尺寸 #它要求每個通道的像數要減除一個常數 blob = cv2.dnn.blobFromImage(image, 1.0, (454, 605),[104., 117., 123.],False, False) #blob轉換檔案格式,轉成模型可以使用的檔案格式 #image放入模型 net.setInput(blob) #開始計算 detections = net.forward() #forward 一層處理完再處理下一層(deeplearning模式) ``` detections 是一個4d資料結構 ![](https://i.imgur.com/rYYk68W.jpg) ```python= detections[0][0][0] #4d結構 #array([0. , 1. , 0.99768186, 0.43984586, 0.35949108,0.52031076, 0.42666915], dtype=float32) #[0,1,信心水準(偵測人臉的信心指數),start x比例,start y比例,end x比例,end y比例] ``` ```python= detected_faces = 0 # Iterate over all detections: #detections.shape[2] #是偵測到的個數 for i in range(0, detections.shape[2]): # Get the confidence (probability) of the current detection: confidence = detections[0, 0, i, 2] #i每個都要跑過,2代表信心指數那個欄位 # Only consider detections if confidence is greater than a fixed minimum confidence: if confidence > 0.3: # Increment the number of detected faces: detected_faces += 1 # 把算出來的定位比例還原成原本的尺寸,這樣我們才可以拿到座標 box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) #座標沒有小數,所以要整理一下 (startX, startY, endX, endY) = box.astype("int") # 把框框畫出來 cv2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 2) #把信心指數寫一下 text = "{:.2f}%".format(confidence * 100) #確定一下找到的框框上面還有沒有空間可以寫字,那就維持,如果沒有,就寫框框下面 y = startY - 10 if startY - 10 > 10 else startY + 10 #0.4字體大小 #2是字的粗細 cv2.putText(image, text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) ``` ```python= cv2.imshow("image",image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/pmg39ux.jpg) **Tensorflow** 需有兩個檔案 opencv_face_detector_uint8.pb opencv_face_detector.pbtxt ```python= import cv2 import numpy as np #依照圖片格式,把圖片讀入 image = cv2.imread('child_resize.jpg') (h, w) = image.shape[:2] #模型要求是BGR格式 #要求300*300的尺寸 #它要求每個通道的像數要減除一個常數 blob = cv2.dnn.blobFromImage(image, 1.0, (584, 886), [104., 117., 123.],False, False) net = cv2.dnn.readNetFromTensorflow("opencv_face_detector_uint8.pb","opencv_face_detector.pbtxt") #image進入模型 net.setInput(blob) #開始計算 detections = net.forward() detected_faces = 0 # Iterate over all detections: for i in range(0, detections.shape[2]): # Get the confidence (probability) of the current detection: confidence = detections[0, 0, i, 2] # Only consider detections if confidence is greater than a fixed minimum confidence: if confidence > 0.3: # Increment the number of detected faces: detected_faces += 1 # 把算出來的定位比例還原成原本的尺寸,這樣我們才可以拿到座標 box = detections[0, 0, i, 3:7] * np.array([w, h, w, h]) #座標沒有小數,所以要整理一下 (startX, startY, endX, endY) = box.astype("int") # 把框框畫出來 cv2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 2) #把信心指數寫一下 text = "{:.2f}%".format(confidence * 100) #確定一下找到的框框上面還有沒有空間可以寫字,那就維持,如果沒有,就寫框框下面 y = startY - 10 if startY - 10 > 10 else startY + 10 #0.4字體大小 #2是字的粗細 cv2.putText(image, text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 0, 255), 1) cv2.imshow("Tensorflow image",image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/SPIHhWD.jpg) :star: :star: :star: 結論呢~~ 深度學習的框架還是比較厲害!! 可以把哈爾偵測失敗的都偵測出來!