画像処理ソリューション
これを見れば画像処理の入門から基礎~応用まで全てがわかるのを目指して!
   
翻訳(Translate)

プロフィール

Akira

ニックネーム:Akira
東京都の町田事業所に勤務
画像処理ソフトの開発を行っています。リンクフリーです!
詳細プロフィールは こちら
お問い合わせは、こちら↓

【補助HP】
画像処理ソリューションWeb版 【Newブログ】
イメージングソリューション

スポンサーリンク


カテゴリ

最近のコメント

カレンダー

09 | 2017/10 | 11
S M T W T F S
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -

趣味のブログ

iPhone萬歳!
iPhoneの情報いろいろ。
ブログ学習帳
ブログ、SEO、アフィリエイト情報など(まだまだこれから)
俺流クラフト日記
ハンドメイド作品の記録(現在、放置中)

スポンサーリンク 最近の記事
(09/18)  計測測定展に光切断のデモを出展しました
(08/17)  ディジタル画像技術事典200に記事が載りました
(06/09)  光切断を画像センシング展で公開
(05/14)  中国(上海)へ行って来ました
(04/12)  韓国へ行って来ました
(03/10)  私の求める新人像
(01/18)  エレクトロテストジャパンにカラー光切断法のデモを出展しました。
(12/23)  ユニークアクセス200万達成!
(12/10)  【カラー光切断法】YouTube動画まとめ
(11/04)  国際画像機器展2014にカラー光切断法を出展します。
(10/05)  第25回コンピュータビジョン勉強会@関東に参加してきました。
(09/08)  フーリエ変換の記事を追加しました。
(08/09)  【画像処理】ランキング低下中
(07/06)  記事の更新が停滞中...
(06/08)  画像センシング展2014でカラー光切断法のデモを行います。
(05/17)  カラー光切断法の動画を公開しました。
(04/30)  ソニーα NEX-5Rで星空撮影
(04/10)  カラー光切断法の取込結果を追加しました
(03/08)  Korea Vision Show 2014へ行ってきました
(02/05)  フーリエ変換シリーズを始めます。
(01/06)  2014年、あけましておめでとうございます。
(12/04)  カラー光切断法を公開(国際画像機器展2013にて)
(11/13)  国際画像機器展2013に出展します
(10/14)  「画像処理のためのC#」はじめます。
(09/16)  【C#,VB.NET】高速描画コントロールをバージョンアップしました。
(09/04)  拡大鏡に輝度値表示、ルーラー機能を追加した個人ツールを公開
(08/05)  7月の拍手Top5
(07/06)  2013年6月人気記事Top5
(05/12)  SONY α NEX-5Rレビュー
(04/24)  SONY α NEX-5RY購入

【OpenCV】色領域の抽出

メインページOpenCV

OpenCVで肌色領域の抽出や特定色の抽出というテーマは良くある話だと思いますが、サンプルプログラムを探すと、

cvCvtColor関数でRGBからHSVに変換
   ↓
cvSplit関数でHSV画像を各チャンネルに分解
   ↓
cvThreshold関数で各チャンネルごとに二値化
   ↓
cvAnd関数で各チャンネルの二値化画像のAndをとり、マスク画像を作成
   ↓
cvAnd関数でHSV画像を各チャンネルの画像をAndをとる
   ↓
cvMerge関数で各チャンネルの画像をRGB画像へ戻す


多少、違うにしても、基本的にはこの様な流れの処理が比較的多いかと思います。

ここで、気になる点。
  • 赤の領域を抽出する場合、例えば色相が0°~10° および 350°~360°の範囲を抽出したい場合などに対応できない。
  • RGB→HSV→RGBへ変換すると、変換の途中で色の階調が少し失われてしまう。
ということで、少し工夫したつもりのソースがこちら↓

