Hatena::ブログ(Diary)

橋本詳解 RSSフィード

ここはメモ帳です

http://shokai.org
http://shokai.org/blog/

2009-02-02

[][][]赤色領域を検出(1)

前の肌色領域検出で、ようするにOpenCVproce55ingみたいに泥臭くピクセルを一つずつx,y座標で走査して比較するのではなく、画像(=バイト配列)に対してビット演算する専用の関数群(cvThreshold,cvSplit,cvAnd,cvSub,cvDivなど)があるからそれを使って、集合のように色を比較してやればいいのだなという事がわかった。

ちょっと頭を切り換えて、ピクセルを見るのではなくビット演算のノリで差や論理積を取っていって必要な領域を切り出せばいいらしい。


opencv.jp - OpenCV-1.0 リファレンス マニュアル(日本語訳)-関数群が参考になる。


で、まずは基本である「赤い場所」の抜き出しを自分で考えてやってみた

ただ、環境や使用するカメラ毎に微調整が必要で、しかも人肌によく誤爆してしまう。このアルゴリズムでは足りない

detect red


デバッグに便利なので、cvSetMouseCallbackマウスイベントが取れるので、冒頭のvoid onMouse()でイベントを受けてマウスカーソル座標とピクセルRGB要素も表示した。2次元の画素が1次元配列に入っているので、IplImage->imageDataOrigin[x+y*width]でアクセスする。そのままprintfすると符号付き8bitなのでunsigned charにcastするのを忘れずに。


マウスを動かしてその下の座標を色を出力している所

detect red


赤いピクセル(赤150以上、緑・青100以下の部分)を論理積で抽出する。

cvSplitでカメラ画像(3チャンネル)をRGBの3つに分けれるので、cvThreholdでRGB要素をそれぞれ個別に赤が150以上、緑が100以下、青が100以下の条件で2値化する。最後に3つの2値化データの論理積を取ると赤い部分だけが残る。


colortrack.cpp

#include <stdio.h>
#include <highgui.h>
#include <cv.h>


IplImage *img = NULL;
IplImage *imgR, *imgG, *imgB, *imgThreshold_R, *imgThreshold_G, *imgThreshold_B, *imgResult, *imgTmp;

void onMouse(int event, int x, int y, int flags, void* param){
  printf("x:%d y:%d r:%d g:%d b:%d %s", x, y, // マウス座標とRGBを出力
    (unsigned char)imgR->imageDataOrigin[x+y*imgR->width],
    (unsigned char)imgG->imageDataOrigin[x+y*imgG->width],
    (unsigned char)imgB->imageDataOrigin[x+y*imgB->width],
    "\n");
}

int main(int argc, char** argv) {
  bool isStop = false;
  CvCapture *capture = NULL;
  capture = cvCreateCameraCapture(0);
  //capture = cvCaptureFromAVI("test.avi");
  if(capture == NULL){
    printf("capture device not found!!");
    return -1;
  }

  img = cvQueryFrame(capture);
  const int w = img->width;
  const int h = img->height;

  imgR = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgG = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgB = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgThreshold_R = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgThreshold_G = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgThreshold_B = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgResult = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
  imgTmp = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);

  char winNameCapture[] = "Capture";
  char winNameResult[] = "Result";

  cvNamedWindow(winNameCapture, CV_WINDOW_AUTOSIZE);
  cvNamedWindow(winNameResult, CV_WINDOW_AUTOSIZE);

  cvSetMouseCallback(winNameCapture, onMouse, 0);
  cvSetMouseCallback(winNameResult, onMouse, 0);

  int waitKey;
  while (1) {
    if(!isStop){
      if((img = cvQueryFrame(capture)) == NULL) break;
      cvSplit(img, imgB, imgG, imgR, NULL); // BGRを分解
      
      // RGB要素をそれぞれ調べて、赤いピクセルを論理積で抽出
      cvThreshold(imgR, imgThreshold_R, 150, 255, CV_THRESH_BINARY); // 赤が150以上
      cvThreshold(imgG, imgThreshold_G, 100, 255, CV_THRESH_BINARY_INV); // 緑が100以下
      cvThreshold(imgB, imgThreshold_B, 100, 255, CV_THRESH_BINARY_INV);
      cvAnd(imgThreshold_R, imgThreshold_G, imgTmp, NULL);
      cvAnd(imgTmp, imgThreshold_B, imgResult, NULL);

      cvShowImage(winNameCapture, img);
      cvShowImage(winNameResult, imgResult);
    }

    waitKey = cvWaitKey(33);
    if(waitKey == 'q') break;
    if(waitKey == ' '){
      isStop = !isStop;
      if(isStop) printf("stop\n");
      else printf("start\n");
    }
  }
  
  cvReleaseCapture(&capture);
  cvDestroyWindow(winNameCapture);
  cvDestroyWindow(winNameResult);
  return 0;
}

kimai0827kimai0827 2011/03/06 22:48 こんばんは。解説およびソースコードを開示していただきありがとうございました。おかげで短時間目的のことができました。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/shokai/20090202/1233589674
Connection: close