import cv2
import os
import numpy as np
import random
import joblib
from sklearn import svm
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

class FaceRecognizer:
def init(self, imgdir='./test_img/', grayfacedir='./test_img_gray', model_path='./svm_model.pkl'):
self.imgdir = imgdir
self.grayfacedir = grayfacedir
self.model_path = model_path
self.svm_model = svm.SVC(kernel='linear', probability=True)
self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
self.label_map = {}
self.pca_model = None # 初始化 pca_model 为 None

def collect_data(self, someone, picturenum=50):
    """
    收集人脸数据并保存图像,同时进行数据增强
    """
    person_dir = self._create_person_directory(someone)
    capture = cv2.VideoCapture(0)

    cv2.waitKey(1)

    count = 0
    while count < picturenum:
        ret, img = capture.read()
        if not ret:
            print("无法获取图像")
            break

        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces = self.detect_faces(gray)

        for (x, y, w, h) in faces:
            face_img = gray[y:y + h, x:x + w]
            augmented_faces = self.augment_face(face_img)

            for augmented_face in augmented_faces:
                picturepath = os.path.join(person_dir, f'{count + 1}.jpg')
                cv2.imwrite(picturepath, augmented_face)
                count += 1

            if count >= picturenum:
                break

        cv2.imshow(f'Capturing Face - {someone}', img)

        if cv2.waitKey(100) == ord('q'):
            break

    capture.release()
    cv2.destroyAllWindows()

def _create_person_directory(self, someone):
    """
    创建人物名称的子文件夹
    """
    person_dir = os.path.join(self.imgdir, someone)
    if not os.path.exists(person_dir):
        os.makedirs(person_dir)
    return person_dir

def detect_faces(self, gray):
    """
    检测图像中的人脸
    """
    faces = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
    return faces

def augment_face(self, face_img):
    """
    对人脸图像进行数据增强,包括旋转、镜像、平移等
    """
    augmented_faces = [cv2.flip(face_img, 1)]  # 镜像(水平翻转)

    angle = random.uniform(-10, 10)  # 随机旋转
    augmented_faces.append(self.rotate_image(face_img, angle))

    translation = random.randint(-5, 5)  # 随机平移
    augmented_faces.append(self.translate_image(face_img, translation))

    return augmented_faces

