文章目录
- 完整代码一览
- 导入库与安装说明
- [加载 Haar 人脸检测器](#加载 Haar 人脸检测器)
- [中文绘制函数 cv2AddChineseText](#中文绘制函数 cv2AddChineseText)
- 准备训练数据
- [创建 FisherFace 识别器并训练](#创建 FisherFace 识别器并训练)
- 打开摄像头并进入主循环
- 人脸检测与识别
- 处理每个人脸
- 显示与退出
- 释放资源
- 运行效果与参数调优
- 常见问题与调整:
完整代码一览
c
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# 人脸检测器
xml_file = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(xml_file)
# 中文绘制函数
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
if isinstance(img, np.ndarray):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
try:
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
except:
fontStyle = ImageFont.load_default()
draw.text(position, text, textColor, font=fontStyle)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
# 加载训练图片
images = []
def image_re(img_path):
a = cv2.imread(img_path, flags=0)
if a is None:
print(f"警告:图片 {img_path} 缺失,跳过")
return
a = cv2.resize(a, dsize=(120, 180))
images.append(a)
# 样本加载(权志龙只有1张会报错,临时复制一份凑数,后续自己补qzl2.png)
image_re('mcy1.png')
image_re('mcy2.png')
image_re('mcy3.png')
image_re('mcy4.png')
image_re('pyy1.png')
image_re('pyy2.png')
image_re('qzl1.png')
image_re('qzl2.png')
# 标签同步增加一位,对应上面8张图
labels = [1, 1, 1, 1, 0, 0, 2, 2]
dic = {0: '彭于晏', 1: 'mcy', 2: '权志龙', -1: '无法识别'}
# Fisher人脸识别器
recognizer = cv2.face.FisherFaceRecognizer_create(threshold=5000)
recognizer.train(images, np.array(labels))
# 摄像头
cap = cv2.VideoCapture(0)
while True:
ret, image = cap.read()
if not ret:
print("摄像头读取失败")
break
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor=1.05, minNeighbors=9, minSize=(8, 8))
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
roi_gray_face = gray[y:y + h, x:x + w]
roi_gray_face = cv2.resize(roi_gray_face, (120, 180))
label, confidence = recognizer.predict(roi_gray_face)
if confidence > 5000:
label = -1
name = dic[label]
image = cv2AddChineseText(image, dic[label], (x, y - 25), (0, 255, 255), 24)
cv2.imshow('单人脸窗口', roi_gray_face)
cv2.imshow('Video', image)
if cv2.waitKey(60) == 27:
break
cap.release()
cv2.destroyAllWindows()
导入库与安装说明
c
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
加载 Haar 人脸检测器
我们打开摄像头首先先进行人脸检测,之后在进行人脸识别,识别之后进行中文标注。
c
xml_file = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
faceCascade = cv2.CascadeClassifier(xml_file)
使用 OpenCV 自带的正脸检测模型,用于从摄像头画面中定位人脸位置。
中文绘制函数 cv2AddChineseText
c
def cv2AddChineseText(img, text, position, textColor=(0, 255, 0), textSize=30):
if isinstance(img, np.ndarray):
img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(img)
try:
fontStyle = ImageFont.truetype("simsun.ttc", textSize, encoding="utf-8")
except:
fontStyle = ImageFont.load_default()
draw.text(position, text, textColor, font=fontStyle)
return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
这个函数专门解决 OpenCV 不能写中文的问题。
先判断输入是否是 OpenCV 的 NumPy 数组。如果是,则用 cv2.cvtColor 将 BGR 转为 RGB,然后用 Image.fromarray 转换为 PIL 图像对象。
if判断结束后在 PIL 图像上创建一个绘图对象 draw,加载中文字体文件 "simsun.ttc"(宋体),大小 textSize。
draw.text 在指定位置写入文字,颜色用 RGB 格式,最后将 PIL 图像转回 NumPy 数组,并转换回 BGR 格式,以便 OpenCV 显示。
准备训练数据
定义加载函数
c
images = []
def image_re(img_path):
a = cv2.imread(img_path, flags=0)
if a is None:
print(f"警告:图片 {img_path} 缺失,跳过")
return
a = cv2.resize(a, dsize=(120, 180))
images.append(a)
读取灰度图(flags=0),统一缩放为 120×180 像素。
如果文件不存在,打印警告并跳过(避免崩溃)。
加载样本图片
c
image_re('mcy1.png')
image_re('mcy2.png')
image_re('mcy3.png')
image_re('mcy4.png')
image_re('pyy1.png')
image_re('pyy2.png')
image_re('qzl1.png')
image_re('qzl2.png')
加载博主(mcy)4 张照片、彭于晏(pyy)2 张照片、权志龙(qzl)2 张(第二张是复制第一张)。
FisherFace 要求每个类别至少有 2 个样本,否则无法计算类间散度矩阵。
如果只有 1 张图,可以临时复制一份。实际项目中应准备至少 2 张不同的照片。
设置标签和映射字典
c
labels = [1, 1, 1, 1, 0, 0, 2, 2]
dic = {0: '彭于晏', 1: 'mcy', 2: '权志龙', -1: '无法识别'}
标签顺序与 images 列表中的图片一一对应:
前 4 张(mcy1~4)→ 标签 1(mcy)
后 2 张(pyy1~2)→ 标签 0(彭于晏)
最后 2 张(qzl 1~2)→ 标签 2(权志龙)
dic 将数字标签转为人名,-1 表示未识别。
创建 FisherFace 识别器并训练
c
recognizer = cv2.face.FisherFaceRecognizer_create(threshold=5000)
recognizer.train(images, np.array(labels))
threshold=5000:设定识别阈值。如果预测的置信度大于 5000,则认为不认识,返回 -1。这个值需要根据实际测试调整,一般范围在 3000~8000。
打开摄像头并进入主循环
c
cap = cv2.VideoCapture(0)
while True:
ret, image = cap.read()
if not ret:
print("摄像头读取失败")
break
人脸检测与识别
c
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = faceCascade.detectMultiScale(gray, scaleFactor=1.05, minNeighbors=9, minSize=(8, 8))
将彩色帧转为灰度图。
detectMultiScale 检测人脸,参数:
scaleFactor=1.05:缩放步长较小,检测更精细。
minNeighbors=9:较大的值减少误报。
minSize=(8,8):最小人脸尺寸 8×8。
处理每个人脸
c
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)# 对每个人脸框绘制绿色矩形
roi_gray_face = gray[y:y + h, x:x + w]
roi_gray_face = cv2.resize(roi_gray_face, (120, 180))# 裁剪出人脸区域(灰度图),并 resize 到 120×180
label, confidence = recognizer.predict(roi_gray_face)
if confidence > 5000:# 调用 recognizer.predict 预测,得到标签和置信度。如果置信度超过阈值,强制设为 -1
label = -1
name = dic[label]
image = cv2AddChineseText(image, dic[label], (x, y - 25), (0, 255, 255), 24) # 用 cv2AddChineseText 在人脸框上方(y - 25)绘制名字
cv2.imshow('单人脸窗口', roi_gray_face)
显示与退出
c
cv2.imshow('Video', image)
if cv2.waitKey(60) == 27:
break
显示带标注的主画面。
释放资源
c
cap.release()
cv2.destroyAllWindows()
释放摄像头,关闭所有窗口。
运行效果与参数调优
运行效果:
摄像头打开后,会实时检测人脸,每个检测到的人脸会被绿色框标出,上方显示识别出的中文名。
如果识别置信度超过阈值,会显示"无法识别"。
常见问题与调整:
- FisherFace 报错"样本数不足"
原因:某个人只有 1 张训练图。解决方案:准备至少 2 张不同图片,或临时复制(不推荐)。
- 中文显示成方框
原因:未找到 simsun.ttc 字体。可以下载一个中文字体(如 msyh.ttc)放到当前目录,修改函数中的字体路径。
- 识别率低
增加每个人的训练样本数量(10~20 张不同角度、光照)。或者调整阈值 threshold,观察打印的置信度,设定一个合理值。
- 检测不到人脸
降低 scaleFactor(如 1.03)和 minNeighbors(如 5),或调整 minSize。