人臉資料庫

      在〈人臉資料庫〉中尚無留言

前一篇的人臉辨識中,其步驟為 : 載入圖片 -> 取得臉部區域 -> 取得特微 -> 辨識取得描述子。光一張圖片就要經過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()

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *