智识第二版 250205班级 人工智能项目
dks
2025-06-10 c64815b9abdaad8713e964429891d3f477204357
项目总结提交
5个文件已添加
431 ■■■■■ 已修改文件
Server/杜康生/code/face_detector.py 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Server/杜康生/code/gather_examples.py 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Server/杜康生/code/main.py 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Server/杜康生/code/shape_predictor_68_face_landmarks.dat 补丁 | 查看 | 原始文档 | blame | 历史
Server/杜康生/document/活体检测模块-项目总结.docx 补丁 | 查看 | 原始文档 | blame | 历史
Server/¶Å¿µÉú/code/face_detector.py
New file
@@ -0,0 +1,239 @@
import cv2
import imutils
import dlib
import numpy as np
import random
import time
from imutils import face_utils
class FaceDetector:
    def __init__(self, cap, win_width, win_height):
        self.cap = cap
        self.WIN_WIDTH = win_width
        self.WIN_HEIGHT = win_height
        self.detector = None  # äººè„¸æ£€æµ‹å™¨
        self.predictor = None  # ç‰¹å¾ç‚¹æ£€æµ‹å™¨
        # é—ªçƒé˜ˆå€¼
        self.EAR_THRESH = None
        self.MOUTH_THRESH = None
        # æ€»é—ªçƒæ¬¡æ•°
        self.eye_flash_counter = None
        self.mouth_open_counter = None
        self.turn_left_counter = None
        self.turn_right_counter = None
        # è¿žç»­å¸§æ•°é˜ˆå€¼
        self.EAR_CONSTANT_FRAMES = None
        self.MOUTH_CONSTANT_FRAMES = None
        self.LEFT_CONSTANT_FRAMES = None
        self.RIGHT_CONSTANT_FRAMES = None
        # è¿žç»­å¸§è®¡æ•°å™¨
        self.eye_flash_continuous_frame = 0
        self.mouth_open_continuous_frame = 0
        self.turn_left_continuous_frame = 0
        self.turn_right_continuous_frame = 0
    # è®¡ç®—眼长宽比例 EAR值
    @staticmethod
    def count_EAR(eye):
        A = np.linalg.norm(eye[1] - eye[5])
        B = np.linalg.norm(eye[2] - eye[4])
        C = np.linalg.norm(eye[0] - eye[3])
        EAR = (A + B) / (2.0 * C)
        return EAR
    # è®¡ç®—嘴长宽比例 MAR值
    @staticmethod
    def count_MAR(mouth):
        A = np.linalg.norm(mouth[1] - mouth[11])
        B = np.linalg.norm(mouth[2] - mouth[10])
        C = np.linalg.norm(mouth[3] - mouth[9])
        D = np.linalg.norm(mouth[4] - mouth[8])
        E = np.linalg.norm(mouth[5] - mouth[7])
        F = np.linalg.norm(mouth[0] - mouth[6])  # æ°´å¹³æ¬§å‡ é‡Œå¾·è·ç¦»
        ratio = (A + B + C + D + E) / (5.0 * F)
        return ratio
    # è®¡ç®—左右脸转动比例 FR值
    @staticmethod
    def count_FR(face):
        rightA = np.linalg.norm(face[0] - face[27])
        rightB = np.linalg.norm(face[2] - face[30])
        rightC = np.linalg.norm(face[4] - face[48])
        leftA = np.linalg.norm(face[16] - face[27])
        leftB = np.linalg.norm(face[14] - face[30])
        leftC = np.linalg.norm(face[12] - face[54])
        ratioA = rightA / leftA
        ratioB = rightB / leftB
        ratioC = rightC / leftC
        ratio = (ratioA + ratioB + ratioC) / 3
        return ratio
    # æœ¬åœ°æ´»ä½“检测
    def detect_face_local(self):
        self.detect_start_time = time.time()
        # ç‰¹å¾ç‚¹æ£€æµ‹å™¨é¦–次加载比较慢,通过判断减少后面加载的速度
        if self.detector is None:
            self.detector = dlib.get_frontal_face_detector()
        if self.predictor is None:
            self.predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
        # é—ªçƒé˜ˆå€¼
        self.EAR_THRESH = 0.25
        self.MOUTH_THRESH = 0.7
        # æ€»é—ªçƒæ¬¡æ•°
        self.eye_flash_counter = 0
        self.mouth_open_counter = 0
        self.turn_left_counter = 0
        self.turn_right_counter = 0
        # è¿žç»­å¸§æ•°é˜ˆå€¼
        self.EAR_CONSTANT_FRAMES = 2
        self.MOUTH_CONSTANT_FRAMES = 2
        self.LEFT_CONSTANT_FRAMES = 4
        self.RIGHT_CONSTANT_FRAMES = 4
        # è¿žç»­å¸§è®¡æ•°å™¨
        self.eye_flash_continuous_frame = 0
        self.mouth_open_continuous_frame = 0
        self.turn_left_continuous_frame = 0
        self.turn_right_continuous_frame = 0
        print("活体检测 åˆå§‹åŒ–æ—¶é—´:", time.time() - self.detect_start_time)
        # å½“前总帧数
        total_frame_counter = 0
        # è®¾ç½®éšæœºå€¼
        now_flag = 0
        random_type = [0, 1, 2, 3]
        random.shuffle(random_type)
        random_eye_flash_number = random.randint(4, 6)
        random_mouth_open_number = random.randint(2, 4)
        print('请按照指示执行相关动作')  # ç®€å•输出提示信息
        # æŠ“取面部特征点的索引
        (lStart, lEnd) = face_utils.FACIAL_LANDMARKS_IDXS["left_eye"]
        (rStart, rEnd) = face_utils.FACIAL_LANDMARKS_IDXS["right_eye"]
        (mStart, mEnd) = face_utils.FACIAL_LANDMARKS_IDXS["mouth"]
        while self.cap.isOpened():
            ret, frame = self.cap.read()
            total_frame_counter += 1
            frame = imutils.resize(frame)
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            rects = self.detector(gray, 0)
            if len(rects) == 1:
                shape = self.predictor(gray, rects[0])
                shape = face_utils.shape_to_np(shape)
                # æå–面部坐标
                left_eye = shape[lStart:lEnd]
                right_eye = shape[rStart:rEnd]
                mouth = shape[mStart:mEnd]
                # è®¡ç®—长宽比
                left_EAR = self.count_EAR(left_eye)
                right_EAR = self.count_EAR(right_eye)
                mouth_MAR = self.count_MAR(mouth)
                leftRight_FR = self.count_FR(shape)
                average_EAR = (left_EAR + right_EAR) / 2.0
                # è®¡ç®—左眼、右眼、嘴巴的凸包
                left_eye_hull = cv2.convexHull(left_eye)
                right_eye_hull = cv2.convexHull(right_eye)
                mouth_hull = cv2.convexHull(mouth)
                # å¯è§†åŒ–
                cv2.drawContours(frame, [left_eye_hull], -1, (0, 255, 0), 1)
                cv2.drawContours(frame, [right_eye_hull], -1, (0, 255, 0), 1)
                cv2.drawContours(frame, [mouth_hull], -1, (0, 255, 0), 1)
                if now_flag >= 4:
                    return True
                if random_type[now_flag] == 0:
                    if self.turn_left_counter > 0:
                        now_flag += 1
                    else:
                        self.check_left_turn(leftRight_FR)
                        self.turn_right_counter = 0
                        self.mouth_open_counter = 0
                        self.eye_flash_counter = 0
                elif random_type[now_flag] == 1:
                    if self.turn_right_counter > 0:
                        now_flag += 1
                    else:
                        self.check_right_turn(leftRight_FR)
                        self.turn_left_counter = 0
                        self.mouth_open_counter = 0
                        self.eye_flash_counter = 0
                elif random_type[now_flag] == 2:
                    if self.mouth_open_counter >= random_mouth_open_number:
                        now_flag += 1
                    else:
                        self.check_mouth_open(mouth_MAR)
                        self.turn_right_counter = 0
                        self.turn_left_counter = 0
                        self.eye_flash_counter = 0
                elif random_type[now_flag] == 3:
                    if self.eye_flash_counter >= random_eye_flash_number:
                        now_flag += 1
                    else:
                        self.check_eye_flash(average_EAR)
                        self.turn_right_counter = 0
                        self.turn_left_counter = 0
                        self.mouth_open_counter = 0
            elif len(rects) == 0:
                pass  # è¿™é‡Œå¯ä»¥æ·»åŠ æœªæ£€æµ‹åˆ°äººè„¸çš„æç¤ºé€»è¾‘
            elif len(rects) > 1:
                pass  # è¿™é‡Œå¯ä»¥æ·»åŠ æ£€æµ‹åˆ°å¤šå¼ äººè„¸çš„æç¤ºé€»è¾‘
            show_video = cv2.cvtColor(cv2.resize(frame, (self.WIN_WIDTH, self.WIN_HEIGHT)), cv2.COLOR_BGR2RGB)
            # è¿™é‡Œå‡è®¾ä½ æœ‰åœ°æ–¹æ˜¾ç¤ºç”»é¢ï¼Œå®žé™…需要关联到对应的界面元素
            if total_frame_counter >= 1000.0:
                return False
    def check_eye_flash(self, average_EAR):
        if average_EAR < self.EAR_THRESH:
            self.eye_flash_continuous_frame += 1
        else:
            if self.eye_flash_continuous_frame >= self.EAR_CONSTANT_FRAMES:
                self.eye_flash_counter += 1
            self.eye_flash_continuous_frame = 0
    def check_mouth_open(self, mouth_MAR):
        if mouth_MAR > self.MOUTH_THRESH:
            self.mouth_open_continuous_frame += 1
        else:
            if self.mouth_open_continuous_frame >= self.MOUTH_CONSTANT_FRAMES:
                self.mouth_open_counter += 1
            self.mouth_open_continuous_frame = 0
    def check_right_turn(self, leftRight_FR):
        if leftRight_FR <= 0.5:
            self.turn_right_continuous_frame += 1
        else:
            if self.turn_right_continuous_frame >= self.RIGHT_CONSTANT_FRAMES:
                self.turn_right_counter += 1
            self.turn_right_continuous_frame = 0
    def check_left_turn(self, leftRight_FR):
        if leftRight_FR >= 2.0:
            self.turn_left_continuous_frame += 1
        else:
            if self.turn_left_continuous_frame >= self.LEFT_CONSTANT_FRAMES:
                self.turn_left_counter += 1
            self.turn_left_continuous_frame = 0
