Union-Find 알고리즘을 이용하여 영역 분할 과정이다. 각각의 픽셀에서 4방향으로 연결된 픽셀이 속한 영역 merge 할 수 있는지를 시도한다. merge 조건은 현재 픽셀의 그레이 값과 인접 영역의 평균 그레이 값의 차이가 주어진 임계값보다 작은 경우다.

$$\text{merge 조건: }\quad | 그레이 값- \text{인접 영역 평균값}| < threshold$$

컬러 영상의 경우에는 RGB 채널에 조건을 부여하거나 gray level만 이용해서 판단할 수 있다. root node 수만큼의 분할 영역이 만들어지고, 임계값이 클수록 분할된 각 영역의 사이즈가 커진다.  

gray = 0.2989 * r + 0.5870 * g + 0.1140 * b ;

같은 평균 픽셀값을 가지고 있더라도 4-방향으로 서로 연결된 영역이 아니면 합병되지 못하고 서로 다른 영역으로 남는다. 이를 하나의 영역으로 만들려면 분할된 영역을 다시 검사를 하는 과정이 필요하다. 자세한 과정은 Statistical Region Merge(SRM) 알고리즘(kipl.tistory.com/98)에 잘 구현이 되어 있다.

int FindCompress(int node, int *parent) {  // find root node;
    if (parent[node] < 0) return node;     // root-node;
    else return parent[node] = FindCompress(parent[node], parent); //찾음과 동시에 depth을 줄임
}
BOOL Predicate(int r1, int r2, int thresh, int *sum, int *sizes) {
    double diff = double(sums[r2]) / sizes[r2] - double(sums[r1]) / sizes[r1];
    return fabs(diff) < thresh;
}
// root가 r1인 집합(단일픽셀)을 root가 r2인 집합으로 합병한다;
// 전체 픽셀합과 픽셀수를 update한다;
void Merge(int r1, int r2, int *sums, int *sizes, int *parent) {
    sums[r2]    += sums[r1];
    sizes[r2]   += sizes[r1];
    parent[r1]   = r2;
    return TRUE;
}
// root 노드는 분할영역의 픽셀 갯수와 픽셀 값의 합을 저장한다.
// 처음 각 픽셀이 root 이고, 픽셀 갯수는 1, 픽셀값은 자신의 픽셀값을 저장;
BOOL UnionFindAverage(BYTE *image, int width, int height, int thresh) {
    int npixs   = width * height;
    int* parent = new int [npixs];    // parent table;
    int* sums   = new int [npixs];  
    int* sizes  = new int [npixs];
    // initial setting;
    for (int pos = 0; pos < npixs; pos++) {
        parent[pos] = -1;                // 각 픽셀이 root임;
        sums[pos]   = image[pos];
        sizes[pos]  = 1;
    }
    // 첫행; LEFT 픽셀이 속한 영역의 평균값과 차이가 thresh 이내이면 left로 합병함;
    for (int x = 1; x < width; x++) {
        int left = FindCompress(x - 1, parent);
        int curr = FindCompress(x, parent);
        if (Predicate(curr, left, thresh, sums, sizes))
            Merge(curr, left, sums, sizes, parent);
    }
    //Flatten(y=0);
    for (int x = 0; x < width; x++) FindCompress(x, parent);
    for (int y = 1, pos = y * width; y < height; y++) {
        // x = 0; up 픽셀이 속학 영역과 합병 체크;
        int up   = FindCompress(pos - width, parent);
        int curr = FindCompress(pos, parent);
        if (Predicate(curr, up, thresh, sums, sizes))
            Merge(curr, up, sums, sizes, parent);
        pos++;
        // UP-LEFT 픽셀 영역과 합병 체크;
        for (int x = 1; x < width; x++, pos++) {
            int left = FindCompress(pos - 1, parent);
            int curr = FindCompress(pos, parent);
            // left와 합병이 되면 left가 up과 합병이 되는지 확인; 안되면 up과 합병시도;
            if (Predicate(curr, left, thresh, sums, sizes)) {
                Merge(curr, left, sums, sizes, parent);
                curr = left;
            }
            int up = FindCompress(pos - width, parent);
            if ((curr != up) && (Predicate(curr, up, thresh, sums, size)))
                Merge(curr, up, sums, sizes, parent);
        }
        // Flatten(y>0);
        for (int x = 0; x < width; x++) FindCompress(x + y * width, parent);
    }
    // 평균 이미지를 만듬(input is overwritten);
    for (int pos = 0; pos < npixs; pos++) {
        // Find root;
        int node = pos;
        while (parent[node] >= 0) node = parent[node];
        int a = int(double(sums[node]) / sizes[node] + 0.5);
        image[pos] = a > 255 ? 255 : a;
    };
    delete [] parent;
    delete [] sums;
    delete [] sizes;
    return TRUE;
};

