現在、作成中…
もくじ
- 特徴点マッチング
- Hough変換
- 特徴点追跡
- 形状特徴
1. 特徴点マッチング
1.1 特徴点を検出する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <opencv2/opencv.hpp> int main(void) { cv::Mat src, dstAkaze, dstOrb, dstSurf; src = cv::imread("C:/opencv/sources/samples/data/box_in_scene.png"); // 特徴点検出アルゴリズムの選択 // # AKAZE, ORBの2種類を試す cv::Ptr<cv::AKAZE> akaze = cv::AKAZE::create(cv::AKAZE::DESCRIPTOR_MLDB, 0, 3, 0.001f); cv::Ptr<cv::ORB> orb = cv::ORB::create(500, 1.2f, 2); // 検出したキーポイント(特徴点)を格納する配列 std::vector<cv::KeyPoint> keyAkaze, keyOrb, keySurf; // キーポイント検出 akaze->detect(src, keyAkaze); orb->detect(src, keyOrb); // 画像上にキーポイントの場所を描く // # DrawMatchesFlags::DRAW_RICH_KEYPOINTS キーポイントのサイズと方向を描く cv::drawKeypoints(src, keyAkaze, dstAkaze, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); cv::drawKeypoints(src, keyOrb, dstOrb, cv::Scalar::all(-1), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS); cv::imshow("原画像", src); cv::imshow("AKAZE", dstAkaze); cv::imshow("ORB", dstOrb); cv::waitKey(); return 0; } |
1.2 特徴量を算出して特徴点を対応付ける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <opencv2/opencv.hpp> int main(void) { cv::Mat src1, src2, dst; src1 = cv::imread("C:/opencv/sources/samples/data/box.png"); src2 = cv::imread("C:/opencv/sources/samples/data/box_in_scene.png"); // 特徴点検出アルゴリズムの選択 // # ORB::create(検出特徴点数, scale factor, ...) cv::Ptr<cv::ORB> orb = cv::ORB::create(100); // 検出したキーポイント(特徴点)を格納する配列 std::vector<cv::KeyPoint> key1, key2; // キーポイントの検出 orb->detect(src1, key1); orb->detect(src2, key2); // 特徴量記述の計算 cv::Mat des1, des2; orb->compute(src1, key1, des1); orb->compute(src2, key2, des2); // 特徴点マッチングアルゴリズムの選択 cv::Ptr<cv::DescriptorMatcher> hamming = cv::DescriptorMatcher::create("BruteForce-Hamming"); // 特徴点マッチング // * 特徴量記述des1とdes2のマッチングを行い、結果をmatchへ書き込む std::vector<cv::DMatch> match; hamming->match(des1, des2, match); // 特徴点マッチングの結果を画像化する cv::drawMatches(src1, key1, src2, key2, match, dst); cv::imshow("画像1", src1); cv::imshow("画像2", src2); cv::imshow("マッチング結果", dst); cv::waitKey(); return 0; } |
1.3 特徴量距離の小さい対応付けを抽出する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
#include <opencv2/opencv.hpp> int main(void) { cv::Mat src1, src2, dst; src1 = cv::imread("C:/opencv/sources/samples/data/box.png"); src2 = cv::imread("C:/opencv/sources/samples/data/box_in_scene.png"); // 特徴点検出アルゴリズムの選択 // # ORB::create(検出特徴点数, scale factor, ...) cv::Ptr<cv::ORB> orb = cv::ORB::create(500); // 検出したキーポイント(特徴点)を格納する配列 std::vector<cv::KeyPoint> key1, key2; // キーポイントの検出 orb->detect(src1, key1); orb->detect(src2, key2); // 特徴量記述の計算 cv::Mat des1, des2; orb->compute(src1, key1, des1); orb->compute(src2, key2, des2); // 特徴点マッチングアルゴリズムの選択 cv::Ptr<cv::DescriptorMatcher> hamming = cv::DescriptorMatcher::create("BruteForce-Hamming"); // 特徴点マッチング // # 特徴量記述des1とdes2のマッチングを行い、結果をmatchへ書き込む std::vector<cv::DMatch> match; hamming->match(des1, des2, match); // 特徴量距離の小さい順にソートする(選択ソート) for (int i = 0; i < match.size() - 1; i++) { double min = match[i].distance; int n = i; for (int j = i + 1; j < match.size(); j++) { if (min > match[j].distance) { n = j; min = match[j].distance; } } std::swap(match[i], match[n]); } // 上位50点を残して、残りのマッチング結果を削除する。 match.erase(match.begin() + 50, match.end()); // 特徴点マッチングの結果を画像化する cv::drawMatches(src1, key1, src2, key2, match, dst); cv::imshow("画像1", src1); cv::imshow("画像2", src2); cv::imshow("マッチング結果", dst); cv::waitKey(); return 0; } |
2. Hough変換
2.1 直線を検出する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 画像を格納するオブジェクトを宣言する cv::Mat src1, src2, src_bin; // 画像ファイルから画像データを読み込む src1 = cv::imread("C:/opencv/sources/samples/data/stuff.jpg", cv::IMREAD_COLOR); if (src1.empty() == true) { // 画像データが読み込めなかったときは終了する return 0; } src2 = src1.clone(); // エッジを検出 cv::cvtColor(src1, src_bin, cv::COLOR_BGR2GRAY); cv::Canny(src_bin, src_bin, 50, 100); cv::imshow("Canny", src_bin); // 標準Hough変換 vector<cv::Vec2f> lines; cv::HoughLines(src_bin, lines, 1, CV_PI / 180, 60); for (int i = 0; i < lines.size(); i++) { float roh = lines[i][0], theta = lines[i][1]; double x0 = roh * cos(theta), y0 = roh * sin(theta); double a = 1000; cv::Point p1(x0 - sin(theta) * a, y0 + cos(theta) * a); cv::Point p2(x0 + sin(theta) * a, y0 - cos(theta) * a); cv::line(src1, p1, p2, cv::Scalar(0, 255, 0), 1, cv::LINE_AA); } cv::imshow("標準Hough変換", src1); // プログレッシブな確率的Hough変換 vector<cv::Vec4i> lines2; HoughLinesP(src_bin, lines2, 1, CV_PI / 180, 50, 10, 50); for (size_t i = 0; i < lines2.size(); i++) { cv::Vec4i p = lines2[i]; cv::line(src2, cv::Point(p[0], p[1]), cv::Point(p[2], p[3]), cv::Scalar(0, 255, 0), 1, cv::LINE_AA); } cv::imshow("プログレッシブな確率的Hough変換", src2); // 何かキーが押されるまで待つ cv::waitKey(); return 0; } |
2.2 円を検出する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 画像を格納するオブジェクトを宣言する cv::Mat src, src_gray; // 画像ファイルから画像データを読み込む src = cv::imread("C:/opencv/sources/samples/data/stuff.jpg", cv::IMREAD_COLOR); if (src.empty() == true) { // 画像データが読み込めなかったときは終了する return 0; } cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY); // Hough円変換 vector<cv::Vec3f> circles; cv::HoughCircles(src_gray, circles, cv::HOUGH_GRADIENT, 1, 50, 100, 20, 1, 100); for (int i = 0; i < circles.size(); i++) { circle(src, cv::Point(circles[i][0], circles[i][1]), circles[i][2], cv::Scalar(0, 255, 0), 1, cv::LINE_AA); } cv::imshow("Hough円変換", src); // 何かキーが押されるまで待つ cv::waitKey(); return 0; } |
3. 特徴点追跡
3.1 動画中の特徴点を追跡する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { cv::VideoCapture cap; cap.open("C:/opencv/sources/samples/data/vtest.avi"); if (cap.isOpened() == false) { return 0; } // reset == TRUE のとき特徴点検出を行う // 最初のフレームで必ず特徴点検出を行うように、初期値を TRUE にする bool reset = true; // image_curr: 現在の入力画像、 image_prev: 直前の入力画像 // points_curr: 現在の特徴点リスト、points_prev: 直前の特徴点リスト cv::Mat frame, image, image_curr, image_prev; vector<cv::Point2f> points_prev, points_curr; for (;;) { cap >> frame; if (frame.empty()) { break; } frame.copyTo(image); cv::cvtColor(image, image_curr, cv::COLOR_BGR2GRAY); if (reset == true) { // 特徴点検出 cv::goodFeaturesToTrack(image_curr, points_curr, 500, 0.01, 10, cv::Mat(), 3, 3, 0, 0.04); cv::cornerSubPix(image_curr, points_curr, cv::Size(10, 10), cv::Size(-1, -1), cv::TermCriteria(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 20, 0.03)); points_prev = points_curr; reset = false; } else { // 特徴点追跡 vector<uchar> status; vector<float> err; cv::calcOpticalFlowPyrLK(image_prev, image_curr, points_prev, points_curr, status, err); // 追跡できなかった特徴点をリストから削除する int i, k; for (i = k = 0; i < status.size(); i++) { if (status[i] == 0) { continue; } points_prev[k] = points_prev[i]; points_curr[k++] = points_curr[i]; } points_curr.resize(k); points_prev.resize(k); } // 特徴点を丸で描く for (int i = 0; i < points_curr.size(); i++) { cv::Scalar c(0, 255, 0); if (cv::norm(points_prev[i] - points_curr[i]) > 0.5) { c = cv::Scalar(0, 100, 255); } cv::circle(image, points_curr[i], 3, c, -1, cv::LINE_AA); } cv::imshow("特徴点追跡", image); int key = cv::waitKey(30); if (key == 'r') { // Rキーが押されたら特徴点を再検出 reset = true; } // image_curr を image_prev に移す(交換する) cv::swap(image_curr, image_prev); // points_curr を points_prev に移す(交換する) cv::swap(points_curr, points_prev); } return 0; } |
4. 形状特徴
4.1 モーメント特徴から図形の重心と主軸を求める
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <opencv2/opencv.hpp> using namespace std; int main(void) { // 画像を格納するオブジェクトを宣言する cv::Mat src, dst; // モーメント特徴のオブジェクトを宣言する cv::Moments m; for (int i = 1; i <= 20; i++) { stringstream filename; filename << "C:/opencv/sources/samples/data/shape_sample/" << i << ".png"; src = cv::imread(filename.str(), cv::IMREAD_GRAYSCALE); if (src.empty() == true) { // 画像データが読み込めなかったときは終了する break; } // モーメント特徴を計算する m = cv::moments(src); // 重心を計算する cv::Point2f gp; gp = cv::Point2f(m.m10 / m.m00, m.m01 / m.m00); cout << "重心座標 (" << gp.x << ", " << gp.y << ")" << endl; // 主軸の角度を計算する double angle; angle = atan2(2 * m.mu11, m.mu20 - m.mu02) / 2; cout << "主軸の角度 " << angle * 180 / CV_PI << " 度" << endl; // 画像に重心点と主軸を描く cv::cvtColor(src, dst, cv::COLOR_GRAY2BGR); cv::Point2f vp = cv::Point2f(src.cols * cos(angle), src.cols * sin(angle)); cv::line(dst, gp + vp, gp - vp, cv::Scalar(100, 50, 255), 2, cv::LINE_AA); // 主軸 cv::circle(dst, gp, 7, cv::Scalar(80, 50, 255), -1, cv::LINE_AA); // 重心 cv::imshow("重心と主軸", dst); cv::waitKey(); } return 0; } |