色による探索 (cv::threshold)

OpenCVのモバイルにおける典型的な目的の一つに、カメラを通してキャプチャした映像、画像に対して「顔認識」「人物認識(肌色認識)」「オブジェクト認識」を行うことがあるが、これらの探索のパラメタには前回紹介した特徴で認識する他に色を特徴のパラメタとして認識する方法がある。

手順

1. 認識する色の情報を決定する
2. 決定した色の範囲を決めて、RGB色空間からHSV色空間に変換する
3. 対象の画像を1.で決定した色情報から、Hue(色相)を使って2値化する
4. 2値化した画像から輪郭を取り出す

早速OpenCVで上記の機能を実装してみよう。

cv::thresholdを使った2値化 (OpencvTest::binarizeHSB)
+ (cv::Mat)binarizeHSB:(cv::Mat)src hueMin:(double)hueMin hueMax:(double)hueMax saturation:(double)saturation brightness:(double)brightness
{
    cv::Mat hsb, hsb1, hsb2;
    cv::Mat huemask, chromamask, brightnessmask;
    cv::Mat result;

    //HSV色空間に変換
    cv::cvtColor(src, hsb, CV_RGB2HSV, 3);

    //HSVにチャネル分割
    cv::extractChannel(hsb, hsb1, 0);

    //Channel[0](色相)で制限をかける
    cv::threshold(hsb1, hsb2, hueMax, 255, CV_THRESH_TOZERO_INV ); 
    cv::threshold(hsb2, huemask, hueMin, 255, CV_THRESH_BINARY );

    //Channel[1](彩度)で制限をかける
    cv::extractChannel(hsb, hsb1, 1);
    cv::threshold(hsb1, chromamask, saturation, 255, CV_THRESH_BINARY );
    cv::bitwise_and(huemask, chromamask, result);

    //Channel[2](輝度)で制限をかける
    cv::extractChannel(hsb, hsb1, 2);
    cv::threshold(hsb1, brightnessmask, brightness, 255, CV_THRESH_BINARY );
    cv::bitwise_and(result, brightnessmask, result);

    return result;
}

内容としてよく見るコードなので解説は割愛する。ポイントはCV_THRESH_TOZERO_INVを使って閾値よりも上を最初に除外してしまうこと。
HSVによる2値化は基本的にHueの範囲で決まるので、SaturationとBrightnessは範囲を設けずに単に閾値として使用している。

実行結果


サンプル画像としてこのエントリの初回で用意したイメージを使い、iOSシミュレータ上で画面を作成、スライダーでHue(色相)の上限と加減の閾値を設定し、それを上の関数に渡すようにアプリケーションを組んだ。
Saturation(彩度)とBrightness(輝度)は今回は固定値 (70以上)に設定している。


Hueを53°位に設定した結果(画像のアスペクト比が崩れてるのはUIImageViewのプロパティ設定ミス)。黄色と黄緑色の矩形が認識されているが、青色は除外されている。また、0°で認識されるはずの赤の矩形は認識されていない。


Hueを最大115°に設定している。元のイメージの黄色と黄緑色、青色の矩形と円は全て認識することが出来ているが、やはりHue0°で認識されていなければならない赤色の矩形が認識されていない。※

特定の色相だけ使えないのでは意味が無い。 
調べて見るとthresholdで色空間を変換する際に階調情報が失われるらしく、掛からないようだ。

もう少し工夫しなくてはならないな。

※OpenCVのHueは本来の0°〜360°ではなく、0°〜180°で表されるが(1バイト長で済むから?)その両端が赤色である。