Corner Detection

Mechboy·2025년 7월 13일

image processing

목록 보기
8/8

Corner Detection

Corner Detection 정의

1. Corner에 대한 정의

  • 코너(Corner)에 대한 정의는 두개의 선분이 접촉하는 포인트를 의미함. 단순한 선분에 비해서 이미지의 특성을많이 저장하고 있기 때문에 이미지 판별에 필요한 랜드마크로 활용이 가능함
  • 코너에 대한 특징이 있다면 x,y 두가지 방향으로 전부 보기때문에 2차원 정보로 식별되기 때문에 방향성 정보가 많은것이 장점
  • 코너는 각 이미지에서 고유한 특성을 가지고 있기 때문에 활용성이 높음, 즉 Affine transform에 Robust 한것이 강점이라고 할 수 있음

2. Shi-Tomasi corner dectection

2.1 Shi-Tomasi corner detection

  • Shi-Tomasi는 harris 개선 버전으로, structure tensor의 고유값을 직접 계산하여 코너를 검출함
  • structure 행렬에서 계산되는 고유값 λ1,λ2\lambda_1, \lambda_2를 이용해서 이미지 포인트의 특성을 파악함
    1. λ1,λ2<Threshold\lambda_1, \lambda_2 < Threshold : No feature;
    2. λ1orλ2>Threshold\lambda_1\: or\: \lambda_2 > Threshold : 하나만 임계치를 넘길 경우에는 엣지로 판별
    3. λ1andλ2>Threshold\lambda_1\: and\: \lambda_2 > Threshold : Corner로 판별
    최종적으로 3번의 조건을 정리하면
    min(λ1,λ2)>Thresholdmin(\lambda_1\: ,\: \lambda_2) > Threshold
    을 통해서 코너를 검출 한다고 보면 됨

3. 구현 방법

3.1 RGB to HSV로 전환

	cv::Mat image = cv::imread(filename, cv::IMREAD_COLOR);
	cv::cvtColor(image, Gray, cv::COLOR_BGR2GRAY);
	cv::resize(image, image_small, cv::Size(0, 0), 0.5, 0.5, cv::INTER_AREA);
	cv::resize(Gray, Gray_small, cv::Size(0, 0), 0.5, 0.5, cv::INTER_AREA);

  • 기본적으로 코너 디텍팅은 grayimage로 구현해야 되기 Color image를 흑백으로 바꿔줌

3.2 각 픽셀별 Gray scale 연산

	Sobel(gray, Ix, CV_32F, 1, 0, block_size);
	Sobel(gray, Iy, CV_32F, 0, 1, block_size);
  • Sobel 커널로 각 지점의 커널값을 구현

3.3 구조 텐서연산을 위한 각 원소 계산

	Ixy = Ix.mul(Iy);
	Ix_square = Ix.mul(Ix);
	Iy_square = Iy.mul(Iy);

3.4 구조 텐서연산을 위한 각 원소 계산

	Ixy = Ix.mul(Iy);
	Ix_square = Ix.mul(Ix);
	Iy_square = Iy.mul(Iy);

3.4 노이즈 보정

	cv::Mat kernel = cv::Mat::ones(3, 3, CV_32F) / 9.0f;
	cv::filter2D(Ix_square, Ix_square, -1, kernel);
	cv::filter2D(Iy_square, Iy_square, -1, kernel);
	cv::filter2D(Ixy, Ixy, -1, kernel);
  • 단일 픽셀에서는 노이즈가 많기 때문에 주변픽셀과 덧셈연산으로 노이즈를 보정해준다.

3.5 개별 포인트에 대한 고유값 연산

	for (int col = 0; col < sz.width; col++) {
		for (int row = 0; row < sz.height; row++) {

			cv::Mat A = (cv::Mat_<float>(2, 2) <<
				Ix_square.at<float>(row, col), Ixy.at<float>(row, col),
				Ixy.at<float>(row, col), Iy_square.at<float>(row, col));

			bool ok = cv::eigen(A, eigenValues, eigenVectors);
			if(ok){
				conner = { eigenValues.at<float>(1, 0),
						  (float)row,
						  (float)col };
				if (conner[0] > threshold) {
					conner_vector.push_back(conner);
				}
			}

			conner = { 0, 0, 0 };
		}
	}
A=[Ix2IxIyIxIyIy2]A = \begin{bmatrix} \langle I_x^2 \rangle & \langle I_x I_y \rangle \\ \langle I_x I_y \rangle & \langle I_y^2 \rangle \end{bmatrix}
  • 상단의 구조행렬에서 고유값을 계산하여 Threshold와 비교하여 코너 저장

3.5 NMS 연산 후 값 저장

	//코너값을 Threshold 기준으로 정렬
	std::sort(
		conner_vector.begin(),
		conner_vector.end(),
		// 비교 함수: a[0]이 클수록 앞(내림차순)
		[](const std::array<float, 3>& a, const std::array<float, 3>& b) {
			return a[0] > b[0];
		}
	);
	std::cout << "Finish sort" << std::endl;

	std::vector<bool> corner_flag(conner_vector.size(), true);

	//정렬된 값으로 NMS 진행해서 반환
	for (int i = 0; i < conner_vector.size(); i++) {
		for (int j = i + 1; j < conner_vector.size(); j++) {
			if(corner_flag[i] == true ) {
				float vec1_x = conner_vector[i][1], vec1_y = conner_vector[i][2];
				float vec2_x = conner_vector[j][1], vec2_y = conner_vector[j][2];

				bool test1 = (vec1_x + min_dist > vec2_x) && (vec1_x - min_dist < vec2_x);
				bool test2 = (vec1_y + min_dist > vec2_y) && (vec1_y - min_dist < vec2_y);
				if (test1 && test2) {
					corner_flag[j] = false;

				}
				else {
					continue;
				}
			}
			else {
				continue;
			}

		}
	}
	std::cout << "Finish nms" << std::endl;
	
	//필요개수 만큼 반환
	for (int i = 0; i < conner_vector.size(); i++) {
		cv::Point2f conner;    // (0, 0)으로 초기화됨
		if (corner_flag[i] == true) {
			conner.x = conner_vector[i][1];
			conner.y = conner_vector[i][2];
			ShiTomasi_corner.push_back(conner);
			if (i < maxcorner) {
				continue;
			}
			else break;
		}
	}

4. 출처

profile
imageprocessing and Data science

0개의 댓글