こんにちは。今回は前回のむりやり笑わせるプログラムよりも簡単なプログラムを作ります。具体的には、ofxFaceTrackerの機能を利用して顔を認識し、追従し、笑い男マークを上書きするプログラムです。攻殻機動隊用語でこれを”目を盗む”と言います。
俺の目を盗みやがったな
実装の方法としては、顔の位置をofxFaceTrackerで取得し、その位置に合わせて笑い男マークを描画するだけです。簡単ですね。
では以下プログラムです。
testApp.h
#pragma once
#include "ofMain.h"
#include "ofxCv.h"
using namespace ofxCv;
using namespace cv;
#include "ofxFaceTrackerThreaded.h"
#define INPUT_MOVIE_WSIZE 640
#define INPUT_MOVIE_HSIZE 480
class testApp : public ofBaseApp {
public:
void setup();
void exit();
void update();
void draw();
void keyPressed(int key);
ofVideoGrabber cam;
ofxFaceTrackerThreaded tracker;
ofVec2f position;
float scale;
ofVec3f orientation;
ofMatrix4x4 rotationMatrix;
Mat translation, rotation;
ofImage ringImg,faceImg;
cv::Mat ringMat,faceMat;
ofMatrix4x4 pose;
bool isFirstRead = true;
ofEasyCam easyCam;
};
testApp.cpp
#include "testApp.h"
using namespace ofxCv;
using namespace FACETRACKER;
void testApp::setup() {
ofSetVerticalSync(true);
ofSetDrawBitmapMode(OF_BITMAPMODE_MODEL_BILLBOARD);
cam.initGrabber(INPUT_MOVIE_WSIZE, INPUT_MOVIE_HSIZE);
tracker.setup();
//笑い男マークの文字のリングパーツをロード
ringImg.loadImage("img/laughingman_ring.png");
ringMat = toCv(ringImg);
//笑い男マークの顔パーツをロード
faceImg.loadImage("img/laughingman_face.png");
faceMat = toCv(faceImg);
}
void testApp::exit() {
tracker.waitForThread();
}
void testApp::update() {
cam.update();
if(cam.isFrameNew()) {
tracker.update(toCv(cam));
position = tracker.getPosition();
scale = tracker.getScale();
orientation = tracker.getOrientation();
rotationMatrix = tracker.getRotationMatrix();
}
}
void testApp::draw() {
ofSetHexColor(0xffffff);
cam.draw(0, 0);
ofDrawBitmapString(ofToString((int) ofGetFrameRate()), 10, 20);
if(tracker.getFound()) {
ofSetLineWidth(1);
//顔が見つかったら、画像の大きさをちょうどいい大きさに(改善必要)
if(isFirstRead){
cv::resize(ringMat, ringMat, cv::Size(ringImg.width/2,ringImg.height/2));
cv::resize(faceMat, faceMat, cv::Size(faceImg.width/2,faceImg.height/2));
isFirstRead = false;
}
ringImg.setFromPixels(ringMat.data, ringMat.cols,ringMat.rows, OF_IMAGE_COLOR_ALPHA);
faceImg.setFromPixels(faceMat.data, faceMat.cols,faceMat.rows, OF_IMAGE_COLOR_ALPHA);
//リングパーツと顔パーツの描画位置を決定
int faceCenterX = (int)tracker.getImagePoint(29).x - ringImg.width/2;
int faceCenterY = (int)tracker.getImagePoint(29).y - ringImg.height/2;
int ringCenterX = (int)tracker.getImagePoint(29).x;
int ringCenterY = (int)tracker.getImagePoint(29).y;
ofPushMatrix();
ofTranslate(ringCenterX, ringCenterY); //座標中心をピボット回転の中心へ移動
ofPushMatrix();
ofRotate(ofGetFrameNum() * -1, 0, 0, 1);//ピボット回転の中心に移動した座標軸を回転
ofPushMatrix();
ringImg.draw(-ringImg.width/2, -ringImg.height/2);//オフセットを初期化し、描画
ofPopMatrix();
ofPopMatrix();
ofPopMatrix();
faceImg.draw(faceCenterX,faceCenterY);
}
}
void testApp::keyPressed(int key) {
if(key == 'r') {
tracker.reset();
}
}
少し複雑な処理をしているのはリング部分を回転させるために座標軸の移動をしている点です。回転中心と画像の中心がずれてしまうと、その場でぐるぐる回るという動作をしなくなってしまうので注意する必要があります。また、写り込んだ顔の大きさに合わせて笑い男マークの縮尺を変更する処理を入れていないので、改善する必要があります。
以下はプログラムを動作をさせている際に撮影した動画です。