今見てくれてる人の数

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

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

【openFrameworks 冒険記11】ofxOpenCvで色々やってみる3!ofxCvContourFinderで輪郭抽出する!

次回、輪郭に線を引くための途中の工程なのですが、

このアルゴリズムでやります。(拝借します)

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

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

KeynoteScreenSnapz001.png

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

 

 

背景をごちゃごちゃな映像にさせると輪郭がうまく取れません。

右下のぐちゃぐちゃ出てるのが検出された輪郭です。

f:id:sudara_bluse:20171116193336p:plain

 

このカーテンのように背景をなるべくシンプルにすると

右下のように割と綺麗に?取得できます。

 

f:id:sudara_bluse:20171117010056p:plain


今回は備忘録程度でお許しを。

 

前回はこちら。この続きをやります。

www.sudara-bluse.tokyo

 

実装方法

ofApp.hでこれ定義

// 輪郭検出用オブジェクト
ofxCvContourFinder contourFinder;
// しきい値
int threshold = 80;

 

ofApp.cppで

二値化 (白黒はっきりさせて)
grayDiff.threshold(パラメータ);

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

 

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

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

 

コード

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 = 80;

};

ofApp.cpp
#include "ofApp.h"

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

    // カメラからの取り込み
    cam.initGrabber(320, 240);

    // カラー画像を格納する領域を確保
    colorImg.allocate(320, 240);
    // グレースケール画像を格納する領域
    grayImg.allocate(320, 240);

    // 現在と差分の領域も確保
    grayBase.allocate(320, 240);
    grayDiff.allocate(320, 240);

    // 保存したかどうか
    isSave = true;

    threshold = 80;


}

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

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

    // 一個一個のピクセルデータをカラーイメージに格納する
    colorImg.setFromPixels(cam.getPixels().getData(), 320, 240);
    colorImg.mirror(false, true);

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


    // isSaveがtrueだった元画像に現在のグレーイメージを入れる
    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, (320*240)/3, 10, true);


    cout << threshold;


}

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

    colorImg.draw(0,0, 320, 240);
    grayImg.draw(330,0, 320, 240);


    grayBase.draw(0,250, 320, 240);
    grayDiff.draw(330, 250, 320, 240);


    contourFinder.draw(340, 500, 320, 240);

}

//--------------------------------------------------------------
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;
    }


}

 

 

次回は輪郭に線を引きます