今見てくれてる人の数

ホンキートンク・スーダラブルース

ゆるコラム、oF、邦ロックや歌謡曲、小説の感想。ドラクエ、JavaScript、ドラゴンボール超など。湘南あたりを転がってる石コロのゆるゆる生きてく超雑多な軌跡です。

【openFrameworks 冒険記12】ofxOpenCvで色々やってみる4!ofxCvContourFinderで抽出した輪郭に線をかく。

前回までofxOpenCvを色々いじってきましたが、

とりあえず第1章として、輪郭に線を引きたかったのです。

こんな感じ。こんくらいの精度で良いのかなぁ??

 

f:id:sudara_bluse:20171117080742p:plain

 

f:id:sudara_bluse:20171117013220p:plain

 

 

この下記の流れで処理して、その後に輪郭に線を書きます!

 

www.sudara-bluse.tokyo

カラーの映像ソースから、移動する物体の輪郭を抽出するまでの
おおまかな流れは以下のような順番になっています。

  • カメラから映像をキャプチャー
  • グレースケールに変換
  • 背景画像を登録
  • 背景画像と現在の画像の差分を計算
  • 差分の画像を2値化
  • 2値化した画像を解析(輪郭抽出、重心を算出)

KeynoteScreenSnapz001.png

openFrameworks – addon を使う 3 : OpenCVを利用した映像認識 | yoppa org

 

 

実装内容 (前回からの追記部分のみ)

ofApp.h

// 輪郭線を格納する動的配列を定義

vector <ofPolyline> edgeLines;

 

ofApp.cpp

updateで


edgeLines.clear(); で前フレームの線をクリア

   

// 輪郭線の座標を結んで線にする

    // 1周目for文で複数の輪郭にアクセス

    for(int i = 0; i< contourFinder.nBlobs; i++){

        ofPolyline line; // 線を定義

        // 2周目for文でそれぞれの輪郭の点にアクセス

        for(int j =0; j<contourFinder.blobs[i].pts.size(); j++){

            // 点を結んで線にする。

            line.addVertex(contourFinder.blobs[i].pts[j]);

        }

        // 作成した線を動的配列に格納

        edgeLines.push_back(line);

    }

 

 drawで

 

    // カラー映像の描画

    ofSetColor(255, 255, 255);

    colorImg.draw(0,0, 640, 480);

    

    // 境界線のサイズと色指定

    ofSetLineWidth(2); // サイズ2

    ofSetColor(255, 255, 0); // 黄色に指定

 

    // 輪郭線の描画

    for(int cnt = 0; cnt< edgeLines.size(); cnt++){

        edgeLines[cnt].draw();

    }

 

スペースキーでなるべく無地な背景を撮影してください。

その後にkeyPressedでしきい値を調整します。

 

 f:id:sudara_bluse:20171117013142p:plain

 

コードはこちら

ofApp.h
#pragma once

#include "ofMain.h"
#include "ofxOpenCv.h"

class ofApp : public ofBaseApp{

    public:
        void setup();
        void update();
        void draw();

        void keyPressed(int key);
        void keyReleased(int key);
        void mouseMoved(int x, int y );
        void mouseDragged(int x, int y, int button);
        void mousePressed(int x, int y, int button);
        void mouseReleased(int x, int y, int button);
        void mouseEntered(int x, int y);
        void mouseExited(int x, int y);
        void windowResized(int w, int h);
        void dragEvent(ofDragInfo dragInfo);
        void gotMessage(ofMessage msg);

    // カメラ
    ofVideoGrabber cam;

    // カメラから入った1フレーム分の画像
    ofxCvColorImage colorImg;

    // カメラから入った1フレーム分のグレースケールの画像
    ofxCvGrayscaleImage grayImg;

    // グレースケール画像で背景用と差分
    ofxCvGrayscaleImage grayBase;
    ofxCvGrayscaleImage grayDiff;

    // フレームを保存したかどうかのフラグ
    bool isSave;

    // 輪郭検出用
    ofxCvContourFinder contourFinder;

    // しきい値
    int threshold;

    // 輪郭線を格納する動的配列
    vector <ofPolyline> edgeLines;


};


 

ofApp.cpp
#include "ofApp.h"


//--------------------------------------------------------------
void ofApp::setup(){

    // 背景を黒に
    ofBackground(0, 0, 0);


    /* 今回は1024*768です! */
    // カメラからの取り込み
    cam.initGrabber(640, 480);

    // それぞれを処理するメモリ領域を確保   allocate(割り当てる)
    colorImg.allocate(640, 480);
    grayImg.allocate(640, 480);
    grayBase.allocate(640, 480);
    grayDiff.allocate(640, 480);

    // フラグ初期化
    isSave = true;

    threshold = 80;

}

//--------------------------------------------------------------
void ofApp::update(){

    // カメラの更新
    cam.update();

    // 一個一個のピクセルデータをカラーイメージにほりこんでる
    colorImg.setFromPixels(cam.getPixels().getData(), 640, 480);
    colorImg.mirror(false, true);

    // グレースケールイメージにカラーデータを入れる
    grayImg = colorImg;

    // スペースキー押した時に、元画像に現在のグレーイメージを入れる
    if(isSave == true){
        grayBase = grayImg;
        isSave = false;
    }
    // 白黒画像と動体検知のベースとなる画像の差を求める
    grayDiff.absDiff(grayBase, grayImg);

    // 二値化 (白黒はっきりさせる)
    grayDiff.threshold(threshold);


    // 輪郭を描く
    // 第1引数 輪郭検出対象
    // 第2引数 検出する最小の大きさ(20)
    // 第3引数 検出する最大の大きさ
    // 第4引数 検出する数
    // 第5引数 穴が空いたものを検出するかどうか trueで 検出する
    contourFinder.findContours(grayDiff, 20, (640*480)/3, 10, true);



    // 動的配列をクリアする
    edgeLines.clear();

    //-----------------------------------------
    // 輪郭線の座標を結んで線にする
    //-----------------------------------------

    // 1周目for文で複数の輪郭にアクセス
    for(int i = 0; i< contourFinder.nBlobs; i++){
        ofPolyline line;
        // 2周目for文でそれぞれの輪郭の点にアクセスし、点を結んで線にする。
        for(int j =0; j<contourFinder.blobs[i].pts.size(); j++){
            // 点を線にする。
            line.addVertex(contourFinder.blobs[i].pts[j]);
        }
        // 作成した線を格納
        edgeLines.push_back(line);
    }
}

//--------------------------------------------------------------
void ofApp::draw(){

    // カラー画像の描画
    ofSetColor(255, 255, 255);
    colorImg.draw(0,0, 640, 480);

    // 境界線のサイズと色指定
    ofSetLineWidth(5);
    ofSetColor(255, 255, 0);
    // 輪郭線の描画
    for(int cnt = 0; cnt< edgeLines.size(); cnt++){
        edgeLines[cnt].draw();
    }


}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){

    switch (key){
        case ' ':
            isSave = true;   // スペースキーを押す事によってフレームを保存
            break;
        case '+':
            threshold ++;     //しきい値を上げる
            if (threshold > 255) threshold = 255;
            break;
        case '-':
            threshold --;     //しきい値を下げる
            if (threshold < 0) threshold = 0;
            break;
    }

}