輪郭の情報を抽出する

jump.png

前に輪郭を抽出するプログラムを作成しました。 今回は、抽出した輪郭の情報を取り出す方法を紹介します。 画像としては以下の画像を使います。

今回使用した画像

まず、前回のプログラムを以下のように改造します。

>>前回のプログラムはこちら

プログラム

#import "AppDelegate.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    // Insert code here to initialize your application
    cvNamedWindow("window",1);
}

-(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)app{
    cvReleaseImage(&image);
    cvDestroyWindow("window");
    return NSTerminateNow;
}

- (IBAction)Button:(NSButton *)sender {
    NSOpenPanel *openPanel=[NSOpenPanel openPanel];
    NSArray *allowedFileTypes=[NSArray arrayWithObjects:@"png",@"PNG",nil];
    [openPanel setAllowedFileTypes:allowedFileTypes];
    NSInteger pressButton=[openPanel runModal];
    if(pressButton==NSOKButton){
        NSURL * filePath=[openPanel URL];
        NSString* fileName=[filePath path];
        const char* path=[fileName UTF8String];

        image=cvLoadImage(path,CV_LOAD_IMAGE_ANYCOLOR);
        if(image==NULL){
            NSLog(@"Not Read");
        }else{
            //ここからが本題です。
            //グレースケール用のIplImageを用意する
            IplImage *img_gray;
            img_gray=cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,1);
            //青色だけを取り出す
            for(int j=0;jheight;j++){
                for(int i=0;iwidth;i++){
                    //青色の画素をグレースケールへ移す
                    img_gray->imageData[img_gray->widthStep*j+i]=image->imageData[image->widthStep*j+i*3];
                }
            }
            //二値化する(大津の方法)
            cvThreshold(img_gray, img_gray, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU);
            //輪郭を抽出する
            //メモリストレージの確保
            CvMemStorage* storage=cvCreateMemStorage(0);
            CvSeq* contours;
            int nCont=cvFindContours(img_gray,storage,&contours,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0));
            NSLog(@"輪郭数=%d",nCont);
            //ここから追加*************
            CvRect rect;
            rect=cvBoundingRect(contours,0);
            cvRectangle(image,cvPoint(rect.x,rect.y),cvPoint(rect.x+rect.width,rect.y+rect.height),CV_RGB(0,255,0),1,CV_AA,0);

            double length;
            length=cvArcLength(contours,CV_WHOLE_SEQ,1);
            NSLog(@"周長=%f",length);

            double area;
            area=cvContourArea(contours, CV_WHOLE_SEQ, 0);
            NSLog(@"面積=%f",area);
            //***********************
            cvDrawContours(image,contours,CV_RGB(255,0,0),CV_RGB(0,255,0),1,2,CV_AA,cvPoint(0,0));
            cvShowImage("window",image);

        }
    }
}
@end

解説

以下の命令で輪郭を囲む四角形の情報を得ることができます。

CvRect cvBoundingRect(
CvArr* points,      //点列データ
int update          //更新フラグ
);

このプログラムで得られた情報から、実際に四角形を描写しています。 輪郭の周長は以下の命令で得ることができます。

double cvArcLength(
const void* curve,       //輪郭データ
CvSlice slice,           //曲線の始点と終点
int is_closed            //曲線が閉じているかどうかのフラグ
);

最後に面積は以下の命令から得ることができます。

double cvContourArea(
const CvArr* contour,     //輪郭データ
CvSlice slice,            //輪郭の始点と終点
int oriented
);

輪郭の周長と面積がわかれば、これらを利用することで形を分類できます。 例えば、円なら 面積/周の自乗=\pi r^{2}/(2\pi r)^{2}=1/4\pi となります。 いろいろ活用してみてください。

著者:安井 真人(やすい まさと)