您现在的位置是:首页 >技术杂谈 >opencv实践项目-人脸检测网站首页技术杂谈

opencv实践项目-人脸检测

wyw0000 2024-07-04 18:01:02
简介opencv实践项目-人脸检测

1. opencv CascadeClassifier人脸检测步骤

  • 从文件加载级联分类器
  • 读取图片并灰度化
  • resize灰度图
  • 直方图均衡化,得到对比度更强的输出图像
  • detectMultiScale检测

2. CascadeClassifier分类器简介

分类器是判别某个事物是否属于某种分类的器件,其结果要么是,要么不是,级联分类器,可以理解为将 N 个单类的分类器串联起来,如果一个事物能属于这一系列串联起来的的所有分类器,则最终结果就成立,若有一项不符,则判定为不成立。比如人脸,它有很多属性,我们将每个属性做一成个分类器,如果一个模型符合了我们定义的人脸的所有属性,则我们人为这个模型就是一个人脸,比如人脸需要有两条眉毛,两只眼睛,一个鼻子,一张嘴,一个大概 U 形状的下巴或者是轮廓等等。

常用方法:

2.1 从文件中加载级联分类器

CV_WRAP bool load( const String& filename );

也可用于判断级联分类器是否加载成功

2.2 目标检测方法

CV_WRAP void detectMultiScale( InputArray image, 
CV_OUT std::vector<Rect>& objects, 
double scaleFactor = 1.1, 
int minNeighbors = 3, int flags = 0, 
Size minSize = Size(), 
Size maxSize = Size() ); 

功能:检测输入图像中不同大小的对象。检测到的对象以矩形列表的形式保存
参数:
image: 包含检测对象的图像的 CV_8U 类型矩阵
objects: 矩形的向量,其中每个矩形包含被检测的对象,矩形可以部分位于原始图像之外
scaleFactor: 指定在每个图像缩放时的缩放比例
minNeighbors:指定每个候选矩形需要保留多少个相邻矩形
flags:含义与函数 cvHaarDetectObjects 中的旧级联相同,一般是 0,它不用于新的级联
minSize:对象最小大小,小于该值的对象被忽略
maxSize:最大可能的对象大小,大于这个值的对象被忽略
返回值:无

3. 代码实现

#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core/ocl.hpp"
#include <iostream>

using namespace std;
using namespace cv;

static void help()
{
    cout << "
This program demonstrates the cascade recognizer. Now you can use Haar or LBP features.
"
            "This classifier can recognize many kinds of rigid objects, once the appropriate classifier is trained.
"
            "It's most known use is for faces.
"
            "Usage:
"
            "./ufacedetect [--cascade=<cascade_path> this is the primary trained classifier such as frontal face]
"
               "   [--nested-cascade[=nested_cascade_path this an optional secondary classifier such as eyes]]
"
               "   [--scale=<image scale greater or equal to 1, try 1.3 for example>]
"
               "   [--try-flip]
"
               "   [filename|camera_index]

"
            "see facedetect.cmd for one call:
"
            "./ufacedetect --cascade="../../data/haarcascades/haarcascade_frontalface_alt.xml" --nested-cascade="../../data/haarcascades/haarcascade_eye_tree_eyeglasses.xml" --scale=1.3

"
            "During execution:
	Hit any key to quit.
"
            "	Using OpenCV version " << CV_VERSION << "
" << endl;
}

void detectAndDraw( UMat& img, Mat& canvas, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip );

int main( int argc, const char** argv )
{
    VideoCapture capture;
    UMat frame, image;
    Mat canvas;

    string inputName;
    bool tryflip;

    CascadeClassifier cascade, nestedCascade;
    double scale;

    cv::CommandLineParser parser(argc, argv,
        "{cascade|data/haarcascades/haarcascade_frontalface_alt.xml|}"
        "{nested-cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|}"
        "{help h ||}{scale|1|}{try-flip||}{@filename||}"
    );
    if (parser.has("help"))
    {
        help();
        return 0;
    }
    string cascadeName = samples::findFile(parser.get<string>("cascade"));
    string nestedCascadeName = samples::findFileOrKeep(parser.get<string>("nested-cascade"));
    scale = parser.get<double>("scale");
    tryflip = parser.has("try-flip");
    inputName = parser.get<string>("@filename");
    if ( !parser.check())
    {
        parser.printErrors();
        help();
        return -1;
    }

    if ( !nestedCascade.load( nestedCascadeName ) )
        cerr << "WARNING: Could not load classifier cascade for nested objects: " << nestedCascadeName << endl;
    if( !cascade.load( cascadeName ) )
    {
        cerr << "ERROR: Could not load classifier cascade: " << cascadeName << endl;
        help();
        return -1;
    }

    cout << "old cascade: " << (cascade.isOldFormatCascade() ? "TRUE" : "FALSE") << endl;

    if( inputName.empty() || (isdigit(inputName[0]) && inputName.size() == 1) )
    {
        int camera = inputName.empty() ? 0 : inputName[0] - '0';
        if(!capture.open(camera))
            cout << "Capture from camera #" <<  camera << " didn't work" << endl;
    }
    else
    {
        inputName = samples::findFileOrKeep(inputName);
        imread(inputName, IMREAD_COLOR).copyTo(image);
        if( image.empty() )
        {
            if(!capture.open( inputName ))
                cout << "Could not read " << inputName << endl;
        }
    }

    if( capture.isOpened() )
    {
        cout << "Video capturing has been started ..." << endl;
        for(;;)
        {
            capture >> frame;
            if( frame.empty() )
                break;

            detectAndDraw( frame, canvas, cascade, nestedCascade, scale, tryflip );

            char c = (char)waitKey(10);
            if( c == 27 || c == 'q' || c == 'Q' )
                break;
        }
    }
    else
    {
        cout << "Detecting face(s) in " << inputName << endl;
        if( !image.empty() )
        {
            detectAndDraw( image, canvas, cascade, nestedCascade, scale, tryflip );
            waitKey(0);
        }
        else if( !inputName.empty() )
        {
            /* assume it is a text file containing the
            list of the image filenames to be processed - one per line */
            FILE* f = fopen( inputName.c_str(), "rt" );
            if( f )
            {
                char buf[1000+1];
                while( fgets( buf, 1000, f ) )
                {
                    int len = (int)strlen(buf);
                    while( len > 0 && isspace(buf[len-1]) )
                        len--;
                    buf[len] = '';
                    cout << "file " << buf << endl;
                    imread(samples::findFile(buf), IMREAD_COLOR).copyTo(image);
                    if( !image.empty() )
                    {
                        detectAndDraw( image, canvas, cascade, nestedCascade, scale, tryflip );
                        char c = (char)waitKey(0);
                        if( c == 27 || c == 'q' || c == 'Q' )
                            break;
                    }
                    else
                    {
                        cerr << "Aw snap, couldn't read image " << buf << endl;
                    }
                }
                fclose(f);
            }
        }
    }

    return 0;
}

