블로그 이미지
Leeway is... the freedom that someone has to take the action they want to or to change their plans.
maetel

Notice

Recent Post

Recent Comment

Recent Trackback

Archive

calendar

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
  • total
  • today
  • yesterday

Category

2010. 5. 18. 01:43 Computer Vision
http://jmerritt.warpax.com/pytsai/

Jonathan Merritt (j.merritt@pgrad.unimelb.edu.au), PhD Student (Equine Biomechanics), The University of Melbourne Equine Centre

posted by maetel
2010. 5. 18. 00:26 Computer Vision
ref.
2010/02/10 - [Visual Information Processing Lab] - R. Y. Tsai "A Versatile Camera Calibration Technique for High Accuracy 3-D Maching Vision Metrology Using Off-the-shelf TV Cameras and Lenses"


(1) 고정되어 있는 것으로 가정한 카메라의 내부 파라미터 값들을 구하고 (2) 실시간으로 들어오는 이미지 프레임마다 카메라의 회전과 이동을 계산하기 위하여 Tsai 알고리즘을 쓰기로 하고, C 또는 C++로 구현된 소스코드 또는 라이브러리를 찾아서 붙여 보기로 한다.


Try #1.
처음에는 CMU의 Reg Willson가 C로 짠 Tsai Camera Calibration 코드 에서 필요한 부분을 include하여 쓰려고 했는데, C++ 문법에 맞지 않는 구식 C 문법으로 코딩된 부분이 많아서 고치는 데 애를 먹었다. (Xcode의 C++ 프로젝트에서 .c 파일을 include하면 compile은 되지만, linking error가 난다. 때문에 .c를 .cpp로 바꾸어야 함.)  그런데 결정적으로, "cal_main.cpp" 파일에 정의된, 캘리브레이션의 최종 결과값을 주는 함수들이 호출하는 optimization을 실행하는 함수 lmdif_()가  Fortan 파일 "lmdif.f"에 정의되어 있고, Fortran을 C로 변환해 주는 "f2c.h"에 의해 이것을 "lmdif.c"로 하여 가지고 있다는 문제가 있었다. lmdif.c를 lmdif.cpp 형태로 만들기 위해서는 Fortran 언어와 Fortran을 C++로 변환하는 방법을 알아야 하므로, 결국 포기했다.



Try #2.
Michigan State University Charles B. Owen Display-Relative Calibration (DRC)을 구현한 DRC 프로그램( DRC.zip )에서 카메라 캘리브레이션에 Tsai의 알고리즘 libtsai.zip을 쓰고 있다. 이 라이브러리는 위의 C 코드를 C++로 수정하면서 "CTsai"라는 클래스를 사용하고 여러 함수들을 수정/보완/결합한 것인데, Visual Studio 용 프로젝트 프로그램을 만들면서 Windows 환경에 기반하여 MFC를 활용하였다. 그래서 이것을 나의 Mac OS X 기반 Xcode 프로젝트에서 그대로 가져다 쓸 수는 없다. 용법은 다음과 같다.

DRC/DisplayRelativeCalibration.cpp:
bool CDisplayRelativeCalibration::ComputeCameraCalibration(void)
{
    CTsai tsai;

    tsai.Width(m_camerawid);
    tsai.Height(m_camerahit);

    for(std::list<Corr>::const_iterator i=m_cameracorr.begin();  i!=m_cameracorr.end();  i++)
    {
        tsai.Point(i->x, i->y, i->z, i->u, i->v);
    }

    if(tsai.PointCount() < 8)
        return Error("Didn't get enough points");

    if(!tsai.Compute())
        return Error("Camera calibration failed");

    for(int n=0;  n<tsai.PointCount();  n++)
    {
        double ux, uy;
        tsai.WorldToImage (tsai.PointX(n), tsai.PointY(n), tsai.PointZ(n), ux, uy);

        m_cameraproj.push_back(CGrPoint(ux, uy, 0));
    }

   
    m_cameraf = tsai.F();
    m_cameracx = tsai.Cx();
    m_cameracy = tsai.Cy();
    m_camerakappa1 = tsai.Kappa1();
    m_camerasx = tsai.Sx();
    memcpy(m_cameramatrix, tsai.CameraMatrix(), sizeof(double) * 16);

    return true;
}




문제점#1.

class CTsai 안의 member functions 중에  ncc_compute_exact_f_and_Tz( )와 ncc_compute_exact_f_and_Tz_error( )가 있는데,

libtsai.h:21
class CTsai
{

    bool ncc_compute_exact_f_and_Tz();
    bool ncc_compute_exact_f_and_Tz_error (int m_ptr, int n_ptr, const double *params, double *err);

};

전자인 ncc_compute_exact_f_and_Tz()가 정의된 부분을 보면, 

Tsai_ncc.cpp:274
bool CTsai::ncc_compute_exact_f_and_Tz()
{
    CLmdif<CTsai> lmdif;

    lmdif.Lmdif (this, ncc_compute_exact_f_and_Tz_error,
            m_point_count, NPARAMS, x,
            NULL, NULL, NULL, NULL);
}

클래스 형태의 템플릿( CLmdif )으로 선언된 "lmdif"의 member function "Lmdif"를 호출할 때, 

min/Lmdif.h:48
template<class T> class CLmdif : private CLmdif_
{

int Lmdif(T *p_user, bool (T::*p_func)(int m, int n, const double *parms, double *err),
        int m, int n, double *x, double *fvec, double *diag, int *ipvt, double *qtf)

};

후자인 같은 member function, ncc_compute_exact_f_and_Tz_error()를 인자로 넣고 있고 (위 부분 코드들 중 오렌지 색 부분), 컴파일 하면 이 부분을 <unknown type>으로 인식하지 못 하겠다는 에러 메시지를 보낸다. 그리고 다음과 같은 형태를 추천한다고 한다.
 
note: candidates are: int CLmdif<T>::Lmdif(T*, bool (T::*)(int, int, const double*, double*), int, int, double*, double*, double*, int*, double*) [with T = CTsai]

function pointer의 형태가 틀린 모양인데, 오렌지색 부분을 그냥 함수가 아닌 어떤 class의 non-static member function을 가리키는 pointer로  &CTsai::ncc_compute_exact_f_and_Tz_error 이렇게 바꾸어 주면, 에러 메시지가 다음과 같이 바뀐다.

error: no matching function for call to 'CLmdif<CTsai>::Lmdif(CTsai* const, bool (*)(int, int, const double*, double*), int&, const int&, double [3], NULL, NULL, NULL, NULL)'

연두색 부분 대신 CTsai::ncc_compute_exact_f_and_Tz_error 이렇게 바꾸어 주면, 에러 메시지가 다음과 같다.

error: no matching function for call to 'CLmdif<CTsai>::Lmdif(CTsai* const, bool (&)(int, int, const double*, double*), int&, const int&, double [3], NULL, NULL, NULL, NULL)'

해결:
편법으로, class CLmdif를 클래스 형 템플릿이 아닌 그냥 클래스로 바꾸어서 선언하고 연두색 부분처럼 호출하면 에러는 안 나기에 일단 이렇게 넘어가기로 한다.


문제점#2.
코드에서 Windows OS 기반 MFC를 사용하고 있어 Mac OS X에서 에러가 난다.

해결:
MFC를 사용하는 "StdAfx.h"는 모두 주석 처리한다.


문제점#3.
Lmdif.h



... 기타 등등의 문제점들을 해결하고, 캘리브레이션을 수행한 결과가 맞는지 확인하자.

source code:
           if ( CRimage.size() > 0 ) // if there is a valid point with its cross ratio
            {  
                correspondPoints(indexI, indexW, p, CRimage, linesYorder.size(), linesXorder.size(), world, CRworld, dxList.size(), dyList.size(), iplMatch, scale );
            }  
            cvShowImage( "match", iplMatch );
            cvSaveImage( "match.bmp", iplMatch );
           
            cout << "# of pairs = " << indexI.size() << " = " << indexW.size() << endl;
           
            // # 6. camera calibration
           
            int numPair = indexI.size();
           
            tsai.Clear();
           
            for( int n = 0;  n < numPair;  n++ )
            {
                tsai.Point(world[indexW[n]].x, world[indexW[n]].y, world[indexW[n]].z, p[indexI[n]].x, p[indexI[n]].y);
               
                cout << "pair #" << n << ": " << p[indexI[n]].x << "  " <<  p[indexI[n]].y << "  : "
                    << world[indexW[n]].x << "  " << world[indexW[n]].y << "  " << world[indexW[n]].z << endl;
            }
           
            if( numPair < 8 )
                cout << "Didn't get enough points" << endl;
           
            if(!tsai.Compute())
                cout << "Camera calibration failed" << endl;
           
            cout << endl << "camera parameter" << endl
            << "focus = " << tsai.F() << endl
            << "principal axis (x,y) = " <<  tsai.Cx() << ", " <<  tsai.Cy() << endl
            << "kappa1 (lens distortion) = " <<  tsai.Kappa1() << endl
            << "skew_x = " << tsai.Sx() << endl;
          
            // reproject world points on to the image frame to check the result of computing camera parameters
            for(int n=0;  n<tsai.PointCount();  n++)
            {
                double ux, uy;
                tsai.WorldToImage (tsai.PointX(n), tsai.PointY(n), tsai.PointZ(n), ux, uy);
                CvPoint reproj = cvPoint( cvRound(ux), cvRound(uy) );
                cvCircle( iplInput, reproj, 3, CV_RGB(200,100,200), 2 );
            }
           
// draw a cube on the image coordinate computed by camera parameters according to the world coordinate
            drawcube( tsai, iplInput, patSize );
            cvShowImage( "input", iplInput );  



아래 사진은 구해진 카메라 내부/외부 파라미터들을 가지고 (1) 실제 패턴의 점에 대응하는 이미지 프레임 (image coordinate) 상의 점을 찾아 (reprojection) 보라색 원으로 그리고, (2) 실제 패턴이 있는 좌표 (world coordinate)를 기준으로 한 graphic coordinate에 직육면체 cube를 노란색 선으로 그린 결과이다.

이미지 프레임과 실제 패턴 상의 점을 1 대 1로 비교하여 연결한 16쌍의 대응점

구한 카메라 파라미터를 가지고 실제 패턴 위의 점들을 이미지 프레임에 reproject한 결과 (보라색 점)와 실제 패턴의 좌표를 기준으로 한 그래픽이 이미지 프레임 상에 어떻게 나타나는지 그린 결과 (노란색 상자)

 

위 왼쪽 사진에서 보여지는 16쌍의 대응점들의 좌표값을 "이미지 좌표(x,y) : 패턴 좌표 (x,y,z)"로 출력한 결과:
# of pairs = 16 = 16
pair #0: 7.81919  36.7864  : 119.45  82.8966  0
pair #1: 15.1452  71.2526  : 119.45  108.484  0
pair #2: 26.1296  122.93  : 119.45  147.129  0
pair #3: 36.6362  172.36  : 119.45  182.066  0
pair #4: 77.3832  20.4703  : 159.45  82.8966  0
pair #5: 85.4293  53.7288  : 159.45  108.484  0
pair #6: 97.8451  105.05  : 159.45  147.129  0
pair #7: 109.473  153.115  : 159.45  182.066  0
pair #8: 96.6046  15.962  : 171.309  82.8966  0
pair #9: 105.046  48.8378  : 171.309  108.484  0
pair #10: 118.177  99.9803  : 171.309  147.129  0
pair #11: 130.4  147.586  : 171.309  182.066  0
pair #12: 145.469  4.50092  : 199.965  82.8966  0
pair #13: 154.186  36.5857  : 199.965  108.484  0
pair #14: 168.033  87.5497  : 199.965  147.129  0
pair #15: 180.732  134.288  : 199.965  182.066  0


