9.3.2 임의의 각도 회전 구현
영상의 회전 변환에서는 삼각함수의 계산이 필요하다. C/C++에서는 sin, cos, tan 등의 삼각함수를 제공하며, 이들 함수를 사용하려면 먼저 <math.h> 파일을 포함(#include)해야 한다. C/C++에서 제공하는 sin, cos 함수의 원형은 다음과 같다.
double sin(double x); double cos(double x);
이들 함수에서 인자로 받는 회전 각도 x는 라디안radian 단위를 사용하기 때문에 우리가 흔히 사용하는 형태의 각도 단위(°)를 사용하려면 다음과 같은 공식으로 변환해야 한다.
const double PI = 3.14159265358979323846; double rad = (angle * PI) / 180.;
여기서 const double형으로 선언된 변수 PI는 원주율 π를 의미한다. 이를 이용하여 회전 각도 angle로 입력된 값은 (angle * PI) / 180 공식에 의해 라디안 값 rad로 변환된다.
그러면 영상의 회전 변환을 수행하는 함수를 작성해보자. 함수의 이름은 IppRotate를 사용하기로 하고, 다음과 같은 함수 선언을 IppGeometry.h 파일에 추가하자.
void IppRotate(IppByteImage& imgSrc, IppByteImage& imgDst, double angle);
IppRotate 함수는 입력 영상 imgSrc를 angle 각도만큼 시계 방향으로 회전한 후, 결과 영상을 imgDst에 저장한다. 이 함수의 앞부분에서는 회전 변환되어 생성될 결과 영상의 크기를 계산하고, 뒷부분에서는 실제 회전 변환을 수행한다. 회전 변환 시 보간법은 양선형 보간법을 사용하였다. IppRotate 함수의 전체 내용은 소스 9-9에 나타내었다.
#include <math.h> const double PI = 3.14159265358979323846; … void IppRotate(IppByteImage& imgSrc, IppByteImage& imgDst, double angle) { int w = imgSrc.GetWidth(); int h = imgSrc.GetHeight(); double rad = (angle * PI) / 180.; double cos_value = cos(rad); double sin_value = sin(rad); // 회전 후 생성되는 영상의 크기를 계산. // 4개의 코너 포인트의 이동 좌표를 계산하여 최대, 최소점의 차이를 구한다. int nx, ny, minx, miny, maxx, maxy, nw, nh; minx = maxx = 0; miny = maxy = 0; nx = static_cast<int>(floor((w - 1)* cos_value + 0.5)); ny = static_cast<int>(floor((w - 1) * sin_value + 0.5)); minx = (minx < nx) ? minx : nx; maxx = (maxx > nx) ? maxx : nx; miny = (miny < ny) ? miny : ny; maxy = (maxy > ny) ? maxy : ny; nx = static_cast<int>(floor(-(h - 1) * sin_value + 0.5)); ny = static_cast<int>(floor((h - 1) * cos_value + 0.5)); minx = (minx < nx) ? minx : nx; maxx = (maxx > nx) ? maxx : nx; miny = (miny < ny) ? miny : ny; maxy = (maxy > ny) ? maxy : ny; nx = static_cast<int>(floor((w - 1) * cos_value - (h - 1) * sin_value + 0.5)); ny = static_cast<int>(floor((w - 1) * sin_value + (h - 1) * cos_value + 0.5)); minx = (minx < nx) ? minx : nx; maxx = (maxx > nx) ? maxx : nx; miny = (miny < ny) ? miny : ny; maxy = (maxy > ny) ? maxy : ny; nw = maxx - minx + 1; nh = maxy - miny + 1; // 실제 회전 변환 imgDst.CreateImage(nw, nh); BYTE** pSrc = imgSrc.GetPixels2D(); BYTE** pDst = imgDst.GetPixels2D(); int i, j, x1, x2, y1, y2; double rx, ry, p, q, temp; for (j = miny; j <= maxy; j++) for (i = minx; i <= maxx; i++) { rx = i * cos_value + j * sin_value; ry = -i * sin_value + j * cos_value; x1 = static_cast<int>(rx); y1 = static_cast<int>(ry); // 원본 영상 내에 포함된 좌표가 아니라면 무시. if (x1 < 0 || x1 > w - 1 || y1 < 0 || y1 > h - 1) continue; x2 = x1 + 1; if (x2 == w) x2 = w - 1; y2 = y1 + 1; if (y2 == h) y2 = h - 1; p = rx - x1; q = ry - y1; temp = (1.0 - p) * (1.0 - q) * pSrc[y1][x1] + p * (1.0 - q) * pSrc[y1][x2] + (1.0 - p) * q * pSrc[y2][x1] + p * q * pSrc[y2][x2]; pDst[j - miny][i - minx] = static_cast<BYTE>(limit(temp)); } }