OpenCV 특징점 검출과 기술

OpenCV 특징점 클래스

  • Feature2D 클래스와 파생 클래스
    • Feature2D 클래스가 전체적인 특징점 클래스의 부모 클래스 역할을 수행함
      • detect() : 특징점 검출
      • compute() : 기술자 계산
      • detectAndCOmpute() : 특징점 검출과 기술자 계산을 한번에 진행
    • Feature2D 클래스를 사용하는 것이 아닌, Feature2D 클래스를 상속받은 다양한 클래스들에서 위 3가지 함수를 호출해서 사용

image-20230503030958982

  • 특징점 표현을 위한 KeyPoint 클래스

image

  • 특징점 클래스 객체 생성 함수

image

  • Feature2D 상속 클래스들은 모두 create() 라는 이름의 정적 멤버 함수를 제공
  • Ptr은 OpenCV에서 구현한 스마트 포인터 클래스
  • 각각의 create() 함수는 다수의 인자를 가지지만, 디폴트 인자가 정의되어 있음
    • 예를 들어 SIFT 같은 경우

image

영상에서 특징점 검출 함수
virtual void Feature2D::detect(InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask = noArray());
  • image : 입력 영상
  • keypoints : (출력) 검출된 특징점 정보 (vector자료형)
  • mask : 마스크 영상
검출된 특징점 그리기 함수
void drawKeyPoints(InputArray image, const std::vector<KeyPoint>& keypoints, InputOutputArray outImage, const Scalar& color = Scalar::all(-1), int flags = DrawMatchesFlags::DEFAULT);
  • image : 입력영상
  • keypoints : 입력 영상에서 검출된 특징점

  • outImage : 출력 영상

  • color : 특징점 색상
  • flags : 특징점 그리기 방법
    • DrawMatchesFlags::DEFAULT : 특징점 위치만을 표현한 작은 크기의 원
    • DrawMatchesFlags::DRAW_RICH_KEYPOINTS : 특징점의 크기와 방향을 반영한 원
특징점 검출 예제 코드
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/ocl.hpp"

using namespace std;
using namespace cv;

int main()
{
	ocl::setUseOpenCL(false); // for ORB time check

	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	TickMeter tm;
	tm.start();

	Ptr<Feature2D> detector = SIFT::create(); // SIFT, KAZE, AKAZE, ORB
	// detector 변수는 SIFT 알고리즘의 객체라고 보면됨

	vector<KeyPoint> keypoints; // KeyPoint 벡터 keypoints
	detector->detect(src, keypoints); // 입력영상 src의 모든 SIFT 특징점을 keypoints에 저장
	// detector 객체를 이용해서 특징점을 검출하기 위해서는 화살표 연산자를 사용하고 detect 함수를 입력하면 됨

	tm.stop();
	cout << "Elapsed time: " << tm.getTimeMilli() << "ms." << endl;
	cout << "keypoints.size(): " << keypoints.size() << endl; // size()를 이용해 몇 개나 검출했는지 확인할 수 있음

	Mat dst;
	
	drawKeypoints(src, keypoints, dst, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	// src 영상의 알록달록한 색으로 keypoints의 크기와 방향을 반영한 원을 표시한 것을 dst 영상에 저장함
	
	/* for(int i = 0; i < keypoints.size(); i++) {
		keypoints[i].pt // 점들의 좌표를 사용해 동그라미를 그려서 어느 위치에 특징점이 검출되었는지 확인가능
	} */

	imshow("dst", dst);
	waitKey();
}

image

특징점 기술자 구하기

  • 기술자 : 각각의 특징점 근방의 부분 영상을 표현하는 실수 또는 이진 벡터
  • OpenCV에서는 Mat 객체로 표현
    • 행 개수 : 특징점 개수
    • 열 개수 : 특징점 기술자 알고리즘에 의해 정의됨

image

실수 기술자
  • 주로 특징점 부근 부분 영상의 방향 히스토그램을 사용
  • 보통 float 자료형을 사용하여 실수 정보를 저장하는 방식
  • SIFT, SURF, KAZE 등의 알고리즘이 실수 기술자를 사용
  • 보통 L2 norm을 사용하여 유사도를 판단함

image

이진 기술자(binary descriptor)
  • 이진 테스트(binary test)를 이용하여 부분 영상의 특징을 기술

  • 보통 uchar(8bit) 자료형을 사용하여 비트 단위로 영상 특징 정보를 저장

  • AKAZE, ORB, BRIEF 등의 알고리즘이 이진 기술자를 사용

  • 이진 기술자는 해밍 거리(Hamming Distance)를 사용하여 유사도를 판단함

    e.g. d1 = 1011101, d2 = 1001001 인 경우 해밍 거리는 2

image

특징점에서 기술자(특징 벡터) 계산 함수
virtual void Feature2D::compute(InputArray images, std::vector<KeyPoint>& keypoints, OutputArrays descriptors);
  • image : (입력) 입력 영상
  • keypoints : (입력) 검출된 특징점 정보(vector 자료형)
  • descriptors : (출력) 특징점 기술자 행렬(Mat 자료형)
특징점 검출 및 기술자(특징 벡터) 계산 함수
virtual void Feature2D::detectAndCompute(InputArray images, InputArray mask, std::vector<KeyPoint>& keypoints, OutputArrays descriptors);
  • image : (입력) 입력 영상
  • mask : (입력) 마스크 영상
  • keypoints : (출력) 검출된 특징점 정보(vector 자료형)
  • descriptors : (출력) 특징점 기술자 행렬(Mat 자료형)
  • useProvidedKeypoints : 인자로 전달된 keypoints 정보 사용 여부
특징점 기술자 계산 예제 코드
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core/ocl.hpp"

using namespace std;
using namespace cv;

int main()
{
	ocl::setUseOpenCL(false); // for ORB time check

	Mat src = imread("lenna.bmp", IMREAD_GRAYSCALE);

	if (src.empty()) {
		cerr << "Image load failed!" << endl;
		return -1;
	}

	TickMeter tm;
	tm.start();

	Ptr<Feature2D> feature = SIFT::create(); // SIFT, KAZE, AKAZE, ORB

	vector<KeyPoint> keypoints;
	Mat desc; 
	feature->detectAndCompute(src, Mat(), keypoints, desc);

	tm.stop();
	cout << "Elapsed time: " << tm.getTimeMilli() << "ms." << endl;
	cout << "keypoints.size(): " << keypoints.size() << endl;
	cout << "desc.size(): " << desc.size() << endl;

	Mat dst;
	drawKeypoints(src, keypoints, dst, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);

	imshow("dst", dst);
	waitKey();
}
  • SIFT 결과
    • 128개의 열로 구성됨

image

  • KAZE 결과
    • 64개의 열로 구성됨

image

  • ORB 결과
    • 32개의 열로 구성됨

image

주요 특징점 알고리즘과 기술자 특성

image

  • 속도가 중요하면 ORB
  • 속도와 성능이 중요하면 AKAZE
  • 성능이 중요하면 SIFT

image