Let's write β

趣味で書いたこととか、RustとLispが好き

OpenCVで顔のパーツの位置の取得

とりあえず雑にやってみました。

#include <opencv/cv.h>
#include <opencv/highgui.h>

void detectObj(const char *cascade_name, IplImage *img, IplImage *gImg, CvMemStorage *storage, int maxcount)
{
        CvHaarClassifierCascade *cascade = 0;
        CvSeq *seq;
        int i;
        static CvScalar colors[] = {
                {{0, 0, 255}}, {{0, 128, 255}},
                {{0, 255, 255}}, {{0, 255, 0}},
                {{255, 128, 0}}, {{255, 255, 0}},
                {{255, 0, 0}}, {{255, 0, 255}}
        };

        cascade = (CvHaarClassifierCascade *)cvLoad(cascade_name, 0, 0, 0);
        printf("find objects..."); fflush(stdout);
        seq = cvHaarDetectObjects(gImg, cascade, storage, 2.0, 4, 0, cvSize(40,40));
        printf("...done");
        for (i = 0; i < (seq ? seq->total : 0) && i < maxcount; i++) {
                CvRect *r = (CvRect *) cvGetSeqElem (seq, i);
                CvPoint center;
                int radius;
                center.x = cvRound (r->x + r->width * 0.5);
                center.y = cvRound (r->y + r->height * 0.5);
                radius = cvRound ((r->width + r->height) * 0.25);
                cvCircle (img, center, radius, colors[i % 8], 3, 8, 0);
        }
}

int main(int argc, char** argv)
{
        CvHaarClassifierCascade *cascade = 0;
        CvSeq *seq;
        IplImage *src_img = 0, *src_gray = 0;
        CvMemStorage *storage = 0;
        CvRect* face_rect;
        CvRect upper_half;
        CvRect lower_half;

        //Cascades
        const char *face_cascade = "/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt2.xml";
        const char *eye_cascade = "/usr/local/share/OpenCV/haarcascades/haarcascade_eye.xml";
        const char *nose_cascade = "/usr/local/share/OpenCV/haarcascades/haarcascade_mcs_nose.xml";
        const char *mouth_cascade = "/usr/local/share/OpenCV/haarcascades/haarcascade_mcs_mouth.xml";
        
        //Read ImageFile
        if (argc < 2 || (src_img = cvLoadImage(argv[1], CV_LOAD_IMAGE_COLOR)) == 0)
                return -1;
        src_gray = cvCreateImage(cvGetSize(src_img), IPL_DEPTH_8U, 1);
        //GrayScaleImage
        storage = cvCreateMemStorage(0);
        cvClearMemStorage(storage);
        cvCvtColor(src_img, src_gray, CV_BGR2GRAY);
        cvEqualizeHist(src_gray, src_gray);
        //Detect Face
        printf("Detecting face.."); fflush(stdout);
        int i;
        static CvScalar colors[] = {
                {{0, 0, 255}}, {{0, 128, 255}},
                {{0, 255, 255}}, {{0, 255, 0}},
                {{255, 128, 0}}, {{255, 255, 0}},
                {{255, 0, 0}}, {{255, 0, 255}}
        };

        cascade = (CvHaarClassifierCascade *)cvLoad(face_cascade, 0, 0, 0);
        printf("find objects..."); fflush(stdout);
        seq = cvHaarDetectObjects(src_gray, cascade, storage, 2.0, 4, 0, cvSize(40,40));
        printf("...done");
        if (seq->total > 0) {
                //顔領域を取得
                face_rect = (CvRect *) cvGetSeqElem(seq, 1);
                //顔の下半分の領域
                lower_half = cvRect(face_rect->x, face_rect->y + face_rect->height / 2,
                                face_rect->width, face_rect->height / 2);
                CvPoint center;
                int radius;
                center.x = cvRound (face_rect->x + face_rect->width * 0.5);
                center.y = cvRound (face_rect->y + face_rect->height * 0.5);
                radius = cvRound ((face_rect->width + face_rect->height) * 0.25);
                cvCircle (src_img, center, radius, colors[i % 8], 3, 8, 0);
                cvSetImageROI(src_img, *face_rect);
                cvSetImageROI(src_gray, *face_rect);
        } else {
                printf("no face detected\n");
                exit(-1);
        }
        printf("..done\n");
        //Detect Eyes
        printf("Detecting eyes.."); fflush(stdout);
        detectObj(eye_cascade, src_img, src_gray, storage, 2);
        printf("..done\n");
        //Detect Nose
        printf("Detecting nose.."); fflush(stdout);
        detectObj(nose_cascade, src_img, src_gray, storage, 1);
        printf("..done\n");
        //Detect Mouth

        cvResetImageROI(src_img);
        cvResetImageROI(src_gray);
        cvSetImageROI(src_img, lower_half);
        cvSetImageROI(src_gray, lower_half);
        printf("Detecting mouth.."); fflush(stdout);
        detectObj(mouth_cascade, src_img, src_gray, storage, 1);
        printf("..done\n");
        
        cvResetImageROI(src_img);
        cvResetImageROI(src_gray);
        
        //Show Image
        cvNamedWindow("Face detection", CV_WINDOW_AUTOSIZE);
        cvShowImage("Face Detection", src_img);
        cvWaitKey(0);
        //Cleanup
        cvReleaseImage(&src_img);
        cvReleaseImage(&src_gray);
        cvReleaseMemStorage(&storage);
        return 0;
}

顔の領域の抽出には時間がかかるので、せっかくその領域を把握したなら
と、その後の探索領域を絞る事によって高速化を図っています。口の位置はメガネの輪郭の下部などが口として認識される事があったので、それを防止するために顔の下半分の領域にしぼって探索するなどして対策しています。

いくつかの事を前提としていて、1人だけうつった顔写真のような物を想定しています。

顔の領域の認識が終ってしまえば、残りの物の探索は高速に完了します。逆に言うと、探索領域のサイズが大きく効いてくるようなので、画像サイズの調整などをすると顔領域の把握の部分での時間が短縮されるかもしれないと思っています。

参考ページ:
http://opencv.jp/sample/object_detection.html

僕が働いているAzit.incでは一緒に働けるエンジニアを募集しています!
採用情報 — 株式会社アジット|Azit Inc.