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



※ 입방 회선 보간

쌍선형 보간은 두 점 사이의 값을 1차 곡선인 직선을 통해 근사한 것이다. 입방 회선 보간에서는 x-1, x, x+1, x+2 네 개의 점을 이용하여 3차 곡선으로 근사한다. 입방 회선 보간도 쌍선형 보간과 마찬가지로 가로 세로 각 축에 대해서 계산한다. 각 위치의 픽셀 값을 I(x)로 표현한다면 I(x+p)는 입방 회선 보간의 3차 함수를 이용해 다음과 같이 구할 수 있다.


I(x+p) = {-I(x-1) + I(x) - I(x+1) + I(x+2)}*p^3 + {(2)I(x-1) - 2I(x) + I(x+1) + I(x+2)}p^2 + {-I(x-1) + I(x+1)}p + I(x)


위 다항식을 가로, 세로 축으로 각각 적용하면 x+p의 입방 회선 보간 결과를 얻을 수 있다. 아래는 x+p의 픽셀 값이 입방 회선 보간을 통해 얻어지는 코드이다. 


BYTE CubicConvIntp(double x, double y) { int px[4], py[4]; //x-1,x,x+1,x+2 값을 구한다. px[0] = IN_IMG((int)x-1, 0, m_nWidth); px[1] = IN_IMG((int)x , 0, m_nWidth); px[2] = IN_IMG((int)x+1, 0, m_nWidth); px[3] = IN_IMG((int)x+2, 0, m_nWidth); //y-1, y, y+1, y+2 값을 구한다. py[0] = IN_IMG((int)y-1, 0, m_nHeight); py[1] = IN_IMG((int)y , 0, m_nHeight); py[2] = IN_IMG((int)y+1, 0, m_nHeight); py[3] = IN_IMG((int)y+2, 0, m_nHeight); // 위 다항식에서 대입할 p, p^2, p^3을 미리 계산한다. double dx = x - px[1]; double dy = y - py[1]; double dx2 = dx*dx; double dx3 = dx2*dx; double dy2 = dy*dy; double dy3 = dy2*dy; BYTE p0, p1, p2, p3; // 네 정수 좌표의 픽셀 값 double c1, c2, c3, c4; // 네 정수 좌표의 가중치 double C[4]; // 가로 방향 보간 결과 //가로 방향에 대해서 입방회선 보간을 한다. // 4개의 입방 회선 보간 결과를 구하는 이유는 // 세로 방향 y-1, y, y+1, y+2에 대해서 입방 회선 보간을 // 해야하기 때문이다. for (int i=0 ; i<4 ; i++) { p0 = m_pImageData[py[i]*m_nWStep + px[0]]; p1 = m_pImageData[py[i]*m_nWStep + px[1]]; p2 = m_pImageData[py[i]*m_nWStep + px[2]]; p3 = m_pImageData[py[i]*m_nWStep + px[3]]; c1 = p1; c2 = -p0 + p2; c3 = 2*(p0-p1) + p2 - p3; c4 = -p0 + p1 - p2 + p3; C[i] = c1 + c2*dx + c3*dx2 + c4*dx3; } //가로 회선 보간 결과 토대로 세로 방향으로 // 입방 회선 보간을 한다. c1 = C[1]; c2 = -C[0] + C[2]; c3 = 2*(C[0]-C[1]) + C[2] - C[3]; c4 = -C[0] + C[1] - C[2] + C[3]; return CLIP(c1 + c2*dy + c3*dy2 + c4*dy3); }


입방 회선 보간은 앞 포스트 쌍선형 보간에 비해 많은 계산이 필요하지만, 더욱 우수한 보간 결과를 보여준다.



※ 쌍입방 보간

쌍입방 보간은 16개의 정수 좌표 픽셀을 이용해 (x,y), (x+1,y), (x,y+1), (x+1, y+1) 영역을 곡면으로 보간하는 방법으로 각 영역을 미분해 곡면을 근사한다. 쌍선형 보간은 우리가 앞에서 4개의 점을 이용했다. 쌍입방 보간은 4개의 좌표를 미분해야 하기 때문에 쌍선형 보간에서 사용되는 4개의 점을 둘러쌓은 12개의 점을 추가로 이용한다. 

쌍입방 보간 결과를 도출하는 다항식을 알기 위해서는 수학적 지식과 이해가 필요해 코드만 구현했다. 

	BYTE BiCubicIntp(double x, double y)
	{
		int x0 = IN_IMG((int)x-1, 0, m_nWidth);
		int x1 = IN_IMG((int)x  , 0, m_nWidth);
		int x2 = IN_IMG((int)x+1, 0, m_nWidth);
		int x3 = IN_IMG((int)x+2, 0, m_nWidth);

		int y0 = IN_IMG((int)y-1, 0, m_nHeight);
		int y1 = IN_IMG((int)y  , 0, m_nHeight);
		int y2 = IN_IMG((int)y+1, 0, m_nHeight);
		int y3 = IN_IMG((int)y+2, 0, m_nHeight);

		BYTE p00 = m_pImageData[y0*m_nWStep+x0];
		BYTE p01 = m_pImageData[y1*m_nWStep+x0];
		BYTE p02 = m_pImageData[y2*m_nWStep+x0];
		BYTE p03 = m_pImageData[y3*m_nWStep+x0];

		BYTE p10 = m_pImageData[y0*m_nWStep+x1];
		BYTE p11 = m_pImageData[y1*m_nWStep+x1];
		BYTE p12 = m_pImageData[y2*m_nWStep+x1];
		BYTE p13 = m_pImageData[y3*m_nWStep+x1];

		BYTE p20 = m_pImageData[y0*m_nWStep+x2];
		BYTE p21 = m_pImageData[y1*m_nWStep+x2];
		BYTE p22 = m_pImageData[y2*m_nWStep+x2];
		BYTE p23 = m_pImageData[y3*m_nWStep+x2];

		BYTE p30 = m_pImageData[y0*m_nWStep+x3];
		BYTE p31 = m_pImageData[y1*m_nWStep+x3];
		BYTE p32 = m_pImageData[y2*m_nWStep+x3];
		BYTE p33 = m_pImageData[y3*m_nWStep+x3];

		double f00 = p11;
		double f10 = p21;
		double f01 = p12;
		double f11 = p22;
		double fx00 = (p21-p01)/2.0;
		double fx10 = (p31-p11)/2.0;
		double fx01 = (p22-p02)/2.0;
		double fx11 = (p32-p12)/2.0;
		double fy00 = (p12-p10)/2.0;
		double fy10 = (p22-p20)/2.0;
		double fy01 = (p13-p11)/2.0;
		double fy11 = (p23-p21)/2.0;
		double fxy00 = (p22-p02-p20+p00)/4.0;
		double fxy10 = (p32-p12-p30+p10)/4.0;
		double fxy01 = (p23-p03-p21+p01)/4.0;
		double fxy11 = (p33-p13-p31+p11)/4.0;

		double a00 = f00;
		double a10 = fx00;
		double a20 = -3*f00 + 3*f10 -2*fx00 - fx10;
		double a30 =  2*f00 - 2*f10 +  fx00 + fx10;
		double a01 = fy00;
		double a11 = fxy00;
		double a21 = -3*fy00 + 3*fy10 - 2*fxy00 - fxy10;
		double a31 =  2*fy00 - 2*fy10 +   fxy00 + fxy10;
		double a02 = -3*f00 + 3*f01 - 2*fy00 - fy01;
		double a12 = -3*fx00 + 3*fx01 - 2*fxy00 - fxy01;
		double a22 =  9*f00 - 9*f10 - 9*f01 + 9*f11 + 6*fx00 + 3*fx10 - 6*fx01 - 3*fx11 + 6*fy00 - 6*fy10 + 3*fy01 - 3*fy11 + 4*fxy00 + 2*fxy10 + 2*fxy01 + fxy11;
		double a32 = -6*f00 + 6*f10 + 6*f01 - 6*f11 - 3*fx00 - 3*fx10 + 3*fx01 + 3*fx11 - 4*fy00 + 4*fy10 - 2*fy01 + 2*fy11 - 2*fxy00 - 2*fxy10 -   fxy01 - fxy11;
		double a03 = 2*f00 - 2*f01 + fy00 + fy01;
		double a13 = 2*fx00 - 2*fx01 + fxy00 + fxy01;
		double a23 = -6*f00 + 6*f10 + 6*f01 - 6*f11 - 4*fx00 - 2*fx10 + 4*fx01 + 2*fx11 - 3*fy00 + 3*fy10 - 3*fy01 + 3*fy11 - 2*fxy00 - fxy10 - 2*fxy01 - fxy11;
		double a33 =  4*f00 - 4*f10 - 4*f01 + 4*f11 + 2*fx00 + 2*fx10 - 2*fx01 - 2*fx11 + 2*fy00 - 2*fy10 + 2*fy01 - 2*fy11 + fxy00 + fxy10 + fxy01 + fxy11;

		double dx = x - x1;
		double dx2 = dx*dx;
		double dx3 = dx2*dx;
		double dy = y - y1;
		double dy2 = dy*dy;
		double dy3 = dy2*dy;

		return	CLIP(
				a00		+ a01*dy	 + a02*dy2	   + a03*dy3 +
				a10*dx	+ a11*dx*dy	 + a12*dx*dy2  + a13*dx*dy3 +
				a20*dx2	+ a21*dx2*dy + a22*dx2*dy2 + a23*dx2*dy3 +
				a30*dx3	+ a31*dx3*dy + a32*dx3*dy2 + a33*dx3*dy3);
	}

+ Recent posts