通過(guò)opencv制作人臉識(shí)別的窗口
人臉檢測(cè),看似要使用深度學(xué)習(xí),覺(jué)得很高大牛逼,其實(shí)通過(guò)opencv就可以制作人臉識(shí)別的窗口。

今天,Runsen教大家將構(gòu)建一個(gè)簡(jiǎn)單的Python腳本來(lái)處理圖像中的人臉,使在OpenCV庫(kù)中兩種方法 。
首先,我們將使用haar級(jí)聯(lián)分類(lèi)器,這對(duì)初學(xué)者來(lái)說(shuō)是一種簡(jiǎn)單的方法(也不太準(zhǔn)確),也是最方便的方法。
其次是單發(fā)多盒檢測(cè)器(或簡(jiǎn)稱(chēng)SSD),這是一種深度神經(jīng)網(wǎng)絡(luò)檢測(cè)圖像中對(duì)象的方法。
使用Haar級(jí)聯(lián)進(jìn)行人臉檢測(cè)
基于haar特征的級(jí)聯(lián)分類(lèi)器的,OpenCV已經(jīng)為我們提供了一些分類(lèi)器參數(shù),因此我們無(wú)需訓(xùn)練任何模型,直接使用。
opencv的安裝
- pip install opencv-python
我們首先導(dǎo)入OpenCV:
- import cv2
下面對(duì)示例圖像進(jìn)行測(cè)試,我找來(lái)了我學(xué)校的兩個(gè)漂亮美女的圖像:
- image = cv2.imread("beauty.jpg")
函數(shù)imread()從指定的文件加載圖像,并將其作為numpy的 N維數(shù)組返回。
在檢測(cè)圖像中的面部之前,我們首先需要將圖像轉(zhuǎn)換為灰度圖:
- image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
下面,因?yàn)橐跏蓟四樧R(shí)別器(默認(rèn)的人臉haar級(jí)聯(lián)),需要下載對(duì)應(yīng)的參數(shù)xml文件,
這里選擇最初的haarcascade_frontalface_default.xml
下面代碼就是加載使用人臉識(shí)別器
- face_cascade = cv2.CascadeClassifier("haarcascade_fontalface_default.xml")
現(xiàn)在讓我們檢測(cè)圖像中的所有面孔:
- # 檢測(cè)圖像中的所有人臉
- faces = face_cascade.detectMultiScale(image_gray)
- print(f"{len(faces)} faces detected in the image.")
detectMultiScale() 函數(shù)將圖像作為參數(shù)并將不同大小的對(duì)象檢測(cè)為矩形列表,因此我們繪制矩形,同樣有rectangle方法提供
- #為每個(gè)人臉繪制一個(gè)藍(lán)色矩形
- for x, y, width, height in faces:
- # 這里的color是 藍(lán) 黃 紅,與rgb相反,thickness設(shè)置寬度
- cv2.rectangle(image, (x, y), (x + width, y + height), color=(255, 0, 0), thickness=2)
最后,讓我們保存新圖像:
- cv2.imwrite("beauty_detected.jpg", image)
基于haar特征的級(jí)聯(lián)分類(lèi)器的結(jié)果圖
我們驚奇的發(fā)現(xiàn)圖片1是沒(méi)有設(shè)備出來(lái)的,這是因?yàn)榇嬖谡系K物,
我們驚奇的發(fā)現(xiàn)圖片2是竟然設(shè)別出來(lái)了兩個(gè)窗口。
Haar級(jí)聯(lián)結(jié)合攝像頭
使用Haar級(jí)聯(lián)進(jìn)行人臉檢測(cè)可以說(shuō)是opencv最基礎(chǔ)的效果,下面我們利用攝像頭將Haar級(jí)聯(lián)進(jìn)行合并,這樣就可以達(dá)到開(kāi)頭的效果。
- import cv2
- #創(chuàng)建新的cam對(duì)象
- cap = cv2.VideoCapture(0)
- #初始化人臉識(shí)別器(默認(rèn)的人臉haar級(jí)聯(lián))
- face_cascade = cv2.CascadeClassifier("haarcascade_fontalface_default.xml")
- while True:
- # 從攝像頭讀取圖像
- _, image = cap.read()
- # 轉(zhuǎn)換為灰度
- image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
- # 檢測(cè)圖像中的所有人臉
- faces = face_cascade.detectMultiScale(image_gray, 1.3, 5)
- # 為每個(gè)人臉繪制一個(gè)藍(lán)色矩形
- for x, y, width, height in faces:
- cv2.rectangle(image, (x, y), (x + width, y + height), color=(255, 0, 0), thickness=2)
- cv2.imshow("image", image)
- if cv2.waitKey(1) == ord("q"):
- break
- cap.release()
- cv2.destroyAllWindows()
使用SSD的人臉檢測(cè)
上面的效果是已經(jīng)過(guò)時(shí)了,但是OpenCV為我們提供了包裝中dnn模塊cv2,從而可以直接加載經(jīng)過(guò)預(yù)訓(xùn)練的深度學(xué)習(xí)模型。
2015年底有人提出了一個(gè)實(shí)時(shí)對(duì)象檢測(cè)網(wǎng)絡(luò)Single Shot MultiBox Detector縮寫(xiě)為SSD
SSD對(duì)象檢測(cè)的Model
SSD對(duì)象檢測(cè)網(wǎng)絡(luò)簡(jiǎn)單說(shuō)可以分為三個(gè)部分:
- 基礎(chǔ)網(wǎng)絡(luò)(backbone) 這里為VGG16
- 特征提取Neck,構(gòu)建多尺度特征
- 檢測(cè)頭 – 非最大抑制與輸出
要開(kāi)始使用SSD在OpenCV中,需要下載RESNET人臉檢測(cè)模型和其預(yù)訓(xùn)練的權(quán)重,然后將其保存到代碼weights工作目錄:
RESNET人臉檢測(cè)模型和權(quán)重
- import cv2
- import numpy as np
- # 下載鏈接:https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
- prototxt_path = "weights/deploy.prototxt.txt"
- # 下載鏈接:https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel
- model_path = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"
- # 加載Caffe model
- model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)
- # 讀取所需圖像
- image = cv2.imread("beauty.jpg")
- # 獲取圖像的寬度和高度
- h, w = image.shape[:2]
現(xiàn)在,需要這個(gè)圖像傳遞到神經(jīng)網(wǎng)絡(luò)中,由于下載的模型是(300, 300) px的。
因此,我們需要將圖像調(diào)整為的(300, 300)px形狀:
- # 預(yù)處理圖像:調(diào)整大小并執(zhí)行平均減法。104.0, 177.0, 123.0 表示b通道的值-104,g-177,r-123
- # 在深度學(xué)習(xí)中通過(guò)減去數(shù)人臉據(jù)集的圖像均值而不是當(dāng)前圖像均值來(lái)對(duì)圖像進(jìn)行歸一化,因此這里寫(xiě)死了
- blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300),(104.0, 177.0, 123.0))
將此blob對(duì)象用作神經(jīng)網(wǎng)絡(luò)的輸入,獲取檢測(cè)到的面部:
- # 將圖像輸入神經(jīng)網(wǎng)絡(luò)
- model.setInput(blob)
- # 得到結(jié)果
- output = np.squeeze(model.forward())
輸出對(duì)象output 具有所有檢測(cè)到的對(duì)象,在這種情況下一般都是人臉,讓我們遍歷output,并在取一個(gè)置信度大于50%的判斷條件:
- font_scale = 1.0
- # output.shape ==(200, 7)
- for i in range(0, output.shape[0]):
- # 置信度
- confidence = output[i, 2]
- # 如果置信度高于50%,則繪制周?chē)姆娇?nbsp;
- if confidence > 0.5:
- # 之前將圖片變成300*300,接下來(lái)提取檢測(cè)到的對(duì)象的模型的置信度后,我們得到周?chē)目?nbsp;output[i, 3:7],然后將其width與height原始圖像的和相乘,以獲得正確的框坐標(biāo)
- box = output[i, 3:7] * np.array([w, h, w, h])
- # 轉(zhuǎn)換為整數(shù)
- start_x, start_y, end_x, end_y = box.astype(np.int)
- # 繪制矩形
- cv2.rectangle(image, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
- # 添加文本
- cv2.putText(image, f"{confidence*100:.2f}%", (start_x, start_y-5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)
最后我們展示并保存新圖像:
- cv2.imshow("image", image)
- cv2.waitKey(0)
- cv2.imwrite("beauty_detected.jpg", image)
下面是完整代碼
- import cv2
- import numpy as np
- # 下載鏈接:https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt
- prototxt_path = "weights/deploy.prototxt.txt"
- # 下載鏈接:https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel
- model_path = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"
- model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)
- image = cv2.imread("beauty.jpg")
- h, w = image.shape[:2]
- blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300),(104.0, 177.0, 123.0))
- model.setInput(blob)
- output = np.squeeze(model.forward())
- font_scale = 1.0
- for i in range(0, output.shape[0]):
- confidence = output[i, 2]
- if confidence > 0.5:
- box = output[i, 3:7] * np.array([w, h, w, h])
- start_x, start_y, end_x, end_y = box.astype(np.int)
- cv2.rectangle(image, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
- cv2.putText(image, f"{confidence*100:.2f}%", (start_x, start_y-5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)
- cv2.imshow("image", image)
- cv2.waitKey(0)
- cv2.imwrite("beauty_detected.jpg", image)
SSD結(jié)合攝像頭的人臉檢測(cè)
SSD結(jié)合攝像頭的人臉檢測(cè)方法更好,更準(zhǔn)確,但是每秒傳輸幀數(shù)FPS方面可能低,因?yàn)樗蝗鏗aar級(jí)聯(lián)方法快,但這問(wèn)題并不大。
下面是SSD結(jié)合攝像頭的人臉檢測(cè)的全部代碼
- import cv2
- import numpy as np
- prototxt_path = "weights/deploy.prototxt.txt"
- model_path = "weights/res10_300x300_ssd_iter_140000_fp16.caffemodel"
- model = cv2.dnn.readNetFromCaffe(prototxt_path, model_path)
- cap = cv2.VideoCapture(0)
- while True:
- _, image = cap.read()
- h, w = image.shape[:2]
- blob = cv2.dnn.blobFromImage(image, 1.0, (300, 300), (104.0, 177.0, 123.0))
- model.setInput(blob)
- output = np.squeeze(model.forward())
- font_scale = 1.0
- for i in range(0, output.shape[0]):
- confidence = output[i, 2]
- if confidence > 0.5:
- box = output[i, 3:7] * np.array([w, h, w, h])
- start_x, start_y, end_x, end_y = box.astype(np.int)
- cv2.rectangle(image, (start_x, start_y), (end_x, end_y), color=(255, 0, 0), thickness=2)
- cv2.putText(image, f"{confidence*100:.2f}%", (start_x, start_y-5), cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 0, 0), 2)
- cv2.imshow("image", image)
- if cv2.waitKey(1) == ord("q"):
- break
- cv2.destroyAllWindows()
- cap.release()