実験及び演習 第4回目 特定の色検出
今回は,画像中の特別な色を検出するプログラムを作成する.
では,まず空のプロジェクトを作成しよう.
以下に,手順を整理しておく.
1.Visual C++ Express Editionを立ち上げる
2.プロジェクトの作成
3.ファイル→新規作成→プロジェクト→Win32コンソールアプリケーション→プロジェクト名入力→次へ→コンソールアプリケーションにチェックが入っているのを確認,空のプロジェクトにチェックを入れる
ソリューションエクスプローラでソースファイル右クリック→追加→新しい項目→ファイル名入力(***.cpp)
3.プロジェクトの静的ライブラリの設定
このファイルの中身をプログラムの冒頭につければOK
表色系のお話
入力した画像は, RGBの「光の3原色」で取り込まれる(ただし,OpenCVでの格納順はBGR).RGBの各色は,0〜255の値を持つ.
たとえば
http://www42.tok2.com/home/abcdefz/tools/color_code.html
では,RGB(0〜255)に従って色が表示される.
RGBは原色ごとの分解なのに,特定の色を抽出するには不便.どういうことか?
答え:同じ色と言っても,明るさや鮮やかさなどにばらつきがある.それらは,RGBをすべて同時に変更する必要があり,設定が難しい.
そこで,色空間の中の軸を変更して,
l 色相(Hue) - 色の種類(赤、青、黄色のような)。0〜360の範囲。OpenCVでは、その値を2で割って整数化した、0〜179の範囲。
l 彩度(Saturation) - 色の鮮やかさ。0〜100%の範囲。
l 明度(Value) - 色の明るさ。0〜100%の範囲。
HSVはRGB色空間の非線形変換であり、色の変換に用いられることもある。(ここを参照)。
いま、R,G,Bの値を、0から1に正規化されて得られているとしよう(もし0〜255の値であれば、255で割って、0〜1の小数にしておく)。
R,G,Bの三つの値のうち、最大のものをMAX、最小のものをMINとすると、この式は次のように書ける。(見えないときは、上記のサイトを参照)。
上記において、Hの範囲は0〜360, Sは0〜1, Vも0〜1であるが、OpenCVでは8ビットの符号無し整数で表現するために
H: 0〜179(上記を2で割って切り捨てた値)
S: 0〜255(上記を255倍した値)
V: 0〜255(上記を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/opencv.hpp>
#include <string>
#ifdef _DEBUG
#pragma comment(lib, "opencv_core230d.lib")
#pragma comment(lib, "opencv_imgproc230d.lib")
#pragma comment(lib, "opencv_highgui230d.lib")
#else
#pragma comment(lib, "opencv_core230.lib")
#pragma comment(lib, "opencv_imgproc230.lib")
#pragma comment(lib, "opencv_highgui230.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(0);
//エラー処理
if (!cap.isOpened()) {
std::cout << "cannot find a
camera." << std::endl;
cv::waitKey(5000);
return -1;
}
cv::Mat hsv, frame, hue, hue1, hue2, saturation, value, hue_saturation,
image_black_white; //変数確保
while (1) {
cap >> frame; //画像取り込み
cv::cvtColor(frame, hsv, CV_BGR2HSV); // RGB(BGR)画像を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);//hsvをsinglechannelsに分解([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); // hue1とhue2のbitごとのandをとる→hue
cv::bitwise_and(hue,
saturation, hue_saturation); // hueとsaturationのbitごとのandをとる→hue_saturation
cv::bitwise_and(hue_saturation,
value, image_black_white); // hue_saturationとvalueのbitごとの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;
}
流れを図で解説。
以上