이미지 처리 연산 과정에서 제곱근 값을 계산해야 경우가 많이 생긴다. 이 경우 math-라이브러리의 sqrt() 함수를 사용하는 것보다 원하는 정밀도까지만 계산을 하는 근사적인 제곱근 함수를 만들어서 사용하는 것이 계산 비용 측면에서 유리할 수 있다.
주어진 양수
함수
제곱근의 경우에는 (
의 형태로 주어진다. 그러나, 이 점화식에는 계산 부하가 상대적으로 큰 나누기 연산이 들어 있다. 그런데
이므로 전체적인 제곱근을 구하는 pseudo 코드는
float fast_sqrt(float x) {
xhalf = 0.5F * x;
y = initial_guess_of (1/sqrt(x));
while (error is not small)
// find approx inverse sqrt root of (x);
y = y * 1.5 - y * y * y * xhalf;
end while
// finally return sqrt(x) = x * (1 / sqrt(x));
return (x * y);
}
보통 iteration 횟수는 고정이 되어 있다. 남은 문제는 초기값 추정뿐이다. Newton 방법에서 잘 추정된 초기값은 몇 번의 반복으로도 원하는 정밀도를 얻을 수 있게 하지만 잘못된 추정은 수렴 속도를 더디게 한다. 또한 초기값의 추정 과정도 매우 간단해야 하는데, 좋은 추정을 하기 위해서는 IEEE에서 규정하는 double/float 포맷에 대한 정보와 수치해석 지식이 필요하다.
IEEE의 32비트 float 상수의 비트 구성은

임의의 양의 float 상수
로 쓰여지면, 정수로 변환된 비트 데이터는
로 표현되므로 비트 데이터에서 지수부는
이제 필요한 것은 잘 선택된
정수 표현 = (190 - E / 2) << 23;
= (190 << 23) - (x의 정수형 변환 & (0xFF << 23)) >> 1;
~ (190 << 23) - (x의 정수형 변환) >> 1; // 유효 숫자 / 2 만큼 에러 발생;
= 0x5F000000 - (*(int *)&x) >> 1;
유효숫자 부분은 좀 더 높은 초기 정밀도를 갖도록 근사를 할 수 있는데 결과만 쓰면
//빠른 float 제곱근의 역수;
float fast_invsqrt(float x) {
float xhalf = 0.5F * x;
// initial guess of 1 / sqrt(x);
int i = 0x5F3759DF - (*(int *)&x) >> 1;
x = *(float *)&i;
//iterate
x = x * (1.5F - x * x * xhalf);
x = x * (1.5F - x * x * xhalf);
// end of calculation of 1 / sqrt(x);
return (x);
};
// 빠른 float 제곱근;
float fast_sqrt(float x) {
float xhalf = 0.5F * x;
//initial guess of 1 / sqrt(x);
int i = 0x5F3759DF - (*(int *)&x) >> 1;
float y = *(float *)&i;
//iterate
y = y * (1.5F - y * y * xhalf);
y = y * (1.5F - y * y * xhalf);
//end of calculation of 1/sqrt(x);
//return sqrt(x)=x * 1/sqrt(x);
return (x * y)
};
이다. 따라서 오차를 최댓값의 절반으로 잡아서 근사하면
그리고
로 쓸 수 있고, 위에서 구한
을 얻을 수 있다.

'Image Recognition > Fundamental' 카테고리의 다른 글
Edge and Corner Detection (0) | 2021.01.27 |
---|---|
점증적인 cosine/sine 값 계산 (1) | 2020.12.28 |
2차원 Gaussian 분포 생성(Creating a 2d Gaussian Distribution) (0) | 2020.12.10 |
PCA Line Fitting (0) | 2020.11.12 |
Histogram Equalization (0) | 2020.11.12 |