Server/¶Å¿µÉú/code/gather_examples.py
New file
@@ -0,0 +1,174 @@
import cv2
import dlib
import os
import numpy as np
# è®¡ç®—眼睛纵横比
def eye_aspect_ratio(eye):
    A = np.linalg.norm(eye[1] - eye[5])
    B = np.linalg.norm(eye[2] - eye[4])
    C = np.linalg.norm(eye[0] - eye[3])
    ear = (A + B) / (2.0 * C)
    return ear
# æ£€æµ‹å¤´éƒ¨è¿åŠ¨ï¼ˆç®€å•ç¤ºä¾‹ï¼Œå¯æ ¹æ®å®žé™…æƒ…å†µä¼˜åŒ–ï¼‰
def detect_head_movement(prev_face_landmarks, face_landmarks):
    if prev_face_landmarks is None:
        return False
    prev_nose = prev_face_landmarks[30]
    current_nose = face_landmarks[30]
    movement_threshold = 10  # è°ƒæ•´é˜ˆå€¼
    return abs(current_nose[0] - prev_nose[0]) > movement_threshold or abs(
        current_nose[1] - prev_nose[1]
    ) > movement_threshold
# ç›´æŽ¥æŒ‡å®šè·¯å¾„和参数
input_path = None  # å¦‚果需要从文件读取可以指定路径,这里设为None表示使用摄像头
# ä½¿ç”¨åŽŸå§‹å­—ç¬¦ä¸²æ¥å®šä¹‰è·¯å¾„ï¼Œé¿å…è½¬ä¹‰é—®é¢˜
output_path = r"H:\python_charm\python_project\database"
detector_path = r"H:\python_charm\python_project\face_detector"
confidence = 0.5
# åŠ è½½äººè„¸æ£€æµ‹æ¨¡åž‹
print("[INFO] loading face detector...")
protoPath = os.path.sep.join([detector_path, "deploy.prototxt"])
modelPath = os.path.sep.join([detector_path, "res10_300x300_ssd_iter_140000.caffemodel"])
net = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
# åˆ›å»ºè¾“出目录
if not os.path.exists(output_path):
    os.makedirs(output_path)
real_output_dir = os.path.join(output_path, "real")
fake_output_dir = os.path.join(output_path, "fake")
if not os.path.exists(real_output_dir):
    os.makedirs(real_output_dir)
if not os.path.exists(fake_output_dir):
    os.makedirs(fake_output_dir)
# åˆå§‹åŒ–dlib的面部关键点检测器
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# åˆå§‹åŒ–参数
EYE_AR_THRESH = 0.3
EYE_AR_CONSEC_FRAMES = 3
COUNTER = 0
TOTAL = 0
prev_face_landmarks = None
has_shaken_head = False
has_blinked = False
saved_fake = 0
saved_real = 0
# å„自采集500å¼ 
target_count = 500
# æ–°å¢žæ ‡å¿—变量,用于记录是否正在连续采集
is_continuous_capturing = False
capture_type = None  # è®°å½•当前采集的类型('real' æˆ– 'fake')
# æ‰“开摄像头
cap = cv2.VideoCapture(0)
print("[INFO] Automatically taking pictures...")
while True:
    ret, frame = cap.read()
    if not ret:
        break
    (h, w) = frame.shape[:2]
    blob = cv2.dnn.blobFromImage(cv2.resize(frame, (300, 300)), 1.0,
                                 (300, 300), (104.0, 177.0, 123.0))
    net.setInput(blob)
    detections = net.forward()
    # æ£€æµ‹åˆ°çš„人脸
    if len(detections) > 0:
        i = np.argmax(detections[0, 0, :, 2])
        detection_confidence = detections[0, 0, i, 2]  # é‡å‘½åå˜é‡ï¼Œé¿å…æ··æ·†
        if detection_confidence > confidence:
            box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
            (startX, startY, endX, endY) = box.astype("int")
            # æå–面部关键点
            rect = dlib.rectangle(int(startX), int(startY), int(endX), int(endY))
            face_landmarks = predictor(frame, rect)
            face_landmarks = np.array([[part.x, part.y] for part in face_landmarks.parts()])
            # æ£€æµ‹å¤´éƒ¨è¿åЍ
            has_shaken_head = has_shaken_head or detect_head_movement(prev_face_landmarks, face_landmarks)
            prev_face_landmarks = face_landmarks
            # æ£€æµ‹çœ¨çœ¼åŠ¨ä½œ
            left_eye = face_landmarks[42:48]
            right_eye = face_landmarks[36:42]
            left_ear = eye_aspect_ratio(left_eye)
            right_ear = eye_aspect_ratio(right_eye)
            ear = (left_ear + right_ear) / 2.0
            if ear < EYE_AR_THRESH:
                COUNTER += 1
            else:
                if COUNTER >= EYE_AR_CONSEC_FRAMES:
                    TOTAL += 1
                    has_blinked = has_blinked or True
                COUNTER = 0
            # ä¿å­˜äººè„¸åŒºåŸŸ
            face = frame[startY:endY, startX:endX]
            if is_continuous_capturing:
                if capture_type == "real":
                    label = "real"
                    saved = saved_real
                    output_dir = real_output_dir
                else:
                    label = "fake"
                    saved = saved_fake
                    output_dir = fake_output_dir
                p = os.path.sep.join([output_dir, f"{label}_{saved}.png"])
                cv2.imwrite(p, face)
                print(f"[INFO] saved {p} to disk")
                if label == "fake":
                    saved_fake += 1
                else:
                    saved_real += 1
                if (capture_type == "real" and saved_real >= target_count) or \
                   (capture_type == "fake" and saved_fake >= target_count):
                    is_continuous_capturing = False
                    print(f"[INFO] Reached the target count for {capture_type} images.")
            else:
                print("[INFO] Please enter 'r' for real or 'f' for fake (q to quit): ")
                key = cv2.waitKey(0) & 0xFF
                if key == ord("r"):
                    if saved_real < target_count:
                        is_continuous_capturing = True
                        capture_type = "real"
                    else:
                        print("[INFO] Already reached the target count for real images. Skipping.")
                elif key == ord("f"):
                    if saved_fake < target_count:
                        is_continuous_capturing = True
                        capture_type = "fake"
                    else:
                        print("[INFO] Already reached the target count for fake images. Skipping.")
                elif key == ord("q"):
                    break
    # æ˜¾ç¤ºç”»é¢
    cv2.imshow("Frame", frame)
    key = cv2.waitKey(1) & 0xFF
    if key == ord("q"):
        break
    # å½“真实和虚假图片都采集到500张时结束
    if saved_fake >= target_count and saved_real >= target_count:
        print("[INFO] Reached the target count for both real and fake images. Exiting...")
        break
cap.release()
cv2.destroyAllWindows()
Server/¶Å¿µÉú/code/main.py
New file
@@ -0,0 +1,18 @@
from flask import Flask, jsonify
from face_detector import FaceDetector
import cv2
app = Flask(__name__)
@app.route('/perform_face_detection', methods=['GET'])
def perform_face_detection():
    cap = cv2.VideoCapture(0)
    win_width = 640
    win_height = 480
    detector = FaceDetector(cap, win_width, win_height)
    result = detector.detect_face_local()
    cap.release()
    return jsonify({"is_live": result})
if __name__ == '__main__':
    app.run(debug=False, host='127.0.0.1', port=5000)
Server/¶Å¿µÉú/code/shape_predictor_68_face_landmarks.dat
Binary files differ
Server/¶Å¿µÉú/document/»îÌå¼ì²âÄ£¿é-ÏîÄ¿×ܽá.docx
Binary files differ