네이버 블로그 이전
statistical region merge 알고리즘을 적용한 결과

 
728x90

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

Octree Quantization  (0) 2021.01.12
Median-Cut 컬러 양자화  (0) 2021.01.12
Multilevel Otsu Thresholding  (0) 2021.01.09
Binary Image에서 Convex Hull  (0) 2021.01.06
Kuwahara Filter  (2) 2020.12.28
Posted by helloktk
,

Tarjan의 Union-Find algorithm을 이용해서 (이진) 이미지의 Connected Component Labeling을 구현한 코드이다. 이미지의 배경은 검정(0)으로 하고 전경은 0이 아닌 값이면 된다. 처음 각 전경에 해당하는 픽셀은 모두 root node로 만든다: parent 정보는 배열 label에 저장되고, root node에 대해서는 label[pos] = pos). 4방향 연결을 기준으로 하면 왼쪽(LEFT)이나 위쪽(TOP)의 픽셀과 연결이 되어 있으면 parent가 더 작은 root 번호를 갖도록 합병한다(UNION).  8-방향 연결인 경우에는 위-왼쪽(TOPLEFT), 위-오른쪽(TOPRIGHT)도 합병한다. 전체 픽셀에 대해서 조사가 끝나면 배열 label에는 각 픽셀의 root 번호가 저장된다. 자신의 위치 값이 label의 값과 같은 경우는 root 노드로 새로운 blob의 시작을 나타낸다.  label 배열에 기록된 root 번호는 합병과정에서 중간 번호가 빠질 수 있을 수 있다. 순차적 레이블링이 필요한 경우에는 배열 label을 다시 스캔해서 번호가 순차적으로 할당되도록 만든다. 

보통의 connected component labeling 알고리즘이 이미지를 두 번 스캔을 해야 하는데 반해 이 알고리즘은 한 번의 스캔만 필요하다. 물론 순차적인 레이블링을 원하면 parent table을 다시 조사해야 한다.

 

#define CCL_LEFT         (-1)
#define CCL_TOP          (-w)
#define CCL_TOPLEFT      (-1 - w)
#define CCL_TOPRIGHT     (1 - w)

#define CCL_BG            (-1)
// 두 노드 (a,b)를 합병함; find(and compress)와 union을 동시에;
#define UNION(a, b, label) {                    \
    int r0 = (a), r1 = (b), p;                  \
    while ((p = label[r0]) != r0) { r0 = p; }   \
    label[(a)] = p;                             \
    while ((p = label[r1]) != r1) { r1 = p; }   \
    label[(b)] = p;                             \
    if (r0 > r1)  label[r0] = r1;               \
    else          label[r1] = r0;               \
}                  
static int FindRoot(int node, int* parent) {
    if (node != parent[node]) parent[node] = FindRoot(parent[node], parent);
    return parent[node];
}
static void Union(int node1, int node2, int *parent) {
    int root1 = FindRoot(node1, parent);
    int root2 = FindRoot(node2, parent);
    if (root1 > root2) parent[root1] = root2;
    else parent[root2] = root1;
}
int ConnectedComponentLabel(BYTE *image, int w, int h, int label[]) {
    BYTE *q = &image[0] ;
    for (int y = 0, pos = 0; y < h; y++) {
        for (int x = 0; x < w; x++, pos++) {
            if (*q++) {                     // Foreground;
                label[pos] = pos;           // 초기 root = 현위치             
                if ((y > 0) && q[CCL_TOP]) 
                    UNION(pos, pos + CCL_TOP, label);         
                if ((x > 0) && q[CCL_LEFT]) 
                    UNION(pos, pos + CCL_LEFT, label);
#if defined (_EIGHTCONN_)
                if ((x + 1 < w) && (y > 0) && q[CCL_TOPRIGHT]) 
                    UNION(pos, pos + CCL_TOPRIGHT, label);
                if ((x > 0) && (y > 0) && q[CCL_TOPLEFT]) 
                    UNION(pos, pos + CCL_TOPLEFT, label);
#endif // _EIGHTCONN_
            } else 
                label[pos] = CCL_BG;        // BACKGROUND;
        }
    }
    // 합병과정에서 빈 레이블이 생길 수 있으므로 순차적 레이블을 같도록 정리(선택 사항);
    // label = -1은 배경임;
    int curlab = 0;                                // 0-부터 시작;
    for (int pos = 0 ; pos < w * h; pos++) {
        int r = label[pos];
        if (r == CCL_BG)   continue;               // background;
        else if (r == pos) label[pos] = curlab++;  // root: assign a new label;
        else               label[pos] = label[r];  // assign renewed parent's label.
    }  
    return (curlab);                               //배경 제외;
};