def rotate_image(self, image, angle):
    """
    旋转图像指定角度
    """
    height, width = image.shape
    center = (width // 2, height // 2)
    rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
    return cv2.warpAffine(image, rotation_matrix, (width, height))

def translate_image(self, image, tx):
    """
    图像平移
    """
    height, width = image.shape
    translation_matrix = np.float32([[1, 0, tx], [0, 1, 0]])
    return cv2.warpAffine(image, translation_matrix, (width, height))

def prepare_data(self):
    """
    准备训练数据并进行标准化处理
    """
    faces = []
    labels = []

    person_id = 0
    for person_name in os.listdir(self.imgdir):
        person_dir = os.path.join(self.imgdir, person_name)
        if not os.path.isdir(person_dir):
            continue

        if person_name not in self.label_map:
            self.label_map[person_id] = person_name  # 新人物,分配新的ID
            person_id += 1

        for img_name in os.listdir(person_dir):
            img_path = os.path.join(person_dir, img_name)
            if img_name.lower().endswith(('.png', '.jpg', '.jpeg')):
                img = cv2.imread(img_path)
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

                faces_detected = self.detect_faces(gray)
                for (x, y, w, h) in faces_detected:
                    face = gray[y:y + h, x:x + w]
                    face_resized = cv2.resize(face, (100, 100))
                    faces.append(face_resized)
                    labels.append(self.get_person_id(person_name))

    faces = np.array(faces)
    faces = faces.reshape(faces.shape[0], -1)  # 展平图像数据
    faces = StandardScaler().fit_transform(faces)  # 进行标准化处理

    return faces, labels

def train_svm(self, n_components=50):
    """
    使用 PCA 进行降维后,使用 SVM 训练模型
    """
    faces, labels = self.prepare_data()
    self.pca = PCA(n_components=n_components, whiten=True)
    faces_pca = self.pca.fit_transform(faces)

    self.svm_model.fit(faces_pca, labels)

    self.save_model()
    joblib.dump(self.pca, './pca_model.pkl')
    print("SVM 模型和 PCA 降维模型训练完成!")

def get_person_id(self, person_name):
    """
    根据人物名称获取对应的ID,如果没有,则让用户命名
    """
    if person_name not in self.label_map.values():
        new_name = input(f"请输入 {person_name} 的名称:")
        person_id = len(self.label_map)
        self.label_map[person_id] = new_name
        print(f"已将 {person_name} 命名为 {new_name}")
        return person_id
    return list(self.label_map.values()).index(person_name)

def save_model(self):
    """
    保存训练好的SVM模型
    """
    joblib.dump(self.svm_model, self.model_path)
    print(f"模型已保存至 {self.model_path}")

    def load_model(self):
        """
        加载已保存的SVM模型和PCA模型
        """
        self.svm_model = joblib.load(self.model_path)
        self.pca_model = joblib.load('./pca_model.pkl')  # 加载PCA模型
        print(f"模型已加载:{self.model_path} 和 ./pca_model.pkl")

    def predict(self, img):
        """
        使用训练好的SVM模型进行人脸识别,并应用PCA降维
        """
        if self.pca_model is None:  # 检查 pca_model 是否已加载
            print("PCA模型尚未加载,请先加载模型。")
            return

        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        faces_detected = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
        for (x, y, w, h) in faces_detected:
            face = gray[y:y + h, x:x + w]
            face_resized = cv2.resize(face, (100, 100))  # 调整大小
            face_flattened = face_resized.flatten().reshape(1, -1)
            face_flattened = StandardScaler().fit_transform(face_flattened)
            face_pca = self.pca_model.transform(face_flattened)  # 使用训练时的PCA进行降维
            label = self.svm_model.predict(face_pca)
            confidence = self.svm_model.decision_function(face_pca)
            person_name = self.label_map.get(label[0], 'Unknown')
            print(f"预测标签:{person_name}, 置信度:{confidence}")
            cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
            cv2.putText(img, person_name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)


def prepare_data(self):
    """
    准备训练数据并进行标准化处理
    """
    faces = []
    labels = []

    person_id = 0
    for person_name in os.listdir(self.imgdir):
        person_dir = os.path.join(self.imgdir, person_name)
        if not os.path.isdir(person_dir):
            continue

        if person_name not in self.label_map:
            self.label_map[person_id] = person_name  # 新人物,分配新的ID
            person_id += 1

        # 处理每个人物的图像
        for img_name in os.listdir(person_dir):
            img_path = os.path.join(person_dir, img_name)
            if img_name.lower().endswith(('.png', '.jpg', '.jpeg')):
                img = cv2.imread(img_path)
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

                # 进行人脸检测
                faces_detected = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5,
                                                                    minSize=(30, 30))
                for (x, y, w, h) in faces_detected:
                    face = gray[y:y + h, x:x + w]
                    face_resized = cv2.resize(face, (100, 100))  # 统一调整大小
                    faces.append(face_resized)
                    labels.append(self.get_person_id(person_name))

    # 标准化
    faces = np.array(faces)
    faces = faces.reshape(faces.shape[0], -1)  # 展平图像数据
    faces = StandardScaler().fit_transform(faces)  # 进行标准化处理

    return faces, labels
def train_svm(self, n_components=50):
    """
    使用 PCA 进行降维后,使用 SVM 训练模型
    """
    faces, labels = self.prepare_data()

    # 使用 PCA 进行降维
    self.pca = PCA(n_components=n_components, whiten=True)
    faces_pca = self.pca.fit_transform(faces)

    # 使用 SVM 进行训练
    self.svm_model.fit(faces_pca, labels)

    # 保存模型和 PCA 对象
    self.save_model()
    joblib.dump(self.pca, './pca_model.pkl')
    print("SVM 模型和 PCA 降维模型训练完成!")

def get_person_id(self, person_name):
    """
    根据人物名称获取对应的ID,如果没有,则让用户命名
    """
    # 如果人物名称不存在,提示用户输入新的名称
    if person_name not in self.label_map.values():
        new_name = input(f"请输入 {person_name} 的名称:")
        # 确保该名字是唯一的
        person_id = len(self.label_map)
        self.label_map[person_id] = new_name
        print(f"已将 {person_name} 命名为 {new_name}")
        return person_id
    else:
        # 如果人物名称已经命名,返回已有ID
        return list(self.label_map.values()).index(person_name)

def save_model(self):
    """
    保存训练好的SVM模型
    """
    joblib.dump(self.svm_model, self.model_path)
    print(f"模型已保存至 {self.model_path}")

def load_model(self):
    """
    加载已保存的SVM模型和PCA模型
    """
    self.svm_model = joblib.load(self.model_path)
    # 加载PCA模型
    self.pca_model = joblib.load('./pca_model.pkl')
    print(f"模型已加载:{self.model_path} 和 ./pca_model.pkl")

