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 함수는 입력 영상 imgSrcangle 각도만큼 시계 방향으로 회전한 후, 결과 영상을 imgDst에 저장한다. 이 함수의 앞부분에서는 회전 변환되어 생성될 결과 영상의 크기를 계산하고, 뒷부분에서는 실제 회전 변환을 수행한다. 회전 변환 시 보간법은 양선형 보간법을 사용하였다. IppRotate 함수의 전체 내용은 소스 9-9에 나타내었다.

    소스 9-9 영상의 회전 변환 구현 함수(IppGeometry.cpp)
    #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));
        }
    }
    
    신간 소식 구독하기
    뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.