//--------------------------------------------------------------- 
//【関数名 】:cv_ColorExtraction 
//【処理概要】:色抽出 
//【引数  】:src_img        = 入力画像(8bit3ch) 
//      :dst_img        = 出力画像(8bit3ch) 
//      :code        = 色空間の指定(CV_BGR2HSV,CV_BGR2Labなど)
//      :ch1_lower    = ch1のしきい値(小)
//      :ch1_upper    = ch1のしきい値(大)
//      :ch2_lower    = ch2のしきい値(小)
//      :ch2_upper    = ch2のしきい値(大)
//      :ch3_lower    = ch3のしきい値(小)
//      :ch3_upper    = ch3のしきい値(大)
//【戻り値 】:なし 
//【備考  】:lower <= upperの場合、lower以上upper以下の範囲を抽出、
//      :lower >  upperの場合、upper以下lower以上の範囲を抽出します。
//---------------------------------------------------------------

void cv_ColorExtraction(IplImage* src_img, IplImage* dst_img,
                            int code,
                            int ch1_lower, int ch1_upper,
                            int ch2_lower, int ch2_upper,
                            int ch3_lower, int ch3_upper
                        ){

    int i, k;   

    IplImage *Color_img;
    IplImage *ch1_img, *ch2_img, *ch3_img;
    IplImage *Mask_img;

    int lower[3];
    int upper[3];
    int val[3];

    CvMat *lut;   

    //codeに基づいたカラー変換
    Color_img = cvCreateImage(cvGetSize(src_img), src_img->depth, src_img->nChannels);
    cvCvtColor(src_img, Color_img, code);
       
    //3ChのLUT作成
    lut    = cvCreateMat(256, 1, CV_8UC3);

    lower[0] = ch1_lower;
    lower[1] = ch2_lower;
    lower[2] = ch3_lower;

    upper[0] = ch1_upper;
    upper[1] = ch2_upper;
    upper[2] = ch3_upper;

    for (i = 0; i < 256; i++){
        for (k = 0; k < 3; k++){
            if (lower[k] <= upper[k]){
                if ((lower[k] <= i) && (i <= upper[k])){
                    val[k] = 255;
                }else{
                    val[k] = 0;
                }
            }else{
                if ((i <= upper[k]) || (lower[k] <= i)){
                    val[k] = 255;
                }else{
                    val[k] = 0;
                }
            }
        }
        //LUTの設定
        cvSet1D(lut, i, cvScalar(val[0], val[1], val[2]));
    }

    //3ChごとのLUT変換(各チャンネルごとに2値化処理)
    cvLUT(Color_img, Color_img, lut);
    cvReleaseMat(&lut);

    //各チャンネルごとのIplImageを確保する
    ch1_img = cvCreateImage(cvGetSize(Color_img), Color_img->depth, 1);
    ch2_img = cvCreateImage(cvGetSize(Color_img), Color_img->depth, 1);
    ch3_img = cvCreateImage(cvGetSize(Color_img), Color_img->depth, 1);

    //チャンネルごとに二値化された画像をそれぞれのチャンネルに分解する
    cvSplit(Color_img, ch1_img, ch2_img, ch3_img, NULL);

    //3Ch全てのANDを取り、マスク画像を作成する。
    Mask_img = cvCreateImage(cvGetSize(Color_img), Color_img->depth, 1);
    cvAnd(ch1_img, ch2_img, Mask_img);
    cvAnd(Mask_img, ch3_img, Mask_img);

    //入力画像(src_img)のマスク領域を出力画像(dst_img)へコピーする
    cvZero(dst_img);
    cvCopy(src_img, dst_img, Mask_img);

    //解放
    cvReleaseImage(&Color_img);
    cvReleaseImage(&ch1_img);
    cvReleaseImage(&ch2_img);
    cvReleaseImage(&ch3_img);
    cvReleaseImage(&Mask_img);

}

