1 说明

  • 本文章基于opencv + VS2015 实现人脸检测

2 效果

请添加图片描述

  • 可以直接打开摄像头对人脸进行识别,这些标识框也会跟随你的人脸移动。隐私问题,我这里对图片进行了识别。

3 相关类及函数介绍

opencv中文文档

3.1 cv::VideoCapture

官方文档说明

  • 功能 :用于从视频文件、图像序列或摄像机捕获视频的类

3.1.1 open

  • bool cv::VideoCapture::open(int index);
  • 功能 : 打开相机进行视频捕获
  • 参数介绍
    • index : 要打开的视频捕获设备的索引(0表示打开默认摄像机)

3.1.2 read

  • bool cv::VideoCapture::read(OutputArray image);
  • 功能 : 抓取、解码并返回下一个视频帧
  • 参数介绍
    • image:图像视频帧在此处返回。如果未抓取任何帧,则图像将为空。

3.1.3 release

  • cv::VideoCapture::release();
  • 功能 : 关闭视频文件或捕获设备

3.2 cv::CascadeClassifier

官方文档说明

  • 功能 : CascadeClassifier是opencv下objdetect模块中用来做目标检测的级联分类器的一个类, 早期opencv版本仅支持haar特征的目标检测,分别在opencv2.2和2.4之后开始支持LBP和HOG特征的目标检测.

3.2.1 load

  • bool cv::CascadeClassifier::load(const String &filename);

  • 功能 : 从文件中加载级联分类器

  • 参数介绍

    • filename : 需要加载的分类器文件
  • opencv安装包中包含以及训练好的分类器文件(opencv\sources\data\haarcascades)

    • haarcascade_frontalface_alt.xml 检测人脸的分类器文件
    • haarcascade_eye.xml 检测眼睛的分类器文件
    • haarcascade_mcs_mouth.xml 检测嘴部的分类器文件(opencv安装包中没有携带, 可从github上去下载)
  • 分类器文件下载

3.2.2 detectMultiScale

  • void cv::CascadeClassifier::detectMultiScale(const Mat& image, CV_OUT vector& objects, double scaleFactor = 1.1, int minNeighbors = 3, int flags = 0, Size minSize = Size(),Size maxSize = Size());
  • 功能 : 检测输入图像中不同大小的对象。检测到的对象将作为矩形列表返回
  • 参数介绍
    • image : 包含检测对象的图像的CV_8U类型矩阵
    • objects : 矩形的向量,其中每个矩形包含被检测的对象, 矩形可以部分位于原始图像之外
    • scaleFactor : 指定在每个图像缩放时的缩放比例. 默认为1.1 即每次搜索窗口扩大10%
    • minNeighbors : 指定每个候选矩形需要保留多少个相邻矩形(匹配成功所需要的周围矩形框的数目,每一个特征匹配到的区域都是一个矩形框,只有多个矩形框同时存在的时候,才认为是匹配成功,比如人脸,这个默认值是3)
    • flag
      • CASCADE_DO_CANNY_PRUNING : 利用canny边缘检测来排除一些边缘很少或者很多的图像区域
      • CASCADE_SCALE_IMAGE : 正常比例检测
      • CASCADE_FIND_BIGGEST_OBJECT : 只检测最大的物体
      • CASCADE_DO_ROUGH_SEARCH : 初略的检测
    • minSize : 目标区域最小范围
    • maxSize : 目标区域最大范围

3.3 cv::waitKey

  • int cv::waitKey(int delay = 0);
  • 功能 : 等待按键按下
  • 参数介绍
    • delay : 超时时间, 单位为毫秒
  • 返回值 : 返回按下按键的值

3.4 cv::imshow

  • void cv::imshow(const String &winname, InputArray mat);
  • 功能 : 将图片显示在窗口中,通过设备屏幕展现出来
  • 参数介绍
    • winname: 窗口名
    • mat:要显示的图片

3.5 cv::destroyAllWindows

  • void cv::destroyAllWindows();
  • 功能 : 删除窗口

