RANSAC Algorithm

Image Recognition 2008. 5. 24. 09:02

어떤 현상을 설명하는 이론 모델을 만들려면 현상에서 관측 데이터를 모아야 한다. 그런데 관측 데이터에는 모델에 대한 잘못된 가정이나 측정의 오차 등에서 생기는 여러 가지 형태의 노이즈가 들어 있게 마련이다. 이러한 노이즈는 모델을 정확히 예측하는 것을 방해한다. 모델 파라미터의 예측을 방해하는 데이터(outliners)가 들어 있는 관측 데이터로부터 어떻게 하면 모델을 구축할 수 있는가라는 질문에 대한 한 해결책을 RANSAC("RANdom SAmple Consensus") 알고리즘이 제공한다. 이 알고리즘은 1981년 Fischler과 Bolles에 의해서 제안되었다.

RANSAC 알고리즘에 들어가는 입력은 관측된 데이터, 관측 결과를 피팅하거나 설명할 수 있는 모델 파라미터, 그리고 신뢰성을 나타내는 파라미터를 필요로 한다.

RANSAC 알고리즘은 주어진 원본 데이터에서 일부를 임의로 선택하는 과정을 반복하여서 최적의 파라미터를 예측하는 프러시저의 형태를 가진다. 전체의 관측 데이터가 M개 있고, 모델 파라미터를 예측하는데 N개의 데이터가 필요한 경우, 알고리즘의 동작은

     1. 임의로 관측 데이터에서 N개의 부분 데이터를 선택한다.
     2. 선택 데이터를 (가상의) inlier로 생각하고 모델을 예측한다.
     3. 원본 데이터(M) 중에서 예측된 모델에 잘 맞는가를 체크한다. 잘 맞는 데이터 수를 K라고 한다.
     4. 만약에 K가 충분하면, 이 모델을 확정하고 알고리즘을 종료한다.
     5. 모델이 좋지 않으면 14 과정을  L번 반복한다.
     6. 충분한 반복 후에도 좋은 모델을 찾지 못하면 예측에 실패한 것으로 간주한다.

1. 임계값 K는 주어진 전체 데이터 중에서 어느 정도의 비율로 찾는 모델에 잘 맞는가로 판단하는 기준인데, 사용자가 결정해야 한다. 대략적으로 주어진 샘플에서 inlier의 비율을 Pg정도라고 생각되면 다음 정도로 잡으면 된다:

 
   K = M * (1- P
g)

2. 얼마나 많은 반복을 해야하는가? 주어진 관측 데이터에서 inlier일 확률이 Pg인 경우에 L번의 모델 예측 시도가 실패할 확률을 계산하여서 이것이 주어진 설정값, pfail 보다도 작은 경우에 모델 예측의 실패로 처리하므로,

  pfail   = L번의 모델예측 실패 확률
          = (한 번 시도가 실패할 확률)L
          = (1 - 한 번 시도가 성공할 확률))L
          = (1 - (랜덤 데이터가 모델을 예측할 확률)N))L
          = (1- (Pg) N)L

이 사실로부터 최대 반복 횟수는 
    
   L = log(pfail)/log(1-(Pg)N)

로 주어진다.
inlier가 반이 섞인 경우 Pg (=주어진 데이터중에서 inlier일 확률) = 0.5,
pfail = 0.01 인 경우, N = 3 (세 점만 있으면 모델 구성이 가능한 원의 피팅이 한 예)이면 최대 반복 횟수는 윗 식에 적용하면,
     
                   L = 35 회

RANSAC 알고리즘은 주어진 관측 데이터에 많은 outlier가 존재하더라도 매우 정확히 모델 파라미터를 예측할 수 있는 능력이 있는 robust 한 알고리즘이지만, 얼마나 많은 반복을 해야 하는가에 대한 상한 값이 제공되지 않는다(사용자가 pfail 를 설정한다). 따라서 사용자 설정에 의한 상한 값은 최적의 모델이 아닐 수 있다.

"The RANSAC procedure is opposite to that of conventional smoothing techniques: Rather than using as much of the data as possible to obtain an initial solution and then attempting to eliminate the invalid data points, RANSAC uses as small an initial data set as feasible and enlarges this set with consistent data when possible".

Martin A. Fischler and Robert C. Bolles (June 1981). "Random Sample Consensus: A Paradigm for Model Fitting with Applications to Image Analysis and Automated Cartography". Comm. of the ACM 24: 381–395.

