26
4月 07

「笑い男模倣ツール」の模倣ツール

概要

  • OpenCVの顔検出モジュール使って作ってみたやつを、お蔵出し。
  • 色情報は使ってない。だからチェ・ゲバラも検出可能(下図参照)。
  • 動作環境はXPが快適に動くぐらい。なかなかシビアです。でも精度はさすが。
  • 特別なことはやってないんで、ソース公開します。Linuxでもコンパイルできるはず。画像を入れ替えて他の誰かを模倣するなり、好きに使ってください。
    • (2007.02.25 — 一部改変)
  • 元ネタはこちらです。神!→工学ナビ
  • 元ネタの元ネタはこちら。神!→攻殻機動隊 STAND ALONE COMPLEX The Laughing Man

スクリーンショット

「撃て!臆病者め!」 チェ・ゲバラさん模倣前

gebara2.jpg

「残念ながら僕、野球が下手ですから」 チェ・ゲバラさん模倣中

動画

downloads

ソースコード(laugh.cpp)

////////////////////////////////////////////////////////////////////////////////
// 『笑い男模倣ツール Catcher in the Rye』の模倣ツール
//      コバヤシマサヨシ 2006.7.31
//          一部変更 - 2007.02.25
//
// 参照URL:
//   http://www1.bbiq.jp/kougaku/koukaku.html   (元ネタ)
//   http://www.kokaku-s.com/tlm/index.html      (元ネタの元ネタ)
////////////////////////////////////////////////////////////////////////////////

//==============================================================================
//インクルード
//==============================================================================
#include <stdio .h>

//OpenCVヘッダ
#include "cv.h"
#include "highgui.h"

//OpenCVライブラリ
#pragma comment (lib, "cv.lib")
#pragma comment( lib, "cxcore.lib")
#pragma comment( lib, "highgui.lib")

//==============================================================================
//グローバル
//==============================================================================
static CvMemStorage* storage = 0;
static CvHaarClassifierCascade* cascade = 0;

//------------------------------------------------------------------------------
// 指定画素のRGB値を取得する
//------------------------------------------------------------------------------
int getpixel(IplImage *image, int x, int y, int *r, int *g, int *b)
{
    *r =(uchar) image->imageData[y *image->widthStep+ x * image->nChannels + 2];
    *g =(uchar) image->imageData[y *image->widthStep+ x * image->nChannels + 1];
    *b =(uchar) image->imageData[y *image->widthStep+x * image->nChannels];
    return 0;
}

//------------------------------------------------------------------------------
// 指定画素にRGB値を代入する
//------------------------------------------------------------------------------
int setpixel(IplImage *image, int x, int y, int *r, int *g, int *b)
{
    image->imageData[y *image->widthStep+ x * image->nChannels + 2] = *r;
    image->imageData[y *image->widthStep+ x * image->nChannels + 1] = *g;
    image->imageData[y *image->widthStep+x * image->nChannels] = *b;
    return 0;
}

//------------------------------------------------------------------------------
// 顔を探索し,マスクを重ねる
//------------------------------------------------------------------------------
int FaceDetect(IplImage *img, IplImage *mask, IplImage *dst)
{
    double scale = 1.5;
    //処理に用いる画像を定義
    IplImage* gray = cvCreateImage(cvSize(img->width,img->height), 8, 1);
    IplImage* small_img = cvCreateImage(cvSize( cvRound (img->width/scale),
        cvRound (img->height/scale)), 8, 1);

    //前処理
    cvCvtColor(img, gray, CV_BGR2GRAY);
    cvResize(gray, small_img, CV_INTER_LINEAR);
    cvEqualizeHist(small_img, small_img);
    cvCopy(img, dst);
    cvClearMemStorage(storage);

    //顔を探索する
    CvSeq* faces = cvHaarDetectObjects(small_img, cascade, storage,
        1.1, 2, 0, cvSize(30, 30));

    //発見した顔の数だけループ
    for (int i=0; i< (faces ? faces->total : 0); i++) {
        CvRect* face = (CvRect*)cvGetSeqElem(faces, i);

        CvPoint center;     //顔候補領域の中心座標
        center.x = cvRound((face->x + face->width*0.5)*scale);
        center.y = cvRound((face->y + face->height*0.5)*scale);

        CvRect mask_size;   //中心座標からマスクの位置を算出する
        mask_size.x = center.x - (mask->width/2);
        mask_size.y = center.y - (mask->height/2);
        mask_size.width = center.x + (mask->width/2);
        mask_size.height = center.y + (mask->height/2);

        for(int x=mask_size.x; x<mask_size .width; x++) {
            for (int y=mask_size.y; y<mask_size.height; y++) {
                int red, green, blue;
                //マスクの画素値を取得
                getpixel(mask, x-mask_size.x, y-mask_size.y, &red, &green, &blue);
                //緑色の画素は無視する
                if (red==0 && green==255 && blue==0) {
                    continue;
                }
                setpixel(dst, x, y, &red, &green, &blue);
            }
        }
    }
    cvReleaseImage(&gray);
    cvReleaseImage(&small_img);
    return 0;
}

//------------------------------------------------------------------------------
//メイン
//------------------------------------------------------------------------------
int main( int argc, char** argv )
{
    CvCapture* capture = 0;
    IplImage *frame;
    IplImage *frame_copy;
    IplImage *result;
    char window_name[] = "Catcher in the Rye";
    const char* cascade_name = "haarcascade_frontalface_alt.xml";  //顔検出用のデータが入ってるXMLファイル
    char mask_name[]="laughingman.bmp";     //マスク画像

    //顔検出データを読み込む
    cascade = (CvHaarClassifierCascade*)cvLoad(cascade_name, 0, 0, 0);
    if (!cascade){  //読み込みに失敗したら終了
        fprintf( stderr, "ERROR: Could not load classifier cascaden" );
        return -1;
    }

    storage = cvCreateMemStorage(0); // メモリ確保

    //カメラデバイスを開く
    capture = cvCaptureFromCAM(0);
    if (!capture) { //カメラが見つからなかったときは終了
        fprintf(stderr, "ERROR: Found No Cameran");
        return -1;
    }

    // ウインドウを開く
    cvNamedWindow(window_name, 1);

    //画像定義
    frame = cvQueryFrame(capture);
    frame_copy = cvCreateImage(cvGetSize(frame), 8, 3);
    result = cvCreateImage(cvGetSize(frame), 8, 3);

    //マスクを読み込む
    IplImage* mask = cvLoadImage(mask_name);

    // メインループ
    while(1) {
        //キャプチャ
        frame = cvQueryFrame(capture);

        //原点位置にあわせてコピーを行う
        //この処理がないと上下が逆になってしまうことがある
        if(frame->origin == IPL_ORIGIN_TL)
            cvCopy(frame, frame_copy);
        else
            cvFlip(frame, frame_copy);

        //顔検出→マークのインポーズ
        FaceDetect(frame_copy, mask, result);

        //キー入力で終了
        if(cvWaitKey( 10 ) >= 0) {
            break;
        }

        //ウインドウに表示する
        cvShowImage(window_name, result);
    }

    //解放
    cvReleaseCapture(&capture);
    cvReleaseImage(&frame_copy);
    cvReleaseImage(&result);
    cvDestroyAllWindows();

    return 0;
}