⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 facedetector.cpp

📁 一个外国人写的人脸检测程序
💻 CPP
字号:


#include "stdafx.h"
#include "facedetector.h"

#include "vec2D.h"
#include "vec2Dc.h"
#include "annetwork.h"
#include "aiclassifier.h"
#include "imageframe.h"


FaceDetector::FaceDetector() : m_status(-1), m_dx(0), m_dy(0), m_status_of_classifiers(0),
                               m_face_rectangle(0), m_face_vector(0), m_projected_face(0),
                               m_motion_amount(-1.0f), m_skin_amount(-1.0f), 
                               m_skin_mask(0), m_tmp_skin_mask(0), m_skin_filter(0),
                               m_preface_filter(0), m_projection_matrix(0), m_face_classifier(0) 
{
        m_histogram = new vec2D(1, 256);
}

FaceDetector::~FaceDetector()
{
        delete m_histogram;

        if (m_face_rectangle != 0) delete m_face_rectangle;
        if (m_face_vector != 0) delete m_face_vector;        
        if (m_skin_mask != 0) {
                delete m_skin_mask;
                delete m_tmp_skin_mask;
        }

        unload_skin_filter();
        unload_preface_filter();
        unload_face_classifier();
        unload_projection_matrix();

        clear_faces();
}

void FaceDetector::init(unsigned int image_width, unsigned int image_height,
                        unsigned int face_width, unsigned int face_height, 
                        const float* scales, unsigned int nscales)
{
        if (m_face_rectangle != 0) delete m_face_rectangle;
        m_face_rectangle = new vec2D(face_height, face_width);
        if (m_face_vector != 0) delete m_face_vector;
        m_face_vector = new vec2D(1, face_height * face_width);
        if (m_skin_mask != 0) {
                delete m_skin_mask;
                delete m_tmp_skin_mask;
        }
        m_skin_mask = new vec2Dc(image_height, image_width);
        m_tmp_skin_mask = new vec2Dc(image_height, image_width);

        m_dx = (face_width - 1) / 2;
        m_dy = (face_height - 1) / 2;

        m_pyramid.init(image_width, image_height, scales, nscales);
        
        m_status = 0;
}

int FaceDetector::load_skin_filter(const wchar_t* fname)
{
        unload_skin_filter();
        m_skin_filter = new AIClassifier(fname);
        if (m_skin_filter->status() == 0)
                m_status_of_classifiers |= SKINAI_BIT;
        return m_skin_filter->status();
}

void FaceDetector::unload_skin_filter()
{
        if (m_skin_filter != 0) {
                delete m_skin_filter;
                m_skin_filter = 0;
                m_status_of_classifiers &= ~SKINAI_BIT;
        }
}

int FaceDetector::load_preface_filter(const wchar_t* fname)
{
        unload_preface_filter();
        m_preface_filter = new AIClassifier(fname);
        if (m_preface_filter->status() == 0)
                m_status_of_classifiers |= PREFACEAI_BIT;
        return m_preface_filter->status();
}

void FaceDetector::unload_preface_filter()
{
        if (m_preface_filter != 0) {
                delete m_preface_filter;
                m_preface_filter = 0;
                m_status_of_classifiers &= ~PREFACEAI_BIT;
        }
}

int FaceDetector::load_projection_matrix(const wchar_t* fname)
{
        unload_projection_matrix();
        m_projection_matrix = new ANNetwork(fname);
        if (m_projection_matrix->status() == 0) {
                m_projected_face = new vec2D(1, m_projection_matrix->output_size());
                m_status_of_classifiers |= PRJMAT_BIT;
        }
        return m_projection_matrix->status();
}

void FaceDetector::unload_projection_matrix()
{
        if (m_projection_matrix != 0) {
                delete m_projected_face;
                m_projected_face = 0;
                delete m_projection_matrix;
                m_projection_matrix = 0;
                m_status_of_classifiers &= ~PRJMAT_BIT;
        }
}
        
int FaceDetector::load_face_classifier(const wchar_t* fname)
{
        unload_face_classifier();
        m_face_classifier = new AIClassifier(fname);
        if (m_face_classifier->status() == 0)
                m_status_of_classifiers |= FACEAI_BIT;
        return m_face_classifier->status();
}

void FaceDetector::unload_face_classifier()
{
        if (m_face_classifier != 0) {
                delete m_face_classifier;
                m_face_classifier = 0;
                m_status_of_classifiers &= ~FACEAI_BIT;
        }
}

void FaceDetector::clear_faces()
{
        for (unsigned int i = 0; i < get_faces_number(); i++)
                delete m_faces[i].face;
        m_faces.clear();
}


