최종 수정 날짜 2019.11.08.
연구실에서 차선 검출 및 도로 검출을 위해서 OpenCV로 작업했던 일이 있었니다.
요즘은 ros에서 PCL을 사용해서 라이다 데이터를 이용하는 작업으로 바꼈지만, 이 전에 도로 영역을 검출하기 위해 했던 공부 했던 Otsu's Algorithm 구현 코드를 올리려고 합니다.
Otsu's 알고리즘은 영상 이진화 방법 중에 하나입니다. 영상의 이진화 방법은 여러가지 방법이 있는데, 그 중에서도 이 Otsu's 알고리즘은 정말 많이 사용되곤 합니다.
우선, 영상의 이진화를 하기 위해서는 임계값을 정해줘야합니다. 흑백영상을 예를 들자면, 일반적으로 흑백 영상의 픽셀값의 범위는 0~255를 가지고 있습니다. 여기서 임계값이 200으로 설정하고 이진화를 한다면 픽셀값이 200을 넘어가면 255, 200을 못 넘는다면 0의 값으로 변경시키며 0 또는 255값만을 가지도록 이진화를 해줄 수가 있습니다. 그런데 영상의 이진화 방법이 참 까다로워질 수 있는 이유는 이진화 결정하는 임계값을 사용자가 정해줘야하는 경우가 있기 때문입니다. 따라서 이러한 시도에는 사용자의 여러 시행착오가 필요하고 개발자로 하여금 인내심을 갖도록 하는 작업이기도 합니다. 이 때 우리가 자동으로 이 임계값을 정하기 위해 사용하는 알고리즘이 Otsu's 알고리즘입니다.
임계값을 t라고 가정한다면 t를 기준으로 픽셀값들을 두 집합으로 나누었을 때, 각 집합의 밝기값의 분포가 균등할수록 좋다는 점에 착안하여서 균등성이 제일 높도록 이진화하는 t에게 높은 점수를 줍니다. 이 균등성은 각 그룹의 분산으로 측정을 하고, 분산이 작을수록 균등성이 높습니다. 이 t를 0~255까지 움직이며 모든 경우의 점수를 계산하고 그 중 가장 높은 점수를 가진 t를 최종 임계값으로 취하는 알고리즘입니다.
이 알고리즘을 구하기 위해 필요로하는 변수가 가중치, 평균, 분산이고, 이 값들을 구하기 위해 사전에 영상에 대한 정규화된 히스토그램이 구해져있어야합니다. 그래야 이 히스토그램을 통해서 가중치, 평균, 분산값을 구할 수가 있게 됩니다.
먼저, 정규화된 히스토그램안에서 임계값 0~t까지의 누적합, t+1부터 255까지 누적합을 구해서 두 개의 가중치를 구합니다. 그리고 0~t까지와 t+1~255까지의 평균을 구하고 평균을 통해 분산을 구해줍니다.
그래서 앞 집합의 가중치과 분산을 곱해준 값과 뒷 집합의 가중치와 분산을 곱해준 값을 더해줍니다.
이렇게 0~255까지 t를 순회가 끝나면 그 중 가장 높은 값을 가지는 t를 최종 임계값으로 취해주면 됩니다.
아래는 C++로 구현한 코드입니다.
구현한 코드는 제가 작업하면서 짠 코드중 Otsu's 알고리즘만 잘라온 것이기 때문에 중간에 이미지나 변수 들은 알아서 변경하셔서 사용하셔야합니다. 나중에 일반화된 코드로 수정하겠습니담.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
int Otsu_Thres = 0;
double inter_class_variance = 0;
for (int O_thres = 0; O_thres < 256; O_thres++) {
int alpha = 0, beta = 0;
int sum1 = 0, sum2 = 0;
double mu1 = 0, mu2 = 0;// , var1, var2;
for (int i = 0; i < O_thres; i++)
sum1 += calcBinary[i];
cout << Otsu_Thres << endl;
cout << "sum1 : " << sum1 << endl;
cout << "sum2 : " << sum2 << endl;
alpha = (double)sum1 / (double)calcBinary.size();
beta = (double)sum2 / (double)calcBinary.size();
cout << "alpha : " << alpha << endl;
cout << "beta : " << beta << endl;
// Calc average
for (int m = 0; m < O_thres; m++) {
mu1 += (double)(m * calcBinary[m]) / (double)sum1;
}
for (int m = O_thres; m < 256; m++) {
mu2 += (double)(m * calcBinary[m]) / (double)sum2;
}
//cout << "mu1 : " << mu1 << endl;
//cout << "mu2 : " << mu2 << endl;
/*// Calc variance
for (int v = 0; v < O_thres; v++) {
var1 += (double)(pow((v - mu1), 2) * calcBinary[v]) / (double)sum1;
}
for (int v = O_thres; v < 256; v++) {
var2 += (double)(pow((v - mu2), 2) * calcBinary[v]) / (double)sum2;
}*/
double temp = alpha * beta * pow((mu1 - mu2), 2);
if (inter_class_variance < temp) {
inter_class_variance = temp;
Otsu_Thres = O_thres;
}
}
|
'Programming > OpenCV' 카테고리의 다른 글
[OpenCV] MeanShift Clustering C++ Code (0) | 2020.05.13 |
---|---|
[OpenCV] QuadTree Clustering C++ Code (0) | 2019.11.25 |
[OpenCV] KMeans Clustering C++ Code (0) | 2019.11.25 |
최종 수정 날짜 2019.11.08.
연구실에서 차선 검출 및 도로 검출을 위해서 OpenCV로 작업했던 일이 있었니다.
요즘은 ros에서 PCL을 사용해서 라이다 데이터를 이용하는 작업으로 바꼈지만, 이 전에 도로 영역을 검출하기 위해 했던 공부 했던 Otsu's Algorithm 구현 코드를 올리려고 합니다.
Otsu's 알고리즘은 영상 이진화 방법 중에 하나입니다. 영상의 이진화 방법은 여러가지 방법이 있는데, 그 중에서도 이 Otsu's 알고리즘은 정말 많이 사용되곤 합니다.
우선, 영상의 이진화를 하기 위해서는 임계값을 정해줘야합니다. 흑백영상을 예를 들자면, 일반적으로 흑백 영상의 픽셀값의 범위는 0~255를 가지고 있습니다. 여기서 임계값이 200으로 설정하고 이진화를 한다면 픽셀값이 200을 넘어가면 255, 200을 못 넘는다면 0의 값으로 변경시키며 0 또는 255값만을 가지도록 이진화를 해줄 수가 있습니다. 그런데 영상의 이진화 방법이 참 까다로워질 수 있는 이유는 이진화 결정하는 임계값을 사용자가 정해줘야하는 경우가 있기 때문입니다. 따라서 이러한 시도에는 사용자의 여러 시행착오가 필요하고 개발자로 하여금 인내심을 갖도록 하는 작업이기도 합니다. 이 때 우리가 자동으로 이 임계값을 정하기 위해 사용하는 알고리즘이 Otsu's 알고리즘입니다.
임계값을 t라고 가정한다면 t를 기준으로 픽셀값들을 두 집합으로 나누었을 때, 각 집합의 밝기값의 분포가 균등할수록 좋다는 점에 착안하여서 균등성이 제일 높도록 이진화하는 t에게 높은 점수를 줍니다. 이 균등성은 각 그룹의 분산으로 측정을 하고, 분산이 작을수록 균등성이 높습니다. 이 t를 0~255까지 움직이며 모든 경우의 점수를 계산하고 그 중 가장 높은 점수를 가진 t를 최종 임계값으로 취하는 알고리즘입니다.
이 알고리즘을 구하기 위해 필요로하는 변수가 가중치, 평균, 분산이고, 이 값들을 구하기 위해 사전에 영상에 대한 정규화된 히스토그램이 구해져있어야합니다. 그래야 이 히스토그램을 통해서 가중치, 평균, 분산값을 구할 수가 있게 됩니다.
먼저, 정규화된 히스토그램안에서 임계값 0~t까지의 누적합, t+1부터 255까지 누적합을 구해서 두 개의 가중치를 구합니다. 그리고 0~t까지와 t+1~255까지의 평균을 구하고 평균을 통해 분산을 구해줍니다.
그래서 앞 집합의 가중치과 분산을 곱해준 값과 뒷 집합의 가중치와 분산을 곱해준 값을 더해줍니다.
이렇게 0~255까지 t를 순회가 끝나면 그 중 가장 높은 값을 가지는 t를 최종 임계값으로 취해주면 됩니다.
아래는 C++로 구현한 코드입니다.
구현한 코드는 제가 작업하면서 짠 코드중 Otsu's 알고리즘만 잘라온 것이기 때문에 중간에 이미지나 변수 들은 알아서 변경하셔서 사용하셔야합니다. 나중에 일반화된 코드로 수정하겠습니담.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
int Otsu_Thres = 0;
double inter_class_variance = 0;
for (int O_thres = 0; O_thres < 256; O_thres++) {
int alpha = 0, beta = 0;
int sum1 = 0, sum2 = 0;
double mu1 = 0, mu2 = 0;// , var1, var2;
for (int i = 0; i < O_thres; i++)
sum1 += calcBinary[i];
cout << Otsu_Thres << endl;
cout << "sum1 : " << sum1 << endl;
cout << "sum2 : " << sum2 << endl;
alpha = (double)sum1 / (double)calcBinary.size();
beta = (double)sum2 / (double)calcBinary.size();
cout << "alpha : " << alpha << endl;
cout << "beta : " << beta << endl;
// Calc average
for (int m = 0; m < O_thres; m++) {
mu1 += (double)(m * calcBinary[m]) / (double)sum1;
}
for (int m = O_thres; m < 256; m++) {
mu2 += (double)(m * calcBinary[m]) / (double)sum2;
}
//cout << "mu1 : " << mu1 << endl;
//cout << "mu2 : " << mu2 << endl;
/*// Calc variance
for (int v = 0; v < O_thres; v++) {
var1 += (double)(pow((v - mu1), 2) * calcBinary[v]) / (double)sum1;
}
for (int v = O_thres; v < 256; v++) {
var2 += (double)(pow((v - mu2), 2) * calcBinary[v]) / (double)sum2;
}*/
double temp = alpha * beta * pow((mu1 - mu2), 2);
if (inter_class_variance < temp) {
inter_class_variance = temp;
Otsu_Thres = O_thres;
}
}
|
'Programming > OpenCV' 카테고리의 다른 글
[OpenCV] MeanShift Clustering C++ Code (0) | 2020.05.13 |
---|---|
[OpenCV] QuadTree Clustering C++ Code (0) | 2019.11.25 |
[OpenCV] KMeans Clustering C++ Code (0) | 2019.11.25 |