def predict(self, img):
    """
    使用训练好的SVM模型进行人脸识别,并应用PCA降维
    """
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces_detected = self.face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

    for (x, y, w, h) in faces_detected:
        face = gray[y:y + h, x:x + w]
        face_resized = cv2.resize(face, (100, 100))  # 调整大小
        face_flattened = face_resized.flatten().reshape(1, -1)

        # 标准化
        face_flattened = StandardScaler().fit_transform(face_flattened)

        # 使用PCA降维
        face_pca = self.pca_model.transform(face_flattened)  # 使用训练时的PCA进行降维

        # 预测结果
        label = self.svm_model.predict(face_pca)
        confidence = self.svm_model.decision_function(face_pca)

        # 获取预测标签
        person_name = self.label_map.get(label[0], 'Unknown')

        # 显示识别结果
        print(f"预测标签:{person_name}, 置信度:{confidence}")

        cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
        cv2.putText(img, person_name, (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9,
                    (0, 255, 0), 2)

# 收集数据和训练方法与之前保持一致

def display_menu(self):
    """
    显示菜单并处理用户输入
    """
    while True:
        print("\n--- 人脸识别菜单 ---")
        print("1. 收集数据")
        print("2. 训练模型")
        print("3. 预测人脸")
        print("4. 加载模型")
        print("5. 保存模型")
        print("6. 退出")
        choice = input("请输入选择(1-6):")

        if choice == '1':
            self.collect_data_menu()
        elif choice == '2':
            self.train_model_menu()
        elif choice == '3':
            self.predict_face_menu()
        elif choice == '4':
            self.load_model()
        elif choice == '5':
            self.save_model()
        elif choice == '6':
            print("退出程序")
            break
        else:
            print("无效输入,请重新选择")

def collect_data_menu(self):
    """
    提供用户选择进行数据收集
    """
    someone = input("请输入要收集数据的人物名称:")
    picturenum = int(input("请输入要收集的图像数量:"))
    self.collect_data(someone, picturenum)

def train_model_menu(self):
    """
    训练模型菜单
    """
    try:
        # 获取PCA降维的组件数量
        n_components_input = input("请输入PCA降维的组件数量(整数或浮动值,0到1之间):")

        if '.' in n_components_input:  # 如果是浮动值
            n_components = float(n_components_input)
        else:  # 如果是整数
            n_components = int(n_components_input)

        if n_components <= 0 or n_components > 1:
            print("请输入一个介于0和1之间的浮动值,或大于0的整数!")
            return

        # 调用训练方法
        self.train_svm(n_components=n_components)

    except ValueError:
        print("输入无效,请输入一个数字(整数或浮动值)!")

def predict_face_menu(self):
    """
    提供用户选择进行人脸预测
    """
    capture = cv2.VideoCapture(0)
    print("正在进行人脸识别,按 'q' 键退出")

    while True:
        ret, img = capture.read()
        if not ret:
            print("无法获取图像")
            break

        self.predict(img)

        cv2.imshow("Face Recognition", img)

        if cv2.waitKey(1) == ord('q'):
            break

    capture.release()
    cv2.destroyAllWindows()

在主程序中调用菜单

if name == "main":
face_recognizer = FaceRecognizer()
face_recognizer.display_menu()
以下是运行
C:\Users\32686\venv\Scripts\python.exe C:\Users\32686\PycharmProjects\实训人脸识别第五组\cs.py

--- 人脸识别菜单 ---

  1. 收集数据
  2. 训练模型
  3. 预测人脸
  4. 加载模型
  5. 保存模型
  6. 退出
    请输入选择(1-6):3
    正在进行人脸识别,按 'q' 键退出
    Traceback (most recent call last):
    File "C:\Users\32686\PycharmProjects\实训人脸识别第五组\cs.py", line 420, in
    face_recognizer.display_menu()
    File "C:\Users\32686\PycharmProjects\实训人脸识别第五组\cs.py", line 351, in display_menu
    self.predict_face_menu()
    File "C:\Users\32686\PycharmProjects\实训人脸识别第五组\cs.py", line 406, in predict_face_menu
    self.predict(img)
    File "C:\Users\32686\PycharmProjects\实训人脸识别第五组\cs.py", line 314, in predict
    face_pca = self.pca_model.transform(face_flattened) # 使用训练时的PCA进行降维
    ^^^^^^^^^^^^^^^^^^^^^^^^

AttributeError: 'NoneType' object has no attribute 'transform'

进程已结束,退出代码为 1

Ready to merge
This branch is ready to get merged automatically.

Sign up or log in to comment