赤色領域を検出(4) 輪郭を囲む

OpenCVプログラミングブック p.154に2値画像の輪郭抽出がcvFindContourでできて、cvDrawContours
で線で囲えると書いてあったので、ちょうど赤色領域抽出でカメラから2値画像を作っていたのでコードを合体させた。


輪郭抽出前に収縮・膨張でノイズを減らしている。もっと収縮膨張度を上げた方が良かったかも


コンソールには輪郭の数を出力している
detect contour of red
detect contour of red

/***
 赤を検出して輪郭抽出
 参考: OpenCVプログラミングブック p.154
***/

#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->imageData[x+y*imgR->width],
    (unsigned char)imgG->imageData[x+y*imgG->width],
    (unsigned char)imgB->imageData[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);

  CvMemStorage* storage = cvCreateMemStorage(0);
  CvSeq* find_contour = NULL;

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

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

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

  while (1) {
    if(!isStop){
      if((img = cvQueryFrame(capture)) == NULL) break;
      cvSplit(img, imgB, imgG, imgR, NULL); // BGRを分解

      // 赤の要素が100以上で、緑と青より1.5倍以上あるピクセルを抽出
      cvThreshold(imgR, imgThreshold_R, 100, 255, CV_THRESH_BINARY);
      cvDiv(imgR, imgG, imgTmp, 10); // 10倍
      cvThreshold(imgTmp, imgThreshold_G, 15, 255, CV_THRESH_BINARY);
      cvDiv(imgR, imgB, imgTmp, 10);
      cvThreshold(imgTmp, imgThreshold_B, 15, 255, CV_THRESH_BINARY);
      cvAnd(imgThreshold_G, imgThreshold_B, imgTmp, NULL);
      cvAnd(imgTmp, imgThreshold_R, imgResult, NULL);
      
      cvErode(imgResult, imgResult, NULL, 1); // 収縮
      cvDilate(imgResult, imgResult, NULL, 1); // 膨張
      
      // 輪郭抽出、青線で囲む
      int contour_num = cvFindContours(cvCloneImage(imgResult), storage, &find_contour,
        sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0,0));
      CvScalar blue = CV_RGB(0,0,255);
      cvDrawContours(img, find_contour, blue, blue, 1, 2, 8, cvPoint(0,0));
      printf("contour: %d\n", contour_num);
      
      cvShowImage(winNameCapture, img);
      cvShowImage(winNameResult, imgResult);
    }

    int 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;
}