그런데 위 오른쪽 사진에서 보여지는 결과는 이전 프레임에서 20쌍의 대응점으로부터 구한 카메라 파라미터 값을 가지고 계산한 결과이다.
# of found lines = 8 vertical, 7 horizontal
vertical lines:
horizontal lines:
p.size = 56
CRimage.size = 56

# of pairs = 20 = 20
pair #0: -42.2331  53.2782  : 102.07  108.484  0
pair #1: -22.6307  104.882  : 102.07  147.129  0
pair #2: -4.14939  153.534  : 102.07  182.066  0
pair #3: 1.81771  169.243  : 102.07  193.937  0
pair #4: -10.9062  41.1273  : 119.45  108.484  0
pair #5: 8.69616  92.7309  : 119.45  147.129  0
pair #6: 27.0108  140.945  : 119.45  182.066  0
pair #7: 32.9779  156.653  : 119.45  193.937  0
pair #8: 57.4164  14.6267  : 159.45  108.484  0
pair #9: 77.7374  65.9516  : 159.45  147.129  0
pair #10: 96.3391  112.934  : 159.45  182.066  0
pair #11: 102.524  128.555  : 159.45  193.937  0
pair #12: 76.5236  7.21549  : 171.309  108.484  0
pair #13: 97.5633  58.2616  : 171.309  147.129  0
pair #14: 116.706  104.705  : 171.309  182.066  0
pair #15: 123.108  120.238  : 171.309  193.937  0
pair #16: 125.015  -11.5931  : 199.965  108.484  0
pair #17: 146.055  39.453  : 199.965  147.129  0
pair #18: 164.921  85.2254  : 199.965  182.066  0
pair #19: 171.323  100.758  : 199.965  193.937  0

camera parameter
focus = 3724.66
principal axis (x,y) = 168.216, 66.5731
kappa1 (lens distortion) = -6.19473e-07
skew_x = 1



대응점 연결에 오차가 없으면, 즉, 패턴 인식이 잘 되면, Tsai 알고리즘에 의한 카메라 파라미터 구하기가 제대로 되고 있음을 확인할 수 있다. 하지만, 현재 full optimization (모든 파라미터들에 대해 최적화 과정을 수행하는 것)으로 동작하게 되어 있고, 프레임마다 모든 파라미터들을 새로 구하고 있기 때문에, 속도가 매우 느리다. 시험 삼아 reprojection과 간단한 graphic을 그리는 과정은 속도에 큰 영향이 없지만, 그전에 카메라 캘리브레이션을 하는 데 필요한 계산 시간이 길다. 입력 프레임이 들어오는 시간보다 훨씬 많은 시간이 걸려 실시간 구현이 되지 못 하고 있다.

따라서, (1) 내부 파라미터는 첫 프레임에서 한 번만 계산하고 (2) 이후 매 프레임마다 외부 파라미터 (카메라의 회전과 이동)만을 따로 계산하는 것으로 코드를 수정해야 한다.




Try#3.
OpenCV 함수 이용

1) 내부 파라미터 계산
cvCalib
rateCamera2


2) lens distortion(kappa1, kappa2)을 가지고 rectification
cvInitUndistortRectifyMap

3) line detection

4) 패턴 인식 (대응점 찾기)

5) 외부 파라미터 계산 (4의 결과 & lens distortion = 0 입력)
cvFindExtrinsicCameraParams2

6) reprojection
2)에서 얻은 rectificated image에 할 것


posted by maetel
2010. 5. 14. 21:50 Computer Vision
Test on the correspondences of feature points
특징점 대응 시험

교점의 cross ratio 값을 구하고, 그 값과 가장 가까운 cross ratio 값을 가지는 점을 패턴에서 찾아 대응시킨다.


Try #1. one-to-all

입력 영상에서 검출한 직선들로부터 생기는 각 교점에서 수평 방향으로 다음 세 개의 교점, 수직 방향으로 다음 세 개의 교점을 지나는 직선에 대한 cross ratio (x,y)값을 구한다. 이상적으로, 1에서 구한 cross ratio 값과 일치하는 cross ratio 값을 가지는 패턴의 격자점이 입력 영상의 해당 교차점과 실제로 대응하는 점이라고 볼 수 있다.

직선 검출에 오차나 오류가 적을 경우, 아래 테스트 결과에서 보듯 입력 영상의 교차점에 대해 실제 패턴의 직선을 1대 1로 즉각적으로 찾는다. 즉, 입력 영상의 한 점에서의 수평 방향 cross ratio 값에 대해 패턴의 모든 수평선들의 cross ratio 값을 일일이 대조하여 가장 근접한 값을 가지는 직선을 대응시키는 방식이다. (아래 오른쪽 사진은 같은 방식으로 수직 방향 cross ratio 값을 가지고 대응되는 직선을 찾는 경우임.) (point-to-line)

수평선 위의 점들에 대한 cross ratio 값만 비교한 결과

수선 위의 점들에 대한 cross ratio 값만 비교한 결과



입력 영상에서 하나의 교차점의 x방향 cross ratio 값과 같은 cross ratio 값을 가지는 세로선을 실제 패턴에서 찾고, y방향 cross ratio 값에 대해서 가로선을 찾으면, 패턴 위에 그 세롯선과 가로선이 교차하는 점 하나가 나온다. 입력 이미지 상의 한 점에 대해 패턴의 모든 직선을 (가로선의 개수+세로선의 개수) 번 비교하여 대응점을 연결하는 것이다. (point-to-point)

(패턴 인식이 성공적인 경우)

(잘못된 대응점 연결이 발생한 경우)




그러므로 현재는 (1) 입력 영상에서 한 직선 위에 있는 것으로 추산된 일련의 점들에서의 cross ratio 값들의 수치적 경향을 고려하지 않고 있으며, (2) 입력 영상에 실제 패턴의 어느 부분(위치나 범위)이 잡힌 것인지를 판단하지 않고 무조건 전체 패턴의 모든 격자점들에 대해서 cross ratio 값을 비교하고 있다.      




Try #2. line-to-line




잘 되는 경우:
# of pairs = 25 = 25
# of imagePoints = 25 , 25
# of worldPoints = 25 , 25
imagePoint (0, 0) : worldPoint (4, 1)
imagePoint (0, 1) : worldPoint (4, 2)
imagePoint (0, 2) : worldPoint (4, 3)
imagePoint (0, 3) : worldPoint (4, 4)
imagePoint (0, 4) : worldPoint (4, 5)
imagePoint (1, 0) : worldPoint (5, 1)
imagePoint (1, 1) : worldPoint (5, 2)
imagePoint (1, 2) : worldPoint (5, 3)
imagePoint (1, 3) : worldPoint (5, 4)
imagePoint (1, 4) : worldPoint (5, 5)
imagePoint (2, 0) : worldPoint (6, 1)
imagePoint (2, 1) : worldPoint (6, 2)
imagePoint (2, 2) : worldPoint (6, 3)
imagePoint (2, 3) : worldPoint (6, 4)
imagePoint (2, 4) : worldPoint (6, 5)
imagePoint (3, 0) : worldPoint (7, 1)
imagePoint (3, 1) : worldPoint (7, 2)
imagePoint (3, 2) : worldPoint (7, 3)
imagePoint (3, 3) : worldPoint (7, 4)
imagePoint (3, 4) : worldPoint (7, 5)
imagePoint (4, 0) : worldPoint (8, 1)
imagePoint (4, 1) : worldPoint (8, 2)
imagePoint (4, 2) : worldPoint (8, 3)
imagePoint (4, 3) : worldPoint (8, 4)
imagePoint (4, 4) : worldPoint (8, 5)



잘 안 되는 경우:
# of pairs = 28 = 28
# of imagePoints = 28 , 28
# of worldPoints = 28 , 28
imagePoint (0, 0) : worldPoint (4, 6)
imagePoint (0, 1) : worldPoint (4, 7)
imagePoint (0, 2) : worldPoint (4, 1)
imagePoint (0, 3) : worldPoint (4, 2)
imagePoint (0, 4) : worldPoint (4, 3)
imagePoint (0, 5) : worldPoint (4, 4)
imagePoint (0, 6) : worldPoint (4, 5)
imagePoint (1, 0) : worldPoint (9, 6)
imagePoint (1, 1) : worldPoint (1, 7)
imagePoint (1, 2) : worldPoint (5, 1)
imagePoint (1, 3) : worldPoint (5, 2)
imagePoint (1, 4) : worldPoint (5, 3)
imagePoint (1, 5) : worldPoint (5, 4)
imagePoint (1, 6) : worldPoint (5, 5)
imagePoint (2, 0) : worldPoint (9, 6)
imagePoint (2, 1) : worldPoint (3, 7)
imagePoint (2, 2) : worldPoint (6, 1)
imagePoint (2, 3) : worldPoint (6, 2)
imagePoint (2, 4) : worldPoint (6, 3)
imagePoint (2, 5) : worldPoint (6, 4)
imagePoint (2, 6) : worldPoint (6, 5)
imagePoint (3, 0) : worldPoint (9, 6)
imagePoint (3, 1) : worldPoint (0, 7)
imagePoint (3, 2) : worldPoint (7, 1)
imagePoint (3, 3) : worldPoint (7, 2)
imagePoint (3, 4) : worldPoint (7, 3)
imagePoint (3, 5) : worldPoint (7, 4)
imagePoint (3, 6) : worldPoint (7, 5)

posted by maetel
2010. 4. 28. 12:54 Computer Vision
Rahbar, K. and Pourreza, H. R. 2008. Inside looking out camera pose estimation for virtual studio. Graph. Models 70, 4 (Jul. 2008), 57-75. DOI= http://dx.doi.org/10.1016/j.gmod.2008.01.001


posted by maetel
2010. 4. 27. 22:19 Computer Vision
ref. 2010/04/27 - [Visual Information Processing Lab] - virtual studio 구현: grid pattern generator

case #1

Visual Studio: project 속성 > 구성 속성 > 디버깅 > 명령 인수
가로선: 40(개수) 15(최소 픽셀수) 45(최대 픽셀수) 20
세로선: 30 15 45 20

세로선 40개 간격:  가로선 30개 간격: 





세로선 40개, 가로선 30개로 생성된 패턴 (400x300pixels)

오른쪽의 패턴을 7배 확대한 영상


현재 코드는 검은색 바탕에 흰색 사각형을 그리게 되는데, 소수 값을 정수 값 (픽셀의 위치 좌표)으로 변환하는 과정에서 오차가 발생한다. 얼핏 격자 무늬로 보이는 오른쪽 그림을 확대한 왼쪽 그림을 보면, 격자 사이가 벌어지거나 겹치는 부분이 생긴다는 것을 알 수 있다.


