'Implementation'에 해당되는 글 2건

  1. 2011.09.14 OpenCV 2.3 Computer vision (4) 4
  2. 2009.11.01 SystemC에서 배포가능 버전 만들기
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
ASIC SoC2009. 11. 1. 22:26
SystemC와  Verilog의 가장 큰 차이점 중에 한가지는 배포본 만들기에 있습니다.

C/C++은 각각의 Class에 필요한 함수와 변수등을 Header파일에 선언하고 함수 등을 선언 한 뒤에
cpp파일에서 구현하게 됩니다.

비록 배포 버전에서는 cpp파일을 object로 바꾸어서 전달할 수 있지만 header파일을 찬찬히 보면,
세부 사양은 알 수 없더라도, 어떤 방식으로 구성되어 있는지 등은 알 수 있습니다.  
SystemC도 엄밀하게는 C++위에서 구성되어 있어서 마찬가지로 이런 문제점이 발생합니다.

IP의 생명인 보안성 부분에서는 상당히 취약하다고 할 수 있습니다.  

그래서 귀찮지만 Pointer로 받아서 구현하는 방식을 취합니다.
이 방식은 ME C++에서 설명한 방식입니다만, SystemC에서 응용할 줄은 몰랐습니다.

PIMPL Idiom 구현 방식입니다. 말 그대로 Pointer로 받아서 구현하는 것이므로,  Pointer IMPlementation 구현 방식입니다.

일종의 Pointer Wrapper 방식이라 할 수 있습니다.

사용자 삽입 이미지
위와 같이 DesignModule을 설계하였으면 이것을 그냥 배포하는 것은 곤란하므로
DesignModule_wrapper를 쒸워서 내부 설계 정보를 최대한 Hidden 시켜서 배포하게 됩니다.

Wrapper가 가지는 조건은 다음과 같이 됩니다.

당연하게 포트가 같아야 합니다.
   - 나중에 실제 모듈로 대치하게 된다면 포트 이름도 같이 해두는게 당연히 좋습니다.


그럼 아래와 같이 예를 들어 보겠습니다.

설계한 모듈은

DesignModule이라고 한다면 header파일과 cpp파일을 가지고 있습니다.

DesignModule.h

SC_MODULE(DesigModule)
{
// Port Declaration
PortA ;
PortB ;
PortC ;
// 이하는 뭔가 중요할 것만 같은 .. 중요해야 되는 디자인 정보.. T__T;;
......
......
}

DesignModule.cpp
.....


이것을 쒸울 Wrapper는

DesignModule_Wrapper.h

class DesignModule; // 미리 클래스라고 알려주어서 컴파일 시에 오류가 생기지 않도록 한다.

SC_MODULE(DesignModule_Wrapper)
{
// Port Declaration
        SC_CTOR(DesignModule_Wrapper){
DesignModule_Wrapper_port();
        };
//
private :
DesignModule * pDesignModule;       // 이렇게 하위 클래스의 포인터를 선언해서 받아온다.
virtual ~ DesignModule_Wrapper();  // 포인터로 선언해서 나중에 new 연산으로 할당
                                                                     //  할 것이므로 Destructor에서 반드시 Delete해 주어야 한다.
                void DesignModule_Wrapper_port();

}

DesignModule_Wrapper.cpp

#include "DesignModulle.h"

DesignModule_Wrapper::DesignModule_Wrapper_port()
{
   pDesignModule = new DesignModule;

   pDesignModule->PortA(PortA);
   pDesignModule->PortB(PortB);
   pDesignModule->PortC(PortC);

}

......
DesignModule_Wrapper ::~ DesignModule_Wrapper()
{
delete pDesignModule;
}

 





DesignModule.h파일을 DesignModule_Wrapper.cpp파일에서 불러들인다는 점이 중요합니다.
즉, 실제 디자인에 관련된 정보는 DesignModule.h파일에 있으며 이 것을 Wrapper용 Header File에서 Include하지 않음으로서 Compile 시에 Dependency를 제거해 주는 것이 목적이 됩니다.

따라서 최종 사용자는

DesignModule_Wrapper.h


DesignModule_Wrapper.o
DesignModule.o

3개 파일을 받게 되는 것입니다.
물론 Obj들은 Lib으로 묶어 버리면 2개 정도 파일만 가면 되는 것이어서 큰 문제가 없이 잘 됩니다.

최종 유저에게 오픈되어 있는 정보는 DesignModule_Wrapper.h 파일 뿐이므로 실제 디자인 내부 정보는 오픈되지 않습니다.

이 방식은 Wrapper를 거치는 방식이므로, 실제 동작시에는 느려진다고 합니다.
어느정도까지 느려질 지는 테스트 해보지 않아서 잘 모르겠습니다.

Posted by GUNDAM_IM