/*
    y - Gray image
    r,g,b - R,G,B components for skinfilter    
*/
int FaceDetector::detect(const vec2D* y, char** r, char** g, char** b, const vec2Dc* search_mask)
{
        if (m_status < 0)
                return m_status;        

        estimate_motion_percent(search_mask);

        m_skin_amount = -1.0f;
        const vec2Dc* pskin_mask = 0;
        //optional skin filter
        if ((r != 0 && g != 0 && b != 0) && m_skin_filter != 0 && 
             m_skin_filter->status() == 0 && m_skin_filter->input_dimension() == 3) {
                skin_filter(r, g, b, search_mask);
                pskin_mask = m_skin_mask;
        }                
                
        //build pyramid of images
        for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++)
                m_pyramid.get_frame(i)->load_frame(y, search_mask, pskin_mask);        
        
        if (m_projection_matrix == 0 || m_face_classifier == 0)
                return -2;
        if (m_face_classifier->input_dimension() != m_projection_matrix->output_size())
                return -3;

        run_classifiers();
        return search_faces();
}

void FaceDetector::run_classifiers()
{
        float ovec;

        int w2 = m_face_rectangle->width() / 2;
        int h2 = m_face_rectangle->height() / 2;

        bool prefilter = false;
        if (m_preface_filter != 0 && m_preface_filter->input_dimension() == m_face_rectangle->length())
                prefilter = true;

        for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++) {
                const vec2D& my = *m_pyramid.get_frame(i)->get_y_blured();
                vec2D& face_map = *m_pyramid.get_frame(i)->get_face_map();
                const vec2Dc& search_map = *m_pyramid.get_frame(i)->get_search_map();

                face_map.set(0.0f);
                for (unsigned int y = get_dy(); y < my.height() - get_dy(); y++) {
                        for (unsigned int x = get_dx(); x < my.width() - get_dx(); x++) {                                                                
                                //negotiate with [motion & skin detector out]
                                if (search_map(y, x) == 0)   
                                        continue;

                                if (m_face_rectangle->copy(my, x - w2, y - h2)) {  //copy from left,top offsets
                                        m_face_rectangle->histeq(*m_histogram);

                                        for (unsigned int m = 0; m < m_face_rectangle->length(); m++)
                                                (*m_face_vector)(0, m) = (*m_face_rectangle)[m];

                                        if (prefilter == true) {
                                                if (m_preface_filter->classify(&(*m_face_vector)(0, 0), &ovec) < 0)  //non-face                                                        
                                                        continue;                                                
                                        }

                                        //project to low dimension
                                        m_projection_matrix->classify(&(*m_face_vector)(0, 0), &(*m_projected_face)(0, 0));
                                        //classify projecte data
                                        m_face_classifier->classify(&(*m_projected_face)(0, 0), &face_map(y, x));                                        
                                }
                                //else failed to copy
                                // ovec=0.0f;
                        }
                }
                //debug
                //face_map.print(L"face_map.txt");
        }
}

unsigned int FaceDetector::search_faces()
{
        ImageFrame* pframe = 0;

        RECT rect;
        FACERECT face_rect;        

        clear_faces();

        float max = 0.0f, tmpmx = 0.0f, zoom = 0.0f;
        unsigned int x = 0, y = 0, index = 0;
        int tmpx = 0, tmpy = 0;
        unsigned int w2 = m_face_rectangle->width() / 2;
        unsigned int h2 = m_face_rectangle->height() / 2;

        //debug out only
        for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++)
                m_pyramid.get_frame(i)->get_tmp_face_map()->copy(*m_pyramid.get_frame(i)->get_face_map());
        //debug out only

        while (true) {

                max = -FLT_MAX;
                index = 0;
                for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++) {
                        pframe = m_pyramid.get_frame(i);
                        pframe->get_face_map()->maxval(tmpmx, tmpx, tmpy, 3, 3, m_dx, m_dy);
                        if (tmpmx > max) {
                                index = i;
                                max = tmpmx;
                                x = tmpx;
                                y = tmpy;
                        }
                }

                pframe = m_pyramid.get_frame(index);
                zoom = pframe->get_zoom(); 
                if (max > 6.0f*0.8f + 3.0f*0.15f) { //3x3 positives             //6 positives + 3 negatives in 3x3 box

                        rect.left = (int)(float(x - w2) / zoom);
                        rect.top = (int)(float(y - h2) / zoom);
                        rect.right = (int)(float(x + w2) / zoom);
                        rect.bottom = (int)(float(y + h2) / zoom);
                        face_rect.rect = rect;
                        face_rect.x = (unsigned int)(float(x) / zoom);
                        face_rect.y = (unsigned int)(float(y) / zoom);
                        face_rect.diag = sqrt(pow((float(w2) / zoom), 2.0f) + pow((float(h2) / zoom), 2.0f));
                        face_rect.face = new vec2D(m_face_rectangle->height(), m_face_rectangle->width());
                        face_rect.face->copy(*pframe->get_y(), x - w2, y - h2);
                        face_rect.face->normalize(0.0f, 1.0f);

                        if (is_foundface_overlaps(face_rect) == false) {
                                m_faces.push_back(face_rect);
                                erase_face_rect(rect);            //erase found face region  [left, right)
                        } else {
                                erase_face_rect((float)x / zoom, (float)y / zoom);
                        }
                } else
                        break;
        }

        return (unsigned int)m_faces.size();
}

