基于 OpenCV 的多功能图像处理和计算机视觉识别人脸项目

🎯 项目功能概述

1. 多种人脸检测方法

  • Haar 级联分类器 :经典快速检测方法,适合实时应用
    Hear级联分类器

  • YuNet 深度学习模型 :基于 ONNX 的现代深度学习模型,检测精度更高
    YuNet 深度学习模型

  • 眼睛检测 :在人脸区域内进一步检测眼部特征点
    眼睛检测

2. 图像预处理功能

  • 标准处理流程 :灰度转换 → 高斯模糊 → 直方图均衡化 → Canny边缘检测
    标准处理流程

  • 高斯模糊处理 :图像降噪和平滑
    高斯模糊处理

  • 拉普拉斯锐化 :边缘增强和细节提升
    拉普拉斯锐化

3. 图像分割技术

  • 颜色分割 :基于 HSV 色彩空间的颜色范围分割
  • 边缘分割 :基于 Canny 算法的轮廓检测
  • 阈值分割 :自适应和固定阈值二值化

4. 图像增强功能

  • 直方图均衡化 :提高图像对比度
  • CLAHE 自适应均衡化 :局部对比度增强
  • Gamma 校正 :亮度和对比度调整

5. 特征提取

  • SIFT 特征检测 :尺度不变特征变换,用于图像匹配和识别

🚀 技术亮点

多算法融合

  • 传统计算机视觉算法(Haar、SIFT)
  • 现代深度学习模型(YuNet、DNN)
  • 自适应参数调整系统

用户友好的界面

  • 图形化操作界面(基于 PyQt/PySide)
  • 实时参数调整
  • 即时效果预览

高度可配置

  • 支持多种检测方法切换
  • 丰富的参数调节选项
  • 灵活的处理流程组合

🎨 应用效果展示

人脸检测效果

  • ✅ 检测准确率高,支持多人脸场景
  • ✅ 实时显示置信度分数
  • ✅ 同时输出人脸框和关键点(眼睛位置)

图像处理效果

  • ✅ 有效的噪声去除和图像增强
  • ✅ 精确的边缘检测和轮廓提取
  • ✅ 智能的颜色分割(如分离黑色西装区域)

特征分析能力

  • ✅ 提取图像关键特征点
  • ✅ 支持图像匹配和比较
  • ✅ 适用于目标识别和跟踪

🛠️ 技术栈

  • 核心框架 :OpenCV 4.x
  • 深度学习 :ONNX Runtime、Caffe
  • 用户界面 :PyQt/PySide
  • 编程语言 :Python 3.x
  • 图像格式 :支持常见的 JPG、PNG、BMP 等

部分代码

人脸检测类,用于检测图像中的人脸

