Favicon

Perspective transformation

Peponi3/27/20253m

C#
NugetPackageOpenCvSharp4ProjectionMatrix

1. Introduction

OpenCV의 원근 변환은 2D 공간에서 행렬을 변환하는 데 사용되는 4 point 변환이다. 이 때, 원본 이미지로부터 선의 특징을 유지하며 변환이 이루어진다. 원근 변환을 수학적으로 표현하면 다음과 같다.

  1. Transformation matrix
    원근 변환을 위한 변환 행렬은 333 * 3 행렬로 표현된다. Matt=[m00m01m02m10m11m12m20m211]Mat_t = \begin{bmatrix} m_{00} & m_{01} & m_{02} \\ m_{10} & m_{11} & m_{12} \\ m_{20} & m_{21} & 1 \end{bmatrix}
  2. Homogeneous coordinates
    2차원 좌표 [xy]\begin{bmatrix} x \\ y \end{bmatrix}를 변환하기 위해 동차 좌표인 [xy1]\begin{bmatrix} x \\ y \\ 1 \end{bmatrix}로 표현한다. Imageo=[xy1]Image_o = \begin{bmatrix} x \\ y \\ 1 \end{bmatrix}
  3. Computation
    MattMat_tImageoImage_o를 곱하여 ImagetImage_t [xyw]\begin{bmatrix} x' \\ y' \\ w \end{bmatrix} 를 산출한다. Imaget=MattImageo=[m00m01m02m10m11m12m20m211][xy1]=[m00x+m01y+m02m10x+m11y+m12m20x+m21y+1]\begin{align} Image_t &= Mat_t \cdot Image_o \notag\\ &= \begin{bmatrix} m_{00} & m_{01} & m_{02} \\ m_{10} & m_{11} & m_{12} \\ m_{20} & m_{21} & 1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} \notag\\ &= \begin{bmatrix} m_{00}x+m_{01}y+m_{02} \\ m_{10}x+m_{11}y+m_{12} \\ m_{20}x+m_{21}y+1 \end{bmatrix} \notag \end{align}
  4. Normalization
    ImagetImage_t의 좌표를 정규화하여 최종 좌표 [xy1]\begin{bmatrix} x'' \\ y'' \\ 1 \end{bmatrix}를 얻는다. Normalized=Imaget/w=1m20x+m21y+1[m00x+m01y+m02m10x+m11y+m12m20x+m21y+1]\begin{align} Normalized &= Image_t \, / \, w \notag \\ &= \frac{1}{m_{20}x+m_{21}y+1} \cdot \begin{bmatrix} m_{00}x+m_{01}y+m_{02} \\ m_{10}x+m_{11}y+m_{12} \\ m_{20}x+m_{21}y+1 \end{bmatrix} \notag \end{align} x=m00x+m01y+m02m20x+m21y+1x'' = \frac{m_{00}x+m_{01}y+m_{02}}{m_{20}x+m_{21}y+1} y=m10x+m11y+m12m20x+m21y+1y'' = \frac{m_{10}x+m_{11}y+m_{12}}{m_{20}x+m_{21}y+1}

OpenCV에서는 transformation matrix를 구하기 위한 Cv2.GetPerspectiveTransform()을 제공하여 쉽게 변환 행렬을 얻을 수 있다. 이 문서에서는 연산을 수행할 4개의 좌표와 이동 좌표를 이용하여 원근 변환을 수행하는 예시를 소개한다.

실습에 사용할 이미지는 다음과 같다.

Image by anikh54 from Pixabay

2. Example

original with pointsOriginal with points
transformedTransformed
private void PerspectiveTransform(Mat image)
{
    // 각 collection의 순서에 맞춰 변환
    List<Point2f> origin = [
        new(180, 470),
        new(294, 470),
        new(90, 625),
        new(387, 625)
        ];
    List<Point2f> destination = [
        new(0, 0),
        new(480, 0),
        new(0, 640),
        new(480, 640)
        ];
 
    // 이미지에 초기 좌표 표시
    origin.ForEach(point => image.Circle((int)point.X, (int)point.Y, 8, Scalar.LightGreen, -1));
 
    // 원근 변환 행렬 생성
    using var perspectiveTransformation = Cv2.GetPerspectiveTransform(origin, destination);
 
    // Transform
    using var perspectiveTransformed = new Mat();
    Cv2.WarpPerspective(image, perspectiveTransformed, perspectiveTransformation, new OpenCvSharp.Size(image.Width, image.Height));
}

3. 참조 자료