3.6 cv::putText

  • void cv::putText(InputOutputArray img, const String& text, Point org, int fontFace, double fontScale, Scalar color, int thickness = 1, int lineType = LINE_8, bool bottomLeftOrigin = false);

  • 功能 : 给视频图像添加文字说明

  • 参数介绍

    • img : 需要添加文字说明的图形对象
    • text : 需要写入的文本内容
    • org : 第一个字符左下角坐标
    • fontFace : 字体类型
      • 衬线体就是有边角装饰的字体, 无衬线字体通常是机械和统一粗细的线条, 没有边角的装饰

      • FONT_HERSHEY_SIMPLEX : 正常大小的无衬线字体

      • FONT_HERSHEY_PLAIN : 小号无衬线字体

      • FONT_HERSHEY_DUPLEX : 正常大小的无衬线字体(比FONT_HERSHEY_SIMPLEX更复杂)

      • FONT_HERSHEY_COMPLEX : 正常大小的衬线字体

      • FONT_HERSHEY_TRIPLEX : 正常大小的衬线字体(比FONT_HERSHEY_COMPLEX更复杂)

      • FONT_HERSHEY_COMPLEX_SMALL : FONT_HERSHEY_COMPLEX的较小版本

      • FONT_HERSHEY_SCRIPT_SIMPLEX : 手写样式字体

      • FONT_HERSHEY_SCRIPT_COMPLEX : 更复杂的FONT_HERSHEY_SCRIPT_SIMPLEX变体

      • FONT_ITALIC : 斜体字体的标志

    • fontScale : 字体大小
    • color : 字体颜色
    • thickness : 字体粗细
    • lineType : 线型
      • LINE_4 : 4联通线型
      • LINE_8 : 8联通线型
      • LINE_AA : 抗锯齿线

4 源代码

  • mian.cpp
#include <iostream>
#include <opencv2/opencv.hpp>

char* face_cascade_name = "D:\\opencv\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_alt.xml";
char* eyes_cascade_name = "D:\\opencv\\opencv\\sources\\data\\haarcascades\\haarcascade_eye.xml";
char* mouth_cascade_name = "D:\\opencv\\opencv\\sources\\data\\haarcascades\\haarcascade_mcs_mouth.xml";

void faceRecongize(cv::CascadeClassifier faceCascade, cv::CascadeClassifier eyesCascade, cv::CascadeClassifier mouthCascade, cv::Mat frame);

int main(){
    cv::VideoCapture *videoCap = new cv::VideoCapture;

	cv::CascadeClassifier faceCascade;
	cv::CascadeClassifier eyesCascade;
	cv::CascadeClassifier mouthCascade;

    // 加载脸部分类器文件
	if (!faceCascade.load(face_cascade_name)) {
		std::cout << "load face_cascade_name failed. " << std::endl;
		return -1;
	}

    // 加载眼睛部分分类器文件
	if (!eyesCascade.load(eyes_cascade_name)) {
		std::cout << "load eyes_cascade_name failed. " << std::endl;
		return -1;
	}

    // 加载嘴部分类器文件
	if (!mouthCascade.load(mouth_cascade_name)) {
		std::cout << "load mouth_cascade_name failed. " << std::endl;
		return -1;
	}

    // 打开摄像机
	videoCap->open(0);


	if (!videoCap->isOpened()) {
		videoCap->release();
		std::cout << "open camera failed"<< std::endl;
        return -1;
	}

	std::cout << "open camera success"<< std::endl;

    while(1){
		cv::Mat frame;
		//读取视频帧
		videoCap->read(frame);

		if (frame.empty()) {
			videoCap->release();
			return -1;
		}

        //进行人脸识别
		faceRecongize(faceCascade, eyesCascade, mouthCascade, frame);

        //窗口进行展示
        imshow("face", frame);

        //等待回车键按下退出程序
		if (cv::waitKey(30) == 13) {
			cv::destroyAllWindows();
			return 0;
		}
    }

    system("pause");
    return 0;
}