그리는 방법을 달리했더니 해결된다. 이미지와 같은 높이의 세로 막대들을 하얀색으로 먼저 그리고 나서, 이미지와 같은 너비의 가로 막대들을 하얀색으로 그리고, 막대들이 서로 교차하는 (하얀색으로 두 번 그린 셈인) 부분을 다시 검은색으로 그렸다. (이건 뭔... 컴퓨터 비전도 이미지 프로세싱도 아니고 그렇다고 컴퓨터 그래픽스라고 보기도 우습고,  중학교 수학 경시대회 난이도 중상 정도의 문제를 푸는 기분이다. )

세로선 40개, 가로선 30개로 생성된 패턴 (400x300pixels)

오른쪽의 패턴을 7배 확대한 영상








각 꼭지점에서의 x방향과 y방향의 cross ratio 값을 계산한 결과는 다음과 같다. 이 중 값이 "-1"로 나온 것은 그 점에서 해당 방향의 직선에 대해 cross ratio를 계산할 수 없는 경우를 표시하기 위해 초기값으로 주었던 것이다.

CRworldX:
0.386312  0.094615  0.414865  0.284612  0.161689  0.323677  0.132262  0.471754  0.166943  0.114793  0.526767  0.146283  0.268856  0.254384  0.210419  0.282485  0.229261  0.262292  0.233925  0.302709  0.107159  0.385672  0.237546  0.276318  0.328597  0.0923081  0.439814  0.151013  0.295795  0.157482  0.342474  0.354428  0.0689949  0.5625  0.0725959  0.503555  0.0725959  -1  -1  -1 
CRworldY:
0.144347  0.293357  0.3338  0.212505  0.221081  0.232091  0.287101  0.203252  0.312434  0.113198  0.555337  0.105233  0.283001  0.261716  0.167103  0.452516  0.180647  0.218986  0.240322  0.260169  0.24469  0.125  0.5625  0.071018  0.512553  0.071018  0.384156  -1  -1  -1 





source code:
// calculate cross ratios in the world coordinate on real pattern
void crossRatioWorld( vector<CvPoint2D32f>& CRworld, vector<CvPoint3D32f>& world,  int dxListSize, int dyListSize, CvPoint2D32f scale )
{
    //    vector<CvPoint2D32f> crossRatioWorld; // cross ratios in the world coordinate on real pattern
    float crX = -1.0, crY = -1.0;
   
    for( int i = 0; i < dxListSize; i++ )   
    {
        for( int j = 0; j < dyListSize; j++ )
        { 
            CRworld.push_back(cvPoint2D32f(crX, crY));
        }
    }
   
    cout << "CRworld.size = " << CRworld.size() << endl;
   
    //  "cr[iP] = p1 * p3 / ((p1 + p2) * (p2 + p3))" in psoBasic.cpp: 316L
    // that is (b-a)(d-c)/(c-a)(d-b) with 4 consecutive points, a, b, c, and d
    float a, b, c, d;
    // cross ratios in horizontal lines
    for( int i = 0; i < dxListSize-3; i++ )   
    {
        a = world[i*dyListSize].x;
        b = world[(i+1)*dyListSize].x;
        c = world[(i+2)*dyListSize].x;
        d = world[(i+3)*dyListSize].x;
       
        crX = ( (b-a)*(d-c) ) / ( (c-a)*(d-b) ) ;
       
        for( int j = 0; j < dyListSize; j++ )
        {
            CRworld[i*dyListSize+j].x = crX;
        }
    }
   
    // cross ratios in vertical lines
    for( int j = 0; j < dyListSize-3; j++ )
    {
        a = world[j].y;
        b = world[j+1].y;
        c = world[j+2].y;
        d = world[j+3].y;
       
        crY = ( (b-a)*(d-c) ) / ( (c-a)*(d-b) ) ;
       
        for( int i = 0; i < dxListSize; i++ )
        {
            CRworld[i*dyListSize+j].y = crY;
        }
    }
   
    cout << "CRworldX: " << endl;
    for( int i = 0; i < dxListSize; i++ )
    {
        cout << /* "CRworldX[" << i << "] = " << */ CRworld[i*dyListSize].x << "  ";
    }
    cout << endl << "CRworldY: " << endl;
    for( int j = 0; j < dyListSize; j++ )
    {
        cout << /* "CRworldY[" << j << "] = " << */ CRworld[j].y << "  ";
    }
   
    // just to check
    /*    for( int i = 0; i < dxListSize; i++ )   
     {
     for( int j = 0; j < dyListSize; j++ )
     {
     cout << "CRworld[" << i << "," << j << "] = " << CRworld[i*dyListSize+j].x << ", " << CRworld[i*dyListSize+j].y << endl;;
     }
     }
     cout << endl;
     */  
}



Grids 40x30 Dim.s 4000x3000







case #2

Visual Studio: project 속성 > 구성 속성 > 디버깅 > 명령 인수
가로선: 15(개수) 10(최소 픽셀수) 40(최대 픽셀수) 20
세로선: 12 10 40 20




dxListSize = 15     dyListSize = 12
worldSize = 180
scale = 1.11126, 0.833261
CRworld.size = 180
CRworldX:
0.267229  0.236729  0.124688  0.485958  0.0692628  0.545564  0.0944922  0.443539  0.171158  0.294299  0.195769  0.150979  -1  -1  -1  
CRworldY:
0.165879  0.399442  0.23958  0.189141  0.133199  0.575565  0.0899842  0.341729  0.207025  -1  -1  -1  



Grids 15x12 Dim.s 1500x1200


posted by maetel
2010. 4. 27. 22:07

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2010. 4. 22. 20:50 Computer Vision
ref.
swPark_2000rti 439쪽: In the initial identification process, we first extract and identify vertical and horizontal lines of the pattern by comparing their cross-ratios, and then we compute the intersections of the lines. Theoretically with this method, we can identify feature points in every frame automatically, but several situations cause problems in the real experiments.


박승우_1999전자공학회지 94쪽: 초기 인식과정에서는 패턴 상의 교점을 인식하기 위해 패턴의 제작과정에서 설명한 것처럼 영상에서 구해진 가로선과 세로선의 Cross-ratio를 패턴의 가로선과 셀로선이 가지는 Cross-ratio와 비교함으로써 몇번째 선인지를 인식하게 된다. 이러한 방법을 이용해 영상으로부터 자동으로 특징점을 찾고 인식할 수 있지만, 실제 적용 상에서는 몇 가지 제한점이 따르게 된다.



0. NMS (Non Maximum Suppression)을 적용한 Hough transform에 의한 Line 찾기