void FaceDetector::erase_face_rect(const RECT& r)
{
        RECT zr;
        for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++) {
                ImageFrame* pframe = m_pyramid.get_frame(i);
                float zoom = pframe->get_zoom();

                zr.left = int((float)r.left * zoom);
                zr.top = int((float)r.top * zoom);
                zr.right = int((float)r.right * zoom) + 1;
                zr.bottom = int((float)r.bottom * zoom) + 1;
                
                pframe->get_face_map()->set(0.0f, zr);   //[left, right)                              
        }
}
void FaceDetector::erase_face_rect(unsigned int x, unsigned int y)
{
        RECT zr;
        int tmpx, tmpy;
        for (unsigned int i = 0; i < m_pyramid.get_frames_number(); i++) {
                ImageFrame* pframe = m_pyramid.get_frame(i);
                float zoom = pframe->get_zoom();

                tmpx = int((float)x * zoom);
                tmpy = int((float)y * zoom);
                zr.left = tmpx - 2;
                zr.top = tmpy - 2;
                zr.right = (tmpx + 2) + 1;
                zr.bottom = (tmpy + 2) + 1;

                pframe->get_face_map()->set(0.0f, zr);   //[left, right)                
        }
}

bool FaceDetector::is_foundface_overlaps(const FACERECT& fr) const
{
        for (unsigned int i = 0; i < (unsigned int)m_faces.size(); i++) {
                const FACERECT* pfr = &m_faces[i];
                //euclid distanse between centers
                float dist = sqrt(pow((float(pfr->x) - float(fr.x)), 2)
                                   + pow((float(pfr->y) - float(fr.y)), 2));
                if (dist <= 0.90f * (fr.diag + pfr->diag))
                        return true;
        }
        return false;
}





void FaceDetector::estimate_motion_percent(const vec2Dc* search_mask)
{
        if (search_mask == 0)
                m_motion_amount = -1.0f;
        else {
                unsigned int motion_pixels = 0;
                unsigned int total_pixels = 0;
                for (unsigned int y = get_dy(); y < search_mask->height() - get_dy(); y++) {
                        for (unsigned int x = get_dx(); x < search_mask->width() - get_dx(); x++) {
                                total_pixels++;
                                if ((*search_mask)(y, x) == 1)
                                        motion_pixels++;
                        }
                }
                m_motion_amount = float(motion_pixels) / float(total_pixels);
        }
}
/*
        check if (m_skin_filter != 0 && m_skin_filter->status() == 0) before call
*/
void FaceDetector::skin_filter(char** r, char** g, char** b, const vec2Dc* search_mask)
{
        float ivec[3] = {0.0f, 0.0f, 0.0f};    //0.0 ... 1.0f range
        float ovec = 0.0f;

        m_skin_mask->set(0);

        unsigned int skin_pixels = 0;
        unsigned int total_pixels = 0;
        for (unsigned int y = get_dy(); y < m_skin_mask->height() - get_dy(); y++) {
                for (unsigned int x = get_dx(); x < m_skin_mask->width() - get_dx(); x++) {
                        total_pixels++;

                        if (search_mask != 0 && ((*search_mask)(y, x) == 0))                                 
                                continue;

                        ivec[0] = (float)((int)r[y][x] + 128) / 255.0f;
                        ivec[1] = (float)((int)g[y][x] + 128) / 255.0f;
                        ivec[2] = (float)((int)b[y][x] + 128) / 255.0f;
                        if (m_skin_filter->classify(ivec, &ovec) >= 0) {
                                (*m_skin_mask)(y, x) = 1;
                                skin_pixels++;
                        }
                }
        }
        m_skin_amount = float(skin_pixels) / float(total_pixels);

        m_tmp_skin_mask->dilate(*m_skin_mask, 5, 5);
        m_skin_mask->erode(*m_tmp_skin_mask, 5, 5);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -