'Color Space'에 해당되는 글 2건

  1. 2011.09.14 OpenCV 2.3 Computer vision (4) 4
  2. 2009.04.11 OpenCV - Convert Image
Computer Vision2011. 9. 14. 13:33
Using the mean shift algorithm to find an object

 Histogram projection으로 원하는 위치의 Histogram을 바탕으로 움직이는 물체를 추적할 수 있습니다. 이에 대한 대표적인 알고리듬이 Mean Shift Algorithm입니다.  이 절에서는 이것을 이용해서 실제 움직이는 물체를 추적하는 것을 해보겠습니다.

별도의 복잡한 알고리듬을 계속 사용하는 것이 아니라서 추적에 약간의 버그가 있음을 미리 알려드립니다.
- 책에 있는 내용에서 버그를 약간 수정한 버전입니다.


QT에서 QT Console로 프로젝트를 만듭니다. 
- 반드시 Qt일 필요는 없지만, 따로 빌드 스크립트 만드는게 귀찮아서 여기서합니다.
- 까짓거 속도가 좀느려도 어떠냐 라는 생각도 있습니다. ㅋㅋ


코드는 아래 것을 보시기 바라고 코드를 그대로 읽어보면 아래와 같습니다.

STEP 1 일단 이미지를 읽어들여서 관심 영역을 설정합니다.
 여기서는 움직이는 차량을 대상으로 합니다.

안타깝게도, 관심 영역은 자동으로 하지 못하므로 일일이 손으로 지정해야 하는 단점이 있습니다.
GIMP에서 픽셀단위로 위치를 확인합니다.


    // Set ROI
    cv::Mat imageROI = image(cv::Rect(127, 170 , 120 , 80));
    cv::rectangle(image,cv::Rect(127, 170 , 120 , 80),cv::Scalar(0,0,255));

그리고 화면에 보이기 위해서 사각형을 하나 그립니다.

그것이 이 아래의 그림입니다.



Step 2 : Hue의 Saturation을 구합니다. 

코드는 아래와 같습니다.

    // Get HUE histogram
    ColorHistogram hc;
    int minSat = 65;

    //cv::MatND colorhist = hc.getHueHistogram( imageROI ,  minSat );
    cv::MatND colorhist = hc.getHueHistogram( imageROI );


실세로는 Class로 선언된 ColorHistogram class에서 GetHueHistogram을 호출하여 가지고 옵니다.
그리고 그 대상은 관심 영역을 대상으로 합니다.

해당 함수는 아래와 같습니다.

	// Computes the 1D Hue histogram with a mask.
	// BGR source image is converted to HSV
	cv::MatND getHueHistogram(const cv::Mat &image) {

		cv::MatND hist;

		// Convert to Lab color space
		cv::Mat hue;
		cv::cvtColor(image, hue, CV_BGR2HSV);

		// Prepare arguments for a 1D hue histogram
		hranges[0]= 0.0;
		hranges[1]= 180.0;
		channels[0]= 0; // the hue channel 

		// Compute histogram
		cv::calcHist(&hue, 
			1,			// histogram of 1 image only
			channels,	// the channel used
			cv::Mat(),	// no mask is used
			hist,		// the resulting histogram
			1,			// it is a 1D histogram
			histSize,	// number of bins
			ranges		// pixel value range
		);

		return hist;
	}

이미지를 RGB에서 HSV로 바꾼뒤에 이중에서 Hue에 대한 것만 찾아서 Histogram을 만들어갑니다. 
Histogram을 만드는 함수는 다시 OpenCV내에 함수를 사용했습니다.
여하튼 이렇게 해서 Hue에 대한 Histogram을 얻어냅니다.

Step 3  ObjectFinder에서 finder를 선언합니다.
그리고 Search에 필요한 히스토그램 데이터를 설정합니다.
코드는 아래와 같습니다.

    // Object Finder
    ObjectFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);


