実験及び演習 第4回目 特定の色検出

 

今回は,画像中の特別な色を検出するプログラムを作成する.

 

プロジェクトを新規で作るのはなかなか手間なので、前回動いたプログラムの「ソリューションフォルダ」をそのままコピーして、使いましょう。フォルダの名前を、prog4としておきましょう。

 

表色系のお話

入力した画像は, RGBの「光の3原色」で取り込まれる(ただし,OpenCVでの格納順はBGR).RGBの各色は,0255の値を持つ.

たとえば

http://mementoo.info/archives/594

 

RGBは原色ごとの分解なのに,特定の色を抽出するには不便.どういうことか?

答え:同じ色と言っても,明るさや鮮やかさなどにばらつきがある.それらは,RGBをすべて同時に変更する必要があり,設定が難しい.

 

そこで,色空間の中の軸を変更して,

l  色相(Hue) - 色の種類(赤、青、黄色のような)。0360の範囲。OpenCVでは、その値を2で割って整数化した、0179の範囲。

l  彩度(Saturation) - 色の鮮やかさ。0100%の範囲。

l  明度(Value) - 色の明るさ。0100%の範囲。

HSVRGB色空間の非線形変換であり、色の変換に用いられることもある。(ここを参照)。

 

いま、R,G,Bの値を、0から1に正規化されて得られているとしよう(もし0255の値であれば、255で割って、01の小数にしておく)。

R,G,Bの三つの値のうち、最大のものをMAX、最小のものをMINとすると、この式は次のように書ける。(見えないときは、上記のサイトを参照)。

 

 

上記において、Hの範囲は0360, S01, V01であるが、OpenCVでは8ビットの符号無し整数で表現するために

H: 0179(上記を2で割って切り捨てた値)

S: 0255(上記を255倍した値)

V: 0255(上記を255倍した値)

で表現している。

 

色空間を示す図

http://msdn.microsoft.com/ja-jp/library/aa511283.aspx

HSVの値を入れてみよう.

http://www.pluto.dti.ne.jp/~kamehr/colors/rgbcmy2.html

 

 

 

アルゴリズム概略(緑色の部分を抽出)

プログラム

#include <iostream>

#include <opencv2/core/core.hpp>

#include <opencv2/opencv.hpp>

 

#ifdef _DEBUG

#pragma comment(lib, "opencv_core249d.lib")

#pragma comment(lib, "opencv_imgproc249d.lib")

#pragma comment(lib, "opencv_highgui249d.lib")

#else

#pragma comment(lib, "opencv_core249.lib")

#pragma comment(lib, "opencv_imgproc249.lib")

#pragma comment(lib, "opencv_highgui249.lib")

#endif

 

#define  LOW_HUE 40              //hueの下限

#define  UP_HUE          80              //hueの上限

#define  LOW_SATURATION   60              //saturation(彩度)の下限

#define  LOW_VALUE       80              //value(明度)の下限

 

 

int main()

{

  string window_capture = "capture"; // キャプチャした画像用

  cv::namedWindow(window_capture, CV_WINDOW_AUTOSIZE);

  string window_output = "output"; // 結果表示用

  cv::namedWindow(window_output, CV_WINDOW_AUTOSIZE);

//カメラのオープン

  cv::VideoCapture cap;

  cap.open(-1);

  //エラー処理

  if (!cap.isOpened()) {

    std::cout << "cannot find a camera." << std::endl;

        cv::waitKey(5000);

        return -1;

  }

//画像が来るまで待つ

cv::Mat frame;

do {

    cap >> frame;

} while (frame.empty())+

//

    cv::Mat hsv, hue, hue1, hue2, saturation, value, hue_saturation, image_black_white;         //変数確保

  while (1) {

    cap >> frame;                                //画像取り込み

    cv::cvtColor(frame, hsv, CV_BGR2HSV);          // RGBBGR)画像をHSV画像に変換する(frame hsv

//赤い領域を取得

        // hsvをチャンネル毎にsinglechannelsというMat型の配列に分解して格納する。その結果、singlechannels[0]Hue, singlechannels[1]Saturation, singlechannels[2]Valueの情報を持つ。

        std::vector<cv::Mat> singlechannels;//Matクラスのベクトルとしてsinglechannelsを定義

        cv::split(hsv, singlechannels);//hsvsinglechannelsに分解([0]:h, [1]:s,[2]:v)

        //それぞれのチャンネルことに閾値を設定して二値化

        cv::threshold(singlechannels[0], hue1, LOW_HUE, 255, CV_THRESH_BINARY);                       // singlechannels[0]LOW_HUEを閾値処理して、LOW_HUE以上の部分が255,それ以下の部分が0になるように、hue1に格納する。

        cv::threshold(singlechannels[0], hue2, UP_HUE, 255, CV_THRESH_BINARY_INV);             // singlechannels[0]UP_HUEを閾値処理して、UP_HUE以上の部分が0,それ以下の部分が255になるように、hue2に格納する。

        cv::threshold(singlechannels[1], saturation, LOW_SATURATION, 255, CV_THRESH_BINARY);    //彩度LOW_SATURATION以上

        cv::threshold(singlechannels[2], value, LOW_VALUE, 255, CV_THRESH_BINARY);             //明度LOW_VALUE以上

        //条件を満たした領域をoutに設定

        cv::bitwise_and(hue1, hue2, hue);                                                  // hue1hue2bitごとのandをとる→hue

        cv::bitwise_and(hue, saturation, hue_saturation);                                    // huesaturationbitごとのandをとる→hue_saturation

 

        cv::bitwise_and(hue_saturation, value, image_black_white);                            // hue_saturationvaluebitごとのandをとる→image_black_white

 

 

    // 画像を表示する

    cv::imshow(window_capture, frame);

    cv::imshow(window_output, image_black_white);

    int key = cv::waitKey(5);

    if (key == 'q') {

      break;

    }

  }

  return 0;

}

 

流れを図で解説

以上