私なりのポイントは
  • cvLut関数を用いる事で、各3chごとに二値化するときに融通がきく。(cvThresholdを駆使しても可能ですが。)
  • cvCopy関数のマスクを使う事で1chのマスク画像で処理ができ、色の階調が失われない。
    cvAnd関数を使って3chのマスク画像とANDをとるよりも、メモリが少し節約できる。)

処理例

【入力画像(src_img)】
【OpenCV】色領域の抽出

緑領域をHSVを用いて抽出する場合、緑の色相は120°前後で、処理後画像に8bit3ch画像を用いると色相Hの値は0~360が0~180に丸め込まれるので、緑の値はHは60となり、Hを50~70の範囲で抽出する場合、

cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 50, 70, 80, 255, 0, 255);

【処理画像(dst_img)】
【OpenCV】色領域の抽出

となります。

同様に赤の付近を抽出する場合は色相が0°付近なので、丸め込み後の値は0~10および170~180を抽出したい。
ということで、このように↓呼び出します。

cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 170, 10, 80, 255, 0, 255);

【処理画像(dst_img)】
【OpenCV】色領域の抽出

となります。
lowerupperの値をにしているのがポイント

肌色領域を抽出したい!という場合も多いかと思いますので、入力画像を

【OpenCV】肌色領域の抽出

とし、

cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 0, 10, 80, 255, 0, 255);

で処理した場合、このよう↓になりました。

【OpenCV】肌色領域の抽出

ちなみに、彩度の値を80~255としているのは、背景の白っぽい領域を除外するために、このようにしています。

色相などの値は、使うカメラや照明などにより値が多少異なります。
今回作成したcv_ColorExtraction関数の引数の値をいじくりながら、いろいろ試してみて下さい。
また、色空間も今回のHSVを使う以外にも、cv_ColorExtraction関数の3番目の引数に
  CV_BGR2HLSを指定するとHLS
  CV_BGR2Labを指定するとCIE Lab
の色空間に基づいて色領域を抽出出来るはず(未確認)なので、お試し下さい。

関連記事

【OpenCV】色変換(cvCvtColor)の組合せ
【OpenCV】マスク処理
色相、彩度、明度の計算方法
  

Loading...
スポンサーリンク

この記事に対するコメント
Re: ご質問あります。
コメント頂きありがとうございます。
この関数(cv_ColorExtraction)はHSV変換に限って赤色抽出をしている訳ではなくて、cv_ColorExtraction関数の3番目の引数(code)にOpenCVのcvCvtColor関数の3番目の引数(code)と同じ値を渡す事で、様々な色変換に対応しています。
と、言いながら、HSV変換の引数(CV_BGR2HSV)を渡すのが比較的分かりやすいかと思います。
(参考)
http://opencv.jp/opencv-2svn/c/miscellaneous_image_transformations.html
【2014/07/22 00:02】 URL | Akira #- [ 編集]

ご質問あります。
初歩的な質問ですみません。
今、画像処理による赤色抽出を行っており、こちらのサイトにお世話になりました。
質問なのですが、もしよろしければこの関数の処理のアルゴリズムをご教授いただけないでしょうか?

また、この関数はHSV変換によって赤色検出をしているという解釈でよろしいでしょうか?
【2014/07/20 10:12】 URL | プログラム初心者 #- [ 編集]

Re: タイトルなし
なかなか簡単には説明できないのですが、
 黒でない部分の面積 = 値がゼロ(0)でない画素数
を求めるには cvCountNonZero
http://opencv.jp/opencv-1.1.0/document/opencvref_cxcore_statistics.html#decl_cvCountNonZero
という関数を使います。
ただし、この関数に渡せる画像データ(IplImage)はシングルチャンネル(モノクロ8bit)かCOI(Color Of Interest)がセットされた・・・とあるので、色を抽出した画像(カラー(8bit3ch))をモノクロ画像(8bit1ch)に変換する必要があります。
それには cvCvtColor
http://opencv.jp/opencv-1.1.0/document/opencvref_cv_filters.html#decl_cvCvtColor
を使って、カラーからモノクロへ変換します。
こんな感じで↓
cvCvtColor(src_img, gray_img, CV_BGR2GRAY);

