MFC

[MFC/VISION] 최소자승법으로 선 그리기(feat.C++)

김먼저 2022. 9. 27. 11:29

visual Studio 2015 - MFC/C++  MIL 라이브러리를 사용해서 작성한 코드 일부분을 가져왔습니다.

 

 

카메라로 찍은 영상을 버퍼에 올리고(흑백),

 

 

해당 픽셀의 좌우 2개, 즉, 5 픽셀 값의 평균값을 계산해서 다시 버퍼를 셋팅합니다.

 

 

(평균값을 사용하는 이유는 중간에 픽셀값이 튀거나 어두운 픽셀이 있다면 정확한 라인을 찾기 힘들기 때문입니다.)

 

 

해당 픽셀 좌우를 비교하여 일정값 이상이면 그 좌표를 저장하는 방식으로 모든 좌표를 저장합니다.

 

 

최소자승법에 사용할 좌표를 얻었으니 이제 선을 찾아봅시다!

 

 

 

최소자승법 식 구현

 

 

 

위 식을 그대로 구현하기만 하면 됩니다!     

 

 

(....?)

 

일단 변수 선언 부터

 

//최소자승법 - 직선 시작 - 모두 double

double sigma_Lx, sigma_Ly, sigma_Rx, sigma_Ry;
sigma_Lx = sigma_Ly = sigma_Rx = sigma_Ry = 0.0;
double Rx_bar, Ry_bar, Lx_bar, Ly_bar;
Rx_bar = Ry_bar = Lx_bar = Ly_bar = 0.0;
 
//왼쪽 기울기
double La_slope_up, La_slope_down;
 
//오른쪽 기울기
double Ra_slope_up, Ra_slope_down;

 

정확하게 측정하기 위해 버퍼 기준으로 왼쪽에서 오른쪽 탐색, 오른쪽에서 왼쪽으로 탐색했습니다.

 

 

slope는 기울기이며 up은 분자, down은 분모를 뜻합니다.

 

 

 

1. 각 X, Y, XY의 합과 그 합의 평균을 구한다.

 

 

//점들에 대한 x, y값들의 합
for (int y = sy; y < ey; y++)
{
	if (fi_Mode == L)
	{
		sigma_Lx += nLP[y];	// ∑L-X
		sigma_Ly += y;		// ∑L-Y
	}
	else if (fi_Mode == R)
	{
		sigma_Rx += nRP[y];	// ∑R-X
		sigma_Ry += y;		// ∑R-Y
	}
}
    
int nCnt = ey - sy;		// y의 갯수

Lx_bar = sigma_Lx / nCnt;		// Lx값들의 평균
Ly_bar = sigma_Ly / nCnt;		// Ly값들의 평균
Rx_bar = sigma_Rx / nCnt;		// Rx값들의 평균
Ry_bar = sigma_Ry / nCnt;		// Ry값들의 평균

 

ㅇㅋ

 

 

2. 직선의 기울기, y절편 구하기

 

// 2 - 1. 왼쪽 직선
CString sLine_Left, sLine_Right;

if (fi_Mode == L)
{
	for (int y = sy; y < ey; y++)
	{
//기존 최소자승법에서 x,y를 바꾸고 계산 - 이유는 기울기가 높아질수록 오차가 매애애애애애우 크기때문.
//기존 x,y
//La_slope_up += (y - Ly_bar) * (nLP[y] - Lx_bar);
//La_slope_down += (nLP[y] - Lx_bar) * (nLP[y] - Lx_bar);
//x,y 스왑
		La_slope_up += (nLP[y] - Lx_bar) * (y - Ly_bar);		//
		La_slope_down += (y - Ly_bar) * (y - Ly_bar);
	}

	La_slope = La_slope_up / La_slope_down;
	La_slope = 1 / La_slope;
	Ly_axis_intercept = Ly_bar - (La_slope * Lx_bar);

	sLine_Left.Format(L"Left Edge Line : %.2lfx - y + (%.21f) = 0", La_slope, Ly_axis_intercept);
	/*double zz = La_slope * Lx_bar - Ly_bar + Ly_axis_intercept;// 검증용*/
}
 
// 2 - 1. 오른쪽 직선
else if (fi_Mode == R)
{
	for (int y = sy; y < ey; y++)
	{
		//기존 x,y
		//Ra_slope_up += (y - Ry_bar) * (nRP[y] - Rx_bar);
		//Ra_slope_down += (nRP[y] - Rx_bar) * (nRP[y] - Rx_bar);

		Ra_slope_up += (nRP[y] - Rx_bar) * (y - Ry_bar);
		Ra_slope_down += (y - Ry_bar) * (y - Ry_bar);
	}
	Ra_slope = Ra_slope_up / Ra_slope_down;
	Ra_slope = 1 / Ra_slope;
	Ry_axis_intercept = Ry_bar - (Ra_slope * Rx_bar);
	sLine_Right.Format(L"Right Edge Line : %.2lfx - y + (%.21f) = 0", Ra_slope, Ry_axis_intercept);
	/*double zzz = Ra_slope * Rx_bar - Ry_bar + Ry_axis_intercept; // 검증용*/
}

 

이렇게 직선의 방정식을 뽑아냈습니다.

 

 

사실 최소자승법보다 더 정확하게 라인을 검출할 수 있는 방법이 있습니다만

 

 

기회가 된다면 또 포스팅하겠습니다.(응 비전 안해)