OpenCV 라이브러리의 HoughLines2() 함수는 전에 기술한 바( http://leeway.tistory.com/801 )와 같이 실제 패턴에서는 하나의 직선 위에 놓인 점들에 대해 이미지 프레임에서 검출된 edges을 가지고 여러 개의 직선을 찾는 결과를 보인다. 이는 HoughLines2() 함수가 출력하는, 직선을 정의하는 두 파라미터 rho와 theta에 대해 ( x*cos(theta) + y*sin(theta) = rho ) 계산된 값들이 서로 비슷하게 나오는 경우에 최적값을 선별하는 과정을 거치지 않고 모든 값들을 그대로 내보내기 때문이다. 그래서 OpenCV의 이 함수를 이용하지 않고, 따로 Hough transform을 이용하여 선을 찾는 함수를 만들되 여기에 NMS (Non Maximum Suppression)를 적용하도록 해 보았다. 하지만 이 함수를 실시간 비디오 카메라 입력에 대해 매 프레임마다 실행시키면 속도가 매우 느려져 쓸 수 없었다. 그래서, 속도 면에서 월등한 성능을 보이는 OpenCV의 HoughLines2() 함수를 그대로 따 오고 대신 여기에 NMS 부분을 추가하여 수정한 함수를 매 입력 프레임마다 호출하는 방법을 택하였고, 실시간 처리가 가능해졌다. (->소스코드)


http://en.wikipedia.org/wiki/Feature-point_detection



1. 직선의 순서 매기기

산출된 수직선들을 이미지 프레임의 왼쪽에서부터 오른쪽으로 나타난 순서대로 번호를 매기고 (아래 그림의 붉은색 번호), 수평선들을 위로부터 아래로 나타난 순서대로 번호를 매긴다 (아래 그림의 푸른색 번호). 이 과정에서 수직선의 경우 x절편, 수평선의 경우 y절편의 값을 기준으로 하여 계산하였다. 



아래 코드에서 "line.x0"가 "line" 직선의 x절편임
// rearrange lines from left to right
void indexLinesY ( CvSeq* lines, IplImage* image )
{   
    // retain the values of "rho" & "theta" of found lines
    int numLines = lines->total;
    // line_param line[numLines]; 이렇게 하면 나중에 이 변수를 밖으로 빼낼 때 (컴파일 에러는 안 나지만) 문제가 됨.
    line_param *line = new line_param[numLines];

    for( int n = 0; n < numLines; n++ )
    {
        float* newline = (float*)cvGetSeqElem(lines,n);
        line[n].rho = newline[0];
        line[n].theta = newline[1];
    }
   
    // rearrange "line" array in geometrical order
    float temp_rho, temp_theta;
    for( int n = 0; n < numLines-1; n++ )
    {
        for ( int k = n+1; k < numLines; k++ )
        {
            float x0_here = line[n].rho / cos(line[n].theta);
            float x0_next = line[k].rho / cos(line[k].theta);
            if( x0_here > x0_next ) {
                temp_rho = line[n].rho;        temp_theta = line[n].theta;
                line[n].rho = line[k].rho;        line[n].theta = line[k].theta;
                line[k].rho = temp_rho;        line[k].theta = temp_theta;
            }
        }
    }
    // calculate the other parameters of the rearranged lines
    for( int n = 0; n < numLines; n++ )
    {
        line[n].a = cos(line[n].theta);
        line[n].b = sin(line[n].theta);
        line[n].x0 = line[n].rho / line[n].a;
        line[n].y0 = line[n].rho / line[n].b;
       
        cout << "x[" << n << "] = " << line[n].x0 << "    y[" << n << "] = " << line[n].y0 ;
        cout << "    rho[" << n << "] = " << line[n].rho << "    theta[" << n << "] = " << line[n].theta << endl;
       
        char txt[100]; sprintf(txt, "%d", n);
        cvPutText(image, txt, cvPoint(line[n].x0, 10+n*10), &cvFont(0.8), CV_RGB(255,50,50));
    }
}

초록색으로 칠한 줄에 대한 설명:
void indexLinesY( CvSeq* lines, IplImage* image ) 함수를 line_param* indexLinesY( CvSeq* lines, IplImage* image )라고 바꾸어 structure로 선언한 line_param 형태의 배열을 출력하도록 하고, 이 출력값을 교점을 구하는 함수의 입력으로 하면
line_param line[numLines];
이렇게 함수 안에서 선언했던 부분이 함수 밖으로 출력되어 다른 함수의 입력이 될 때 입력값이 제대로 들어가지 않는다. 다음과 같이 바꾸어 주어야 함.
line_param *line = new line_param[numLines];

ref. http://cplusplus.com/reference/std/new/



상기 0-1의 과정을 적용한 코드의 실행 결과




그런데 다음과 같은 문제를 발견함.

x방향 DoG 필터링한 영상

y방향 DoG 필터링한 영상

Hough transform에 NMS를 적용하여 검출한 직선에 순번을 매긴 결과


이미지 프레임에서 찾은 수평선들을 보면 제일 위쪽의 직선이 0번이 아니라 4번부터 순번이 매겨져 있다. 프레임 바깥에 (위쪽에) 세 개의 직선이 더 있다는 뜻인데...

수직성 상의 edges 검출 영상

수평선 상의 edges 검출 영상

수직선들을 왼쪽부터 오른쪽으로, 수평선들을 위에서 아래로 정열한 결과


왼쪽 두 개는 line detection에 입력으로 쓰인 영상이고, 마지막 것은 이로부터 순서대로 정열한 직선을 규정하는 매개변수 출력값이다. 0번부터 3번 수평선의 y절편 값이 음수로 나타나고 있다.



2. 교점의 순서 매기기

격자 무늬의 직선들의 교점(intersections)을 과정1에서 계산한 직선의 순번을 이용하여 indexing한다. 빨간 세로선 0번과 파란 가로선 0번의 교점은 00번, 이런 식으로.

// index intersection points of lines in X and Y
CvPoint* indexIntersections ( line_param* lineX, line_param* lineY, int numLinesX, int numLinesY, IplImage* image )
// find intersections of lines, "linesX" & "linesY", and draw them in "image"
{
    int numPoints = (numLinesX+1) * (numLinesY+1);
    CvPoint *p = new CvPoint[numPoints]; // the intersection point of lineX[i] and lineY[j]
    char txt[100]; // text to represent the index number of an intersection
   
    for( int i = 0; i < MIN(numLinesX,100); i++ )
    {
        for( int j = 0; j < MIN(numLinesY,100); j++ )
        {             
            int indexP = i*numLinesY + j;     
            float Px = ( lineX[i].rho*lineY[j].b - lineY[j].rho*lineX[i].b ) / ( lineX[i].a*lineY[j].b - lineX[i].b*lineY[j].a ) ;
            float Py = ( lineX[i].rho - lineX[i].a*Px ) / lineX[i].b ;
            p[indexP].x = cvRound(Px);
            p[indexP].y = cvRound(Py);
           
            // display the points in an image
            cvCircle( image, p[indexP], 3, CV_RGB(0,255,50) /* , <#int line_type#>, <#int shift#> */ );   
            sprintf(txt, "%d", indexP);   
            cvPutText(image, txt, p[indexP], &cvFont(0.7), CV_RGB(50,255,250));           
        }
    }       
    return p;
}


입력 영상 input을 단일 채널 temp로 바꾸어 1차 DoG 필터링을 하여 검출된 edges를 양 방향 세기 비교와 NMS를 통해 수평 방향과 수직 방향으로 나눈 영상 detected edges를 입력으로 하여 Hough transform에 NMS를 적용하여 line detection을 한 결과를 input 창에 그리고, 이미지 프레임 좌표를 기준으로 검출된 직선들에 순서를 매겨 이로부터 교차점의 위치와 순번을 계산하여 input 창에 표시한다.



현재 상태의 문제점: (1) 패턴과 카메라 모두 정지하여 입력 영상(상좌)이 고정된 경우에, DoG 필터링한 결과(중)는 비교적 안정적이지만 수평, 수직 방향 세기 비교와 NMS를 통해 각 방향에 대해 뽑은 edges를 표시한 영상(하)은 프레임이 들어올 때마다 변화가 있다. 그래서 이 두 영상을 입력으로 하여 직선 찾기를 한 결과(상좌 빨간색 선들)와 이로부터 계산한 교차점들의 위치 및 순번(상좌 연두색 동그라미와 하늘색 숫자)도 불안정하다. (2) 또한 패턴과의 거리에 대해 카메라 렌즈의 초점이 맞지 않으면 결과가 좋지 않다.     





3. 교점의 cross ratio 구하기

각 교점에서 수평 방향으로 다음 세 개의 교점, 수직 방향으로 다음 세 개의 교점을 지나는 직선에 대한 cross ratios를 구한다. 

직선 검출에 오차나 오류가 적을 경우, 아래 테스트 결과에서 보듯 입력 영상의 교차점에 대해 실제 패턴의 직선을 1대 1로 즉각적으로 찾는다.



matching 시험 결과 영상 (위: 실제 패턴 / 아래: 입력 영상)




http://en.wikipedia.org/wiki/Sum_of_squares


posted by maetel
2010. 4. 22. 20:14

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

2010. 4. 22. 20:05 Computer Vision
Graphics and Media Lab
CMC department, Moscow State University
http://graphics.cs.msu.ru/en/science/research/calibration/cpp
posted by maetel
2010. 4. 14. 16:40 Computer Vision
매킨토시에서 OpenCV 버전 2로 업그레이드
http://opencv.willowgarage.com/wiki/Mac_OS_X_OpenCV_Port

현재 연구실 맥미니 사양 (Leopard)
 System Version:    Mac OS X 10.5.8 (9L30)
 Kernel Version:    Darwin 9.8.0


0. MacPorts 업그레이드


1) MacPorts 포트 정보 확인

$ port info macports

MacPorts @1.8.2 (sysutils)
Variants:             darwin_10, darwin_7, darwin_8, darwin_8_i386,
                      darwin_8_powerpc, darwin_9, darwin_9_i386,
                      darwin_9_powerpc, universal

Description:          MacPorts provides the infrastructure that allows easy
                      installation and management of freely available software
                      on Mac OS X 10.4 or newer systems.
Homepage:             http://www.macports.org/

Platforms:            darwin, freebsd
License:              unknown
Maintainers:          macports-mgr@lists.macosforge.org


2) 기존 MacPorts 버전 확인 및 새 버전 다운로드

$ sudo port selfupdate

MacPorts base version 1.710 installed
Downloaded MacPorts base version 1.800

Installing new MacPorts release in /opt/local as root:admin - TCL-PACKAGE in /Library/Tcl; Permissions: 0755


3) MacPorts 새 버전 설치

$ sudo port -v selfupdate
Password:



4) OpenCV 포트 확인

$ port search opencv

opencv @2.0.0 (graphics, science)
    Intel(R) Open Source Computer Vision Library

이 포트를 설치하면 64 비트 OpenCV 2.0이 된다고 하는데, 이 말은 눈표범에 해당하는 게 아닌가 한다. 그냥 표범인 지금 맥미니는 (확실친 않지만) 32비트인 것으로 확인되었다.  어쨌든, 무엇보다 snow leopard 스노우 러퍼드 (Mac OS X 10.6) 사용자는 quicktime (iSight)과 carbon (GUI) support를 포기해야 한다고 되어 있다. 나는 그냥 러퍼드 (Mac OS X 10.5)이지만, 왠지 불안하므로 맥포트로 OpenCV를 업그레이드 하는 방법은 일단 제외하기로 한다. 대신, 공식 위키 안내대로 CMake를 이용하여 OpenCV 새 버전을 설치하기로 한다.


1. CMake를 사용하여 OpenCV 설치하기

1) subversion 설치

 맥포트로 subversion을 다운로드, 설치한다.

$ sudo port install subversion

subversion ( http://en.wikipedia.org/wiki/Subversion_%28software%29 )은 예전 내 파워북에 설치해서 써 본 경험이 있기는 하다. 그때도 이렇게 오래 걸렸었나...



2) CMake 설치

$ sudo port install cmake



cf. CMake?
http://en.wikipedia.org/wiki/CMake
http://www.cmake.org/


3) OpenCV source code 다운로드

svn co https://code.ros.org/svn/opencv/trunk/opencv

에러 메시지가 나와서 기존의 "opencv" 폴더명을 바꾸고 다시 명령을 줬더니, 새로 "opencv" 폴더와 다음 파일들이 생성된다.


마지막에 "Checked out revision 3024."라는 메시지가 나왔다.
https://code.ros.org/trac/opencv/changeset/3024


4) Make 파일 만들기

생성된 opencv 폴더에 들어가서 CMake로 유닉스 메이크 파일을 생성한다. (옵션을 추가할 수 있다. 공식 위키 안내 참조)

$ cd opencv
$ sudo cmake -G "Unix Makefiles"




5) 빌드하기


$ sudo make -j8





$ sudo make install




6) 확인

-1) 파인더 창에서 보이지 않는 디렉토리는 "/usr/local/" 파인더 메뉴의 Go > Go to folder에서 직접 입력하여 들어갈 수 있다.
-2) OpenCV 새 버전을 MacPorts로 설치하지 않았으므로, 맥포츠 명령어 "port installed"로 설치된 포트들을 검색하면 이전에 맥포츠로 설치한 1.0.0 버전만 확인할 수 있다. (이전에 맥포츠로 설치한 1.0.0 버전은 "/opt/local/var/macports/software/opencv/1.0.0_0/opt/local/lib"에 들어 있다.)



2. Xcode에서 OpenCV 라이브러리 사용하기

공식 위키의 안내문:

Using the OpenCV libraries in an Xcode OS X project

These instructions were written for Xcode 3.1.x

  • Create a new XCode project using the Command Line Utility/Standard Tool template
  • Select Project -> Edit Project Settings

  • Set Configuration to All Configurations
  • In the Architectures section, double-click Valid Architectures and remove all the PPC architectures
  • In the Search Paths section set Header Search Paths to /usr/local/include/opencv
  • Close the Project Info window
  • Select Project -> New Group and create a group called OpenCV Frameworks

  • With the new group selected, select Project -> Add to Project…

  • Press the "/" key to get the Go to the folder prompt
  • Enter /usr/local/lib
  • Select libcxcore.dylib, libcvaux.dylib, libcv.dylib, libhighgui.dylib, and libml.dylib.

  • Click Add
  • Uncheck Copy Items… and click Add

Now you should be able to include the OpenCV libraries, compile, and run your project


1) 빌드 환경 설정
 
XCode 메뉴에서 Project -> Edit Project Settings를 클릭하면 Project Info 창이 뜬다. Build 탭에 들어가서
-1) Configuration 설정이 "Active (Debug)"로 되어 있는 것을 "All Configurations"로 변경한다.
-2) Architectures에서 "Valid Architectures"를 더블 클릭하여 목록이 뜨면 그 중 PPC 아케텍처에 해당하는 것들을 모두 삭제한다.
-3) Search Paths에서 Header Search Paths를 " /usr/local/include/opencv  "로 설정한다.


2) OpenCV  프레임웍스를 프로젝트에 추가

-1) Project Info 창을 닫고, 프로젝트에 "New Group"을 추가하여 "OpenCV Frameworks"라 명명한다.
-2) 이 그룹을 선택한 상태로 인용부 설명대로 usr/local/lib에 위치한 5개의 라이브러리 파일을 추가한다.




3. Xcode 프로젝트 테스트...ing




/* Test: video capturing from a camera
 camera: Logitech QuickCam Pro 4000
 */

//#include <OpenCV/OpenCV.h>
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;