예제 코드:
ransac을 이용한 라인 피팅: http://blog.naver.com/helloktk/80029006029
ransac을 이용한 원 피팅: http://kipl.tistory.com/32

 

RANSAC: Circle Fit

RANSAC 알고리즘을 써서 주어진 2차원 점집합에서 원을 추정한다. 원을 만들기 위해서는 최소한 3점이 필요하고, 또 일직선에 있지 않아야 한다. 이렇게 만들어진 원은 세 점을 꼭짓점으로 하는

kipl.tistory.com

ransac을 이용한 타원 피팅: kipl.tistory.com/110

 

RANSAC Ellipse Fitting

타원은 원뿔을 평면으로 잘랐을 때 생기는 곡선 중의 하나로 다음 이차 형식(quadratic form)으로 표현된다. . 이 이차 형식의 계수를 구하기 위해서는 타원 위의 5개의 서로 다른 점이 필요하다 $(a

kipl.tistory.com

 

728x90

'Image Recognition' 카테고리의 다른 글

Gaussian Mixture Model  (2) 2008.06.07
Rasterizing Voronoi Diagram  (0) 2008.05.26
Contour Tracing  (0) 2008.05.22
Gausssian Scale Space  (0) 2008.05.22
Watershed Algorithm 적용의 예  (2) 2008.05.21
,

Contour Tracing

Image Recognition 2008. 5. 22. 22:51

이진 영상에서 전경의 경계를 저장하는 함수다. 전경의 최외각을 8-방향 연결성을 체크하면서 추적하도록 설계되었다(chain code를 참조하면 된다). 전경의 두께가 1 픽셀이더라도 항상 닫힌 윤곽선을 형성하게 만들었다(다르게 행동하도록 바꿀 수 있다). 전경에 서로 연결되지 않은 blob가 여러 개 있는 경우도 쉽게 처리할 수 있게 출력은 벡터 컨테이너를 사용했다.

struct Cntr {
   int x, y ; //position;
   int idirn ;//chain-code;
   Cntr() {} 
   Cntr(int x_, int y_, int idirn_)  : x(x_), y(y_), idirn(idirn_){}
};
typedef std::vector<Cntr> CntrVector;
#define FGVAL   255
#define BGVAL   0
#define VISITED 33          /* pixel value on accepted contour */
int GetNextCntr (BYTE** image, int *x, int *y, int *idirn);

int ContourTrace (BYTE** image/*binary image(FGVAL,BGVAL)*/, int width, int height,
                 std::vector<CntrVector* > &CntList) {
    /* CAUTION:: one-pixel border should have BGVAL!!!!*/
    for (int x = width; x-->0;)  image[0][x] = image[height-1][x] = BGVAL;
    for (int y = height; y-->0;) image[y][0] = image[y][width-1] = BGVAL;
    
    for (int y = height-1; y-->1;) {
        for (int x = width-1; x-->1;) {
            if (image[y][x] == FGVAL && image[y][x - 1] == BGVAL) {
                CntrVector *pCntrVec = new CntrVector ;
                int idirn = 2;
                int xStart = x, yStart = y;
                pCntrVec->push_back(Cntr(x, y, idirn));
                do {
                    image[y][x] = VISITED;  /* set the default value to VISITED */
                    GetNextCntr (image, &x, &y, &idirn);
                    pCntrVec->push_back(Cntr(x, y, idirn));
                } while (!(x == xStart && y == yStart));
                CntList.push_back(pCntrVec);
            }
        }
    }
    return CntList.size();
};

/* 다음 픽셀을 검사. 체인 코드; */
int GetNextCntr (BYTE** image, int *x, int *y, int *idirn);

728x90

'Image Recognition' 카테고리의 다른 글

Gaussian Mixture Model  (2) 2008.06.07
Rasterizing Voronoi Diagram  (0) 2008.05.26
RANSAC Algorithm  (0) 2008.05.24
Gausssian Scale Space  (0) 2008.05.22
Watershed Algorithm 적용의 예  (2) 2008.05.21
,

MFC를 써서 폴리곤의 내부를 칠하기 위해서는 윈도  GDI region을 다루는  CRgn  클래스의 CRgn::CreatePolygonRgn() 메쏘드를 써서 해당 폴리곤으로 bound 된 영역을 만든 이후에 CDC::FillRgn() 함수를 이용하여서 원하는 색으로 칠할 수 있다. 직접  raster에 칠하려고 할 때는 이러한 방법이 너무 불편하다 (물론 라스터(DIB)를 윈도 비트맵으로 변환한 후에 위의 방법대로 칠하고, 다시 윈도 비트맵을 라스터로 바꾸는 작업을 하면 되는데 번거롭다).

쉽게 생각할 수 있는 방법은  point_in_polygon()  방법을 써서 각각의 픽셀이 주어진 폴리곤 내부점인지를 확인하면서 작업을 할 수 있다. 그러나 이 방법은 한 픽셀을 검사하기 위해서 주어진 폴리곤의 모든 에지를 다 건들어야 하는 낭비를 하게 된다. 보다 효율적인 방법은 point_in_polygon 알고리즘을 구현할 때 사용하는 원리를 이용하면 적어도 한 스캔라인의 모든 픽셀에 대해서는 딱 한 번만 전체 폴리곤의 에지를 검사하게 만들 수 있다. inclusion 테스트는 해당점에서 출발하는 수평 반직선이 폴리곤과 몇 번 교차하는지를 세서 내부점인지 외부점인지를 판별한다. 이것을 생각하면 수평선이 폴리곤과 교차하는 점들을 모두 구하여서(한번 폴리곤의 전체 에지 검사가 필요) 크기의 순서대로 정렬할 때, 짝수번째 구간(0->1, 2->3,.....)에 들어가는 스캔라인의 픽셀들은 모두 폴리곤의 내부점이 된다는 것을 알 수 있다.

사용자 삽입 이미지


알고리즘 순서:

  1. 각각의 스캔라인에 대해서 교차점들을 구한다:
    꼭짓점 i와 꼭짓점 j(=i-1)를 연결하는 직선의 방정식이 x = x[i] + (x[j] - x[i])*(y - y[i]) / (y[j] - y[i]) 이므로 현재의 scanline 위치에서 y값이 두 꼭지점의 y값 (y[i], y[j]) 사이에 있으면, 교차점의 x-좌표는 윗식의 좌변 값이 된다.
  2. 교차점들을 크기의 순서대로 정렬한다.
  3. 짝수번째 구간에 들어가는 스캔라인상의 픽셀들은 해당 색깔로 칠한다.
/*sample code;*/
void FillPolygon(const std::vector<CPoint>& poly,
                 DWORD color,
                 BYTE *image, int width, int height, int bpp) {
    std::vector<int> nodeX(poly.size()); //never exceeds npoly;
    for (int y = 0; y < height; y++) {
        // find intersection nodes;
        int nodes = 0;
        for (int i = 0, j = poly.size()-1; i < poly.size(); j = i++)
            if (poly[i].y < y && poly[j].y >= y || poly[j].y < y && poly[i].y >= y) {
                // 수평인 에지는 그리지 않는다(부등식을 자세히 보라)
                nodeX[nodes++] = (int)(poly[i].x + double(y - poly[i].y) * double(poly[j].x \
                                 - poly[i].x) / double(poly[j].y - poly[i].y) + .5); 
                                 // round to integer!!.
            }
            
        // sort nodes (ascending order);
        std::sort(nodeX.begin(), nodeX.begin() + nodes);
        // fill the pixels between node pairs.
        for (int i = 0; i < nodes; i += 2) {
            if (nodeX[i + 0] >= width) break;
            if (nodeX[i + 1] > 0) {
                if (nodeX[i + 0] < 0)     nodeX[i + 0] = 0 ;
                if (nodeX[i + 1] > width) nodeX[i + 1] = width;
                for (int x = nodeX[i]; x < nodeX[i + 1]; x++) 
                    SetPixel(x, y, color, image, width, height, bpp); 
                    //SetPixel()은 다른 프로토타입을 가질 수 있다.
            }
        }
    } 
};

사용자 삽입 이미지

/**
** http://blog.naver.com/helloktk/80050645334 에서 옮김.
*/

728x90

'Computational Geometry' 카테고리의 다른 글

Polygon Triangulation (II)  (0) 2008.05.26
Polygon Triangulation  (4) 2008.05.25
Fortune's Sweep Algorithm  (0) 2008.05.22
Triangulating Monotone Polygon  (0) 2008.05.22
Trapezoidalization  (0) 2008.05.22
,