移動する物体を追跡する

輪郭線を描画する

前回はダンゴムシの重心座標を中心とする円を描画していましたが、今回はダンゴムシの輪郭線を描画することにします。膨張・収縮処理を施した後なので、ぴったりの輪郭線が描画できるかどうかはわかりません。今回はdrawContours関数を用います。この関数は、vector<vector >型の輪郭情報と描画するMatの名前、描画する輪郭の色や太さを引数に取ります。次のように記述した場合、imgという名前のMatに対して、contours_subsetに含まれる輪郭を太さ4の線で描画することを指示します。

drawContours(img,contours_subset,-1,Scalar(0,0,255),4);

 

太さの部分を負の数にすると、塗りつぶしで描画することを指示します。

drawContours(img,contours_subset,-1,Scalar(0,0,255),-1);

 

参考までに、ダンゴムシの輪郭を太さ1、4、-1で指定した場合には次のようになります。
drawcontours
前回の座標のファイル出力と動画の書き出しから追加したのは3行のみです。

vector<vector<Point> > contours_subset;
contours_subset.push_back(contours.at(max_area_contour));
drawContours(img,contours_subset,-1,Scalar(0,255,0),4);

 

まず、描画したい輪郭のインデックスがmax_area_contourであるのがわかっています。そこで、描画したい輪郭のみを保持するためにcontours_subsetという名前の、vector<vector >型の構造体を定義しています。この構造体の宣言は動画のループの中で行っているため、フレームが新しくなるごとに内容はリセットされることに注意してください。次に、実際に描画したい輪郭の情報である、contours.at(max_area_contour)push_back関数によりcontours_subsetに追加しています。今回は描画する輪郭がひとつだけですが、場合によってはcontours_subsetに複数の輪郭をpush_backすることで複数の輪郭を描画できます。最後にdrawContours関数にcontours_subset、描画するMatの名前、描画する輪郭の色、太さの値を入れて輪郭を描画します。

#include "opencv/cv.h"
#include "opencv/highgui.h"
#include <vector>
#include <stdio.h>
using namespace cv;
using namespace std;

int main(){
    Mat img;
    Mat gray_img;
    Mat bin_img;
    Mat element = Mat::ones(3,3,CV_8UC1);
    VideoCapture cap("pillbug.mp4");
    int max_frame=cap.get(CV_CAP_PROP_FRAME_COUNT);
    
    int v_w=cap.get(CV_CAP_PROP_FRAME_WIDTH);
    int v_h=cap.get(CV_CAP_PROP_FRAME_HEIGHT);
    VideoWriter writer("pillbug.avi", CV_FOURCC_DEFAULT, 30,cvSize(v_w, v_h), true);
    char filename[] = "output.txt";
    FILE* fp;
    fp = fopen(filename, "w");
    
    for(int i=0; i<max_frame;i++){ cap>>img;
        cvtColor(img, gray_img, CV_BGR2GRAY);
        threshold(gray_img,bin_img,70,255,THRESH_BINARY);
        bin_img=~bin_img;
        dilate(bin_img, bin_img, element, cv::Point(-1,-1), 1);
        dilate(bin_img, bin_img, element, cv::Point(-1,-1), 1);
        dilate(bin_img, bin_img, element, cv::Point(-1,-1), 1);
        vector<vector<Point> > contours;
        findContours(bin_img, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
        double max_area=0;
        int max_area_contour=-1;
        for(int j=0;j<contours.size();j++){
            double area=contourArea(contours.at(j));
            if(max_area<area){
                max_area=area;
                max_area_contour=j;
            }
        }
        int count=contours.at(max_area_contour).size();
        double x=0;
        double y=0;
        for(int k=0;k<count;k++){
            x+=contours.at(max_area_contour).at(k).x;
            y+=contours.at(max_area_contour).at(k).y;
        }
        x/=count;
        y/=count;
        circle(img, Point(x,y),50, Scalar(0,0,255),3,4);

        //追加ここから

        vector<vector<Point> > contours_subset;
        contours_subset.push_back(contours.at(max_area_contour));
        drawContours(img,contours_subset,-1,Scalar(0,255,0),4);

        //追加ここまで

        imshow("Video",img);
        
        fprintf(fp, "%d,%d,%d\n",i,(int)x,(int)y);
        writer<<img;
        if(i==20){
            imwrite("pillbug-output.png", img);
        }
        
        waitKey(1);
    }
    fclose(fp);
    return 0;
}