blog.naver.com/helloktk/ 에서 옮겼고, 출력 부분을 바꿈;

<CCL을 이용하여 DataMatrix-code를 검출하는 과정>

이진화된 QR 코드 이미지

 

connected component labeling with colors

 

<20000 성분의 체스보드에 대한 4-방향 연결 테스트;>

출력은 정수형으로 바꾸어야 한다.  8-방향 연결로 하면 1개 성분만 나온다.

 

8 방향 연결 조건에서는 1개
4 방향 연결 조건에서는 W * H / 2개
percolation simulator에 사용된 예: 위쪽이나 아래쪽에서 연결된 경로를 표시했다.

 

 

728x90
Posted by helloktk
,

http://www.lix.polytechnique.fr/~nielsen/tpami04-nn.pdf

Abstract—This paper explores a statistical basis for a process often described in computer vision: image segmentation by region merging following a particular order in the choice of regions. We exhibit a particular blend of algorithmics and statistics whose segmentation error is, as we show, limited from both the qualitative and quantitative standpoints. This approach can be efficiently approximated in linear time/space, leading to a fast segmentation algorithm tailored to processing images described using most common numerical pixel attribute spaces. The conceptual simplicity of the approach makes it simple to modify and cope with hard noise corruption, handle occlusion, authorize the control of the segmentation scale, and process unconventional data such as spherical images. Experiments on gray-level and color images, obtained with a short readily available C-code, display the quality of the segmentations obtained. 

인간은 영상을 보면 매우 쉽게 비슷한 픽셀 값을 갖는 영역으로 분리해서 인식을 한다. 우리가 보는 영상이 여러 개의 균일한 영역으로 나누어진 기본 영상에 랜덤 노이즈가 추가되어 만들어졌다고 생각해보자. 영상의 픽셀 값이 기본 영상의 픽셀 값에 일정 구간(Q)에서 값을 취하는 랜덤 변수가 더해져서 만들어졌다고 보면, 영상에서 다른 두 영역($R, R'$)의 픽셀 평균값의 차이 $(\overline{R}-\overline{R'})$와 기본 영상에서 랜덤 변수에 의한 통계적 기댓값의 차이 $(E(\overline{R}-\overline{R'}))$는 주어진 $0 < δ < 1$ 에 다음식을 만족함을 보일 수 있다.

따라서, 두 영역 $R$과 $R'$에 대해서 우변의 값이 이 부등식을 만족하지 않으면 두 영역은 하나로 인식될 수 있다.

 

#region count = 576; 많은 영역이 1픽셀로 구성이 되었있다;
#region count > 1 = 302;
#region count > 2 = 222;
#region count > 3 = 179;
#region count > 4 = 140;

 

 

728x90

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

RANSAC: Ellipse Fitting  (1) 2012.10.07
Autofocus Algorithm  (0) 2012.06.03
Local Histogram Equalization  (0) 2012.03.10
2차원 Savitzky-Golay Filters 응용  (0) 2012.02.28
webcam용 QR code detector  (0) 2012.02.19
Posted by helloktk
,