해당 포스트는 "열혈강의 영상처리 프로그래밍" 책의 내용을 요약한 것이다.



해당 포스트는 이전 포스트 영상의 평행 이동, 회전, 확대 및 축소를 보고 읽기를 바란다.


※ 여러 기하학적 변환을 한꺼번에 적용하기

영상의 기하학적 변환을 평행 이동, 회전, 크기 변환 따로 적용해도 되지만 한꺼번에 적용하면 더 효율적일 것이다.

예를 들어 영상의 중심을 기준으로 회전 변환을 시킨다고 해보자. 영상의 회전 변환은 원점을 기준으로 회전하기 때문에 영상의 중심을 원점으로 평행 이동 후 회전 변환을 한 다음에 영상의 중심을 처음 위치로 평행 이동시켜야 한다. 이와 같은 변환을 하나의 식으로 표현해보자. 먼저 위 그림의 각 단계마다 영상의 좌표를 (x, y), (x1, y1), (x2, y2), (x', y') 라고 하자. 각 단계에서 점들의 관계는 다음과 같이 나타낼 수 있다.




위 식을 (x, y)에 대하여 나타내보자



       

         

     

         


위 수식을 코드로 표현하면 다음과 같다.

double cR = cos(theta);
double sR = cos(theta);
double srcX = ( cR * (x-cx) + sR * (y-cy)) + cx;
double srcY = ( -sR * (x-cx) + cR * (y-cy)) + cy;
imageOut.GetAt(x, y) = imageIn.BiLinearIntp(srcX, srcY);

여기에 추가로 확대와 평행 이동을 적용하면 평행 이동과 회전, 확대 및 축소를 모두 수행할 수 있는 일반적인 변환을 다음과 같이 수행할 수 있다.

double cR = cos(theta);
double sR = cos(theta);
double srcX = s * ( cR * (x-cx) + sR * (y-cy)) + cx - tx;
double srcY = s * ( -sR * (x-cx) + cR * (y-cy)) + cy - ty;
imageOut.GetAt(x, y) = imageIn.BiLinearIntp(srcX, srcY);

위에서 s는 확대 비율이고 tx와 ty는 영상의 중심이 평행 이동할 크기를 나타낸다. 각 변환을 조절하는 변수들이 theta=0, s=1, tx=0, ty=0의 값을 가지면 해당 변수에 대한 변환이 일어나지 않기 때문에 이 수식을 기본으로 하여 각 변수의 값만 조절하는 것으로 영상의 기하학적 변환을 손쉽게 할 수 있다.



※ 변환 행렬을 이용한 기하학적 변환

위에서 영상의 평행 이동, 회전, 확대/ 축소를 모두 수행할 수 있는 일반적인 변환 식에 대해서 배웠다. 이 때 변환 행렬을 사용하면 영상의 변환을 더욱 간단한 수식으로 표현 가능하다. 2차원 평면 공간에서 영상 변환을 수행하는 행렬은 3*3크기로 나타낼 수 있는 데 이 행렬을 H라 하면 다음과 같은 영상 변환 관계식이 정의된다.

 

      

  여기서 위와 같이 1을 추가한 동차 좌표 표현이 사용된다. 동차 표현에서는 2차원 벡터가 총 세 개의 원소로 (x,y,z,)와 같이 표현 되는 데 이를 다시 (x,y)와 같이 일반적인 좌표로 변환하려면 (x/z, y/z) 와 같이 세 번째 원소로 나누면된다. 더 자세한 동차 좌표에 대한 개념은 url 걸어놓은 사이트에서 확인 바란다. 인제 변환 행렬 H가 어떻게 구성되는지 알아보자.

가로 방향으로 tx, 세로 방향으로 ty 만큼 평행 이동하는 변환 행렬은 다음과 같다.



여기에 영상의 회전 변환을 추가하면 다음과 같다.



그러면 우리가 영상의 일반적인 변환을 위해서 구한 행렬식을 이용해서 변환 행렬 H를 구해보자. 다음은 우리가 앞서 구현 했던 행렬식이다. 


 


위 행렬식에서 추가로 영상의 평행 이동 tx, ty를 더한 후 이를 변환 행렬 H로 표현하면 다음과 같이 표현할 수 있다.

위 변환 행렬의 각 원소를 다음과 같은 변수로 표현하면 각 변수에 대해서 코드가 다음과 같다.



아래의 영상 변환식을 통해 x' = m_dH1*x + m_dH2*y + m_dH3, y' = m_dH4*x + m_dH2*y + m_dH3 가 된다. 이를 코드로 표현하면 밑에와 같이 된다.




void CImageWarperDlg::OnBnClickedButtonWarp()
{
	// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
	if (m_imageIn.IsEmpty())
		return;

	m_imageOut.SetConstValue(0);

	double cR = cos(m_dRotation*M_PI/180.0);
	double sR = sin(m_dRotation*M_PI/180.0);
	double z = 1.0 / m_dScale;
	double cx = m_nWidth/2.0;  // 영상 가로 중심
	double cy = m_nHeight/2.0; // 영상 세로 중심

	m_dH1 = z*cR;
	m_dH2 = z*sR;
	m_dH3 = z*( cR*(-cx) + sR*(-cy)) + cx - m_dTransX;
	m_dH4 = -z*sR;
	m_dH5 = z*cR;
	m_dH6 = z*(-sR*(-cx) + cR*(-cy)) + cy - m_dTransY;
	m_dH7 = 0;
	m_dH8 = 0;
	m_dH9 = 1;
	UpdateData(FALSE);

	int r, c;
	for (r=0 ; r<m_nHeight ; r++)
	{
		for (c=0 ; c<m_nWidth ; c++)
		{
			double srcX = z*( cR*(c-cx) + sR*(r-cy)) + cx - m_dTransX;
			double srcY = z*(-sR*(c-cx) + cR*(r-cy)) + cy - m_dTransY;
			
			if (srcX>=0 && srcX<m_nWidth && srcY>=0 && srcY<m_nHeight)
			{
				m_pOut[r*m_nWStep+c] = m_imageIn.BiLinearIntp(srcX, srcY);					
			}
		}
	}

	ShowImage(m_imageOut, "Image");
	m_imageOut.SaveImage("Warped.bmp");
}

+ Recent posts