반응형
Descriptor Matcher
- 객체 찾기템플릿이 있는 경우Block matching한계점 극복
- 특징점을 이용한 매칭디스크립터 매칭 클래스 구조
- 디스크립터를 이용한 매칭
- 매칭 과정디스크립터를 일일이 하나씩 모두 검사하여 가장 가까운 디스크립터를 찾는 방법
- FlannBasedMatcher
- Fast Library for Apporximate Nearest Neighbors.
- 가장 가까운 이웃의 근사값으로 매칭 수행.
- 큰 데이터 셋 과 고차원 특성에서 빠른 성능을 보임.
특징점(Keypoint 혹은 feature point)
- 영상에서 특징이 될 만한 지점
- 중요한 정보를 가지고 있다고 판단
- 필요성
- 객체 검출 및 인식
- 추적
- 영상 간 매칭
▶ 특징 검출기 및 디스크립터
- 특징 검출기
- 영상에서 관심 있는 특징점(keypoint) 검출
- 에지, 코너, 영역 등
- KeyPoint 클래스 객체의 리스트로 반환
- 영상에서 관심 있는 특징점(keypoint) 검출
- 디스크립터
- 검출된 특징점 주위의 밝기, 색상, 그라디언트 방향 등 매칭
- 정보 계산KeyPoint Detector + Descriptor
- 특징점과 디스크립터를 같이 추출.
- 디스크립터는 특징점 마다 존재함.
- 실제 유사도 판변을 위한 데이터로 활용
- BRISK
- ORB
- KAZE, AKAZE
- SIFT(xfeatures2d)
- SURF(xfeatures2d)
- KeyPoint Detector
- 특징점을 검출하는 클래스
- 코너 같은 부분을 주로 검출
- FastFeatureDetector
- MSER
- SimpleBlobDetector
- GFTTDector
- Descriptor Matcher
- 두 영상에서 추출된 디스크립터의 유사도 비교
- 유사한 디스크립터들을 매칭하는 역할
- 매칭은 KNN 알고리즘 기반
- 계산 방법은 Brute-fore와 Flann 방법 제공
- BFMatcher
- FlannBasedMatcher
KeyPoint Detector (특징점 검출기)
주로 코너점을 검출한다.
▶ FastFeatureDetector
- FAST 알고리즘 기반
- 특징점 검출 및 디스크립터 생성
▶ MSER(Maximally Stable Extremal Regions)
- 그레이스케일 영상 혹은 컬러 영상에서 주변에 비해 밝거나 어두운 영역 중 임계값을 변화시키며 변화율이 작은 영역 검출
- 주로 connected component로 연결되어 있는 BLOB(Binary Large OBjects) detection에 사용
- delta로 임계값 지정
- delta가 클수록 검출되는 영역은 감소
- 사용 함수
- cv2.MSER_create(...)
- cv2.MSER_detectRegion(...)
▶ SimpleBlobDetector
- 원으로 BLOB 검출
- 영상을 임계값 범위에서 이진영상 생성
- 연결된 윤곽선 검출 및 중심점 계산
- 중심점 사이의 최소 간격에 의해 인접한 중심점 그룹핑
- 중심점 재 계산 및 그룹의 반지름을 특징 크기로 반환
- 사용 함수
- cv2.SimpleBlobDetector_Params()
- cv2.SimpleBolbDetector_create(...)
▶ GFTTDetector
- goodFeaturesToTrack 함수를 내부적으로 사용하여 특징 검출
- Harris나 MinEigenVal을 이용하여 측정 된 코너점의 최대값과 설정된 퀄리티값을 곱하여 이 보다 작은 코너값은 모두 제거.
- 사용 함수
- cv2.GFTTDetector_create(..)
KeyPoint Detector + Descriptor
keypoint와 descriptor를 같이 추출한다.
descriptor는 keypoint에 해당하는 정보이므로 기본적으로 keypoint와 같은 개수로 생성되어지며 실제 유사도를 판별하기 위한 데이터로 활용되어 진다.
▶ ORB
- Oriented BRIEF(Binary Robust Independent Elementary Features)
- FAST + BRIEF + Harris corner detector
- SIFT, SURF를 대체하기 위해 개발되었고 속도 빠름
- 회전 불변성
- 특징점 검출
- 피라미드 FAST혹은 Harris 응답 사용하여 특징 선택
- 1차 모멘트 이용 방향 검색
- 디스크립터 계산
- BRIEF 사용
- 특징점에 대한 디스크립터로 특징점 검출 방버은 제공하지 않음
- FAST처럼 어두운 픽셀, 밝은 픽셀, 유사한 픽셀로 분류
- BRIEF 사용
- BRIEF
- 특징점 주변의 픽셀 쌍을 미리 정하고 해당 픽셀 값 크기를 비교하여 0 혹은 1로 특징을 기술
- 사용함수
- cv2.ORB_create(...)
▶ BRISK
- 특징점 검출
- FAST 혹은 AGIST 피라미드 기반 특징점 검출디스크립터 계산
- 특징점 근처에서 동심원 기반 샘플링 패턴을 기용하여 이진 디스크립터 계산
- FAST 혹은 AGIST 피라미드 기반 특징점 검출디스크립터 계산
▶ KAZE, AKAZE
- 기존의 스케일 공간 특징점 검출 방법의 한계
- 가우시안 피라미드로 인한 특징점 약화
- 비선형 확산 필터링 사용(nonlinear diffusion filtering)
- 물체 경계 유지
- 특징점 검출 정확도 높임
- 속도는 SIFT와 비슷하고 SURF 보다 느림
- AKAZE는 FED(Fast Explicit Diffusion)로 비선형 공간에서 피라미드 구축 시 속도 개선
- 디스크립터 : 64비트 정규화 벡터
▶ SIFT
- Harris corner detection의 한계
- 코너는 회전 불변의 특징점
- 스케일 변환에 대응을 못함
- 크기가 다른 두 객체 영상에서 코너 점을 이용하여 서로 같은 위치를 찾는 것은 한계가 있음
- 크기 불변 특징 변환 등장
- Scale Invariant Feature Transform
- 2004년 캐나다 브리티시 컬럼비아 대학교 Lowe교수 발표
- 가우시안 피라미드와 DoG구성
- 블러링 된 영상으로 스케일 스페이스 구성 - Octave
- 영상 크기에 따라 여러 옥타브 구성DoG(Difference of Gaussian)
- 인접한 가우시안 블러링의 차영상 이용.
- SIFT는 DoG영상을 고려한 지역 극 값 위치 특징점 사용.
- 엣지 성분이 강하거나 명암비가 낮은 지점은 특징점에서 제외.특징점 검출
- DoG에 모든 점에 대한 극점 검사
- 26개 이웃 대비 가장 크거나 작으면 keypoint
- 통과한 점은 후보 특징점이 됨극점 제거
- 낮은 대비 극점 제거
- 엣지에 놓인 극점 제거
- 특징점에 대해 가장 강향 방향 결정
- 특징점은 위치, 스케일, 방향을 고려하여 결정됨디스크립터 계산
- 그라디언트 히스토그램
- 특징점 주위 16x16 윈도우 설정 후 4x4 윈도우로 재구성
- 16개의 작은 윈도우 x 8개 bin 값 = 특징점 당 128개의 feature vector 생성
- 그라디언트 히스토그램
- DoG에 모든 점에 대한 극점 검사
▶ SURF
- Speed-up Robust Feature.
- SIFT 알고리즘보다 속도 향상, 강인한 특징 추출.
- 박스 필터와 적분 영상 사용.특징점 검출
- Fast Hessian detector 사용
- Hessian 행렬 기반 특징점 검출 알고리즘.
- 행렬식이 최대 값인 위치에서 blob 검출 시 사용.
- 박스 필터와 적분 영상을 이용하여 Dxx, Dyy, Dxy를 구함.
- SIFT는 영상을 scale하여 특징점 추출.
- SURF는 박스 필터 영역을 scale하여 특징점 추출.
In [29]:
src = cv2.imread('./image/chessBoard.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0.0)
orbF = cv2.ORB_create()
orbF1 = cv2.ORB_create(scoreType=1)
kp = orbF.detect(gray)
kp1 = orbF1.detect(gray)
dst = cv2.drawKeypoints(gray, kp, None , color=(0,0,255))
dst1 = cv2.drawKeypoints(gray, kp1, None , color=(0,0,255))
print('len(kp)=', len(kp))
print('len(kp1)=', len(kp1))
cv2.imshow('orb', dst)
cv2.imshow('orb1', dst1)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.waitKey(1)
len(kp)= 484
len(kp1)= 489
Out[29]:
-1
BRISK_create()¶
In [8]:
src = cv2.imread('./image/chessBoard.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
gray1 = cv2.GaussianBlur(gray, (5,5), 0.0)
briskF = cv2.BRISK_create()
kp = briskF.detect(gray)
kp1 = briskF.detect(gray1)
dst = cv2.drawKeypoints(gray, kp, None , color=(0,0,255))
dst1 = cv2.drawKeypoints(gray1, kp1, None , color=(0,0,255))
print('len(kp)=', len(kp))
print('len(kp1)=', len(kp1))
cv2.imshow('orb', dst)
cv2.imshow('orb1', dst1)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.waitKey(1)
len(kp)= 408
len(kp1)= 363
Out[8]:
-1
SIFT_create()¶
In [11]:
src = cv2.imread('./image/chessBoard.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
sfitF = cv2.SIFT_create()
kp = sfitF.detect(gray)
dst = cv2.drawKeypoints(gray, kp, None , color=(0,0,255))
print('len(kp)=', len(kp))
cv2.imshow('orb', dst)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.waitKey(1)
len(kp)= 166
Out[11]:
-1
8개의 디텍터 확인¶
In [30]:
src = cv2.imread('./image/chessBoard.jpg')
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
def detecting(gray):
detF1 = cv2.FastFeatureDetector_create()
detF2 = cv2.MSER_create(10)
detF3 = cv2.SimpleBlobDetector_create()
detF4 = cv2.GFTTDetector_create()
detF5 = cv2.ORB_create()
detF6 = cv2.BRISK_create()
detF7 = cv2.KAZE_create()
detF8 = cv2.SIFT_create()
dt_list = [detF1, detF2, detF3, detF4, detF5, detF6, detF7, detF8]
name_list = ["FastFeatureDetector", "MSER", "SimpleBlobDetector",
"GFTTDetector", "ORB", "BRISK", "KAZE", "SIFT"]
for idx ,dt in enumerate(dt_list) :
kp = dt.detect(gray)
dst = cv2.drawKeypoints(gray, kp, None , color=(0,0,255))
print(f'{name_list[idx]} len(kp) =', len(kp))
cv2.imshow(f'{name_list[idx]} len(kp) = {len(kp)}', dst)
cv2.waitKey()
cv2.destroyAllWindows()
cv2.waitKey(1)
detecting(gray)
FastFeatureDetector len(kp) = 166
MSER len(kp) = 208
SimpleBlobDetector len(kp) = 14
GFTTDetector len(kp) = 116
ORB len(kp) = 500
BRISK len(kp) = 408
KAZE len(kp) = 343
SIFT len(kp) = 166
매칭 실습¶
In [102]:
src1_bgr = cv2.imread('./image/book1.jpg')
src2_bgr = cv2.imread('./image/book2.jpg')
img1 = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2GRAY) # template image
img2 = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2GRAY) # input image
src1_rgb = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2RGB)
src2_rgb = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2RGB)
# 1. ORG 객체 생성
orbF = cv2.ORB_create()
# 2. 이미지별 피처 포인트 찾고 벡터 변환
kp1, des1 = orbF.detectAndCompute(img1, None)
kp2, des2 = orbF.detectAndCompute(img2, None)
# 3. 매처 객체 생성
bfm = cv2.BFMatcher_create()
fbm = cv2.FlannBasedMatcher_create()
# 5. 매칭 진행
bfm_matches = bfm.match(des1, des2)
try :
fbm_matches = fbm.match(des1, des2)
except :
fbm_matches = fbm.match(np.float32(des1), np.float32(des2))
print('bfm_matches :', len(bfm_matches))
print('fbm_matches :', len(fbm_matches))
# 6. 매칭 결과 화면에 그리기
dst = cv2.drawMatches(src1_rgb, kp1, src2_rgb, kp2, bfm_matches, None)
dst3 = cv2.drawMatches(src1_rgb, kp1, src2_rgb, kp2, fbm_matches, None)
# BRISK - bfm, flann
briskF = cv2.BRISK_create()
kp1, des1 = briskF.detectAndCompute(img1, None)
kp2, des2 = briskF.detectAndCompute(img2, None)
bfm = cv2.BFMatcher_create()
matches = bfm.match(des1, des2)
fbm = cv2.FlannBasedMatcher_create()
try :
fbm_matches = fbm.match(des1, des2)
except :
fbm_matches = fbm.match(np.float32(des1), np.float32(des2))
dst2 = cv2.drawMatches(src1_rgb, kp1, src2_rgb, kp2, bfm_matches, None)
dst4 = cv2.drawMatches(src1_rgb, kp1, src2_rgb, kp2, fbm_matches, None)
print('bfm_matches :', len(bfm_matches))
print('fbm_matches :', len(fbm_matches))
plt.style.use("grayscale")
plt.figure(figsize=(15,8)) #width, height
ax1 = plt.subplot(3,2,1)
ax2 = plt.subplot(3,2,2)
ax3 = plt.subplot(3,2,3)
ax4 = plt.subplot(3,2,4)
ax5 = plt.subplot(3,2,5)
ax6 = plt.subplot(3,2,6)
ax1.imshow(src1_rgb)
ax2.imshow(src2_rgb)
ax3.imshow(dst)
ax4.imshow(dst2)
ax5.imshow(dst3)
ax6.imshow(dst4)
ax1.axis('off')
ax2.axis('off')
ax3.axis('off')
ax4.axis('off')
ax5.axis('off')
ax6.axis('off')
ax1.set_title("src1_rgb")
ax2.set_title("src2_rgb")
ax3.set_title("orb-bfm")
ax4.set_title("brisk-bfm")
ax5.set_title("orb-flann")
ax6.set_title("brisk-flann")
bfm_matches : 479
fbm_matches : 479
bfm_matches : 479
fbm_matches : 1110
Out[102]:
Text(0.5, 1.0, 'brisk-flann')
좋은 매칭 결과 찾기¶
In [3]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
src1_bgr = cv2.imread('./image/book1.jpg')
src2_bgr = cv2.imread('./image/book2.jpg')
img1 = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2GRAY) # template image
img2 = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2GRAY) # input image
src1_rgb = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2RGB)
src2_rgb = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2RGB)
# * 좋은 매칭 결과 필터링
# 매칭 결과를 거리기준 오름차순으로 정렬
def goodDst(matches, img1, kp1, img2, kp2) :
matches = sorted(matches, key=lambda x:x.distance)
# 최소 거리 값과 최대 거리 값 확보
min_dist, max_dist = matches[0].distance, matches[-1].distance
# 최소 거리의 20% 지점을 임계점으로 설정
ratio = 0.2
good_thresh = (max_dist - min_dist) * ratio + min_dist
# 임계점 보다 작은 매칭점만 좋은 매칭점으로 분류
good_matches = [m for m in matches if m.distance < good_thresh]
print('matches:%d/%d, min:%.2f, max:%.2f, thresh:%.2f' \
%(len(good_matches),len(matches), min_dist, max_dist, good_thresh))
# 좋은 매칭점만 그리기
res = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, \
flags=cv2.DRAW_MATCHES_FLAGS_NOT_DRAW_SINGLE_POINTS)
return res
# 1. ORG 객체 생성
orbF = cv2.ORB_create(1000)
# 2. 이미지별 피처 포인트 찾고 벡터 변환
kp1, des1 = orbF.detectAndCompute(img1, None)
kp2, des2 = orbF.detectAndCompute(img2, None)
# 3. 매처 객체 생성
bfm = cv2.BFMatcher_create()
fbm = cv2.FlannBasedMatcher_create()
# 4. 매칭 진행
bfm_matches = bfm.match(des1, des2)
try :
fbm_matches = fbm.match(des1, des2)
except :
fbm_matches = fbm.match(np.float32(des1), np.float32(des2))
print('bfm_matches :', len(bfm_matches))
print('fbm_matches :', len(fbm_matches))
# 6. 매칭 결과 화면에 그리기
bfm_goodDst = goodDst(bfm_matches, src1_rgb, kp1, src2_rgb, kp2)
fbm_goodDst = goodDst(fbm_matches, src1_rgb, kp1, src2_rgb, kp2)
# BRISK - bfm, flann
briskF = cv2.BRISK_create()
kp1, des1 = briskF.detectAndCompute(img1, None)
kp2, des2 = briskF.detectAndCompute(img2, None)
bfm = cv2.BFMatcher_create()
matches = bfm.match(des1, des2)
fbm = cv2.FlannBasedMatcher_create()
try :
fbm_matches = fbm.match(des1, des2)
except :
fbm_matches = fbm.match(np.float32(des1), np.float32(des2))
bfm_goodDst1 = goodDst(bfm_matches, src1_rgb, kp1, src2_rgb, kp2)
fbm_goodDst2 = goodDst(fbm_matches, src1_rgb, kp1, src2_rgb, kp2)
print('bfm_matches :', len(bfm_matches))
print('fbm_matches :', len(fbm_matches))
plt.style.use("grayscale")
plt.figure(figsize=(15,20)) #width, height
ax1 = plt.subplot(4,1,1)
ax2 = plt.subplot(4,1,2)
ax3 = plt.subplot(4,1,3)
ax4 = plt.subplot(4,1,4)
ax1.imshow(bfm_goodDst)
ax2.imshow(bfm_goodDst)
ax3.imshow(bfm_goodDst)
ax4.imshow(fbm_goodDst)
ax1.axis('off')
ax2.axis('off')
ax3.axis('off')
ax4.axis('off')
ax1.set_title("src1_rgb")
ax2.set_title("src2_rgb")
ax3.set_title("orb-bfm")
ax4.set_title("orb-flann")
bfm_matches : 907
fbm_matches : 907
matches:3/907, min:39.51, max:461.07, thresh:123.82
matches:3/907, min:39.51, max:494.20, thresh:130.45
matches:3/907, min:39.51, max:461.07, thresh:123.82
matches:7/1110, min:108.03, max:848.74, thresh:256.17
bfm_matches : 907
fbm_matches : 1110
Out[3]:
Text(0.5, 1.0, 'orb-flann')
좋은 매칭 결과 찾기2¶
In [ ]:
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 1. 템플릿 이미지, 매칭 이미지 불러오기
src1_bgr = cv2.imread('./image/apple_logo.jpeg')
src2_bgr = cv2.imread('./image/test_macbook.jpg')
img1 = cv2.cvtColor(src1_bgr, cv2.COLOR_BGR2GRAY) # template image
img2 = cv2.cvtColor(src2_bgr, cv2.COLOR_BGR2GRAY) # input image
# 2. descriptor
orb = cv2.ORB_create(nfeatures=50)
brisk = cv2.BRISK_create()
detector = [orb, brisk]
for i in [0, 1]:
didx = i
print('orb') if didx == 0 else print('brisk')
kp1, des1 = detector[didx].detectAndCompute(img1, None)
kp2, des2 = detector[didx].detectAndCompute(img2, None)
# 2. matcher
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
flann = cv2.FlannBasedMatcher_create()
matcher = [bf, flann]
for j in [0, 1]:
midx = j
print('BruteForce') if midx == 0 else print('Flann')
try:
matches = matcher[midx].match(des1, des2)
except:
matches = matcher[midx].match(np.float32(des1), np.float32(des2))
# result display
matches = sorted(matches, key = lambda x:x.distance)
print('len(matches)=', len(matches))
for k, l in enumerate(matches[:10]):
print('matches[{}]=(m:{}, queryIdx:{}, trainIdx:{}, distance:{})'.format(k, l,
matches[k].queryIdx,
matches[k].trainIdx,
matches[k].distance))
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches, None, flags=2)
plt.figure(figsize=(30, 60))
plt.imshow(img3)
plt.show()
minDist = matches[0].distance
good_matches = list(filter(lambda m:m.distance <= 2 * minDist, matches))
print('len(good_matches)=', len(good_matches))
if len(good_matches) < 5:
print('sorry, too small good matches')
exit()
img4 = cv2.drawMatches(img1, kp1, img2, kp2, good_matches, None, flags=2)
plt.figure(figsize=(30, 60))
plt.imshow(img4)
plt.show()
반응형
'Python' 카테고리의 다른 글
[Python] K-means Algorithm (0) | 2022.11.17 |
---|---|
[Python] PCA, LDA (0) | 2022.11.17 |
[Python] openCV : Moments (0) | 2022.11.17 |
[Python] openCV : Hough Transformation (0) | 2022.11.17 |
[Python] openCV : Corner Detect (0) | 2022.11.17 |