SIFT+RANSACのサンプル
いろいろなサイト[1]を参考にしつつ、とりあえず作ってみたのだけど、精度がイマイチです。
■コード一式(VS2010用)
http://navi.cs.kumamoto-u.ac.jp/~koutaki/opencv_matching.zip
FindHomographyでRANSACを使って、斜影変換行列を求めているのだけど、
四角になるはずの検出結果がおかしい。なんかやり方がおかしいのでしょうか。
誰か教えてください。
// // 特徴点&対応付けプログラム // #include <stdio.h> #include <cv.h> #include <highgui.h> #include <nonfree/nonfree.hpp> // ver.2.4から必要 #include <vector> using namespace std; // 表示用画像の作成 IplImage* stack_imgs( IplImage* img1, IplImage* img2 ) { IplImage* stacked = cvCreateImage( cvSize( MAX(img1->width, img2->width), img1->height + img2->height ), IPL_DEPTH_8U, 3 ); cvZero( stacked ); cvSetImageROI( stacked, cvRect( 0, 0, img1->width, img1->height ) ); cvAdd( img1, stacked, stacked, NULL ); cvSetImageROI( stacked, cvRect(0, img1->height, img2->width, img2->height) ); cvAdd( img2, stacked, stacked, NULL ); cvResetImageROI( stacked ); return stacked; } // ホモグラフィHを使って斜影変換を行うルーチン CvPoint persp_xform_pt( CvPoint & pt, cv::Mat & H ) { CvPoint ret; CvMat XY, UV; double xy[3] = { pt.x, pt.y, 1.0 }, uv[3] = { 0 }; CvPoint2D64f rslt; cvInitMatHeader( &XY, 3, 1, CV_64FC1, xy, CV_AUTOSTEP ); cvInitMatHeader( &UV, 3, 1, CV_64FC1, uv, CV_AUTOSTEP ); CvMat T = H; cvMatMul( &T, &XY, &UV ); rslt = cvPoint2D64f( uv[0] / uv[2], uv[1] / uv[2] ); ret.x = rslt.x; ret.y = rslt.y; return ret; } int main (int argc, char **argv) { CvMemStorage *storage = cvCreateMemStorage (0); if (argc == 1) { fprintf (stderr, "usage: %s input_imagefile\n", argv[0]); exit (0); } // ウインドウの設定 cvNamedWindow ("srcimage", CV_WINDOW_AUTOSIZE); //入力画像表示用 cvNamedWindow ("livecapture", CV_WINDOW_AUTOSIZE); //キャプチャ画像表示用 // キャプチャデバイスの設定 CvCapture *cap; IplImage *cap_img_rgb = 0; IplImage *cap_img_gray = 0; cap = cvCreateCameraCapture(CV_CAP_ANY); if (cap == NULL) { fprintf (stderr, "cvCreateCameraCapture() failed...\n"); exit (0); } cvSetCaptureProperty (cap, CV_CAP_PROP_FRAME_WIDTH, 320); cvSetCaptureProperty (cap, CV_CAP_PROP_FRAME_HEIGHT, 240); // 入力画像から特徴点を抽出 IplImage* src_img_rgb = cvLoadImage (argv[1]); // 入力画像の特徴点を表示 IplImage* src_img_gray = 0; if (src_img_gray == 0) { src_img_gray = cvCreateImage (cvGetSize(src_img_rgb), 8, 1); } cvCvtColor (src_img_rgb, src_img_gray, CV_RGB2GRAY); // ここからカメラからキャプチャした画像を処理するループ double threshold = 1.0; double edge_threshold = 8.0; double magnification = 3.0; // SIFTの特徴点検出・特徴量抽出器を用意 cv::SiftFeatureDetector detector(threshold, edge_threshold ); cv::SiftDescriptorExtractor extractor( magnification ); // SURFの場合 //cv::SurfFeatureDetector detector; //cv::SurfDescriptorExtractor extractor; // 画像1から特徴点を検出 std::vector<cv::KeyPoint> keypoints1; cv::Mat mat1(src_img_gray); detector.detect( mat1, keypoints1 ); // 画像1の特徴点における特徴量を抽出 cv::Mat descriptor1; extractor.compute( mat1, keypoints1, descriptor1 ); //表示用出力画像 IplImage * stacked; while (true) { double st = (double)cvGetTickCount(); // キャプチャデバイスから画像を取得 cap_img_rgb = cvQueryFrame (cap); if (cap_img_gray == 0) { cap_img_gray = cvCreateImage (cvGetSize(cap_img_rgb), 8, 1); } cvCvtColor (cap_img_rgb, cap_img_gray, CV_RGB2GRAY); // 画像2から特徴点を検出 cv::Mat mat(cap_img_gray); std::vector<cv::KeyPoint> keypoints2; detector.detect( mat, keypoints2 ); // 画像2の特徴点における特徴量を抽出 cv::Mat descriptor2; extractor.compute( mat, keypoints2, descriptor2 ); // 対応点を求める vector< cv::DMatch > matches_o; vector< cv::DMatch > matches; // 特徴量比較による特徴点の対応付け cv::FlannBasedMatcher matcher; matcher.match(descriptor2, descriptor1, matches_o ); // 類似度が高かった対応付けのみを抽出 for(int i = 0;i < matches_o.size();i++) { if(sqrt(matches_o[i].distance/128.0) < 1.5) { matches.push_back(matches_o[i]); } } // 描画用画像の作成 stacked = stack_imgs( src_img_rgb, cap_img_rgb); // 特徴点描画 for(int i = 0;i < keypoints1.size();i++) { CvPoint center; center.x = keypoints1[i].pt.x; center.y = keypoints1[i].pt.y; cvCircle(stacked, center, 2, CV_RGB(160,0,0)); }; for(int i = 0;i < keypoints2.size();i++) { CvPoint center; center.x = keypoints2[i].pt.x; center.y = keypoints2[i].pt.y + src_img_rgb->height; cvCircle(stacked, center, 2, CV_RGB(160,0,0)); }; if(matches.size() > 4) { std::vector< cv::Vec2f > points1( matches.size()); std::vector< cv::Vec2f > points2( matches.size()); for(int i = 0;i < matches.size();i++) { CvPoint pt1, pt2; pt1.x = keypoints1[matches[i].trainIdx].pt.x; pt1.y = keypoints1[matches[i].trainIdx].pt.y; pt2.x = keypoints2[matches[i].queryIdx].pt.x; pt2.y = keypoints2[matches[i].queryIdx].pt.y + src_img_rgb->height; cvLine(stacked, pt1,pt2, CV_RGB(255,0,0)); // 対応点ペアの作成 pt1.x = keypoints1[matches[i].trainIdx].pt.x; pt1.y = keypoints1[matches[i].trainIdx].pt.y; pt2.x = keypoints2[matches[i].queryIdx].pt.x; pt2.y = keypoints2[matches[i].queryIdx].pt.y; points1[i][0] = pt1.x; points1[i][1] = pt1.y; points2[i][0] = pt2.x; points2[i][1] = pt2.y; }; // RANSACによるホモグラフィ計算 cv::Mat H; H = cv::findHomography( points1, points2, CV_RANSAC, 5.0); // 結果を描画 { CvPoint pt1, pt2; int B = 40; CvPoint xpt1, xpt2, xpt3, xpt4, pt; pt.x = 128-B;pt.y = 128 - B; xpt1 = persp_xform_pt( pt, H ); pt.x = 128+B;pt.y = 128 - B; xpt2 = persp_xform_pt( pt, H ); pt.x = 128+B;pt.y = 128 + B; xpt3 = persp_xform_pt( pt, H ); pt.x = 128-B;pt.y = 128 + B; xpt4 = persp_xform_pt( pt, H ); pt1.x = xpt1.x;pt1.y = xpt1.y + src_img_rgb->height; pt2.x = xpt2.x;pt2.y = xpt2.y + src_img_rgb->height; cvLine( stacked, pt1, pt2, CV_RGB(0, 255, 0), 2, 8, 0 ); pt1.x = xpt2.x;pt1.y = xpt2.y + src_img_rgb->height; pt2.x = xpt3.x;pt2.y = xpt3.y + src_img_rgb->height; cvLine( stacked, pt1, pt2, CV_RGB(0, 255, 0), 2, 8, 0 ); pt1.x = xpt3.x;pt1.y = xpt3.y + src_img_rgb->height; pt2.x = xpt4.x;pt2.y = xpt4.y + src_img_rgb->height; cvLine( stacked, pt1, pt2, CV_RGB(0, 255, 0), 2, 8, 0 ); pt1.x = xpt4.x;pt1.y = xpt4.y + src_img_rgb->height; pt2.x = xpt1.x;pt2.y = xpt1.y + src_img_rgb->height; cvLine( stacked, pt1, pt2, CV_RGB(0, 255, 0), 2, 8, 0 ); } }; // 処理結果を表示 cvShowImage("livecapture", stacked); // ESCキーを押したら終了 int c = cvWaitKey(10); if (c == 27) break; } // 終了処理 cvDestroyAllWindows (); cvReleaseImage (&src_img_gray); cvReleaseImage (&src_img_rgb); cvReleaseImage (&cap_img_gray); cvReleaseCapture (&cap); cvReleaseMemStorage (&storage); return (0); }