void detectAndDraw( UMat& img, Mat& canvas, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip )
{
    double t = 0;
    vector<Rect> faces, faces2;
    const static Scalar colors[] =
    {
        Scalar(255,0,0),
        Scalar(255,128,0),
        Scalar(255,255,0),
        Scalar(0,255,0),
        Scalar(0,128,255),
        Scalar(0,255,255),
        Scalar(0,0,255),
        Scalar(255,0,255)
    };
    static UMat gray, smallImg;

    t = (double)getTickCount();

    cvtColor( img, gray, COLOR_BGR2GRAY );
    double fx = 1 / scale;
    resize( gray, smallImg, Size(), fx, fx, INTER_LINEAR_EXACT );
    equalizeHist( smallImg, smallImg );

    cascade.detectMultiScale( smallImg, faces,
        1.1, 3, 0
        //|CASCADE_FIND_BIGGEST_OBJECT
        //|CASCADE_DO_ROUGH_SEARCH
        |CASCADE_SCALE_IMAGE,
        Size(30, 30) );
    if( tryflip )
    {
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
                                 //|CASCADE_FIND_BIGGEST_OBJECT
                                 //|CASCADE_DO_ROUGH_SEARCH
                                 |CASCADE_SCALE_IMAGE,
                                 Size(30, 30) );
        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); ++r )
        {
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        }
    }
    t = (double)getTickCount() - t;
    img.copyTo(canvas);

    double fps = getTickFrequency()/t;
    static double avgfps = 0;
    static int nframes = 0;
    nframes++;
    double alpha = nframes > 50 ? 0.01 : 1./nframes;
    avgfps = avgfps*(1-alpha) + fps*alpha;

    putText(canvas, format("OpenCL: %s, fps: %.1f", ocl::useOpenCL() ? "ON" : "OFF", avgfps), Point(50, 30),
            FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0,255,0), 2);

    for ( size_t i = 0; i < faces.size(); i++ )
    {
        Rect r = faces[i];
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;

        double aspect_ratio = (double)r.width/r.height;
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        {
            center.x = cvRound((r.x + r.width*0.5)*scale);
            center.y = cvRound((r.y + r.height*0.5)*scale);
            radius = cvRound((r.width + r.height)*0.25*scale);
            circle( canvas, center, radius, color, 3, 8, 0 );
        }
        else
            rectangle( canvas, Point(cvRound(r.x*scale), cvRound(r.y*scale)),
                       Point(cvRound((r.x + r.width-1)*scale), cvRound((r.y + r.height-1)*scale)),
                       color, 3, 8, 0);
        if( nestedCascade.empty() )
            continue;
        UMat smallImgROI = smallImg(r);
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
            //|CASCADE_FIND_BIGGEST_OBJECT
            //|CASCADE_DO_ROUGH_SEARCH
            //|CASCADE_DO_CANNY_PRUNING
            |CASCADE_SCALE_IMAGE,
            Size(30, 30) );

        for ( size_t j = 0; j < nestedObjects.size(); j++ )
        {
            Rect nr = nestedObjects[j];
            center.x = cvRound((r.x + nr.x + nr.width*0.5)*scale);
            center.y = cvRound((r.y + nr.y + nr.height*0.5)*scale);
            radius = cvRound((nr.width + nr.height)*0.25*scale);
            circle( canvas, center, radius, color, 3, 8, 0 );
        }
    }
    imshow( "result", canvas );
}

该代码为官方示例代码,基本调用方法如下:

example_tapi_ufacedetect --cascade=D:wywopencvopencv-4.5.0datahaarcascadeshaarcascade_frontalface_alt.xml --nested-cascade=D:wywopencvopencv-4.5.0datahaarcascadeshaarcascade_eye_tree_eyeglasses.xml --scale=1 --try-flip=true E:codeotherSeetaFaceDetectorFaceDetectorx64Release6.jpg

本文部分引用:https://zhuanlan.zhihu.com/p/159013461

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。