LoginSignup
38
41

More than 5 years have passed since last update.

OpenCVでHSV変換して色抽出

Last updated at Posted at 2014-07-22

HSVに変換して色抽出する話はそこら中に転がっているのですが,
なんだかんだハマってしまったのでメモ.

http://imagingsolution.blog107.fc2.com/blog-entry-248.html
この通りやればよかったんですが,C++っぽく書き直したかったから書き直した.

コード

colorExtraction.cpp
void colorExtraction(cv::Mat* src, cv::Mat* dst,
    int code,
    int ch1Lower, int ch1Upper,
    int ch2Lower, int ch2Upper,
    int ch3Lower, int ch3Upper
    )
{
    cv::Mat colorImage;
    int lower[3];
    int upper[3];

    cv::Mat lut = cv::Mat(256, 1, CV_8UC3);   

    cv::cvtColor(*src, colorImage, code);

    lower[0] = ch1Lower;
    lower[1] = ch2Lower;
    lower[2] = ch3Lower;

    upper[0] = ch1Upper;
    upper[1] = ch2Upper;
    upper[2] = ch3Upper;

    for (int i = 0; i < 256; i++){
        for (int k = 0; k < 3; k++){
            if (lower[k] <= upper[k]){
                if ((lower[k] <= i) && (i <= upper[k])){
                    lut.data[i*lut.step+k] = 255;
                }else{
                    lut.data[i*lut.step+k] = 0;
                }
            }else{
                if ((i <= upper[k]) || (lower[k] <= i)){
                    lut.data[i*lut.step+k] = 255;
                }else{
                    lut.data[i*lut.step+k] = 0;
                }
            }
        }
    }

    //LUTを使用して二値化
    cv::LUT(colorImage, lut, colorImage);

    //Channel毎に分解
    std::vector<cv::Mat> planes;
    cv::split(colorImage, planes);

    //マスクを作成
    cv::Mat maskImage;
    cv::bitwise_and(planes[0], planes[1], maskImage);
    cv::bitwise_and(maskImage, planes[2], maskImage);

    //出力
    cv::Mat maskedImage;
    src->copyTo(maskedImage, maskImage);
    *dst = maskedImage;
}

ハマりどころ

LUT

LUT(ルックアップテーブル)の作成がうまくいってなかった.
3chのLUTを生成するとき,3次元の配列を突っ込むのではなくて3倍?でかい配列にする必要がありました.

ダメな例

bad_lut.cpp
int lut[256][3];
for(int i = 0; i < 256; i++){
  for(int k = 0; k < 3; k++){
    lut[i][k] = ...
  }
}
cv::Mat lutMat = cv::Mat( 256, 1, CV_8UC3, lut );

//うまくいかない
cv::LUT(src, lutMat, dst);

画像のマスク

cv::MatのcopyTo(dst, mask)は,どうやらdstに非ゼロの値が入っているとマスクされないようで,
まずdstをゼロで埋める必要があるっぽいです.
今回は新しくcv::Matを用意してやりました.

実行例

適当なカラー画像を用意します.
色抽出するときはRGBで範囲を指定するよりもHSVの方が適しているので,
RGBをHSVに変換してから色を抽出します.

OpenCVでは,H,S,Vを以下の範囲で指定してやります.
H:色相 0~180
S:彩度 0~255
V:明度 0~255

main.cpp
cv::Mat origImage; //適当に画像を突っ込む
cv::Mat extractedImage;

//H:150-165, S:100-255, V:70-255
colorExtraction(&origImage, &extracted, CV_BGR2HSV, 150, 165, 100, 255, 70, 255);

color.png

マスク
mask.png

抽出画像
extract.png

背景が少し残ってしまいましたが概ね綺麗に抽出できてます.

参考

http://imagingsolution.blog107.fc2.com/blog-entry-248.html
http://docs.opencv.org/modules/core/doc/operations_on_arrays.html
http://www.me.sie.dendai.ac.jp/labWiki/wiki.cgi?page=%B3%AC%C4%B4%CA%D1%B4%B9

38
41
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
41