void faceRecongize(cv::CascadeClassifier faceCascade, cv::CascadeClassifier eyesCascade, cv::CascadeClassifier mouthCascade, cv::Mat frame) {
	std::vector<cv::Rect> faces;

    // 检测人脸
	faceCascade.detectMultiScale(frame, faces, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
	for (int i = 0; i < faces.size(); i++) {
		
        // 用椭圆画出人脸部分
        cv::Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);
		ellipse(frame, center, cv::Size(faces[i].width / 2, faces[i].height / 2), 0, 0, 360, cv::Scalar(255, 0, 255), 4, 8, 0);
		
		cv::Mat faceROI = frame(faces[i]);
		std::vector<cv::Rect> eyes;

        // 检测眼睛
		eyesCascade.detectMultiScale(faceROI, eyes, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
		for (int j = 0; j < eyes.size(); j++)
		{
            // 用圆画出眼睛部分
			cv::Point eye_center(faces[i].x + eyes[j].x + eyes[j].width / 2, faces[i].y + eyes[j].y + eyes[j].height / 2);
			int radius = cvRound((eyes[j].width + eyes[j].height)*0.25);
			circle(frame, eye_center, radius, cv::Scalar(255, 0, 0), 4, 8, 0);
		}

		cv::Mat mouthROI = frame(faces[i]);
		std::vector<cv::Rect> mouth;

        // 检测嘴部
		mouthCascade.detectMultiScale(mouthROI, mouth, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE, cv::Size(30, 30));
		for (int k = 0; k < mouth.size(); k++)
		{
            //用长方形画出嘴部
			cv::Rect rect(faces[i].x + mouth[k].x, faces[i].y + mouth[k].y, mouth[k].width, mouth[k].height);
			rectangle(frame, rect, cv::Scalar(0, 255, 0), 2, 8, 0);
		}

        // 检测到两个眼睛和一个嘴巴, 可认为检测到有效人脸
		if (eyes.size() >= 2 && mouth.size() >= 1) {
            
            // 人脸上方区域写字进行标识
			cv::Point centerText(faces[i].x + faces[i].width / 2 - 40, faces[i].y - 20);
			cv::putText(frame, "face", centerText, cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);
		}

	}
}
  • CMakeLists.txt
cmake_minimum_required (VERSION 3.5)
project (faceRecongize2015)

MESSAGE(STATUS "PROJECT_SOURCE_DIR " ${PROJECT_SOURCE_DIR})
SET(SRC_LISTS ${PROJECT_SOURCE_DIR}/src/main.cpp)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

# 配置头文件目录
include_directories(${PROJECT_SOURCE_DIR}/src)
include_directories("D:\\opencv\\opencv\\build\\include")
include_directories("D:\\opencv\\opencv\\build\\include\\opencv2")

# 设置不显示命令框
if(MSVC)
	set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
endif()

# 添加库文件
set(PRO_OPENCV_LIB "D:\\opencv\\opencv\\build\\x64\\vc15\\lib\\opencv_world460.lib" "D:\\opencv\\opencv\\build\\x64\\vc15\\lib\\opencv_world460.lib")

IF(WIN32)
    # 生成可执行程序
	ADD_EXECUTABLE(faceRecongize2015 ${SRC_LISTS})
	# 链接库文件
    TARGET_LINK_LIBRARIES(faceRecongize2015 ${PRO_OPENCV_LIB})
ENDIF()

5 编译说明

  • 编译前先安装opencv, cmake, Visual Studio 2015

  • 我的opencv的安装目录(D:\opencv), 源码安装比较麻烦, 建议直接下载安装包进行安装。

  • opencv安装包下载

  • opencv源码下载

  • 目录结构

- src
  - mian.cpp
- build_x64
- CMakeLists
  • 编译命令(在build_x64目录下执行)
cmake -G "Visual Studio 14 2015 Win64" ..
cmake --build ./ --config Release
  • 说明
    • 编译成功后,将opencv\build\x64\vc15\bin下的opencv_world460.dll opencv_world460d.dll拷贝到生成的可执行程序目录下, 然后再运行程序。
    • 下载的opencv安装包中只包含了64位的库, 如果需要32位的库可下载源代码去编译。
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