Posted Updated

Image

이번 포스팅에서는 Image Rotation 에 대해 다루려고 한다.

Image Rotation 공식을 이용하여 영상을 회전시켰다.

Lena.png 파일을 각각 30도, 45도, 60도 회전시킨 결과이다.

[소스 코드]

사용 언어: C++

환경: Microsoft Windows 10 Home, Visual Studio 2022, C:\opencv...

#include <opencv2/opencv.hpp>
#include <iostream>
#include <cmath>
using namespace cv;
using namespace std;

Mat rotateImage(const Mat& src, double degree) {
    int W = src.cols;
    int H = src.rows;
    Mat result(H, W, CV_8UC1, Scalar(0)); // 모든 픽셀 값을 0으로 초기화하고 시작

    double rad = degree * CV_PI / 180.0; // 라디안 = 각도 x π x 180
    double cos_t = cos(rad);
    double sin_t = sin(rad);

    for (int y = 0; y < H; y++) {
        for (int x = 0; x < W; x++) {

            // 이미지의 중심을 원점으로 이동
            double nx = x - W / 2.0;
            double ny = y - H / 2.0;

            // Image Rotation 공식 적용
            double srcX = cos_t * nx + sin_t * ny + W / 2.0; // y축은 아래 방향으로 증가(중요)
            double srcY = -sin_t * nx + cos_t * ny + H / 2.0;

            // 범위 체크 후 보간(여기서는 Bilinear Interpolation) -> 변환한 회전 좌표는 정수가 아닌 실수이므로 어떤 픽셀 값을 쓸 지 결정해야 함
            if (srcX >= 0 && srcX < W - 1 && srcY >= 0 && srcY < H - 1) {
                int x0 = (int)floor(srcX);
                int y0 = (int)floor(srcY);
                int x1 = min(x0 + 1, W - 1);
                int y1 = min(y0 + 1, H - 1);

                double dx = srcX - x0;
                double dy = srcY - y0;

                double p1 = src.at<uchar>(y0, x0);
                double p2 = src.at<uchar>(y0, x1);
                double p3 = src.at<uchar>(y1, x0);
                double p4 = src.at<uchar>(y1, x1);

                double q1 = (1 - dx) * p1 + dx * p2;
                double q2 = (1 - dx) * p3 + dx * p4;
                double q = (1 - dy) * q1 + dy * q2;

                // 픽셀 밝기 값 범위 보정(0 ~ 255)
                if (q < 0) {
                    q = 0;
                }

                if (q > 255) {
                    q = 255;
                }

                result.at<uchar>(y, x) = static_cast<uchar>(q);
            }
        }
    }

    return result;
}

int main() {
    Mat src = imread("Lena.png", IMREAD_GRAYSCALE);

    if (src.empty()) {
        cout << "이미지를 열 수 없습니다." << endl;

        return -1;
    }

    double angles[3] = { 30.0, 45.0, 60.0 };

    for (double deg : angles) {
        Mat rotated_image = rotateImage(src, deg);

        imshow(to_string((int)deg) + "도", rotated_image);
    }

    waitKey(0);

    return 0;
}

8비트일 때, unsigned char 형 픽셀의 범위는 0부터 255까지이므로 데이터를 최종적으로 출력하기 전에 이 범위 안으로 한정시키는 과정이 필요하다.

원점을 중심으로 회전하는 다음 회전식을 이용하였다.

Image

따라서, 이미지의 중심을 원점으로 이동시킨 후 공식을 적용한다.

다만, 영상 좌표계에서 y축은 아래로 증가하기 때문에 sin Θ 값은 -를 붙여서 사용해야 한다.

회전 후에는 + W / 2.0, + H / 2.0으로 다시 원래 중심으로 이동시켜준다.

또한, 변환한 회전 좌표는 정수가 아닌 실수이기 때문에 어떤 픽셀 값을 쓸지 결정해야 한다. 필자는 Bilinear Interpolation 기법을 사용하여 픽셀 값을 결정하였다.

Leave a comment