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를 이용해서 이미지 포인트의 특성을 파악함
- λ1,λ2<Threshold : No feature;
- λ1orλ2>Threshold : 하나만 임계치를 넘길 경우에는 엣지로 판별
- λ1andλ2>Threshold : Corner로 판별
최종적으로 3번의 조건을 정리하면 min(λ1,λ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);
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=[⟨Ix2⟩⟨IxIy⟩⟨IxIy⟩⟨Iy2⟩]
- 상단의 구조행렬에서 고유값을 계산하여 Threshold와 비교하여 코너 저장
3.5 NMS 연산 후 값 저장
std::sort(
conner_vector.begin(),
conner_vector.end(),
[](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);
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;
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. 출처