[Python急救站]人臉識別技術練習

Jinylin發表於2024-04-28

這段時間做了一個用於初學者學習人臉識別系統的程式,在上程式碼時,先給說說事前準備:

首先我們需要一個OpenCV的一個haarcascade_frontalface_default.xml檔案,只要去GitHub上面即可下載:https://github.com/opencv/opencv[Python急救站]人臉識別技術練習

點選Code,選擇Download ZIP,下載後解壓在目錄下opencv-4.x\data\haarcascades中可以找到haarcascade_frontalface_default.xml,這個時候將這個檔案複製到你的工程目錄下。

第二個要準備的檔案是:lfw-deepfunneled,這個檔案在網站中下載:https://vis-www.cs.umass.edu/lfw/#download

進入網站後下拉找到Download the database: ,然後在這個模組中找到All images aligned with deep funneling,點選下載後,將壓縮包解壓到工程目錄下即可。

然後建立一個模型訓練的py檔案,程式碼如下:

import os
import numpy as np
import cv2
from matplotlib import pyplot as plt
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import classification_report, confusion_matrix
from joblib import dump

# 設定資料集路徑
lfw_home = 'D:/Pythonxiangmu/Python/renlian/lfw-deepfunneled'  # 替換為實際路徑
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')


# 函式:從目錄載入影像並提取人臉區域
def load_images_and_labels(directory):
    images = []
    labels = []
    for label in sorted(os.listdir(directory)):
        if not os.path.isdir(os.path.join(directory, label)):
            continue
        for img_path in os.listdir(os.path.join(directory, label)):
            img = cv2.imread(os.path.join(directory, label, img_path), cv2.IMREAD_GRAYSCALE)
            faces = face_cascade.detectMultiScale(img, scaleFactor=1.1, minNeighbors=5)
            if len(faces) > 0:
                for (x, y, w, h) in faces:
                    face_img = img[y:y + h, x:x + w]
                    face_img_resized = cv2.resize(face_img, (130, 195))  # 保持與應用程式碼一致
                    images.append(face_img_resized.flatten())
                    labels.append(label)
    return np.array(images), np.array(labels)


def train_face_recognition_model(dataset_path):
    # 載入影像和標籤
    X, labels = load_images_and_labels(dataset_path)

    # 資料集劃分
    X_train, X_test, y_train, y_test = train_test_split(X, labels, test_size=0.25, random_state=42)

    # 特徵縮放
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)

    # PCA降維
    n_components = 150  # 選擇合適的主成分數
    pca = PCA(n_components=n_components, whiten=True)
    X_train_pca = pca.fit_transform(X_train_scaled)
    X_test_pca = pca.transform(X_test_scaled)

    # 訓練邏輯迴歸模型
    clf = LogisticRegression(C=1e5)
    clf.fit(X_train_pca, y_train)

    # 預測與評估
    y_pred = clf.predict(X_test_pca)
    print("Classification Report:")
    print(classification_report(y_test, y_pred))
    print("Confusion Matrix:")
    print(confusion_matrix(y_test, y_pred))

    # Eigenfaces進行視覺化(可選)
    n_components_to_show = 10
    """視覺化前n個Eigenfaces"""
    eigenfaces = pca.components_
    eigenface_titles = ["Eigenface %d" % i for i in range(1, n_components_to_show + 1)]

    # 選擇前n個Eigenfaces進行視覺化

    for i in range(n_components_to_show):
        eigenface = eigenfaces[i].reshape(195, 130)  # 根據之前調整的尺寸進行重塑
        plt.subplot(2, 5, i + 1)
        plt.imshow(eigenface, cmap='gray')
        plt.title(eigenface_titles[i])
        plt.axis('off')  # 不顯示座標軸

    plt.show()
    # 儲存模型與前處理器
    dump(clf, 'model.pkl')
    dump(scaler, 'scaler.pkl')
    dump(pca, 'pca.pkl')


# 呼叫函式以執行訓練流程
train_face_recognition_model(lfw_home)

執行結束後,就會在你的工程目錄下建立三個訓練好的pkl檔案。

然後再建立一個人臉識別用的py檔案,程式碼如下:

import os
from tkinter import Tk, Label, messagebox
import cv2
import joblib
import numpy as np
from PIL import Image, ImageTk

# 載入預先訓練好的LogisticRegression模型,用於後續的人臉識別
model = joblib.load('model.pkl')

# 載入特徵縮放器,用於標準化輸入資料,提高模型識別效能
scaler = joblib.load('scaler.pkl')

# 載入PCA模型,用於降維處理,減少計算複雜度並去除噪聲
pca = joblib.load('pca.pkl')