class FaceDetection:  # 人脸检测类,用于检测图像中的人脸
    def __init__(self, scaleFactor=1.028, minNeighbors=4, detection_method="haar",
                 yunet_model_path="model/face_detection_yunet_2023mar_int8bq.onnx",
                 yunet_conf_thresh=0.7,
                 yunet_nms_thresh=0.3):
        """初始化人脸检测器,设置参数
        
        参数:
            scaleFactor: 图像缩放比例因子,值越小,检测越精确但速度越慢
            minNeighbors: 确定候选矩形区域被确认为人脸的相邻矩形数
            detection_method: 检测方法,可选 "haar", "dnn", "eye"
                - haar: 使用传统的Haar级联分类器检测人脸
                - dnn: 使用深度学习模型检测人脸(更精确但需更多计算资源)
                - eye: 在检测到的人脸区域内检测眼睛
                - yunet: 使用YUNet模型进行人脸检测(需要下载模型文件)
            yunet_model_path: YUNet模型文件路径
            yunet_conf_thresh: YUNet模型的置信度阈值,低于此值的检测结果将被忽略
            yunet_nms_thresh: YUNet模型的非极大值抑制阈值,用于去除重叠的检测框
        """
        # 加载预训练的Haar级联分类器
        self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
        self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
        self.scaleFactor = scaleFactor  # 每次缩放的比例因子
        self.minNeighbors = minNeighbors  # 构成检测对象的最小相邻矩形数
        self.detection_method = detection_method  # 检测方法
        self.yunet_model_path = yunet_model_path  # YUNet模型路径
        self.yunet_conf_threshold = yunet_conf_thresh  # YUNet模型置信度阈值
        self.yunet_nms_threshold = yunet_nms_thresh  # YUNet模型非极大值抑制阈值

    def detect_faces(self, image):
        """根据选择的方法检测图像中的人脸
        
        根据初始化时指定的detection_method选择不同的人脸检测算法
        """
        if image is None:
            print("无法读取图像,请检查图像路径。")
            return

        # 确保图像为三通道彩色图像
        if len(image.shape) == 2:
            image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)

        # 创建图像副本以避免修改原始数据
        result_image = image.copy()

        # 将图像转换为灰度图像,以便进行人脸检测
        gray = cv2.cvtColor(result_image, cv2.COLOR_BGR2GRAY)
        print("使用的检测方法:", self.detection_method)  # 打印当前使用的检测方法
        # 根据选择的方法执行人脸检测
        if self.detection_method == "haar":
        # 使用Haar级联分类器检测人脸
            faces = self.face_cascade.detectMultiScale(gray, self.scaleFactor, self.minNeighbors)

            # 在原图像上绘制人脸边界框
            for (x, y, w, h) in faces:
                cv2.rectangle(result_image, (x, y), (x + w, y + h), (255, 0, 0), 2)
                
        elif self.detection_method == "YuNet" or self.detection_method == "eye":
            # 使用YUNet模型进行人脸检测
            # 注意:这需要模型文件,如果不存在会报错
            h, w = image.shape[:2]
            if not os.path.exists(self.yunet_model_path):
                print("模型文件不存在,请下载模型文件。")
                return image
            else:
                try:
                    # 创建 YuNet 检测器实例
                    # 注意:input_size 设为 (0, 0) 表示在 detect 时自动适应图像尺寸
                    self.detector_yunet = cv2.FaceDetectorYN.create(
                        model=self.yunet_model_path,
                        config="", # ONNX 通常不需要 config
                        input_size=(0, 0), # 在 detect 时设置
                        score_threshold=self.yunet_conf_threshold,
                        nms_threshold=self.yunet_nms_threshold,
                        top_k=5000 # 保留多少候选框
                    )
                    if self.detector_yunet is None:
                         print("错误: 创建 YuNet 检测器失败。")
                except Exception as e:
                    print(f"错误: 加载 YuNet 模型时出错: {e}")
                    self.detector_yunet = None
            # 设置当前图像的输入尺寸
            self.detector_yunet.setInputSize((w, h))
            # 检测人脸
            try:
                retval, faces = self.detector_yunet.detect(image)
            except Exception as e:
                print(f"错误: YuNet 检测过程中出错: {e}")
                return image # 返回原始图像

            # 绘制结果
            if self.detection_method == "YuNet" and faces is not None:
                print(f"YuNet 检测到 {len(faces)} 张人脸。") # 打印检测到的人脸数量
                for face in faces:
                    box = list(map(int, face[:4])) # 边界框 [x1, y1, width, height]
                    confidence = face[-1] # 置信度分数

                    # 绘制边界框
                    cv2.rectangle(result_image, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (0, 255, 0), 2)

                    # 准备置信度文本
                    text = f"{confidence:.2f}"
                    # 计算文本放置位置 (框的上方)
                    text_pos_y = box[1] - 10 if box[1] - 10 > 10 else box[1] + 15 # 避免文本超出图像顶部
                    cv2.putText(result_image, text, (box[0], text_pos_y), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
            elif self.detection_method == "eye":
                for face in faces:
                    box = list(map(int, face[:4])) # 边界框 [x1, y1, width, height]

                    landmarks = list(map(int, face[4:14]))
                    right_eye = (landmarks[0], landmarks[1])
                    left_eye = (landmarks[2], landmarks[3])
                    # --- 结束提取 ---
                    confidence = face[-1] # 置信度分数

                    # 绘制人脸边界框 (保持不变, 仍为绿色)
                    cv2.rectangle(result_image, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (0, 255, 0), 2)

                    # --- 绘制眼睛的红色方框 ---
                    eye_box_size = 5 # 方框半径 (总大小为 10x10)
                    # 右眼方框
                    cv2.rectangle(result_image,
                                  (right_eye[0] - eye_box_size, right_eye[1] - eye_box_size), # 左上角
                                  (right_eye[0] + eye_box_size, right_eye[1] + eye_box_size), # 右下角
                                  (0, 0, 255), # 红色 (BGR)
                                  1) # 线条粗细为 1
                    # 左眼方框
                    cv2.rectangle(result_image,
                                  (left_eye[0] - eye_box_size, left_eye[1] - eye_box_size), # 左上角
                                  (left_eye[0] + eye_box_size, left_eye[1] + eye_box_size), # 右下角
                                  (0, 0, 255), # 红色 (BGR)
                                  1) # 线条粗细为 1
                    
                    # --- 绘制红色的眼睛 ---
                    # --- 提取关键点 ---
                    # 假设前两个点是右眼和左眼中心 (请根据实际模型输出确认)
                    # landmarks = list(map(int, face[4:14]))
                    # right_eye = (landmarks[0], landmarks[1])
                    # left_eye = (landmarks[2], landmarks[3])
                    # # nose = (landmarks[4], landmarks[5])
                    # # right_mouth = (landmarks[6], landmarks[7])
                    # # left_mouth = (landmarks[8], landmarks[9])
                    # # --- 结束提取 ---
                    confidence = face[-1] # 置信度分数

                    # 绘制人脸边界框 (保持不变)
                    cv2.rectangle(result_image, (box[0], box[1]), (box[0] + box[2], box[1] + box[3]), (0, 255, 0), 2)

                    # --- 绘制眼睛关键点 ---
                    cv2.circle(result_image, right_eye, 3, (0, 0, 255), -1) # 红色圆点标记右眼
                    cv2.circle(result_image, left_eye, 3, (0, 0, 255), -1)  # 红色圆点标记左眼
            else:
                print("YuNet 未检测到人脸。")
            
                    
        # elif self.detection_method == "eye":
        #     # 先检测人脸,然后在人脸区域内检测眼睛
        #     faces = self.face_cascade.detectMultiScale(gray, self.scaleFactor, self.minNeighbors)
            
        #     for (x, y, w, h) in faces:
        #         # 绘制人脸区域
        #         cv2.rectangle(result_image, (x, y), (x + w, y + h), (255, 0, 0), 2)
                
        #         # 在检测到的人脸区域内分割出人脸部分
        #         roi_gray = gray[y:y + h, x:x + w]
        #         roi_color = result_image[y:y + h, x:x + w]
                
        #         # 在人脸区域内检测眼睛
        #         eyes = self.eye_cascade.detectMultiScale(roi_gray)
        #         for (ex, ey, ew, eh) in eyes:
        #             # 在人脸区域内绘制眼睛矩形
        #             cv2.rectangle(roi_color, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)

        return result_image  # 返回标记了人脸/眼睛的图像

图像预处理类,用于执行各种图像预处理操作

class ImagePreprocessing:  # 图像预处理类,用于执行各种图像预处理操作
    def __init__(self, threshold1=50, threshold2=150, kernel_size=5, process_method="standard"):
        """初始化图像预处理器,设置参数
        
        参数:
            threshold1: Canny边缘检测的第一个阈值
            threshold2: Canny边缘检测的第二个阈值
            kernel_size: 高斯模糊或拉普拉斯算子的核大小
            process_method: 预处理方法,可选 "standard", "gaussian", "laplacian"
                - standard: 标准处理流程(灰度转换、边缘检测、均衡化、二值化)
                - gaussian: 高斯模糊处理,用于平滑和降噪
                - laplacian: 拉普拉斯锐化,用于增强边缘和细节
        """
        self.threshold1 = threshold1  # Canny边缘检测的第一个阈值
        self.threshold2 = threshold2  # Canny边缘检测的第二个阈值
        self.kernel_size = kernel_size  # 滤波器核大小
        self.process_method = process_method  # 预处理方法

    def preprocess_image(self, image):
        """根据选择的方法对图像进行预处理
        
        根据初始化时指定的process_method选择不同的预处理方法
        """
        if image is None:
            print("无法读取图像,请检查图像路径。")
            return

        # 创建图像副本以避免修改原始数据
        result_image = image.copy()

        # 根据选择的方法执行预处理
        if self.process_method == "standard":
            # 1. 将彩色图像转换为灰度图像
            gray_image = cv2.cvtColor(result_image, cv2.COLOR_BGR2GRAY)

            # 2. 高斯模糊去噪(确保 kernel_size 为奇数)
            k = self.kernel_size if self.kernel_size % 2 == 1 else self.kernel_size + 1
            blurred = cv2.GaussianBlur(gray_image, (k, k), 0)

            # 3. 直方图均衡化增强对比度
            equalized = cv2.equalizeHist(blurred)

            # 4. Canny 边缘检测,使用 threshold1/threshold2
            edges = cv2.Canny(equalized, self.threshold1, self.threshold2)

            # 5. 二值化(可选,对 equalized 做固定阈值)
            # _, binary = cv2.threshold(equalized, 127, 255, cv2.THRESH_BINARY)

            # 6. 缩放到 50%
            h, w = edges.shape[:2]
            scale = 0.5
            dim = (int(w * scale), int(h * scale))
            edges = cv2.resize(edges, dim, interpolation=cv2.INTER_AREA)
            
            return edges
            
        elif self.process_method == "gaussian":
            # 高斯模糊处理,用于降噪和平滑
            # 如果是彩色图像,直接进行高斯模糊处理
            
            # 确保kernel_size是奇数
            kernel_size = self.kernel_size
            if kernel_size % 2 == 0:
                kernel_size += 1
                
            # 应用高斯模糊
            gaussian_blur = cv2.GaussianBlur(result_image, (kernel_size, kernel_size), 0)
            
            # 缩放图像到50%的原始大小
            scale_percent = 50
            width = int(gaussian_blur.shape[1] * scale_percent / 100)
            height = int(gaussian_blur.shape[0] * scale_percent / 100)
            dim = (width, height)
            
            gaussian_blur = cv2.resize(gaussian_blur, dim, interpolation=cv2.INTER_AREA)
            
            # 转为灰度图以便于显示和后续处理
            if len(gaussian_blur.shape) == 3:
                gaussian_blur = cv2.cvtColor(gaussian_blur, cv2.COLOR_BGR2GRAY)
                
            return gaussian_blur
            
        elif self.process_method == "laplacian":
            # 拉普拉斯锐化,增强边缘和细节
            
            # 将彩色图像转换为灰度图像
            if len(result_image.shape) == 3:
                gray_image = cv2.cvtColor(result_image, cv2.COLOR_BGR2GRAY)
            else:
                gray_image = result_image.copy()
                
            # 应用高斯模糊以减少噪声(可选)
            blurred = cv2.GaussianBlur(gray_image, (3, 3), 0)
            
            # 应用拉普拉斯算子进行边缘检测和锐化
            # 使用不同的核大小参数
            kernel_size = self.kernel_size
            if kernel_size % 2 == 0:
                kernel_size = 3  # 默认使用3x3核
                
            # 应用拉普拉斯变换
            laplacian = cv2.Laplacian(blurred, cv2.CV_8U, ksize=kernel_size)
            
            # 归一化结果以增强细节
            laplacian = cv2.normalize(laplacian, None, 0, 255, cv2.NORM_MINMAX)
            
            # 缩放图像到50%的原始大小
            scale_percent = 50
            width = int(laplacian.shape[1] * scale_percent / 100)
            height = int(laplacian.shape[0] * scale_percent / 100)
            dim = (width, height)
            
            laplacian = cv2.resize(laplacian, dim, interpolation=cv2.INTER_AREA)
            
            return laplacian
            
        # 如果没有匹配的方法,返回原始图像的灰度版本
        if len(result_image.shape) == 3:
            result_image = cv2.cvtColor(result_image, cv2.COLOR_BGR2GRAY)
            
        # 缩放结果
        scale_percent = 50
        width = int(result_image.shape[1] * scale_percent / 100)
        height = int(result_image.shape[0] * scale_percent / 100)
        dim = (width, height)
        result_image = cv2.resize(result_image, dim, interpolation=cv2.INTER_AREA)
            
        return result_image

这个项目展示了从传统计算机视觉到现代深度学习的技术演进,提供了一个完整的图像处理解决方案,适合于需要多种算法对比和参数调优的应用场景。

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