setHistogram은 아래와 같은 함수입니다.

	// Sets the reference histogram
	void setHistogram(const cv::MatND& h) {

		isSparse= false;
		histogram= h;
		cv::normalize(histogram,histogram,1.0);
	}

주어진 histogram데이터를 0~1사이에서  normalize시켜서 보관한다.

finder.setThreshold(0.2f);

은 그냥 내부 변수중 Threshld값을 0.2f로 설정해준다. 이 값은 나중에 연산할 때 사용한다.

Step 4 : 이미지를 HSV로 변경한 후에 이미지를 분할한다. 

    //convert to HSV space
    cv::cvtColor(image, hsv, CV_BGR2HSV);

    //split image
    vector<cv::Mat> v;
    cv::split(hsv,v);

HSV의 image를 다시 각각의 컬러 값들을 V plane으로 분리시켜둡니다.
이는 H값만을 대상으로 작업하기 편하게 만든 것입니다.
v[1]이 H값입니다.

Step 5 : Threshold를 찾아서 마스킹 시킵니다.

    // Identify pixel with low saturation
    cv::threshold(v[1],v[1], minSat, 255,cv::THRESH_BINARY);

이렇게 해서 얻은 이미지는 아래와 같다.



Step 6 HUE에 대한 Backproject을 얻습니다.

    // Let's obtain backrojection of the hue channel
    // Get back-projection of hue histogram
    int ch[1]={0};
    cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
    cv::namedWindow("Result Hue");
    cv::imshow("Result Hue",result);

 

결과 이미지는 아래와 같다.


- 약간 뭔가 이상한 그림이긴 하지만, 하여튼 결과는 위와 같다.

그리고 Low와 Saturation시킨 그림을 마스킹 시켜서 이미지를 다시 얻는다.

    // Eliminate low saturation pixels
    cv::bitwise_and(result,v[1],result);
    cv::namedWindow("Result Hue and");
    cv::imshow("Result Hue and",result);

 이미지는 아래와 같다.

앞서의 이미지보다는 약간 정돈된 느낌을 주지만, 뭐...



Step 8 : 두번째 이미지를 읽어들여서 HSV로 바꾼뒤에 다시 H 값을 얻습니다.
이 코드는 앞서 설명한 코드와 동일한 부분이므로,  그냥 Pass
그리고 나머지 부분도 그대로 앞서와 동일한 코드이므로 그대로 진행합니다.


Step 9 :  마지막으로 MeanShift를 수행합니다.

     cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
     cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;

이렇게 얻은 사각형을 이미지에 그리고 화면에 출력합니다.
이렇게 얻어낸 이미지는 이 글의 마지막에 있습니다.



완성된 코드는 아래와 같습니다.

#include <iostream>
#include <vector>
using namespace std;
 

#include <QtCore/QCoreApplication>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/contrib/contrib.hpp>

#include "colorhistogram.h"
#include "objectFinder.h"
 



