如何用OpenCV检测面部痘痘?新手遇识别难题求技术指导
面部痘痘检测标记优化方案与入门指导
一、先搞定面部区域精准隔离(解决误标嘴唇问题)
你之前用MediaPipe Face Mesh裁剪效果差,是没利用它的关键点生成精准掩码。正确步骤:
- 用MediaPipe Face Mesh检测面部468个关键点,提取面部轮廓的关键点集合(比如下巴、脸颊、额头的特征点)
- 将这些点转换成闭合多边形,生成仅覆盖面部的掩码,再减去嘴唇区域的掩码,直接排除嘴唇、头发等非目标区域
二、适配深色皮肤的颜色特征选择
HSV对深色皮肤的红色区分度差,换成Lab颜色空间更适配肤色分析:
- Lab的a通道代表红绿色差,痘痘在a通道会呈现明显偏红特征;结合L通道(亮度)区分痘痘和斑点——痘痘与周围皮肤有明显亮度差,斑点颜色则更均匀
- 不要单靠颜色,结合纹理特征:痘痘有凸起、边缘清晰,用Canny边缘检测或局部方差筛选有纹理的区域,过滤平滑的斑点
三、区分痘痘与斑点/嘴唇的具体方法
- 排除嘴唇:用MediaPipe Face Mesh的嘴唇外围关键点生成掩码,从面部区域中直接减去这部分
- 过滤斑点:用形态学开运算(先腐蚀再膨胀)去掉细小斑点;或通过轮廓面积、圆度筛选——痘痘轮廓通常更不规则,面积比斑点更大
四、改进后的代码示例
import cv2 import numpy as np import mediapipe as mp # 初始化MediaPipe Face Mesh mp_face_mesh = mp.solutions.face_mesh face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, refine_landmarks=True) def get_face_mask_without_lip(image): h, w = image.shape[:2] rgb_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) results = face_mesh.process(rgb_image) face_mask = np.zeros((h, w), dtype=np.uint8) lip_mask = np.zeros((h, w), dtype=np.uint8) if results.multi_face_landmarks: for face_landmarks in results.multi_face_landmarks: # 提取面部轮廓关键点索引集合 face_contour_indices = [10,338,297,332,284,251,389,356,454,323,361,288,397,365,379,378,400,377,152,148,176,149,150,136,172,58,132,93,234,127,162,21,54,103,67,109] face_points = np.array([(int(landmark.x*w), int(landmark.y*h)) for landmark in [face_landmarks.landmark[i] for i in face_contour_indices]], np.int32) cv2.fillPoly(face_mask, [face_points], 255) # 提取嘴唇外围关键点索引集合 lip_contour_indices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17] lip_points = np.array([(int(landmark.x*w), int(landmark.y*h)) for landmark in [face_landmarks.landmark[i] for i in lip_contour_indices]], np.int32) cv2.fillPoly(lip_mask, [lip_points], 255) # 面部掩码减去嘴唇掩码,得到仅保留非嘴唇面部区域的掩码 return cv2.subtract(face_mask, lip_mask) # 读取图片 image = cv2.imread('./acne.jpeg') h, w = image.shape[:2] # 获取过滤嘴唇后的面部掩码 face_mask = get_face_mask_without_lip(image) # 转换到Lab颜色空间分析 lab_image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB) l_channel, a_channel, b_channel = cv2.split(lab_image) # 筛选a通道偏红区域(痘痘特征)+ L通道亮度差异区域 a_thresh = cv2.inRange(a_channel, 130, 170) # 可根据不同肤色调整阈值 l_thresh = cv2.inRange(l_channel, 50, 200) color_candidate = cv2.bitwise_and(a_thresh, l_thresh) # 用面部掩码过滤,仅保留面部区域的候选 candidate_mask = cv2.bitwise_and(color_candidate, color_candidate, mask=face_mask) # 形态学操作:去小斑点+填补痘痘空隙 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)) processed_mask = cv2.morphologyEx(candidate_mask, cv2.MORPH_OPEN, kernel, iterations=1) processed_mask = cv2.morphologyEx(processed_mask, cv2.MORPH_CLOSE, kernel, iterations=2) # 查找轮廓并筛选痘痘 contours, _ = cv2.findContours(processed_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) result_image = image.copy() for cnt in contours: area = cv2.contourArea(cnt) # 过滤太小的斑点,面积阈值根据图片分辨率调整 if 10 < area < 200: x, y, cnt_w, cnt_h = cv2.boundingRect(cnt) cv2.rectangle(result_image, (x,y), (x+cnt_w, y+cnt_h), (0,0,255), 2) # 显示结果 cv2.imshow('Original', image) cv2.imshow('Processed Mask', processed_mask) cv2.imshow('Pimple Detection Result', result_image) cv2.waitKey(0) cv2.destroyAllWindows()
五、后续优化方向
- 机器学习进阶:收集不同肤色的痘痘标注图片,用YOLO、轻量CNN等模型训练,比传统图像处理鲁棒性强很多
- 自适应阈值:用Otsu二值化代替固定阈值,针对不同肤色自动调整参数
- 纹理特征强化:加入局部二值模式(LBP)提取痘痘纹理,结合颜色特征提升准确率
内容的提问来源于stack exchange,提问作者vmin
相关产品推荐
相关产品推荐