# 定義函式:從給定路徑載入參考照片並提取其面部特徵
def load_reference_image(reference_path):
    # 使用OpenCV讀取灰度影像作為參考照片
    reference_image = cv2.imread(reference_path, cv2.IMREAD_GRAYSCALE)

    # 利用預設的人臉檢測器檢測參考照片中的人臉
    reference_faces = face_cascade.detectMultiScale(reference_image)

    # 確保至少檢測到一張人臉,否則丟擲錯誤
    if len(reference_faces) == 0:
        raise ValueError("No face detected in reference image.")

    # 獲取檢測到的第一張人臉區域
    reference_face_roi = reference_image[
                         reference_faces[0][1]:reference_faces[0][1] + reference_faces[0][3],
                         reference_faces[0][0]:reference_faces[0][0] + reference_faces[0][2]
                         ]

    # 對提取的參考人臉區域進一步預處理,準備用於模型識別
    preprocessed_reference_face = preprocess_face(reference_face_roi)

    # 返回預處理後的面部特徵資料
    return preprocessed_reference_face


# 定義函式:預處理輸入的人臉影像,以便用於模型識別
def preprocess_face(face_image):
    # 確保影像尺寸與訓練時一致(130x195)
    face_image_resized = cv2.resize(face_image, (130, 195))

    # 影像資料展平
    img_flattened = face_image_resized.flatten()  # 調整尺寸後,直接展平

    # 特徵縮放
    img_scaled = scaler.transform(img_flattened.reshape(1, -1))

    # PCA降維
    img_pca = pca.transform(img_scaled)

    # 使用訓練好的模型進行預測

    return img_pca


# 定義函式:識別影片流中的臉部並判斷是否為目標人物
def recognize_faces(frame):
    # 將影片幀轉換為灰度影像,便於人臉檢測
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 使用預定義的人臉級聯分類器檢測影像中的人臉
    face_rects = face_cascade.detectMultiScale(gray_frame, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    # 遍歷檢測到的每一個人臉區域
    for (x, y, w, h) in face_rects:
        # 提取人臉區域
        face_roi = gray_frame[y:y + h, x:x + w]
        # 對該人臉區域進行預處理
        preprocessed_face = preprocess_face(face_roi)
        # 使用模型預測該人臉屬於各個類別的機率
        probabilities = model.predict_proba(preprocessed_face.reshape(1, -1))[0]
        # 獲取最可能的預測類別
        predicted_label = np.argmax(probabilities)
        # 若預測類別與目標人物標籤匹配,則認為識別到了目標人物
        if predicted_label == target_person_label:
            return True
    # 若未識別到目標人物,則返回False
    return False


# 載入OpenCV預訓練的人臉檢測模型(基於Haar特徵的級聯分類器)
base_dir = r"D:\Pythonxiangmu\Python\renlian"
xml_path = os.path.join(base_dir, "haarcascade_frontalface_default.xml")
face_cascade = cv2.CascadeClassifier(xml_path)

# 設定目標人物在模型中的標籤假設
target_person_label = 0

# 初始化Tkinter圖形介面,用於展示影片流及識別結果
root = Tk()
root.title("Face Recognition System")
root.geometry("800x600")

# 建立一個Label控制元件用於動態顯示影片幀
label = Label(root)
label.pack(fill="both", expand=True)

# 載入並預處理參考照片,獲取其特徵編碼
photo_path = os.path.join(base_dir, "zhaopian.jpg")
reference_face_encoding = load_reference_image(photo_path)


# 定義影片流展示的回撥函式,持續更新顯示內容並執行人臉識別
def show_frame():
    # 從攝像頭讀取一幀影像
    ret, frame = cap.read()
    if ret:  # 確保成功讀取到幀
        # 嘗試識別當前幀中的人臉
        is_recognized = recognize_faces(frame)
        # 若識別到目標人物,彈出提示框
        if is_recognized:
            messagebox.showinfo("Recognition Result", "Face recognized!")
        # 轉換影像色彩空間以適應Tkinter顯示,並調整尺寸
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        pil_image = Image.fromarray(rgb_frame)
        resized_image = pil_image.resize((root.winfo_width(), root.winfo_height()))
        # 轉換為Tkinter相容的影像格式並更新顯示
        tk_image = ImageTk.PhotoImage(image=resized_image)
        label.config(image=tk_image)
        label.image = tk_image  # 防止影像物件被提前釋放
        # 定時呼叫自身以實現連續更新
        root.after(1, show_frame)


# 初始化攝像頭裝置
cap = cv2.VideoCapture(0)

# 啟動影片流的顯示迴圈
show_frame()

# 執行Tkinter事件迴圈
root.mainloop()

# 關閉攝像頭資源
cap.release()

執行後就開始人臉識別,這樣就完成啦!

注意,程式碼中的所有的路徑都應該改成自己的路徑!其中的畫素可以修改,但是要注意訓練指令碼要和識別指令碼的畫素一致,否則會報錯!

如果有什麼問題歡迎在評論區提問,也可以發個人郵箱:linyuanda@linyuanda.com

相關文章