int main(int argc, char *argv[])
{
    cv::Mat hsv;

    QCoreApplication a(argc, argv);

    // Read Data
    cv::Mat image = cv::imread( "./0172.jpg" );
    if (!image.data)
            return 0;
 

    // Set ROI
    cv::Mat imageROI = image(cv::Rect(127, 170 , 120 , 80));
    cv::rectangle(image,cv::Rect(127, 170 , 120 , 80),cv::Scalar(0,0,255));
 

    //Display Origitanl Image
    cv::namedWindow("Image 1 :: Orignal");
    cv::imshow("Image 1 :: Orignal",image);
 


    // Get HUE histogram
    ColorHistogram hc;
    int minSat = 65;
 

    //cv::MatND colorhist = hc.getHueHistogram( imageROI ,  minSat );
    cv::MatND colorhist = hc.getHueHistogram( imageROI );
 


    // Object Finder
    ObjectFinder finder;
    finder.setHistogram(colorhist);
    finder.setThreshold(0.2f);
 


    //convert to HSV space
    cv::cvtColor(image, hsv, CV_BGR2HSV);
 

    //split image
    vector<cv::Mat> v;
    cv::split(hsv,v);
 

    // Identify pixel with low saturation
    cv::threshold(v[1],v[1], minSat, 255,cv::THRESH_BINARY);
    cv::namedWindow("Saturation");
    cv::imshow("Saturation",v[1]);
 

    // Let's obtain backrojection of the hue channel
    // Get back-projection of hue histogram
    int ch[1]={0};
    cv::Mat result= finder.find(hsv,0.0f,180.0f,ch,1);
    cv::namedWindow("Result Hue");
    cv::imshow("Result Hue",result);
 

    // Eliminate low saturation pixels
    cv::bitwise_and(result,v[1],result);
    cv::namedWindow("Result Hue and");
    cv::imshow("Result Hue and",result);
 


    // load second image
    image = cv::imread("./0181.jpg");

    // Display image
     cv::namedWindow("Image 2");
     cv::imshow("Image 2",image);
 

     // Convert to HSV space
     cv::cvtColor(image, hsv, CV_BGR2HSV);

     // Split the image
     cv::split(hsv,v);

     // Eliminate pixels with low saturation
     cv::threshold(v[1],v[1],minSat,255,cv::THRESH_BINARY);
     cv::namedWindow("Saturation");
     cv::imshow("Saturation",v[1]);
 

     // Get back-projection of hue histogram
     result= finder.find(hsv,0.0f,180.0f,ch,1);

     cv::namedWindow("Result Hue");
     cv::imshow("Result Hue",result);
 

     // Eliminate low stauration pixels
     cv::bitwise_and(result,v[1],result);
     cv::namedWindow("Result Hue and");
     cv::imshow("Result Hue and",result);
 

     // Get back-projection of hue histogram
     finder.setThreshold(-1.0f);
     result= finder.find(hsv,0.0f,180.0f,ch,1);
     cv::bitwise_and(result,v[1],result);
     cv::namedWindow("Result Hue and raw");
     cv::imshow("Result Hue and raw",result);
 

     cv::Rect rect(127, 170 , 120 , 80);
     cv::rectangle(image, rect, cv::Scalar(0,0,255));

     cv::TermCriteria criteria(cv::TermCriteria::MAX_ITER,10,0.01);
     cout << "meanshift= " << cv::meanShift(result,rect,criteria) << endl;

     cv::rectangle(image, rect, cv::Scalar(0,255,0));
 

    // Display Image
    cv::namedWindow("Image Result");
    cv::imshow("Image Result",image);
 

    cv::waitKey();
    return 0;


}


트래킹된 결과는 아래와 같습니다.


 위의 두개의 이미지를 가지고 자동차의 움직임을 추적하는 것입니다.




'Computer Vision' 카테고리의 다른 글

OpenCV를 이용한 Face Detection  (0) 2013.05.19
3D Noise Reduction algorithm test  (0) 2011.10.05
OpenCV 2.3 Computer vision (3)  (0) 2011.09.06
OpenCV 2.3 Computer vision (2)  (0) 2011.09.05
OpenCV 2.3 Computer vision (1)  (2) 2011.09.05
Posted by GUNDAM_IM
Computer Vision2009. 4. 11. 17:41

OpenCV Convert Image


() 글을 읽기 전에
글은 저와 같은 OpenCV초보자를 위해서 정리하는 것입니다.
전문가시라면 굳이 읽을 필요가 없습니다.
공부하면서 정리하는 글이라서 서툰 부분이 많이많이 보입니다.


영상 처리를 하기 위해서는 때때로 컬러 공간을 바꾸어 줘야 할 때가 많습니다.

영상에서 필요한 정보를 찾아서 추출하기 위해서는 RGB공간에서 얻을 수 없는 정보를

다른 컬러 공간에서 얻어낼 수 있기 때문입니다.