int main()
{
    IplImage* image = 0; // image
    // initialize capture from a camera
    CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
    cvNamedWindow("camera");
   
    while(1) {
//        printf("bbbbbbbbbbbbbb");
        if ( !cvGrabFrame(capture) ){
            printf("Could not grab a frame\n\7");
            exit(0);
        }
        else {
            printf("ccccccccccccccccccccc");
            cvGrabFrame( capture ); // capture a frame           
            image = cvRetrieveFrame(capture); // retrieve the caputred frame
           
            cout << image->width << "   " << image->height << endl;
           
            cvShowImage( "camera", image );
           
            if( cvWaitKey(10) >= 0 )
                break;
        }
    }
   
    cvReleaseCapture( &capture ); // release the capture source
    cvDestroyWindow( "camera" );
   
    return 0;
   
}




[Session started at 2010-04-15 01:11:16 +0900.]
2010-04-15 01:11:22.273 opencv2test01[1192:7f23] *** _NSAutoreleaseNoPool(): Object 0xc5f0d0 of class NSThread autoreleased with no pool in place - just leaking
Stack: (0x9143bf4f 0x91348432 0x9134e1a4 0xa260db7 0xa265e9a 0xa2649d3 0xa268cbd 0xa268130 0x90088935 0x93fcedb9 0x93e8f340 0x93e8f6ac 0x90088935 0x93fd117d 0x93e981c4 0x93e8f6ac 0x90088935 0x93fcfa81 0x93e7bc5d 0x93e80b2d 0x93e7b167 0x90088935 0x97ab89f8 0xdbf116 0xe6a016 0xe6a116 0x96917155 0x96917012)
ccccccccccccccccccccc320   240
ccccccccccccccccccccc320   240

