배경의 각 픽셀에 인접 픽셀까지의 수평/수직 방향으로는 Euclidean distance가 1 만큼 떨어져 있고, 대각선 방향으로는 22만큼 떨어져 있다. 이 거리에 82.83382.833을 곱하면 거리를 각각 3344로 근사할 수 있어서 정수 연산 만으로 distance transform을 구할 수 있다.

approximate distance transform
exact Euclidean distance transform

 

#define BACKGROUND (0)
void distanceTransform_approx(BYTE **image, int w, int h, int **map) {
    // initialization;
    for (int y = h; y-->0;)
        for (int x = w; x-->0;) map[y][x] = 0;
    // forward_pass
    // y=x=0;
    if (image[0][0] == BACKGROUND) map[0][0] = 0xFFFF;//  INFINITY;
    // y=0;
    for (int x=1; x<w; x++)
        if (image[0][x] == BACKGROUND) map[0][x] = 3 + map[0][x-1];
    for (int y=1; y<h; y++) {
        // x=0;
        if (image[y][0] == BACKGROUND) 
            map[y][0] = min(3 + map[y-1][0], 4 + map[y-1][1]);
        for (int x=1; x<w-1; x++) 
            if (image[y][x] == BACKGROUND)
                map[y][x] = min(4 + map[y-1][x-1], min(3 + map[y-1][x], 
                           min(4 + map[y-1][x+1], 3 + map[y][x-1])));
        // x=w-1;
        if (image[y][w-1] == BACKGROUND) 
            map[y][w-1] = min(4 + map[y-1][w-2], min(3 + map[y-1][w-1], 3 + map[y][w-2]));
    }
    // backward_pass
    // y=h-1;
    for (int x = w-1; x-->0;)
        if (image[h-1][x] == BACKGROUND) 
            map[h-1][x] = min(map[h-1][x], 3 + map[h-1][x+1]);

    for (int y = h-1; y-->0;) {
        // x=w-1;
        if (image[y][w-1] == BACKGROUND)
            map[y][w-1] = min(map[y][w-1], min(3 + map[y+1][w-1], 4 + map[y+1][w-2]));
        for (int x=w-1; x-->1;) 
            if (image[y][x] == BACKGROUND)
                map[y][x] = min(map[y][x], min(4 + map[y+1][x+1], 
                           min(3 + map[y+1][x], min(4 + map[y+1][x-1], 3 + map[y][x+1]))));
        // x=0;
        if (image[y][0] == BACKGROUND)
            map[y][0] = min(map[y][0], min(4 + map[y+1][1], 
                            min(3 + map[y+1][0], 3 + map[y][1])));
    }
};
728x90

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

FFT 구현  (0) 2024.07.31
CLAHE (2)  (1) 2024.06.26
Graph-based Segmentation  (1) 2024.05.26
Linear Least Square Fitting: perpendicular offsets  (0) 2024.03.22
Cubic Spline Kernel  (1) 2024.03.12
,

Ref: Efficient Graph-Based Image Segmentation, Pedro F. Felzenszwalb and Daniel P. Huttenlocher, International Journal of Computer Vision, 59(2) September 2004.

struct edge {
    float w;	//weight=color_distance
    int a, b;   //vertices=pixel positions;
    edge() {}
    edge(int _a, int _b, float _w): a(_a), b(_b), w(_w) {}
};
bool operator<(const edge &a, const edge &b) {
  return a.w < b.w;
}
#define SQR(x) ((x)*(x)) 
#define DIFF(p,q) \
    (sqrt(SQR(red[(p)]-red[(q)])+SQR(green[(p)]-green[(q)])+SQR(blue[(p)]-blue[(q)])))
int segment_image(CRaster& raster, float c, int min_size, CRaster& out) {
    if (raster.GetBPP() != 24) return 0;
    const CSize sz = raster.GetSize();
    const int width = sz.cx, height = sz.cy;
    std::vector<float> red(width * height);
    std::vector<float> green(width * height);
    std::vector<float> blue(width * height);
    for (int y = 0, curr = 0; y <height; y++) {
        BYTE *p = (BYTE *)raster.GetLinePtr(y);
        for (int x = 0; x < width; x++, curr++) 
            blue[curr] = *p++, green[curr] = *p++, red[curr] = *p++;
    }
    // gaussian smoothing;
    const float sigma = 0.5F;
    smooth(blue,  width, height, sigma);
    smooth(green, width, height, sigma);
    smooth(red,   width, height, sigma);

    std::vector<edge> edges;
    edges.reserve(width * height * 4);
    for (int y = 0, curr = 0; y < height; y++) {
        for (int x = 0; x < width; x++, curr++) {
            if (x < width-1)
                edges.push_back(edge(curr, curr+1, DIFF(curr, curr+1)));
            if (y < height-1) 
                edges.push_back(edge(curr, curr+width, DIFF(curr, curr+width)));
            if ((x < width-1) && (y < height-1))
                edges.push_back(edge(curr, curr+width+1, DIFF(curr, curr+width+1)));
            if ((x < width-1) && (y > 0)) 
                edges.push_back(edge(curr, curr-width+1, DIFF(curr, curr-width+1)));
        }
    }

    if (c < 0) c = 1500; 
    if (min_size < 0) min_size = 10;
    universe *u = segment_graph(width*height, edges, c);
    // join small size regions;
    for (int i = edges.size(); i--> 0;) {
        edge &e = edges[i];
        int a = u->find(e.a), b = u->find(e.b);
        if ((a != b) && ((u->size(a) < min_size) || (u->size(b) < min_size)))
            u->join(a, b);
    }
    int num_rgns = u->count();

    // paint segmented rgns with root pixel color;
    out = raster;
    for (int y = height-1; y-->0;) 
        for (int x = width-1; x-->0;) {
            int a = u->find(y * width + x);
            out.SetPixel0(x, y, RGB(blue[a],green[a],red[a]));
        } 
    delete u;
    return num_rgns;
}
// Segment a graph
// c: constant for treshold function.
universe *segment_graph(int num_vertices, std::vector<edge>& edges, float c) { 
    // sort by weight
    std::sort(edges.begin(), edges.end());
    // disjoint-set
    universe *u = new universe(num_vertices);
    // init thresholds = {c};
    std::vector<float> threshold(num_vertices, c);

    for (int i = 0; i < edges.size(); i++) {
        edge &e = edges[i];
        int a = u->find(e.a), b = u->find(e.b);
        if (a != b)
            if ((e.w <= threshold[a]) && (e.w <= threshold[b])) {
                u->join(a, b);
                a = u->find(a);
                threshold[a] = e.w + c / u->size(a);
            }
    }
    return u;
};

