前一篇的人臉辨識中,其步驟為 : 載入圖片 -> 取得臉部區域 -> 取得特微 -> 辨識取得描述子。光一張圖片就要經過4個步驟,耗費了好多秒。然後如果有1000張圖片呢,就再乘以1000倍。
每次要驗証一張新的臉孔,就要先耗費上述的幾千秒,然後再驗証,這乾脆把電腦丟掉好了。但如果把上述耗費幾千秒計算所得到的描述子存在資料庫,需要時由資料庫取回,不就是秒殺級的了嗎!!
請注意,目前 dlib 19.22.01版本,千萬不要使用dlib.cnn_face_detection_model_v1 來取得人臉偵測位置。因為圖片只要超過4000*3000=12,000,000相素,就算是 RTX 3080Ti 12G的 Ram,都會發生 out of memory的錯誤,而且誤判會更嚴重。
訓練模型
一樣要下載人臉外型68個特徵訓練模型 shape_predictor_68_face_landmarks.dat.bz2 及ResNet人臉辨識訓練模型 dlib_face_recognition_resnet_model_v1.dat.bz2 ,將上面二個下載解開後, 置於跟程式碼同一目錄。
資料表欄位
人臉辨識描述子是numpy的array格式,有128個 float32 數字,要如何存到資料庫中呢? 其實不論有多少個浮點數,在資料表中只要有三個欄位即可
id : int
姓名 : varchar(20)
描述子 : blob
blob是長文字資料形態。所以請先在MySQL中建立一個資料庫(cloud),再建立一個資料表(人臉辨識),然後建立上述三個欄位即可
numpy轉換
描述子是numpy的array格式,裏面的資料是 float32 形態,那又要怎麼存入資料庫的 blob 欄位呢? 此時需先使用 numpy.dumps()。請注意,因為資料格式為 blob,所以需使用 cursor.execute(cmd, args),這樣就不會去檢查字元碼而出現非unicode的錯誤。args必需為tuple,如下所示
npDescriptor = numpy.array(face_descriptor)
dump= npDescriptor.dumps()
cmd = "insert into face (name, descriptor) values (%s, %s)"
cursor.execute(cmd, (name, dump))
那怎麼由資料庫的blob還原成 numpy的array呢? 網路上大都是教你使用 np.loads(),但這方式已被捨棄了,所以正確的方式是要使用 pickle.loads()。請記得需先 import pickle, 如下所示
import pickle
feature=pickle.loads(row[1])
寫入資料庫
底下代碼,可以把photodb資料夾內的圖片,取得描述子數位化後,將描述子寫入資料庫。
注意 : 寫入資料庫的 name 欄位是抓取照片檔名而來,所以照片只能是單獨照,也就是只能有一張臉。
from MahalSdk.cv import cv
from MahalSdk.cloud import cloud
import os
import numpy as np
import dlib
def face_descriptor(img):
faces = detector_face(img, 1)
face=faces[0]
return(
np.asarray(
detector_recognition.compute_face_descriptor(
img,
detector_shape(img, face)
)
)
)
detector_face = dlib.get_frontal_face_detector()
detector_shape = dlib.shape_predictor(
"shape_predictor_68_face_landmarks.dat"
)
detector_recognition= dlib.face_recognition_model_v1(
"dlib_face_recognition_resnet_model_v1.dat"
)
conn=cloud.connect("cloud")
cursor=conn.cursor()
cursor.execute("truncate table 人臉辨識")
cmd="insert into 人臉辨識 (姓名, 描述子) values (%s, %s)"
path="./train"
for file in os.listdir(path):
name=file.split("_")[0]
img=cv.read(os.path.join(path, file))[:,:,::-1].copy()
dump=face_descriptor(img).dumps()
cursor.execute(cmd, (name, dump))
conn.commit()
conn.close()
人臉辨識
上述的資料庫都寫好了,那麼就可以由下面的代碼來測試新圖片的辨識效能。請將要偵測的圖片置於專案目錄下的 ./test目錄
from MahalSdk.cv import cv
from MahalSdk.cloud import cloud
import os
import dlib
import numpy as np
import pylab as plt
import pickle
def face_descriptor(img):
faces = detector_face(img, 1)
face=faces[0]
return(
np.asarray(
detector_recognition.compute_face_descriptor(
img,
detector_shape(img, face)
)
)
)
detector_face = dlib.get_frontal_face_detector()
detector_shape = dlib.shape_predictor(
"shape_predictor_68_face_landmarks.dat"
)
detector_recognition= dlib.face_recognition_model_v1(
"dlib_face_recognition_resnet_model_v1.dat"
)
conn=cloud.connect("cloud")
cursor=conn.cursor()
cursor.execute('select * from 人臉辨識')
rows = cursor.fetchall()
names = [r[1] for r in rows]
descriptors = [pickle.loads(r[2]) for r in rows]
conn.close()
path="./test"
plt.rcParams['font.sans-serif'] = ['Microsoft JhengHei']
for i, file in enumerate(os.listdir(path)):
img=cv.read(os.path.join(path, file))[:,:,::-1].copy()
target=face_descriptor(img)
distance=[np.linalg.norm(d-target) for d in descriptors]
index=np.argmin(distance)
if distance[index]<=0.45:
name = names[index]
else:
name = "查無此人"
ax=plt.subplot(3, 4, i+1)
ax.set_title(name)
ax.imshow(img[:,:,::-1].copy())
ax.axis("off")
plt.show()
後話
理科太太(陳映彤) 與許茹芸長的實在太像,我真的分不出來,但上述的人臉辨識,可以輕易的辨識出來。
辛芷蕾現代及古裝照,完全是不同的二種人,但騙不過上面的人臉辨識,劉亦菲也是如此。
Web Cam 人臉辨識
底下代碼,可以使用 Web Cam 拍攝影像並辨識人臉,亦可播放 .mp4 影片。
train 目錄下的照片請從如下網址下扯 : train.zip
import pickle import cv2 import dlib import numpy as np from MahalSdk.cv import cv from MahalSdk.cloud import cloud def face_descriptor(img, face, detector_shape, detector_recognition): return ( np.asarray( detector_recognition.compute_face_descriptor( img, detector_shape(img, face) ) ) ) #detector_face = dlib.get_frontal_face_detector() detector_face = dlib.cnn_face_detection_model_v1( 'mmod_human_face_detector.dat' ) detector_shape = dlib.shape_predictor( "shape_predictor_68_face_landmarks.dat" ) detector_recognition= dlib.face_recognition_model_v1( "dlib_face_recognition_resnet_model_v1.dat" ) conn=cloud.connect("cloud") cursor=conn.cursor() cursor.execute("select * from 人臉辨識") rs=cursor.fetchall() names=[r[1] for r in rs] descriptors=[pickle.loads(r[2]) for r in rs] cam=cv2.VideoCapture("c.mp4") #cam.set(cv2.CAP_PROP_POS_MSEC, 930000)#a.mp4 #cam.set(cv2.CAP_PROP_POS_MSEC, 30000)#a.mp4 #cam.set(cv2.CAP_PROP_POS_MSEC, 120_000)#b.mp4 cam.set(cv2.CAP_PROP_POS_MSEC, 200_000)#c.mp4 while True: success, img1=cam.read() if success: h1, w1, _=img1.shape img2=cv.resize(img1, width=800) h2, w2, _ = img2.shape rate=w1/w2 faces=detector_face(img2, 1) for f in faces: f=f.rect x1=int(f.left()*rate);y1=int(f.top()*rate) x2=int(f.right()*rate);y2=int(f.bottom()*rate) img1=cv.rectangle(img1, (x1, y1), (x2, y2), color=(0,255,0)) target=face_descriptor(img2, f, detector_shape, detector_recognition) distance=[np.linalg.norm(d-target) for d in descriptors] index=np.argmin(distance) if distance[index]<=0.40: name=names[index] img1 = cv.text(img1, names[index], xy=(x1, y1-50), color=(0, 0, 255), size=50) cv2.imshow("Video", img1) if cv2.waitKey(1)==27:break else: break cam.release()