[Session started at 2010-04-15 01:11:24 +0900.]
Loading program into debugger…
GNU gdb 6.3.50-20050815 (Apple version gdb-962) (Sat Jul 26 08:14:40 UTC 2008)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-apple-darwin".Program loaded.
sharedlibrary apply-load-rules all
Attaching to program: `/Users/lym/Documents/VIP/2010/opencv2test01/build/Debug/opencv2test01', process 1192.
unable to read unknown load command 0x22
unable to read unknown load command 0x22
StartNextIsochRead-ReadIsochPipeAsync: Error: kIOReturnIsoTooOld - isochronous I/O request for distant past!

The Debugger Debugger is attaching to process(gdb)

실행 중지하면, (gdb) 대신 다음 메시지가 추가되며 프로그램 종료된다.

StartNextIsochRead-ReadIsochPipeAsync: Error: kIOReturnIsoTooOld - isochronous I/O request for distant past!
kill

The Debugger Debugger is attaching to process(gdb)




카메라에서 비디오 입력 받지 말고, 폴더에서 이미지 파일 읽으면

/* Test: video capturing from a camera
 camera: Logitech QuickCam Pro 4000
 */

//#include <OpenCV/OpenCV.h>
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;

int main()
{
    IplImage* image = 0; // image
    // initialize capture from a camera
    CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
    cvNamedWindow("camera");
   
    while(1) {
//        printf("bbbbbbbbbbbbbb");
        if ( !cvGrabFrame(capture) ){
            printf("Could not grab a frame\n\7");
            exit(0);
        }
        else {
            printf("ccccccccccccccccccccc");
            cvGrabFrame( capture ); // capture a frame           
//            image = cvRetrieveFrame(capture); // retrieve the caputred frame
            image = cvLoadImage("werol.jpg"); // retrieve the caputred frame
           
            cout << image->width << "   " << image->height << endl;
           
            cvShowImage( "camera", image );
           
            if( cvWaitKey(10) >= 0 )
                break;
        }
    }
   
    cvReleaseCapture( &capture ); // release the capture source
    cvDestroyWindow( "camera" );
   
    return 0;
   
}



 




[Session started at 2010-04-15 01:26:38 +0900.]
usbConnectToCam-SetConfiguration: Error: kIOReturnNotResponding - device not responding
usbConnectToCam-SetConfiguration: Error: kIOReturnNotResponding - device not responding
usbConnectToCam-SetConfiguration: Error: kIOReturnNotResponding - device not responding
2010-04-15 01:26:43.235 opencv2test01[1333:7f23] *** _NSAutoreleaseNoPool(): Object 0xc56040 of class NSThread autoreleased with no pool in place - just leaking
Stack: (0x9143bf4f 0x91348432 0x9134e1a4 0xa260db7 0xa265e9a 0xa2649d3 0xa268cbd 0xa268130 0x90088935 0x93fcedb9 0x93e8f340 0x93e8f6ac 0x90088935 0x93fd117d 0x93e981c4 0x93e8f6ac 0x90088935 0x93fcfa81 0x93e7bc5d 0x93e80b2d 0x93e7b167 0x90088935 0x97ab89f8 0xdbf116 0xe6a016 0xe6a116 0x96917155 0x96917012)
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825





카메라 캡처 부분을 지우면

/* Test: video capturing from a camera
 camera: Logitech QuickCam Pro 4000
 */

//#include <OpenCV/OpenCV.h>
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;

int main()
{
    IplImage* image = 0; // image
    // initialize capture from a camera
    CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
    cvNamedWindow("camera");
   
    while(1) {
//        printf("bbbbbbbbbbbbbb");
        if ( !cvGrabFrame(capture) ){
            printf("Could not grab a frame\n\7");
            exit(0);
        }
        else {
            printf("ccccccccccccccccccccc");
//            cvGrabFrame( capture ); // capture a frame           
//            image = cvRetrieveFrame(capture); // retrieve the caputred frame
            image = cvLoadImage("werol.jpg"); // retrieve the caputred frame
           
            cout << image->width << "   " << image->height << endl;
           
            cvShowImage( "camera", image );
           
            if( cvWaitKey(10) >= 0 )
                break;
        }
    }
   
    cvReleaseCapture( &capture ); // release the capture source
    cvDestroyWindow( "camera" );
   
    return 0;
   
}


[Session started at 2010-04-15 01:32:37 +0900.]
2010-04-15 01:32:43.091 opencv2test01[1377:7f23] *** _NSAutoreleaseNoPool(): Object 0xc50970 of class NSThread autoreleased with no pool in place - just leaking
Stack: (0x9143bf4f 0x91348432 0x9134e1a4 0xa260db7 0xa265e9a 0xa2649d3 0xa268cbd 0xa268130 0x90088935 0x93fcedb9 0x93e8f340 0x93e8f6ac 0x90088935 0x93fd117d 0x93e981c4 0x93e8f6ac 0x90088935 0x93fcfa81 0x93e7bc5d 0x93e80b2d 0x93e7b167 0x90088935 0x97ab89f8 0xdbf116 0xe6a016 0xe6a116 0x96917155 0x96917012)
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825
ccccccccccccccccccccc558   825




카메라 입력도 이미지 파일 로드도 없이 그냥 이미지를 하나 만들어 준 다음 디스플레이/저장해 보면,

/* Test: video capturing from a camera
 camera: Logitech QuickCam Pro 4000
 */

//#include <OpenCV/OpenCV.h>
#include <cv.h>
#include <highgui.h>
#include <iostream>
using namespace std;

int main()
{
   
    IplImage *iplImg = cvCreateImage(cvSize(500, 500), 8, 3);
    cvZero(iplImg);
    cvLine(iplImg, cvPoint(10, 10), cvPoint(300, 300), CV_RGB(255, 0, 0), 20);
    cvCircle(iplImg, cvPoint(400, 400), 40, CV_RGB(0, 255, 255), 5);
    cvNamedWindow("temp"); cvShowImage("temp", iplImg); cvSaveImage("temp.bmp", iplImg);  cvWaitKey();

    return 0;
}



실행 결과 "temp 창"에 뜨는 (비정상적인) 이미지와 파일로 (정상적으로) 저장되는 "temp.bmp"는 아래와 같다.

실행 폴더에 저장된 temp.bmp

"temp" 창 부분을 화면 캡처한 이미지





http://tech.groups.yahoo.com/group/OpenCV/message/70200


/opt/local/.........../opencv/1.0.0_0/opt/local/include/opencv/

..................../opt/local/lib






MacPorts로 설치했던  OpenCV 1.0.0 테스트

프로젝트 헤더 파일 경로 설정: /opt/local/include/opencv
프로젝트에 추가할 라이브러리 파일 위치:  /opt/local/lib






$ sudo port install opencv


posted by maetel
2010. 4. 12. 16:09 Computer Vision
ref.

Szeliski, Computer Vision: Algorithms and Applications (March 24, 2010 draft): 4.3.2 Hough transforms

http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm

Sonka & Hlavac & Boyle, Image Processing, Analysis, and Machine Vision, third edition: 6.2.6 Hough transforms

http://en.wikipedia.org/wiki/Hough_transform




출처: Szeliski, Computer Vision: Algorithms and Applications (March 24, 2010 draft) 252쪽


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

GML C++ Camera Calibration Toolbox  (0) 2010.04.22
OpenCV 2.1 설치 on Mac OS X  (0) 2010.04.14
OpenCV: cvHoughLines2() 연습 코드  (0) 2010.04.07
OpenCV: cvFitLine() 연습 코드  (0) 2010.04.06
virtual studio 구현: line fitting test  (0) 2010.04.06
posted by maetel
2010. 4. 7. 00:16 Computer Vision
OpenCV 라이브러리의 Hough transform에 의한 직선 찾기 함수


CvSeq* cvHoughLines2(CvArr* image, void* storage, int method, double rho, double theta, int threshold, double param1=0, double param2=0)

Finds lines in a binary image using a Hough transform.

Parameters:
  • image – The 8-bit, single-channel, binary source image. In the case of a probabilistic method, the image is modified by the function
  • storage – The storage for the lines that are detected. It can be a memory storage (in this case a sequence of lines is created in the storage and returned by the function) or single row/single column matrix (CvMat*) of a particular type (see below) to which the lines’ parameters are written. The matrix header is modified by the function so its cols or rows will contain the number of lines detected. If storage is a matrix and the actual number of lines exceeds the matrix size, the maximum possible number of lines is returned (in the case of standard hough transform the lines are sorted by the accumulator value)
  • method

    The Hough transform variant, one of the following:

    • CV_HOUGH_STANDARD - classical or standard Hough transform. Every line is represented by two floating-point numbers $(\rho , \theta )$, where $\rho $ is a distance between (0,0) point and the line, and $\theta $ is the angle between x-axis and the normal to the line. Thus, the matrix must be (the created sequence will be) of CV_32FC2 type
    • CV_HOUGH_PROBABILISTIC - probabilistic Hough transform (more efficient in case if picture contains a few long linear segments). It returns line segments rather than the whole line. Each segment is represented by starting and ending points, and the matrix must be (the created sequence will be) of CV_32SC4 type
    • CV_HOUGH_MULTI_SCALE - multi-scale variant of the classical Hough transform. The lines are encoded the same way as CV_HOUGH_STANDARD
  • rho – Distance resolution in pixel-related units
  • theta – Angle resolution measured in radians
  • threshold – Threshold parameter. A line is returned by the function if the corresponding accumulator value is greater than threshold
  • param1

    The first method-dependent parameter:

    • For the classical Hough transform it is not used (0).
    • For the probabilistic Hough transform it is the minimum line length.
    • For the multi-scale Hough transform it is the divisor for the distance resolution $\rho $. (The coarse distance resolution will be $\rho $ and the accurate resolution will be $(\rho / \texttt{param1})$).
  • param2

    The second method-dependent parameter:

    • For the classical Hough transform it is not used (0).
    • For the probabilistic Hough transform it is the maximum gap between line segments lying on the same line to treat them as a single line segment (i.e. to join them).
    • For the multi-scale Hough transform it is the divisor for the angle resolution $\theta $. (The coarse angle resolution will be $\theta $ and the accurate resolution will be $(\theta / \texttt{param2})$).

Memory storage is a low-level structure used to store dynamicly growing data structures such as sequences, contours, graphs, subdivisions, etc.


입력 이미지가 8비트 단일 채널이어야 하므로,
다음과 같이 "IPL_DEPTH_32F"로 생성했던 입력 이미지 (iplDoGx)를 바꾸어 "8" 비트 depth짜리 새로운 이미지 (iplEdgeY)에 저장한다.

            cvConvert(iplDoGx, iplEdgeY);


두번째 인자 " void* storage" 는 탐지된 직선을 저장할 메모리. 이 함수의 아웃풋에 해당한다.

CvMemStorage

Growing memory storage.

typedef struct CvMemStorage
{
struct CvMemBlock* bottom;/* first allocated block */
struct CvMemBlock* top; /* the current memory block - top of the stack */
struct CvMemStorage* parent; /* borrows new blocks from */
int block\_size; /* block size */
int free\_space; /* free space in the \texttt{top} block (in bytes) */
} CvMemStorage;



CvMemStorage* cvCreateMemStorage(int blockSize=0)

Creates memory storage.

Parameter:blockSize – Size of the storage blocks in bytes. If it is 0, the block size is set to a default value - currently it is about 64K.


 그 아웃풋을 다음의 CvSeq 형태의 자료 구조체 안에 저장한다.

CvSeq

Growable sequence of elements.

#define CV_SEQUENCE\_FIELDS() \
int flags; /* micsellaneous flags */ \
int header_size; /* size of sequence header */ \
struct CvSeq* h_prev; /* previous sequence */ \
struct CvSeq* h_next; /* next sequence */ \
struct CvSeq* v_prev; /* 2nd previous sequence */ \
struct CvSeq* v_next; /* 2nd next sequence */ \
int total; /* total number of elements */ \
int elem_size;/* size of sequence element in bytes */ \
char* block_max;/* maximal bound of the last block */ \
char* ptr; /* current write pointer */ \
int delta_elems; /* how many elements allocated when the sequence grows
(sequence granularity) */ \
CvMemStorage* storage; /* where the seq is stored */ \
CvSeqBlock* free_blocks; /* free blocks list */ \
CvSeqBlock* first; /* pointer to the first sequence block */

typedef struct CvSeq
{
CV_SEQUENCE_FIELDS()
} CvSeq;

The structure CvSeq is a base for all of OpenCV dynamic data structures.


그 저장된 값을 읽는 함수

char* cvGetSeqElem(const CvSeq* seq, int index)

Returns a pointer to a sequence element according to its index.

#define CV_GET_SEQ_ELEM( TYPE, seq, index )  (TYPE*)cvGetSeqElem( (CvSeq*)(seq), (index) )
Parameters:
  • seq – Sequence
  • index – Index of element




accumulator value 란?







"detected edges" 이미지에 대해 Hough transform에 의한 line fitting 한 결과를 "input" 이미지에 그리고 있음




opencv/opencv/src/cv/cvhough.cpp 를 열면, 다음의 네 부분으로 나뉘어 정의되어 있다.
Classical Hough Transform
Multi-Scale variant of Classical Hough Transform 
Probabilistic Hough Transform      
Circle Detection

이 중 "Classical Hough Transform" 부분은 다음과 같음.
typedef struct CvLinePolar
{
    float rho;
    float angle;
}
CvLinePolar;
/*=====================================================================================*/

#define hough_cmp_gt(l1,l2) (aux[l1] > aux[l2])

static CV_IMPLEMENT_QSORT_EX( icvHoughSortDescent32s, int, hough_cmp_gt, const int* )

/*
Here image is an input raster;
step is it's step; size characterizes it's ROI;
rho and theta are discretization steps (in pixels and radians correspondingly).
threshold is the minimum number of pixels in the feature for it
to be a candidate for line. lines is the output
array of (rho, theta) pairs. linesMax is the buffer size (number of pairs).
Functions return the actual number of found lines.
*/
static void
icvHoughLinesStandard( const CvMat* img, float rho, float theta,
                       int threshold, CvSeq *lines, int linesMax )
{
    int *accum = 0;
    int *sort_buf=0;
    float *tabSin = 0;
    float *tabCos = 0;

    CV_FUNCNAME( "icvHoughLinesStandard" );

    __BEGIN__;

    const uchar* image;
    int step, width, height;
    int numangle, numrho;
    int total = 0;
    float ang;
    int r, n;
    int i, j;
    float irho = 1 / rho;
    double scale;

    CV_ASSERT( CV_IS_MAT(img) && CV_MAT_TYPE(img->type) == CV_8UC1 );

    image = img->data.ptr;
    step = img->step;
    width = img->cols;
    height = img->rows;

    numangle = cvRound(CV_PI / theta);
    numrho = cvRound(((width + height) * 2 + 1) / rho);

    CV_CALL( accum = (int*)cvAlloc( sizeof(accum[0]) * (numangle+2) * (numrho+2) ));
    CV_CALL( sort_buf = (int*)cvAlloc( sizeof(accum[0]) * numangle * numrho ));
    CV_CALL( tabSin = (float*)cvAlloc( sizeof(tabSin[0]) * numangle ));
    CV_CALL( tabCos = (float*)cvAlloc( sizeof(tabCos[0]) * numangle ));
    memset( accum, 0, sizeof(accum[0]) * (numangle+2) * (numrho+2) );

    for( ang = 0, n = 0; n < numangle; ang += theta, n++ )
    {
        tabSin[n] = (float)(sin(ang) * irho);
        tabCos[n] = (float)(cos(ang) * irho);
    }

    // stage 1. fill accumulator
    for( i = 0; i < height; i++ )
        for( j = 0; j < width; j++ )
        {
            if( image[i * step + j] != 0 )
                for( n = 0; n < numangle; n++ )
                {
                    r = cvRound( j * tabCos[n] + i * tabSin[n] );
                    r += (numrho - 1) / 2;
                    accum[(n+1) * (numrho+2) + r+1]++;
                }
        }

    // stage 2. find local maximums
    for( r = 0; r < numrho; r++ )
        for( n = 0; n < numangle; n++ )
        {
            int base = (n+1) * (numrho+2) + r+1;
            if( accum[base] > threshold &&
                accum[base] > accum[base - 1] && accum[base] >= accum[base + 1] &&
                accum[base] > accum[base - numrho - 2] && accum[base] >= accum[base + numrho + 2] )
                sort_buf[total++] = base;
        }

    // stage 3. sort the detected lines by accumulator value
    icvHoughSortDescent32s( sort_buf, total, accum );

    // stage 4. store the first min(total,linesMax) lines to the output buffer
    linesMax = MIN(linesMax, total);
    scale = 1./(numrho+2);
    for( i = 0; i < linesMax; i++ )
    {
        CvLinePolar line;
        int idx = sort_buf[i];
        int n = cvFloor(idx*scale) - 1;
        int r = idx - (n+1)*(numrho+2) - 1;
        line.rho = (r - (numrho - 1)*0.5f) * rho;
        line.angle = n * theta;
        cvSeqPush( lines, &line );
    }

    __END__;

    cvFree( &sort_buf );
    cvFree( &tabSin );
    cvFree( &tabCos );
    cvFree( &accum );
}






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

OpenCV 2.1 설치 on Mac OS X  (0) 2010.04.14
Hough transform  (0) 2010.04.12
OpenCV: cvFitLine() 연습 코드  (0) 2010.04.06
virtual studio 구현: line fitting test  (0) 2010.04.06
virtual studio 구현: gradient filtering  (0) 2010.04.04
posted by maetel
2010. 4. 6. 23:27 Computer Vision
OpenCV 라이브러리의 line fitting 함수

void cvFitLine(const CvArr* points, int dist_type, double param, double reps, double aeps, float* line)

Fits a line to a 2D or 3D point set.

Parameters:
  • points – Sequence or array of 2D or 3D points with 32-bit integer or floating-point coordinates
  • dist_type – The distance used for fitting (see the discussion)
  • param – Numerical parameter (C) for some types of distances, if 0 then some optimal value is chosen
  • reps – Sufficient accuracy for the radius (distance between the coordinate origin and the line). 0.01 is a good default value.
  • aeps – Sufficient accuracy for the angle. 0.01 is a good default value.
  • line – The output line parameters. In the case of a 2d fitting, it is an array of 4 floats (vx, vy, x0, y0) where (vx, vy) is a normalized vector collinear to the line and (x0, y0) is some point on the line. in the case of a 3D fitting it is an array of 6 floats (vx, vy, vz, x0, y0, z0) where (vx, vy, vz) is a normalized vector collinear to the line and (x0, y0, z0) is some point on the line

ref.
Structural Analysis and Shape Descriptors — OpenCV 2.0 C Reference





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

Hough transform  (0) 2010.04.12
OpenCV: cvHoughLines2() 연습 코드  (0) 2010.04.07
virtual studio 구현: line fitting test  (0) 2010.04.06
virtual studio 구현: gradient filtering  (0) 2010.04.04
OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
posted by maetel
2010. 4. 6. 23:26 Computer Vision
overview:
렌즈의 왜곡 현상 때문에 이미지 상에 검출된 edge points들은 (직선에서 왜곡된) 2차 곡선을 그리게 된다. 이 곡선의 방정식을 먼저 구한 후에, 렌즈 왜곡 변수를 "0"으로 두고 나오는 직선들로부터 비로소 cross-ratio 값이 보존된다.

ref.  2010/02/10 - [Visual Information Processing Lab] - Seong-Woo Park & Yongduek Seo & Ki-Sang Hong

swPark_2000rti 440쪽: "The cross-ratio is not preserved for the (image) frame coordinate, positions of the feature points in an image, or for the distorted image coordinate. Cross-ratio is invariant only for the undistorted coordinate." (swPark_20

박승우_1999전자공학회지 96쪽: "이렇게 곡선으로 나타난 가로선과 세로선을 직선으로 피팅할 경우 cross-ratio는 왜곡 현상 때문에 이 선들에 대해서는 보존되지 않게 된다. 따라서 정확한 피팅을 위해서는 아래와 같이 렌즈의 왜곡변수(k1)를 고려한 이차곡선으로의 피팅이 필요하다.

Y = a*X + b/(1+k1*R^2) = a*X + b/(1+k1*(X^2+Y^2)) <--- 이 식은 영어 논문 (19)식과 한글 논문 (15)식을 조합, 수정한 식임. 확인 필요

이 식을 피팅해서 계수 a, b, k1를 구하고, 여기서 k1=0을 두면 왜곡이 보상된 점에 대한 직선식을 구할 수 있다. 이렇게 구해진 직선들을 패턴의 가로선들과 세로선들의 cross-ratio와 비교함으로써 영상에서 찾아진 선들을 인식할 수 있다. 또한 영상에서의 특징점은 이 식에 의해 피팅된 가로선들과 세로선들의 교점으로 정확하게 구할 수 있다."


그런데, 
현재 시험용 패턴과 코드로부터 촬영, 검출된 이미지 상의 점들은 거의 직선에 가깝다. 우선 OpenCV 라이브러리의 cvHoughLines2() 함수에 의한 직선 찾기를 해 보자.

2010/04/07 - [Visual Information Processing Lab] - OpenCV: cvHoughLines2() 연습 코드


1) 교점 구하기 테스트
line fitting을 통해 찾은 직선들로부터 패턴 격자의 corner points를 구하는 것을 시험해 본다.




실시간으로 산출하는 데 무리가 없음이 확인되었다.

2)
그러나, line fitting의 결과가 깔끔하지 않은 문제를 우선 해결해야 한다. (rho, theta, threshold 등의 함수 매개변수 값을 조정하는 것을 포함하여 사용 중인 웹캠에 적합한 데이터 처리가 필요하다.)

현재의 코드로부터 나오는 결과를 정리해 두면 아래와 같다.

NMS와 동시에 수평선 또는 수직선 위의 점들을 따로 추출한 결과 이미지 ("iplEdgeX"와  "iplEdgeY")를 cvHoughLines2() 함수의 입력으로 하고,
double rho = 1.0; // distance resolution in pixel-related units
double theta = 1.0; // angle resolution measured in radians
int threshold = 20; // (A line is returned by the function if the corresponding accumulator value is greater than threshold)
위와 같이 매개변수 값을 주면 검출된 직선들과 그로부터 계산한 교점들은 다음과 같이 나타난다.

수직선 상의 edges만 검출한 영상

수평선 상의 edges만 검출한 영상

Hough transform에 의한 line fitting 한 결과



(Non Maximal suppression (NMS)을 하기 전에) 1차 DoG 필터를 이미지 프레임의 x 방향, y 방향으로 적용한 결과 이미지 ("iplDoGx"와 "iplDoGy")를 cvHoughLines2() 함수의 입력으로 하고,
double rho = 1.0; // distance resolution in pixel-related units
double theta = 1.0; // angle resolution measured in radians
int threshold = 20; // (A line is returned by the function if the corresponding accumulator value is greater than threshold)
위와 같이 매개변수 값들을 주면 검출된 직선들과 그로부터 계산한 교점들은 다음과 같이 나타난다.

x방향으로 DoG 필터를 적용한 이미지

y방향으로 DoG 필터를 적용한 이미지

Hough transform에 의한 line fitting한 결과




그러니까... 실제로 한 직선 상의 점들로부터 여러 개의 직선을 찾게 되는 것은 edge points로 detection된 (흰색으로 보이는) 픽셀 부분의 세기값이 약하거나 일정하지 않기 때문인 것 같다. 입력 이미지를 binary로 바꾸고 cvHoughLines2()의 입력으로 accumulator value에 기준값을 주는 파라미터 threshold를 증가시키면 될 것 같다.



Try #1. 입력 이미지 이진화

NMS와 동시에 수평선 또는 수직선 위의 점들을 따로 추출한 결과 이미지 ("iplEdgeX"와  "iplEdgeY")를 이진화하고, 
double rho = 1.0; // distance resolution in pixel-related units
double theta = 1.0; // angle resolution measured in radians
int threshold = 40; // ("A line is returned by the function if the corresponding accumulator value is greater than threshold.")
위와 같이 매개변수 값들을 주면 검출된 직선들과 그로부터 계산한 교점들은 다음과 같이 나타난다.

수직선 상의 edges만 검출하여 이진화한 영상

수평선 상의 edges만 검출하여 이진화한 영상

Hough transform에 의한 line fitting한 결과


실제로 한 직선에 여러 개의 직선이 검출되는 빈도는 현저히 줄지만 대신 실제 직선 중에 검출되지 않는 것이 생긴다.


Try #2. line fitting의 입력 이미지 처리 & 매개변수 조정



Try #3. 실제로 하나인데 여러 개로 겹쳐서 나오는 직선들의 평균을 취해 하나로 합침

다음과 같은 입력 영상에 대해 탐지된 직선들의 방정식을 정의하는 매개변수 (rho와 theta) 값을 출력해 보면 아래와 같이 나온다.

# of found lines = 8 vertical   22 horizontal
vertical
rho = 172.6    theta = 0
rho = 133    theta = 0.139626
rho = -240.2    theta = 2.84489
rho = -209    theta = 2.98451
rho = 91.8    theta = 0.279253
rho = 173.8    theta = 0
rho = 52.6    theta = 0.401426
rho = 53.8    theta = 0.418879
horizontal
rho = 81    theta = 1.55334
rho = 53.4    theta = 1.55334
rho = 155    theta = 1.55334
rho = 114.6    theta = 1.55334
rho = 50.6    theta = 1.5708
rho = 29.8    theta = 1.55334
rho = 76.6    theta = 1.5708
rho = 112.6    theta = 1.5708
rho = 9.8    theta = 1.55334
rho = 152.6    theta = 1.5708
rho = 153.8    theta = 1.5708
rho = 150.6    theta = 1.5708
rho = 6.6    theta = 1.5708
rho = 78.6    theta = 1.5708
rho = 205.4    theta = 1.55334
rho = 27.8    theta = 1.5708
rho = 8.6    theta = 1.5708
rho = 201.8    theta = 1.5708
rho = 110.6    theta = 1.5708
rho = 49.8    theta = 1.5708
rho = 48.6    theta = 1.5708
rho = 111.8    theta = 1.5708





잠시 현재 상태 기록: cross ratios를 이용한 격자 무늬 패턴과 line detection 시험 + feature points matching을 위한 교점 찾기와 순번 부여 시험


to do next:
1) line detection의 error 교정
2) (rhoX, thetaX, rhoY, thetaY로 정의되는) 교점 indexing



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

OpenCV: cvHoughLines2() 연습 코드  (0) 2010.04.07
OpenCV: cvFitLine() 연습 코드  (0) 2010.04.06
virtual studio 구현: gradient filtering  (0) 2010.04.04
OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
Image Filtering  (0) 2010.04.03
posted by maetel
2010. 4. 4. 17:33 Computer Vision
패턴 격자 무늬의 꼭지점 찾기

ref. swPark_2000rti.pdf

2010/04/04 - [Visual Information Processing Lab] - OpenCV: cvFilter2D() 연습 코드
2010/04/03 - [Visual Information Processing Lab] - Image Filtering



1) 1차 DoG filter 만들기: x방향과 y방향의 local maxima를 찾는다.

swPark_2000rti.pdf 440쪽:
"To find the edge of the grid, a first-order Derivative of Gaussian (DoG) filter with a kernel h = [-1, -7, -15, 0, 15, 7, 1] is used."




1차 DoG 필터 테스트:

입력 영상

흑백 영상

수평 방향 1차 DoG 필터링한 영상

수직 방향 1차 DoG 필터링한 영상





2) 다음 이미지에서 보다 확실히 나타나는, 2가지 문제를 해결해야 함

입력 영상

흑백 영상

x방향 DoG 필터링한 영상

y방향 DoG 필터링한 영상


(1) 패턴 격자에서 흑->백으로 넘어갈 때에만 edge 검출 (백->흑인 경우에는 무효)
(2) 검출된 edge 영역이 너무 두터움 (Non-Maxima Suppression을 해 주어야 한다고 함)


필터링 함수가 실제로 어떻게 이미지에 (색상/세기)값을 넣는지 볼 수 있을까 하여 cvFilter() 함수의 정의를 찾아 보니, 다음 부분이 나옴.

opencv/opencv/src/cv/cvfilter.cpp
CV_IMPL void
cvFilter2D( const CvArr* srcarr, CvArr* dstarr, const CvMat* _kernel, CvPoint anchor )
{
    cv::Mat src = cv::cvarrToMat(srcarr), dst = cv::cvarrToMat(dstarr);
    cv::Mat kernel = cv::cvarrToMat(_kernel);

    CV_Assert( src.size() == dst.size() && src.channels() == dst.channels() );

    cv::filter2D( src, dst, dst.depth(), kernel, anchor, 0, cv::BORDER_REPLICATE );
}

본론인 "cv::filter2D"에 대하여 같은 파일 안에 다음의 정의가 있음.

template<typename ST, class CastOp, class VecOp> struct Filter2D : public BaseFilter
{
    typedef typename CastOp::type1 KT;
    typedef typename CastOp::rtype DT;
   
    Filter2D( const Mat& _kernel, Point _anchor,
        double _delta, const CastOp& _castOp=CastOp(),
        const VecOp& _vecOp=VecOp() )
    {
        anchor = _anchor;
        ksize = _kernel.size();
        delta = saturate_cast<KT>(_delta);
        castOp0 = _castOp;
        vecOp = _vecOp;
        CV_Assert( _kernel.type() == DataType<KT>::type );
        preprocess2DKernel( _kernel, coords, coeffs );
        ptrs.resize( coords.size() );
    }

    void operator()(const uchar** src, uchar* dst, int dststep, int count, int width, int cn)
    {
        KT _delta = delta;
        const Point* pt = &coords[0];
        const KT* kf = (const KT*)&coeffs[0];
        const ST** kp = (const ST**)&ptrs[0];
        int i, k, nz = (int)coords.size();
        CastOp castOp = castOp0;

        width *= cn;
        for( ; count > 0; count--, dst += dststep, src++ )
        {
            DT* D = (DT*)dst;

            for( k = 0; k < nz; k++ )
                kp[k] = (const ST*)src[pt[k].y] + pt[k].x*cn;

            i = vecOp((const uchar**)kp, dst, width);

            for( ; i <= width - 4; i += 4 )
            {
                KT s0 = _delta, s1 = _delta, s2 = _delta, s3 = _delta;

                for( k = 0; k < nz; k++ )
                {
                    const ST* sptr = kp[k] + i;
                    KT f = kf[k];
                    s0 += f*sptr[0];
                    s1 += f*sptr[1];
                    s2 += f*sptr[2];
                    s3 += f*sptr[3];
                }

                D[i] = castOp(s0); D[i+1] = castOp(s1);
                D[i+2] = castOp(s2); D[i+3] = castOp(s3);
            }

            for( ; i < width; i++ )
            {
                KT s0 = _delta;
                for( k = 0; k < nz; k++ )
                    s0 += kf[k]*kp[k][i];
                D[i] = castOp(s0);
            }
        }
    }

    Vector<Point> coords;
    Vector<uchar> coeffs;
    Vector<uchar*> ptrs;
    KT delta;
    CastOp castOp0;
    VecOp vecOp;
};



Try #1.
-1) 필터링의 결과 이미지의 bit depth를 "8"이 아니라 "IPL_DEPTH_32F"로 바꾼 다음,  음수로 나온 gradient 값을 양수로 바꾸어 준다.
그런데, 입력 영상을 담을 메모리를 별도로 생성하지 않고, 다음과 같이 비디오 프레임 캡처 시 만들어 주므로 인위적으로 설정해 줄 수 없다.
iplInput = cvRetrieveFrame(capture);

그래서 cvConvert() 함수를 이용한다. 정의는 다음과 같다.

OpenCV.framework/Versions/A/Headers/cxcore.h
#define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )

void cvConvertScale(const CvArr* src, CvArr* dst, double scale=1, double shift=0)

Converts one array to another with optional linear transformation.

#define cvCvtScale cvConvertScale
#define cvScale cvConvertScale
#define cvConvert(src, dst ) cvConvertScale((src), (dst), 1, 0 )
Parameters:
  • src – Source array
  • dst – Destination array
  • scale – Scale factor
  • shift – Value added to the scaled source array elements


-2) Non Maximum Suppression  (NMS)
이웃 화소들의 세기값을 비교하여 해당 픽셀이 최대값이 아니면 "0"으로 하여 지워 준다



입력 영상

x방향 DoG filtering하고 NMS한 영상

y방향 DoG filtering하고 NMS한 영상





Szeliski, Computer Vision: Algorithms and Applications, 214쪽



Szeliski, Computer Vision: Algorithms and Applications, 215쪽

ref.
http://research.microsoft.com/en-us/um/people/szeliski/Book/
2010/04/06 - [Visual Information Processing Lab] - Non Maximum Suppression (NMS)


 
Try #2.
Sobel 마스크 이용



3)  gradient의 방향 판별
 
: 검출된 edge의 Gx, Gy의 절대값을 비교하여 vertical인지 horizontal인지 direction을 판별한다.
이로부터 그 점이 수평선 위의 점인지 수직선 위의 점인지를 구별하여 다음 단계인 line fitting에 적용한다.

ref.  2010/02/23 - [Visual Information Processing Lab] - virtual studio 구현: workflow







입력 영상

수직선 상의 edges만 검출한 영상

수평선 상의 edges만 검출한 영상




입력 영상

수직선 상의 edges만 검출한 영상

수평선 상의 edges만 검출한 영상


잘 됨 ^^

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

OpenCV: cvFitLine() 연습 코드  (0) 2010.04.06
virtual studio 구현: line fitting test  (0) 2010.04.06
OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
Image Filtering  (0) 2010.04.03
OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
posted by maetel
2010. 4. 4. 00:00 Computer Vision
http://en.wikipedia.org/wiki/Convolution

void cvFilter2D(const CvArr* src, CvArr* dst, const CvMat* kernel, CvPoint anchor=cvPoint(-1, -1))

Convolves an image with the kernel.

Parameters:
  • src – The source image
  • dst – The destination image
  • kernel – Convolution kernel, a single-channel floating point matrix. If you want to apply different kernels to different channels, split the image into separate color planes using Split and process them individually
  • anchor – The anchor of the kernel that indicates the relative position of a filtered point within the kernel. The anchor shoud lie within the kernel. The special default value (-1,-1) means that it is at the kernel center



elt3470@naver: 사용자가 kernel에 원하는 행렬을 입력함으로써, LPF, HPF 등을 직접 디자인해서 사용할 수 있습니다.

=> 그러므로,
DoG (Derivative of Gaussian) 필터도 만들어 넣을 수 있다.



예로, 5x5 Gaussian  kernel을 만들어서 필터링하면 다음과 같이 영상을 smoothing하게 된다.

ref. 
2010/04/03 - [Visual Information Processing Lab] - Image Filtering





입력 영상

흑백 영상

2차원 5x5 Gaussian convolution 영상 (smoothing)



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

virtual studio 구현: line fitting test  (0) 2010.04.06
virtual studio 구현: gradient filtering  (0) 2010.04.04
Image Filtering  (0) 2010.04.03
OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
OpenCV: CV_IMAGE_ELEM  (0) 2010.04.02
posted by maetel
2010. 4. 3. 23:13 Computer Vision
Richard Szeliski, Computer Vision: Algorithms and Applications: 3.2 Linear Filtering


szeliski_3fig13

Richard Szeliski, Computer Vision: Algorithms and Applications 115쪽





Image Filtering — OpenCV 2.0 C Reference


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

virtual studio 구현: gradient filtering  (0) 2010.04.04
OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
OpenCV: CV_IMAGE_ELEM  (0) 2010.04.02
OpenCV: cvFindContours  (0) 2010.04.02
posted by maetel
2010. 4. 3. 22:27 Computer Vision
OpenCV 2.0 C++ reference - Image Filtering


void cvSobel(const CvArr* src, CvArr* dst, int xorder, int yorder, int apertureSize=3)

Calculates the first, second, third or mixed image derivatives using an extended Sobel operator.

Parameters:
  • src – Source image of type CvArr*
  • dst – Destination image
  • xorder – Order of the derivative x
  • yorder – Order of the derivative y
  • apertureSize – Size of the extended Sobel kernel, must be 1, 3, 5 or 7


간단한 예로, 다음과 같은 (1차 미분, 크기3의) 마스크를 적용하면,

x방향 Sobal mask:

\vecthreethree {-1}{0}{1} {-2}{0}{2} {-1}{0}{1}


y방향 Sobel mask:

\vecthreethree {-1}{-2}{-1} {0}{0}{0} {1}{2}{1}






입력 영상

흑백 영상

x방향 Sobel 마스크로 필터링한 영상

y방향 Sobel 마스크로 필터링한 영상




입력 영상

흑백 영상

x방향 Sobel 마스크로 필터링한 영상

y방향 Sobel 마스크로 필터링한 영상




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

OpenCV: cvFilter2D() 연습 코드  (0) 2010.04.04
Image Filtering  (0) 2010.04.03
OpenCV: CV_IMAGE_ELEM  (0) 2010.04.02
OpenCV: cvFindContours  (0) 2010.04.02
Harris corner detector  (0) 2010.03.31
posted by maetel
2010. 4. 2. 19:39 Computer Vision
IPLimage 이미지 행렬의 색상값 읽기

/* get reference to pixel at (col,row),
   for multi-channel images (col) should be multiplied by number of channels */
#define CV_IMAGE_ELEM( image, elemtype, row, col )       \
    (((elemtype*)((image)->imageData + (image)->widthStep*(row)))[(col)])

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

Image Filtering  (0) 2010.04.03
OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
OpenCV: cvFindContours  (0) 2010.04.02
Harris corner detector  (0) 2010.03.31
OpenCV: cvCanny() 연습 코드  (0) 2010.03.31
posted by maetel
2010. 4. 2. 17:43 Computer Vision

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

OpenCV: cvSobel() 연습 코드  (3) 2010.04.03
OpenCV: CV_IMAGE_ELEM  (0) 2010.04.02
Harris corner detector  (0) 2010.03.31
OpenCV: cvCanny() 연습 코드  (0) 2010.03.31
Canny edge detection  (0) 2010.03.30
posted by maetel
2010. 3. 31. 20:44 Computer Vision
C. Harris and M.J. Stephens. A combined corner and edge detector. In Alvey Vision Conference, pages 147–152, 1988.



OpenCV: cvCornerHarris()

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

OpenCV: CV_IMAGE_ELEM  (0) 2010.04.02
OpenCV: cvFindContours  (0) 2010.04.02
OpenCV: cvCanny() 연습 코드  (0) 2010.03.31
Canny edge detection  (0) 2010.03.30
ARToolKit - simpleTest  (0) 2010.03.17
posted by maetel
2010. 3. 31. 16:58 Computer Vision
OpenCV 라이브러리의 Canny edge detection 함수

void cvCanny(const CvArr* image, CvArr* edges, double threshold1, double threshold2, int aperture_size=3)

Implements the Canny algorithm for edge detection.

Parameters:
  • image – Single-channel input image
  • edges – Single-channel image to store the edges found by the function
  • threshold1 – The first threshold
  • threshold2 – The second threshold
  • aperture_size – Aperture parameter for the Sobel operator (see Sobel)


cvCanny() 함수의 입출력 이미지는 단일 채널 (single channel)이어야 하므로,
비디오 입력에서 컬러 영상을 받은 경우 흑백 이미지(gray image)로 전환해 주어야 한다.

void cvCvtColor(const CvArr* src, CvArr* dst, int code)

Converts an image from one color space to another.

Parameters:
  • src – The source 8-bit (8u), 16-bit (16u) or single-precision floating-point (32f) image
  • dst – The destination image of the same data type as the source. The number of channels may be different
  • code – Color conversion operation that can be specifed using CV_ *src_color_space* 2 *dst_color_space* constants (see below)





입력 영상

흑백 영상

edge 검출 영상 (Canny 알고리즘)




cf.
2010/03/30 - [Visual Information Processing Lab] - Canny edge detection
cv. Image Processing and Computer Vision

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

OpenCV: cvFindContours  (0) 2010.04.02
Harris corner detector  (0) 2010.03.31
Canny edge detection  (0) 2010.03.30
ARToolKit - simpleTest  (0) 2010.03.17
Three-dimensional computer vision: a geometric viewpoint By Olivier Faugeras  (0) 2010.03.15
posted by maetel
2010. 3. 30. 21:05 Computer Vision
Canny algorithm for edge detection


Canny, J. 1986. A Computational Approach to Edge Detection. IEEE Transactions on Pattern Analysis and Machine Intelligence. 8(6).



The Hypermedia Image Processing Reference - Feature Detectors - Canny Edge Detector

OpenCV: cvCanny()

posted by maetel
information arts: intersections of art, science, and technology
stephen wilson
The MIT Press

googleBooks

http://userwww.sfsu.edu/~swilson/book/infoartsbook.html



Biology
(microbiology, genetics, animal and plant behavior, the body, brain & body processes, body imaging, and medicine)

Physical Sciences
(particle physics, atomic energy, geology, physics, chemistry, astronomy, space science, and GPS technology)

Mathematics and Algorithms
(algorists, fractals, genetic art, artificial life)

Kinetics
(conceptual electronics, sound installation, and robotics)

Telecommunications
(telephone, radio, telepresence, web art)

Digital Systems
(interactive media, VR, alternative sensors - touch, motion, gaze, personal characteristics, activated objects, haptics, artificial intelligence, 3-D sound, speech, scientific visualization, surveillance, information systems)


http://en.wikipedia.org/wiki/Information_art

http://www.leonardo.info

http://www.isastorganization.org/

http://en.wikipedia.org/wiki/C._P._Snow


Roger F. Malina (Chairman, Leonardo/ISAST) :  Perhaps in our lifetime we will see the emergence of "new Leonardos", creative individuals or teams who will not only develop a meaningful art for our times but also drive new agendas in science and stimulate technological innovation that addresses today's human needs.


Joel Slayton:  ... Although art and science share many characteristics, a special role for the arts exists in the evolution and deployment of technology -- the implication being that by operating outside the conventions of traditional practice, unique and significant research enterprises can and will unfold. Information Arts helps us understand on a deeper level that experimental research is culturally necessary and serves to transform how to simulate, interact with, and experience the world.



posted by maetel
2010. 3. 24. 20:23 Footmarks
서강대학교 영상대학원 기업 특강: 3D 영상산업의 전망과 동향
강사: Stereo Pictures 성필문 회장

2010-03-24 수 늦은 5시 @가브리엘관 704호


Introduction: 영상 산업의 Reality 향상에 대한 노력

변화의 시작:
Hollywood 영화감독들
미국 영화 관객
Digital 3D 영화관
과거의 Analog 3D movies
최근의 Digital 3D movies
Digital 3D TV

입체영상의 제작 방법

입체영상 제작의 대안: 2D to 3D converting

Hollywood의 움직임

입체영상 컨텐츠 산업의 전망


스테레오픽처스
http://stereopictures.com/
 
Sasson Film Design (Canada)
http://www.sassoonfilmdesign.com/

Convergence ---



posted by maetel