モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

目次目次を開く/閉じる

輪郭に関連した画像処理 (OpenCV3 C++)

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2020/02/02
最終更新最終更新
2024/07/22
記事区分記事区分
一般公開

目次

    パフォーマンス重視のRust開発者。WebAssemblyにも挑戦中。

    cv::Canny などで検出したエッジをもとに cv::findContours で輪郭を計算できます。輪郭に関連した処理の例を記載します。

    輪郭の描画

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
        cv::Mat src = cv::imread("aaa.png", cv::IMREAD_COLOR);
    
        // グレースケールに変換
        cv::Mat gray;
        cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
    
        // エッジ検出はノイズの影響を受けやすいため平滑化しておきます。
        cv::Mat blur;
        cv::blur(gray, blur, cv::Size(3,3));
    
        // エッジ検出のアルゴリズムとして cv::Canny を利用します。
        cv::Mat canny;
        int thresh = 100;
        cv::Canny(blur, canny, thresh, thresh * 2);
    
        // cv::findContours は第一引数を破壊的に利用するため imshow 用に別変数を用意しておきます。
        cv::Mat canny2 = canny.clone();
    
        // cv::Point の配列として、輪郭を計算します。
        std::vector<std::vector<cv::Point> > contours;
        std::vector<cv::Vec4i> hierarchy;
        cv::findContours(canny, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
    
        std::cout << contours.size() << std::endl; //=> 36
        std::cout << contours[contours.size() - 1][0] << std::endl; //=> [154, 10]
    
        // 輪郭を可視化してみます。分かりやすさのため、乱数を利用して色付けします。
        cv::Mat drawing = cv::Mat::zeros(canny.size(), CV_8UC3);
        cv::RNG rng(12345);
    
        for( size_t i = 0; i< contours.size(); i++ ) {
            cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
            cv::drawContours(drawing, contours, (int)i, color);
        }
    
        cv::imshow("src", src);
        cv::imshow("gray", gray);
        cv::imshow("blur", blur);
        cv::imshow("canny", canny2);
        cv::imshow("drawing", drawing);
        cv::waitKey(0);
        return 0;
    }
    

    バウンディングボックス、最小内包円

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
        cv::Mat src = cv::imread("bbb.png", cv::IMREAD_COLOR);
    
        // 輪郭を計算します。
        cv::Mat gray;
        cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
        cv::blur(gray, gray, cv::Size(3,3));
        cv::Mat canny;
        int thresh = 150;
        cv::Canny(gray, canny, thresh, thresh * 2);
        std::vector<std::vector<cv::Point> > contours;
        cv::findContours(canny, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
    
        // 輪郭を描画します。
        cv::RNG rng(12345);
        cv::Mat dstContours = cv::Mat::zeros(canny.size(), CV_8UC3);
        for(size_t i = 0; i < contours.size(); i++) {
            cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
            cv::drawContours(dstContours, contours, (int)i, color);
        }
    
        // 少ない点で輪郭を近似します。
        std::vector<std::vector<cv::Point> > contours_poly(contours.size());
        for(size_t i = 0; i < contours.size(); i++) {
            std::vector<cv::Point> poly;
            cv::approxPolyDP(contours[i], poly, 3, true);
            contours_poly[i] = poly;
        }
    
        cv::Mat dstContoursPoly = cv::Mat::zeros(canny.size(), CV_8UC3);
        for(size_t i = 0; i < contours.size(); i++) {
            cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
            cv::drawContours(dstContoursPoly, contours_poly, (int)i, color);
        }
    
        std::cout << contours[0].size() << std::endl; //=> 9
        std::cout << contours_poly[0].size() << std::endl;//=> 2
    
        // x-y 軸に沿ったバウンディングボックス
        std::vector<cv::Rect> boundRect(contours.size());
    
        // 最小内包円
        std::vector<cv::Point2f>centers(contours.size());
        std::vector<float>radius(contours.size());
    
        for(size_t i = 0; i < contours.size(); i++) {
            boundRect[i] = cv::boundingRect(contours_poly[i]);
            cv::minEnclosingCircle(contours_poly[i], centers[i], radius[i]);
        }
    
        cv::Mat dst = src.clone();
        for( size_t i = 0; i< contours.size(); i++ ) {
            cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256));
            cv::rectangle(dst, boundRect[i].tl(), boundRect[i].br(), color, 2);
            cv::circle(dst, centers[i], (int)radius[i], color, 2);
        }
    
        cv::imshow("src", src);
        cv::imshow("dstContours", dstContours);
        cv::imshow("dstContoursPoly", dstContoursPoly);
        cv::imshow("dst", dst);
        cv::waitKey(0);
        return 0;
    }
    

    輪郭の凸包

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
        cv::Mat src = cv::imread("ddd.png", cv::IMREAD_COLOR);
    
        // 輪郭を計算します。
        cv::Mat gray;
        cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
        cv::blur(gray, gray, cv::Size(3,3));
        cv::Mat canny, canny2;
        cv::Canny(gray, canny, 5, 50);
        canny2 = canny.clone();
        std::vector<std::vector<cv::Point> > contours;
        cv::findContours(canny, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
    
        // 凸包を計算します。
        std::vector<std::vector<cv::Point> > hull(contours.size());
        for(size_t i = 0; i < contours.size(); i++) {
            cv::convexHull(contours[i], hull[i]);
        }
    
        // 凸包であるかどうかの調査
        std::cout << cv::isContourConvex(contours[contours.size() - 1]) << std::endl; //=> 0
        std::cout << cv::isContourConvex(hull[contours.size() - 1]) << std::endl; //=> 1
    
        // 分かりやすさのため描画します。
        cv::Mat dst = cv::Mat::zeros(canny.size(), CV_8UC3);
        cv::RNG rng(12345);
        for(size_t i = 0; i< contours.size(); i++) {
            cv::Scalar color = cv::Scalar(rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
            // cv::drawContours(dst, contours, (int)i, color);
            cv::drawContours(dst, hull, (int)i, color, 3);
        }
    
        cv::imshow("src", src);
        cv::imshow("canny", canny2);
        cv::imshow("dst", dst);
        cv::waitKey(0);
        return 0;
    }
    

    輪郭の内部面積、長さ、モーメント

    輪郭の内部面積と長さは、それぞれ cv::contourAreacv::arcLength で計算できます。面積についてはモーメントを計算することによっても得られます。モーメントは以下のように重心を計算したり傾きを補正する際に利用できます。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
        cv::Mat src = cv::imread("ddd.png", cv::IMREAD_COLOR);
    
        // 輪郭を計算します。
        cv::Mat gray;
        cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
        cv::blur(gray, gray, cv::Size(3,3));
        cv::Mat canny, canny2;
        cv::Canny(gray, canny, 5, 50);
        canny2 = canny.clone();
        std::vector<std::vector<cv::Point> > contours;
        cv::findContours( canny, contours, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE );
    
        // モーメントの計算
        std::vector<cv::Moments> mu(contours.size());
        for( size_t i = 0; i < contours.size(); i++ ) {
            mu[i] = cv::moments(contours[i]);
        }
    
        // 重心の計算
        std::vector<cv::Point2f> mc(contours.size());
        for( size_t i = 0; i < contours.size(); i++ ) {
            //add 1e-5 to avoid division by zero
            mc[i] = cv::Point2f(mu[i].m10 / (mu[i].m00 + 1e-5),
                                mu[i].m01 / (mu[i].m00 + 1e-5));
        }
    
        // 輪郭の長さ、面積の計算
        for( size_t i = 0; i < contours.size(); i++ ) {
            std::cout << "Area (M_00): " << mu[i].m00 << std::endl;
            std::cout << "cv::contourArea: " << cv::contourArea(contours[i]) << std::endl;
            std::cout << "Length: " << cv::arcLength(contours[i], true) << std::endl;
        }
    
        // 輪郭、重心の描画
        cv::Mat dst = cv::Mat::zeros(canny.size(), CV_8UC3);
        cv::RNG rng(12345);
        for( size_t i = 0; i< contours.size(); i++ ) {
            cv::Scalar color = cv::Scalar( rng.uniform(0, 256), rng.uniform(0, 256), rng.uniform(0, 256));
            cv::drawContours(dst, contours, (int)i, color, 2);
            cv::circle(dst, mc[i], 4, color);
        }
    
        cv::imshow("src", src);
        cv::imshow("canny", canny2);
        cv::imshow("dst", dst);
        cv::waitKey(0);
        return 0;
    }
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    パフォーマンス重視のRust開発者。WebAssemblyにも挑戦中。

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事