https://kipl.tistory.com/460

 

Statistical Region Merging

Statistical region merging은 이미지의 픽셀을 일정한 기준에 따라 더 큰 영역으로 합병하는 bottom-up 방식의 과정이다. 두 영역 R1R1R2R2가 하나의 영역으로 합병이 되기 위해서는 두 영역의 평균 픽

kipl.tistory.com

728x90

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

CLAHE (2)  (1) 2024.06.26
Approximate Distance Transform  (0) 2024.06.02
Linear Least Square Fitting: perpendicular offsets  (0) 2024.03.22
Cubic Spline Kernel  (1) 2024.03.12
Ellipse Fitting  (0) 2024.03.02
,

주어진 데이터를 잘 피팅하는 직선을 찾기 위해서는 데이터를 이루는 각 점의 yy 값과 같은 위치에서 구하려는 직선과의 차이 (residual)의 제곱을 최소화시키는 조건을 사용한다. 그러나 직선의 기울기가 매우 커지는 경우에는  데이터에 들어있는 outlier에 대해서는 그 차이가 매우 커지는 경우가 발생할 수도 있어 올바른 피팅을 방해할 수 있다. 이를 수정하는 간단한 방법 중 하나는 yy값의 차이를 이용하지 않고 데이터와 직선 간의 최단거리를 이용하는 것이다. 

수평에서 기울어진 각도가 θθ이고 원점에서 거리가 ss인 직선의 방정식은 

xsinθycosθ+s=0xsinθycosθ+s=0

이고, 한 점 (xi,yi)(xi,yi)에서 이 직선까지 거리는

di=|sinθyicosθxi+s|di=|sinθyicosθxi+s|

이다. 따라서 주어진 데이터에서 떨어진 거리 제곱의 합이 최소인 직선을 구하기 위해서는 다음을 최소화시키는 θ,sθ,s을 구해야 한다. L=i(sinθxicosθyi+s)2L=i(sinθxicosθyi+s)2(θ,s)=argmin(L)(θ,s)=argmin(L)

θθss에 대한 극값 조건에서 

12Lθ=12sin2θi(x2iy2i)cos2θxiyi+ssinθixi+scosθiyi=012Lθ=12sin2θi(x2iy2i)cos2θxiyi+ssinθixi+scosθiyi=0

12Ls=cosθyisinθixiNs=012Ls=cosθyisinθixiNs=0

주어진 데이터의 질량중심계에서 계산을 수행하면 ixi=iyi=0ixi=iyi=0 이므로 데이터의 2차 모멘트를 A=i(x2iy2i),B=ixiyiA=i(x2iy2i),B=ixiyi로 놓으면 직선의 파라미터를 결정하는 식은

12Asin2θBcos2θ=0tan2θ=2BAs=0

두 번째 식은 직선이 질량중심(질량중심계에서 원점)을 통과함을 의미한다. 첫번째 식을 풀면

tanθ=A±A2+(2B)22B

두 해 중에서 극소값 조건을 만족시키는 해가 직선을 결정한다. 그런데

122Lθ2=Acos2θ+2Bsin2θ=±A2+(2B)2>0

이므로 위쪽 부호로 직선(xsinθ=ycosθ)이 정해진다. 질량중심계에서는 원점을 지나지만 원좌표계로 돌아오면 데이터의 질량중심을 통과하도록 평행이동시키면 된다.

(A+A2+(2B)2)(xˉx)=2B(yˉy)

여기서 주어진 데이터의 질량중심은 원좌표계에서

ˉx=1Nixi,ˉy=1Niyi

이다. 또한 원좌표계에서 AB의 계산은 

A=i[(xiˉx)2(yiˉy)2],B=(xiˉx)(yiˉy)

이 결과는 데이터 분포에 PCA를 적용해서 얻은 결과와 동일하다. PCA에서는 공분산 행렬의 고유값이 큰 쪽에 해당하는 고유벡터가 직선의 방향을 결정했다. https://kipl.tistory.com/211.  또한 통계에서는 Deming regression이라고 불린다.

 

PCA Line Fitting

평면 위에 점집합이 주어지고 이들을 잘 기술하는 직선의 방정식을 구해야 할 경우가 많이 발생한다. 이미지의 에지 정보를 이용해 선분을 찾는 경우에 hough transform과 같은 알고리즘을 이용하는

kipl.tistory.com

728x90

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

Approximate Distance Transform  (0) 2024.06.02
Graph-based Segmentation  (1) 2024.05.26
Cubic Spline Kernel  (1) 2024.03.12
Ellipse Fitting  (0) 2024.03.02
Bilateral Filter  (0) 2024.02.18
,