Graph-Based Image Segmentation은 Felzenszwalb와 Huttenlocher가 2004년에 제안한 영상 분할 알고리즘이다. 영상을 그래프로 표현하여, 각 픽셀을 vertex, 인접한 픽셀 사이의 연결을 edge로 간주한다. 간선의 가중치는 두 픽셀의 색상 차이(또는 밝기 차이)로 정의되며, 색상이 비슷할수록 작은 값을 갖는다.
알고리즘은 먼저 모든 간선을 가중치의 오름차순으로 정렬한 후, Kruskal의 MST 알고리즘과 유사한 방식으로 영역을 병합한다. 처음에는 각 픽셀이 하나의 독립된 영역이며, 가중치가 작은 간선부터 차례로 검사한다. 서로 다른 두 영역을 연결하는 간선의 가중치가 두 영역의 내부 차이를 나타내는 threshold 보다 작으면 두 영역을 하나로 합친다. 이 임계값은
$$T(R)=\operatorname{Int}(R)+\frac{c}{|R|}$$로 정의된다. 여기서 $\operatorname{Int}(R)$은 영역 내부에서 선택된 최대 간선 가중치(내부 변화량), $|R|$은 영역의 픽셀 수, $c$는 사용자가 지정하는 상수이다. 작은 영역은 $\tfrac{c}{|R|}$ 항이 커서 쉽게 병합되고, 큰 영역은 더 엄격한 조건에서만 병합되므로 노이즈에 강하면서도 의미 있는 경계를 잘 유지할 수 있다.
모든 간선을 처리한 뒤에는 사용자가 지정한 최소 크기보다 작은 영역을 인접 영역과 추가로 병합하여 작은 잡음 영역을 제거한다. 이 과정은 Union-Find(Disjoint Set) 자료구조를 이용하므로 영역의 병합과 검색을 매우 효율적으로 수행할 수 있으며, 전체 시간 복잡도는 간선 정렬을 포함하여
$$O(E\log E)$$이다. 4- 또는 8-연결 격자 영상에서는 edge 수 $E$가 픽셀 수 $N$에 비례하므로 전체 복잡도는 사실상 $O(N\log N)$에 해당한다.
이 코드에서는 RGB 색 공간에서 두 픽셀의 유클리드 거리
$$w=\sqrt{(R_1-R_2)^2+(G_1-G_2)^2+(B_1-B_2)^2}$$를 간선의 가중치로 사용하고, 분할 전에 $\sigma=0.5$의 Gaussian smoothing을 적용하여 잡음의 영향을 줄인 후 그래프 분할을 수행한다. 마지막으로 최소 크기보다 작은 영역을 병합하고, 각 영역을 대표(root) 픽셀의 색으로 채워 분할 결과를 생성한다.

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;
};
Statistical Region Merging
Statistical region merging은 이미지의 픽셀을 일정한 기준에 따라 더 큰 영역으로 합병하는 bottom-up 방식의 과정이다. 두 영역 $R_1$과 $R_2$가 하나의 영역으로 합병이 되기 위해서는 두 영역의 평균 픽
kipl.tistory.com
'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 (2) | 2024.03.12 |
| Ellipse Fitting (0) | 2024.03.02 |


