人脸识别并不只是“找到图片里有没有人脸”。一个完整的小工具通常需要完成两步:先检测人脸的位置,再把检测到的人脸编码与已知人物进行比对并标注出来。本文使用 Python 和 face_recognition 库,从零实现一个可以在命令行运行的人脸识别工具。
从人脸检测到身份识别
人脸检测回答的是“人脸在哪里”,输出通常是一组边界框,例如图片中某张脸的上、右、下、左坐标。
人脸识别则需要进一步把每张脸转换成数值特征,也就是人脸编码。程序可以将未知图片中的编码与已知人物的编码进行比较,从而判断两张脸是否属于同一个人。
可以把处理流程概括为:
- 从已知人物目录加载照片。
- 为每张已知照片生成人脸编码。
- 读取待识别图片并检测其中的人脸。
- 为检测到的人脸生成编码。
- 与已知编码比较,并输出人物姓名。
- 在图片上绘制边框和标签。
face_recognition 对这些步骤提供了比较直接的 Python API,适合构建教程、内部工具和原型。
准备项目环境
下面的示例假设使用 Python 3.9 或更高版本。face_recognition 依赖 dlib,在部分系统上安装时可能需要 C++ 编译工具,因此建议先在虚拟环境中操作。
python -m venv .venv
# macOS/Linux
source .venv/bin/activate
# Windows PowerShell
# .venv\\Scripts\\Activate.ps1
python -m pip install --upgrade pip
python -m pip install face_recognition pillow
创建如下目录结构,并把已知人物照片放入 known/:
face-tool/
├── recognize.py
├── known/
│ ├── alice.jpg
│ └── bob.jpg
└── input.jpg
文件名会被当作人物姓名。例如 alice.jpg 会产生标签 alice。为了让识别结果更稳定,每张已知照片最好只包含一个清晰、正面的脸。
实现命令行识别器
下面是一个完整示例。它会读取 known/ 中的照片,识别输入图片里的所有人脸,并将结果保存到指定的输出文件。
运行前,将代码保存为 recognize.py,然后根据自己的目录修改命令参数即可。
from __future__ import annotations
import argparse
from pathlib import Path
import face_recognition
from PIL import Image, ImageDraw
IMAGE_EXTENSIONS = {".jpg", ".jpeg", ".png"}
def load_known_faces(directory: Path) -> tuple[list[str], list[list[float]]]:
names: list[str] = []
encodings: list[list[float]] = []
for path in sorted(directory.iterdir()):
if path.suffix.lower() not in IMAGE_EXTENSIONS:
continue
image = face_recognition.load_image_file(path)
face_encodings = face_recognition.face_encodings(image)
if not face_encodings:
print(f"Skip {path}: no face found")
continue
if len(face_encodings) > 1:
print(f"Skip {path}: expected one face, found {len(face_encodings)}")
continue
names.append(path.stem)
encodings.append(face_encodings[0])
if not encodings:
raise RuntimeError(f"No usable reference faces found in {directory}")
return names, encodings
def recognize_image(
input_path: Path,
output_path: Path,
known_names: list[str],
known_encodings: list[list[float]],
) -> None:
image = face_recognition.load_image_file(input_path)
locations = face_recognition.face_locations(image)
encodings = face_recognition.face_encodings(image, locations)
output = Image.fromarray(image)
draw = ImageDraw.Draw(output)
for location, encoding in zip(locations, encodings):
matches = face_recognition.compare_faces(
known_encodings,
encoding,
tolerance=0.6,
)
distances = face_recognition.face_distance(known_encodings, encoding)
label = "Unknown"
if distances.size:
best_match_index = int(distances.argmin())
if matches[best_match_index]:
label = known_names[best_match_index]
top, right, bottom, left = location
draw.rectangle((left, top, right, bottom), outline="lime", width=3)
draw.text((left, max(0, top - 20)), label, fill="lime")
print(f"{label}: box=({left}, {top}, {right}, {bottom})")
output.save(output_path)
print(f"Saved annotated image to {output_path}")
def main() -> None:
parser = argparse.ArgumentParser(description="Recognize faces in an image")
parser.add_argument("input", type=Path, help="Image to analyze")
parser.add_argument("--known", type=Path, default=Path("known"))
parser.add_argument("--output", type=Path, default=Path("result.jpg"))
args = parser.parse_args()
if not args.input.is_file():
parser.error(f"Input image does not exist: {args.input}")
if not args.known.is_dir():
parser.error(f"Known-face directory does not exist: {args.known}")
names, encodings = load_known_faces(args.known)
recognize_image(args.input, args.output, names, encodings)
if __name__ == "__main__":
main()
执行:
python recognize.py input.jpg --known known --output result.jpg
程序会在终端打印每张脸的识别结果,并生成带有边框和姓名标签的 result.jpg。
调整识别准确率
示例中的 tolerance=0.6 是一个可调参数。数值越小,匹配条件越严格,误识别可能减少,但同一个人在光线、角度变化较大时也更容易被标记为 Unknown。数值越大,匹配更宽松,但把不同的人误认为同一个人的风险会上升。
实际项目可以这样实践:
- 为同一个人保存多张不同角度和光线条件下的照片。
- 为每张参考照片保存编码,而不是只保留一张编码。
- 对识别结果使用人脸距离阈值,而不只依赖布尔值
matches。 - 在正式业务中记录低置信度结果,交给人工确认。
- 对输入图片先缩小,再执行检测,以降低处理时间。
如果图片很大,可以在检测前缩放图片,但要记住绘制边框时需要把坐标换算回原始尺寸。处理视频时也不必逐帧识别,可以每隔几帧检测一次,并缓存短时间内的结果。
使用边界与隐私风险
人脸识别涉及生物特征数据。这个命令行工具适合学习和受控环境中的原型验证,并不意味着可以直接用于门禁、考勤或公共场所监控。
上线前需要明确数据采集授权、保存期限、访问权限和删除机制,同时评估不同光照、肤色、年龄和拍摄角度带来的识别差异。参考照片目录也应按照敏感数据处理,避免提交到公共代码仓库。
落地检查清单
- 确认每张参考照片包含且只包含一个人脸。
- 为未知人脸保留
Unknown分支。 - 用实际业务图片调节
tolerance,不要盲目使用默认值。 - 记录识别距离和输入来源,便于排查误识别。
- 在保存、传输和删除人脸数据前建立明确的隐私策略。
这个示例展示了从人脸检测、特征编码、匹配到结果标注的完整链路。掌握这条链路后,可以继续扩展到批量图片处理、视频流识别,或者将识别逻辑封装成 HTTP 服务。