예를들어서 사진에서 휘도를 바꾸고 싶을 경우

YCbCr로 바꾼후에 Y를 변경하고 다시 이것을 RGB로 바꾸는 과정을 거치게 됩니다.

이럴 경우 컬러 공간을 변환하는 함수가 있어야 하는데

OpenCV에서는 아래 함수가 그 동작을 도와줍니다.

cvCvtColor( 입력 이미지 , 출력 이미지 , 옵션 ); 


옵션은 아주 많이 있지만 아래에 몇가지 중요한 옵션을 설명하였습니다.


  CV_RGB2GRAY  : 흑백으로 바꾸기

                            수식은 다음과 같다.

RGB[A]->Gray: Y<-0.299*R + 0.587*G + 0.114*B


  CV_RGB2YCrCb : YCrCb로 바꾸기

    Y <- 0.299*R + 0.587*G + 0.114*B

  Cr <- (R-Y)*0.713 + delta

  Cb <- (B-Y)*0.564 + delta


  CV_RGB2HLS    : ( Hue Lightness Saturation ) 으로 변환한다.

   // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  V,,max,, <- max(R,G,B)

  V,,min,, <- min(R,G,B)

  L <- (V,,max,, + V,,min,,)/2

  S <- (V,,max,, - V,,min,,)/(V,,max,, + V,,min,,)  if L < 0.5

        (V,,max,, - V,,min,,)/(2 - (V,,max,, + V,,min,,))  if L = 0.5

           (G - B)*60/S,  if V,,max,,=R

  H <- 180+(B - R)*60/S,  if V,,max,,=G

        240+(R - G)*60/S,  if V,,max,,=B

  if H<0 then H<-H+360

  On output 0=L=1, 0=S=1, 0=H=360.

  The values are then converted to the destination data type:

      8-bit images:

           L <- L*255, S <- S*255, H <- H/2

      16-bit images (currently not supported):

          L <- L*65535, S <- S*65535, H <- H

      32-bit images:

          H, L, S are left as is


  CV_RGB2HSV : RGB 값을 HSV로 바꾼다. HSV란 Hue Saturation Value를 의미한다.


  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  V <- max(R,G,B)

  S <- (V-min(R,G,B))/V   if V?0, 0 otherwise

           (G - B)*60/S,  if V=R

  H <- 120+(B - R)*60/S,  if V=G

       240+(R - G)*60/S,  if V=B

  if H<0 then H<-H+360

  On output 0=V=1, 0=S=1, 0=H=360.

  The values are then converted to the destination data type:

      8-bit images:

          V <- V*255, S <- S*255, H <- H/2 (to fit to 0..255)

      16-bit images (currently not supported):

          V <- V*65535, S <- S*65535, H <- H

      32-bit images:

          H, S, V are left as is

  CV_RGB2Lab : CIE Lab Color로 변환한다.

  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  // convert R,G,B to CIE XYZ

  |X|    |0.412453  0.357580  0.180423| |R|

  |Y| <- |0.212671  0.715160  0.072169|*|G|

  |Z|    |0.019334  0.119193  0.950227| |B|

  X <- X/Xn, where Xn = 0.950456

  Z <- Z/Zn, where Zn = 1.088754

  L <- 116*Y^1/3-16      for Y>0.008856

  L <- 903.3*Y           for Y<=0.008856

  a <- 500*(f(X)-f(Y)) + delta

  b <- 200*(f(Y)-f(Z)) + delta

  where f(t)=t^1/3^              for t>0.008856

        f(t)=7.787*t+16/116   for t<=0.008856

  where delta = 128 for 8-bit images,

                0 for floating-point images

  On output 0=L=100, -127=a=127, -127=b=127

  The values are then converted to the destination data type:

      8-bit images:

          L <- L*255/100, a <- a + 128, b <- b + 128

       16-bit images are currently not supported

       32-bit images:

          L, a, b are left as is


  •   CV_RGB2Luv  CIE Luv Color로 변환한다.

  // In case of 8-bit and 16-bit images

  // R, G and B are converted to floating-point format and scaled to fit 0..1 range

  // convert R,G,B to CIE XYZ

  |X|    |0.412453  0.357580  0.180423| |R|

  |Y| <- |0.212671  0.715160  0.072169|*|G|

  |Z|    |0.019334  0.119193  0.950227| |B|

  L <- 116*Y^1/3^      for Y>0.008856

  L <- 903.3*Y      for Y<=0.008856

  u' <- 4*X/(X + 15*Y + 3*Z)

  v' <- 9*Y/(X + 15*Y + 3*Z)

  u <- 13*L*(u' - u,,n,,), where u,,n,,=0.19793943

  v <- 13*L*(v' - v,,n,,), where v,,n,,=0.46831096

  On output 0=L=100, -134=u=220, -140=v=122

  The values are then converted to the destination data type:

      8-bit images:

          L <- L*255/100, u <- (u + 134)*255/354, v <- (v + 140)*255/256

      16-bit images are currently not supported

      32-bit images:

          L, u, v are left as is


코드는 아래와 같습니다.


#include <stdio.h>

#include <unistd.h>

#include <string.h>

#include <errno.h>


#include <opencv/cv.h>

#include <opencv/highgui.h>


int main(int argc , char *argv[]){


  IplImage *imgB ;





  if (argc < 3 )

        {

                printf("usage : colorcvt.elf srcimage dstimage option \n");

                printf("      srcimage : source image file name\n");

                printf("      dstimage : destination image file name\n");

                printf("      option   : 0 : CV_RGB2GRAY \n");

                printf("                 1 : CV_RGB2YCrCb \n");

                printf("                 2 : CV_RGB2HLS \n");

                printf("                 3 : CV_RGB2HSV \n");

                printf("                 4 : CV_RGB2Lab \n");

                printf("                 5 : CV_RGB2Luv \n");

                exit(0);

        }


  // 이미지 읽기

  IplImage *imgA = cvLoadImage( argv[1] , 1);


  // 대상 이미지 확정

  switch( *argv[3] )

  {

   case '0' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 1);

              cvCvtColor(imgA , imgB , CV_RGB2GRAY );

              break;

   case '1' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2YCrCb  );

              break;

   case '2' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2HLS  );

              break;

   case '3' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2HSV  );

              break;

   case '4' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2Lab  );

              break;

   case '5' :

              imgB = cvCreateImage( cvGetSize( imgA), IPL_DEPTH_8U, 3);

              cvCvtColor(imgA , imgB , CV_RGB2Luv  );

              break;


  }



  cvNamedWindow("Converted Image",0);

  cvShowImage("Converted Image",(CvArr*)imgB);

  cvWaitKey(0);

  cvDestroyWindow("Converted Image");


  cvSaveImage( argv[2] , imgB);

  cvReleaseImage( &imgA );

  cvReleaseImage( &imgB );

  return 0;

}


아래는 각각의 옵션으로 했을때의 그림입니다.

컬러 스페이스가 틀린것을 강제로 출력한 형태이므로 다른 컬러 공간의 그림은 정확하게 표현되지는 않습니다. 그냥 이렇게 되었다는 것만 보면 될 듯 합니다.


원본 이미지


사용자 삽입 이미지


흑백으로 변환 이미지

사용자 삽입 이미지



사용자 삽입 이미지

사용자 삽입 이미지


사용자 삽입 이미지
사용자 삽입 이미지










'Computer Vision' 카테고리의 다른 글

OpenCV - cvSmooth  (2) 2009.04.22
Rob Hess의 SIFT [6]  (0) 2009.04.18
OpenCV - Image Mask  (0) 2009.03.16
OpenCV - Extract  (2) 2009.03.12
OpenCV - Image Rotation & Scale  (0) 2009.03.10
Posted by GUNDAM_IM