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

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

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

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

画像から特定の色の領域を抜き出す (OpenCV3 C++)

モーダルを閉じる

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

モーダルを閉じる

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

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

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

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

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

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

公開日公開日
2020/01/29
最終更新最終更新
2024/08/17
記事区分記事区分
一般公開

目次

    アカウント プロフィール画像 (サイドバー)

    AIやデータ解析に興味を持っています

    0
    ステッカーを贈るとは?

    OpenCV3 C++ を用いて画像から特定の色の領域を取り出す方法のうち、HSV 色空間における色相を指定する方法と、バックプロジェクション (逆投影法) を利用する方法の二つを記載します。

    HSV 色空間における色相を指定する方法

    色を表現する空間には RGB の他に HSV (Hue 色相、Saturation 彩度、Value 明度) があります。HSV 色空間は人間の感覚に近い指標を利用しているため色を選びやすいという特徴があり、特定の色領域を抜き出すことも容易になります。関連ページ

    例えば、以下の画像からオレンジ色の部分を抜き出したいとします。

    Wikipedia によると、オレンジ色は RGB 値で (243, 152, 0) です。これを HSV に変換すると以下のようになります。OpenCV では RGB ではなく BGR の順番で指定する必要があることに注意します。

    python3 -m IPython
    In [1]: import numpy as np
    In [2]: import cv2 as cv
    In [3]: orange = np.uint8([[[0, 152, 243]]])
    In [4]: cv.cvtColor(orange, cv.COLOR_BGR2HSV)
    Out[4]: array([[[ 19, 255, 243]]], dtype=uint8)
    

    オレンジ色の色相 Hue は、OpenCV においては約 19 であることが分かりました。色相についてプラスマイナス 10 程度のフィルタを作って画像を抜き出すと以下のようになります。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
    
        // BGR 色空間
        cv::Mat src = cv::imread("aaa.png", cv::IMREAD_COLOR);
    
        // HSV 色空間での表現に変換
        cv::Mat hsv;
        cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
    
        // inRange によって Hue が特定の範囲にある領域の mask を取得します。
        cv::Mat mask;
        cv::inRange(hsv, cv::Scalar(9, 100, 100), cv::Scalar(29, 255, 255), mask);
    
        // src と src の and 演算を、mask の領域 (0 以外の領域) についてのみ行うことで、画像を切り出します。
        double min, max;
        cv::minMaxLoc(mask, &min, &max);
        std::cout << min << std::endl; //=> 0
        std::cout << max << std::endl; //=> 255
    
        cv::Mat res;
        cv::bitwise_and(src, src, res, mask);
    
        cv::imshow("src", src);
        cv::imshow("mask", mask);
        cv::imshow("res", res);
        cv::waitKey(0);
        return 0;
    }
    

    色相の制限値を調整すると抜き出される領域が変化します。

    cv::inRange(hsv, cv::Scalar(9, 100, 100), cv::Scalar(12, 255, 255), mask);
    

    cv::inRange(hsv, cv::Scalar(13, 100, 100), cv::Scalar(14, 255, 255), mask);
    

    バックプロジェクションによる方法

    以下の画像について、特定の領域 ROI と似た色の領域を切り出すことを考えます。

    ROI は画像の部分行列として用意できます。

    ヒストグラムの計算について

    cv::calcHist で各種ヒストグラムを計算できます。以下は画像に含まれる BGR 画素値のヒストグラムを計算する例です。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
        cv::Mat src = cv::imread("aaa.png", cv::IMREAD_COLOR);
    
        // BGR チャンネルを配列として別々の Mat に分けます。
        std::vector<cv::Mat> bgr_planes;
        cv::split(src, bgr_planes);
    
        // BGR それぞれについて、画素値 [0, 255] のヒストグラムを計算します。
        cv::Mat b_hist, g_hist, r_hist;
        int histSize = 256;
        float range[] = { 0, 256 };
        const float* histRange = { range };
        bool uniform = true, accumulate = false;
        cv::calcHist( &bgr_planes[0], 1, 0, cv::Mat(), b_hist, 1, &histSize, &histRange, uniform, accumulate );
        cv::calcHist( &bgr_planes[1], 1, 0, cv::Mat(), g_hist, 1, &histSize, &histRange, uniform, accumulate );
        cv::calcHist( &bgr_planes[2], 1, 0, cv::Mat(), r_hist, 1, &histSize, &histRange, uniform, accumulate );
    
        // 以下はヒストグラムを可視化するための処理です。
    
        // 可視化するための画像 Mat を用意します。
        int hist_w = 512, hist_h = 400;
        int bin_w = cvRound( (double) hist_w/histSize );
        cv::Mat histImage( hist_h, hist_w, CV_8UC3, cv::Scalar(0, 0, 0) );
    
        std::cout << histImage.rows << std::endl; //=> 400
    
        double min, max;
        cv::minMaxLoc(b_hist, &min, &max);
        std::cout << max << std::endl; //=> 11725
    
        // ヒストグラムの各 bin について、値を [0, 400] に変換します。
        cv::normalize(b_hist, b_hist, 0, histImage.rows, cv::NORM_MINMAX);
        cv::normalize(g_hist, g_hist, 0, histImage.rows, cv::NORM_MINMAX);
        cv::normalize(r_hist, r_hist, 0, histImage.rows, cv::NORM_MINMAX);
    
        cv::minMaxLoc(b_hist, &min, &max);
        std::cout << max << std::endl; //=> 400
    
        // 折れ線グラフを描画します。
        for( int i = 1; i < histSize; i++ ) {
            cv::line(histImage, cv::Point( bin_w*(i-1), hist_h - cvRound(b_hist.at<float>(i-1)) ),
                      cv::Point( bin_w*(i), hist_h - cvRound(b_hist.at<float>(i)) ),
                      cv::Scalar(255, 0, 0), 2, 8, 0);
            cv::line(histImage, cv::Point( bin_w*(i-1), hist_h - cvRound(g_hist.at<float>(i-1)) ),
                      cv::Point( bin_w*(i), hist_h - cvRound(g_hist.at<float>(i)) ),
                      cv::Scalar(0, 255, 0), 2, 8, 0);
            cv::line(histImage, cv::Point( bin_w*(i-1), hist_h - cvRound(r_hist.at<float>(i-1)) ),
                      cv::Point( bin_w*(i), hist_h - cvRound(r_hist.at<float>(i)) ),
                      cv::Scalar(0, 0, 255), 2, 8, 0);
        }
        cv::imshow("Source image", src);
        cv::imshow("calcHist Demo", histImage);
        cv::waitKey(0);
        return 0;
    }
    

    バックプロジェクションを用いた画像の切り出し

    ROI 画像についてヒストグラムを計算します。ここでは簡単のため Hue 値についてのみ考えます。もとの画像における各ピクセルの Hue 値について、ヒストグラムのどの bin に属するかを調べます。属する bin のヒストグラムの値 [0, 255] をそのピクセルのグレースケール画素値として記録することでバックプロジェクション画像を生成します。

    バックプロジェクションは、各ピクセルが ROI 画像と同じ色相を持つ確率を表現しています。バックプロジェクションを mask としてもとの画像から ROI と似た色の領域を切り出すことができます。

    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    int main() {
    
        // BGR 色空間
        cv::Mat src = cv::imread("aaa.png", cv::IMREAD_COLOR);
    
        // ROI の指定
        cv::Mat roi = src.rowRange(100, 200).colRange(0, 50);
    
        // HSV 色空間
        cv::Mat hsvSrc, hsvRoi;
        cv::cvtColor(src, hsvSrc, cv::COLOR_BGR2HSV);
        cv::cvtColor(roi, hsvRoi, cv::COLOR_BGR2HSV);
    
        std::cout << hsvSrc.size() << std::endl; //=> [200 x 200]
        std::cout << hsvSrc.channels() << std::endl; //=> 3
        std::cout << hsvSrc.depth() << std::endl; //=> 0 (= CV_8U)
    
        // Hue を格納する行列を用意
        cv::Mat hueSrc;
        cv::Mat hueRoi;
        hueSrc.create(hsvSrc.size(), hsvSrc.depth());
        hueRoi.create(hsvRoi.size(), hsvRoi.depth());
    
        // ここでは簡単のため Hue 情報だけを用いたヒストグラムを考えます。
        // Saturation と Value を捨てます。
        int ch[] = {0, 0};
        cv::mixChannels(&hsvSrc, 1, &hueSrc, 1, ch, 1);
        cv::mixChannels(&hsvRoi, 1, &hueRoi, 1, ch, 1);
    
        // ROI について Hue 値のヒストグラムを作成します。
        cv::Mat histRoi;
        int histSize = 25;
        float range[] = {0, 180}; // OpenCV における Hue の範囲は [0, 180) です。
        const float* histRange = { range };
        cv::calcHist(&hueRoi, 1, 0, cv::Mat(), histRoi, 1, &histSize, &histRange, true, false);
    
        // ヒストグラムの各 bin について、値を [0, 255] に変換します。
        cv::normalize(histRoi, histRoi, 0, 255, cv::NORM_MINMAX);
    
        // src 画像の各ピクセルの Hue 値について、ヒストグラムのどの bin に属するかを調べます。
        // 属する bin のヒストグラムの値 [0, 255] をそのピクセルのグレースケール画素値として
        // backproj に記録します。
        cv::Mat backproj;
        cv::calcBackProject(&hueSrc, 1, 0, histRoi, backproj, &histRange);
    
        // 50 を閾値として黒 0 か白 255 に二値化します。
        cv::Mat mask;
        cv::threshold(backproj, mask, 50, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
    
        // src と src の and 演算を、mask の領域 (0 以外の領域) についてのみ行うことで、画像を切り出します。
        cv::Mat res;
        cv::bitwise_and(src, src, res, mask);
    
        cv::imshow("src", src);
        cv::imshow("roi", roi);
        cv::imshow("backproj", backproj);
        cv::imshow("mask", mask);
        cv::imshow("res", res);
        cv::waitKey(0);
        return 0;
    }
    

    ROI を変更すると切り出される領域が変化します。

    cv::Mat roi = src.rowRange(100, 150).colRange(100, 150);
    

    バックプロジェクションについて、ノイズが多い場合はモルフォロジー変換を利用すると良い場合があります。

    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    AIやデータ解析に興味を持っています

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

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

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

    Feedbacks

    Feedbacks コンセプト画像

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

      関連記事