このモノクロ画像(gray_img)を cvCountNonZero へ渡せば、面積が計算できると思います。

と、ここに書けるのは、この程度ですが、うまく他の情報も検索してみて、頑張って下さい。
【2013/10/09 22:07】 URL | Akira #- [ 編集]


色領域の抽出に関しての質問ではないのですが、処理後の黒ではない部分の面積を求めたい場合はどのようにすればいいのでしょうか?
画像処理を始めたばかりなので初歩的な質問で申し訳ございません。
【2013/10/09 21:35】 URL | #pQfpZpjU [ 編集]

Re: ご回答有難うございました。
まさに、その通りです!
【2013/07/08 21:05】 URL | Akira #- [ 編集]

ご回答有難うございました。
参考ページも非常に参考になりました。ご教示頂きありがとうございました。
白色近辺のみを抽出するとなると
cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 0, 255, 0, 20, 230, 255);
というように、色相を全範囲、彩度は低め、明度が高め、に設定すればよいのですね!
【2013/07/08 12:09】 URL | どすけん #- [ 編集]

Re: ご質問
どすけんさん、コメント頂きありがとうございます。
まず、
cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 26, 84, 0, 255, 0, 255);
と指定した場合は、Hの値が26~84、Sの値が0~255、Vの値が0~255の部分を抽出します。
そこで、
RGBが(255, 255, 255)の場合は、HSVが(0, 0, 255)
RGBが(255, 255, 254)の場合は、HSVが(30, 1, 255)
ついでに
RGBが(255, 254, 255)の場合は、HSVが(150, 1, 255)
となります。
つまりRGBの値が少しだけ変化したからと言って、色相Hの値も少ししか変化しない訳ではなく、特に彩度の値が小さい場合(R,G,Bの値に差が少ない場合)は色相Hは大きく振れるのでご注意下さい。
参考までに色相、明度、彩度の記事もあるので、そちらを参照下さい。
http://imagingsolution.net/imaging/hue-saturation-brightness-calc/
以上、よろしくお願い致します。
【2013/07/05 23:59】 URL | Akira #- [ 編集]

ご質問
ご質問なのですが、RGBが(255, 255, 255)の真っ白な画像に対して
cv_ColorExtraction(src_img, dst_img, CV_BGR2HSV, 26, 84, 0, 255, 0, 255);
とすると白の部分が抽出されず、結果が真っ黒になってしまいます。
RGBが(255, 255, 254)のように微妙にずらすと抽出されるのですが、何故このようになるのでしょうか?
【2013/07/05 21:59】 URL | どすけん #- [ 編集]


ご回答ありがとうございます。
【2012/11/11 02:15】 URL | C++ #EICwjQSI [ 編集]

Re: タイトルなし
コメント、ありがとうございます。
ここに書いてあるソースは、OpenCVで扱う画像データのIplImageという型のデータを渡す"関数"なので、このソースだけでは実行できません。
そのため、最低限、OpenCVで画像データを表示するプログラムが別途必要となるのですが、そのプログラムに、この関数を組み込んで頂くと、色の抽出が出来るようになりますので、よろしくお願いします。
【2012/11/11 01:58】 URL | Akira #- [ 編集]


すみません、
ここに書かれているソースのみで実行できるのですか?
【2012/11/11 01:26】 URL | C++ #EICwjQSI [ 編集]


この記事に対するコメントの投稿














管理者にだけ表示を許可する


この記事に対するトラックバック
トラックバックURL
→http://imagingsolution.blog107.fc2.com/tb.php/248-264ef4fe
この記事にトラックバックする(FC2ブログユーザー)

現在の閲覧者数: / 合計