Circle Hough Transform : https://en.wikipedia.org/wiki/Circle_Hough_Transform


컴퓨터비전이라는 과목에 대한 과제이다.


Circle Tough Transform을 이용하여 원을 그리면 된다.


반지름을 알지 못하는 원에 대한 Circle Hough Transform의 간략한 코드는 위키 사이트를 참고한다.


OpenCV에 이미 Circle Hough Transform을 알아서 해주는 기능이 있지만 이를 이용하지 않는 과제이다.







예제로 받은 코드는 다음과 같다.


다만, 조금 이상한 부분이 존재한다.


해당 과목을 진행하시는 교수님은 MATLAB을 주로 사용하는 것으로 안다.


그래서 동적 배열이 적용되는 이미지 배열 기능을 넣지 않았나 싶다.


먼저 이에 해당 하는 부분을 수정하도록 한다. 주어진 과제만 수정하고자 한다면 딱히 의미는 없다.




int *vote = new int[width*height*rad_max];



이미지에서 화면 밖으로 벗어나지 않는 원을 찾고자 한다면


반지름의 최대값은 특정 값으로 제한된다.



const int rad_max = min(width, height) / 2;



그렇다면 배열 초기화에 대한 코드는 다음과 같이 바뀐다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			for (int k = 0; k < rad_max; ++k)
			{
				vote[i*(height*rad_max) + j * (rad_max)+k] = 0;
			}
		}
	}


예제 코드에서는 캐니 알고리즘이 contours에 미리 적용되어져 있다.


이를 활용하여 Circle Hough Transform에 대한 voting을 구현한다.


Wiki에 나와있는 기본 공식을 참고하여 작성하면 다음과 같다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			if (contours.at<uchar>(j, i) == 0)
				continue;
			for (int k = rad_min; k < rad_max; ++k)
			{
				for (int t = 0; t < thetha; t++)
				{
					int a = i - k * cos(t * PI / 180);
					int b = j - k * sin(t * PI / 180);
					if (a >= 0 && a <width && b >= 0 && b<height)
						vote[a*(height*rad_max) + b * rad_max + k] += 1;
				}
			}
		}
	}


그리는 부분은 주석을 참고하여 작성하면 된다.



	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < height; j++)
		{
			for (int k = rad_min; k < rad_max; k++)
			{
				if (vote[i*(height *rad_max) + j * rad_max + k] > threshold)
				{
					Point center(i, j);
					circle(image, center, k, Scalar(255, 0, 0), 1);
				}
			}
		}
	}


여기까지 해주면 기본은 완성이다.


다만 처음할 때 불편했던 점이 있었는데, Threshold를 임의로 찾아야 한다는 점이 불편했다.


정확한 Threshold를 찾는 법에 대해서는 아직 모르겠고, 좀 더 쉽게 Threshold 범위를 유추하는 법을 찾고자 한다.



int threshold = -1;
float threshold_persent = 0.8f;
int real_threshold = 0;


먼저 변수를 몇개 추가했다.


theshold 초기값을 -1로 한 이유는 아래의 변수들을 이용하기 위함이다.


threshold_persent는 최대 vote값에서 몇 % 아래를 threshold로 잡을 것인지 정하기 위한 변수이다.


real_threshold는 %를 통해 일정 범위를 유추했따면 좀 더 세세한 변수를 찾아내기 위한 변수이다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			for (int k = rad_min; k < rad_max; ++k)
			{
				if (vote[i*(height*rad_max) + j * (rad_max)+k] > threshold)
				{
					threshold = vote[i*(height*rad_max) + j * (rad_max)+k];
				}
			}
		}
	}
	//포괄적인 수정값
	int maxvote = threshold;
	threshold *= threshold_persent;
	//세부적인 수정값
	if (real_threshold != 0)
		threshold = real_threshold;


먼저 최대 vote 값을 찾아낸 후, 여기서 일정 % 아래값을 theshold로 잡는다.


real_threshold 값을 설정했다면 해당 값을 threshold로 잡는다.


real_theshold 값을 정했다면 위의 반복문을 수행하지 않아도 되나, maxvote 값을 확인하기 위하여 유지하였다.



	printf("max vote : %d ", maxvote);
	if (real_threshold == 0)
		printf("range : %.1f%% ", (1-threshold_persent)*100);
	printf("threshold : %d\n", threshold);




처음 20%의 범위를 통해 207에서 전체 원이 다 보인다는 것을 확인했고, 수치를 조금씩 올려본 결과 212가 가장 적절한 threshold임을 알아냈다.


원에서 살짝 어긋나보이는 것은 voting을 할 때의 a, b의 계산된 값이 int로 들어오는 것으로 인한 한계라고 예상된다.


threshold를 쉽게 찾아내고 싶었지만 아직 정확한 방법을 찾지 못했기에 다음과 같이 진행하였다.





첫 과제였기에 조건(Circle Hough Transform)에 대한 내용보다는 그 외 코드 수정 및 부가 기능에 대한 내용만 많이 작성되어져 있다.




t * PI / 180 이 아니라 PI*(t/180)은 왜 안되냐는 질문이 있었다.


int / int 일 때 값은 무조건 int이다.


PI * t를 먼저 계산하면 double이 되고 double / int를 하면 double이다.




a, b에 넣을 때는 다시 int가 된다. 이 때 a, b는 자동으로 floor 값을(내림값)을 받는다.


a, b에 집어넣기 전에 계산된 값을 round 함수를 이용하여 반올림을 하면 좀 더 정확하지 않겠냐는 의견이 있었다.


맞는 것 같아서 테스트를 해봤다.


threshold 값을 모든 원이 보일 수 있는 최대값으로 설정하였으나 선이 두꺼워졌고


원 안쪽으로 살짝 들어간 선이 몇개 있어서 사용하지 않았다.


이미지가 작아서 그런지는 모르겠지만 조금 고민해볼 문제라고 생각한다.




코딩하는 것보다 블로그에 글 올리는게 더 어렵다.


< > 기호 두개 때문에 자꾸 코드가 이상하게 들어간다.


highlight.js를 사용하지 않고 그냥 이미지로 붙여넣을지 고민중이다.

+ Recent posts