2024-01-06|閱讀時間 ‧ 約 0 分鐘

[OpenCV應用][Python]利用SIFT得取圖片物件旋轉角度,並旋轉回正常角度

利用OPENCV,實現SIFT應用,尋找圖片中物件的旋轉角度

前言

SIFT的介紹,維基百科寫的就會比我清楚很多,就不在贅述
維基百科連結

本文介紹主要提出SIFT提取關鍵角點的座標,由此算出物件的旋轉角度

程式功能介紹 : 導入待檢測圖及樣本圖,則會依照樣本圖中的物件為基準,算出待檢圖實際旋轉的角度

實驗方法:找兩張一模一樣的圖,待檢測圖先旋轉90度,樣本圖為此原狀,利用此程式算出旋轉角度並轉正存圖

實驗結果:算出旋轉角度:90.01496444740344 度

結果圖

org_img : 待檢測圖,故意旋轉90度

temp_img : 樣本圖,使待檢測圖有個檢測的基準result_img : 算出旋轉角度後,在將待檢測圖旋轉存入

程式碼及圖片連結


程式碼範例解說

import cv2
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.pyplot as plt

#這是一個 FeatureMatching 類別的初始化方法,用來進行特徵匹配的準備工作。
class FeatureMatching:
def __init__(self, query_image='data/query.jpg'):
self.sift = cv2.SIFT_create()
self.img_query = cv2.imread(query_image, 0)
#讀取temp
if self.img_query is None:
print("Could not find train image " + query_image)
raise SystemExit
self.shape_query = self.img_query.shape[:2] # 對應的是y x
# detectAndCompute 返回關鍵點,跟描述符,供後續特徵匹配做使用
self.key_query, self.desc_query = self.sift.detectAndCompute(self.img_query, None)
FLANN_INDEX_KDTREE = 0
#FLANN 特徵匹配器
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5) #使用K-D Tree算法進行最近鄰搜索,trees=5 表示構建K-D Tree時使用的樹的數量。
search_params = dict(checks=50) #checks=50 表示進行搜索時,每次搜索最近鄰時檢查的節點數量。
self.flann = cv2.FlannBasedMatcher(index_params, search_params)

#抽取特徵
def _extract_features(self, frame):
sift = cv2.SIFT_create()
key_train, desc_train = sift.detectAndCompute(frame, None)
return key_train, desc_train
#匹配特徵
def _match_features(self, desc_frame):
matches = self.flann.knnMatch(self.desc_query, desc_frame, k=2)
good_matches = []
for m, n in matches:
if m.distance < 0.7 * n.distance:
good_matches.append(m)
return good_matches
def _detect_corner_points(self, key_frame, good_matches):
src_points = np.float32([self.key_query[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) #是 query 圖像中好的匹配點對應的特徵點坐標
dst_points = np.float32([key_frame[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) #是 frame 圖像中好的匹配點對應的特徵點坐標
H, mask = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0) #計算兩組對應點之間的單應變換,這裡用於計算 query 到 frame 的變換
matchesMask = mask.ravel().tolist()
h, w = self.img_query.shape[:2]
src_corners = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2) #是 query 圖像的四個角點的坐標,這裡是左上、左下、右下和右上。這些點用於形成一個矩形,後面會被變換到 frame 圖像上
dst_corners = cv2.perspectiveTransform(src_corners, H) #得到在 frame 圖像上的角點坐標
return dst_corners, H, matchesMask

def _frontal_keypoints(self, frame, H):
Hinv = np.linalg.inv(H) #計算單應性矩陣 H 的逆矩陣,得到透視變換的反變換矩陣。
# 從單應性矩陣 H 中提取旋轉部分
print(f'Hinv"{Hinv}')
rotation_matrix = Hinv[:2, :2] # 從逆變換矩陣中提取旋轉部分,這是一個 2x2 的矩陣。
# 計算旋轉角度(以度為單位)
rotation_angle = np.arctan2(rotation_matrix[1, 0], rotation_matrix[0, 0]) * (180 / np.pi) #使用反正切函數 arctan2 計算旋轉角度
print(f"旋轉角度:{rotation_angle} 度")
dst_size = frame.shape[:2]
img_front = cv2.warpPerspective(frame, Hinv, dst_size, flags=cv2.INTER_LINEAR) #應用反變換,對輸入圖像進行逆透視變換,獲得正面視角的圖像。
return img_front , rotation_angle

def match(self, frame):
img_train = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
shape_train = img_train.shape[:2]
key_train, desc_train = self._extract_features(img_train)
good_matches = self._match_features(desc_train)
if len(good_matches) < 4:
self.num_frames_no_success += 1
return False, frame
# drawMatchesKnn 函數在兩張圖像之間繪製特徵匹配的結果, self.img_query:即模板圖像 img_train即待匹配的圖像
img_match = cv2.drawMatchesKnn(self.img_query, self.key_query, img_train, key_train, [good_matches], None,
flags=2)
plt.imshow(img_match) #繪製匹配的結果
plt.show()
#算出變換後的角點座標,單映矩陣,matchesMask被用來只繪製模型的內點
dst_corners, Hinv, _ = self._detect_corner_points(key_train, good_matches)
dst_ravel = dst_corners.ravel()
#檢查角點座標是否超出範圍
dst_ravel_Spec = 1500 #可允許範圍
if (dst_ravel > shape_train[0] + dst_ravel_Spec).any() and (dst_ravel > -dst_ravel_Spec).any() \
and (dst_ravel > shape_train[1] + dst_ravel_Spec).any():
self.num_frames_no_success += 1
print('角點離圖片太遠')
return False, frame
#轉換成正面視角
img_front, img_angle = self._frontal_keypoints(frame, Hinv)
return True, img_front , img_angle

def rotate_image(image, angle):
# 取得影像中心點坐標
center = tuple(np.array(image.shape[1::-1]) / 2)
# 設定旋轉矩陣
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
# 進行影像旋轉
rotated_image = cv2.warpAffine(image, rotation_matrix, image.shape[1::-1], flags=cv2.INTER_LINEAR)
# 將旋轉後的影像存儲
output_path = "F:/python/opencv/Result.jpg"
cv2.imwrite(output_path, rotated_image)
return rotated_image

#載入原圖
img_train = cv2.imread('rotation.jpg')
#載入樣本圖
temp_img = cv2.imread('temp_org.jpg')
matching = FeatureMatching(query_image='temp_org.jpg')

flag , img_front, img_angle = matching.match(img_train)
#算出的角度需要 換方向 加負號
Result_img = rotate_image(img_train , -img_angle)

# 將結果轉換回彩色圖
img_train = cv2.cvtColor(img_train, cv2.COLOR_BGR2RGB)
temp_img = cv2.cvtColor(temp_img, cv2.COLOR_BGR2RGB)
Result_img = cv2.cvtColor(Result_img, cv2.COLOR_BGR2RGB)
# 顯示原始圖片和結果
plt.subplot(1, 3, 1), plt.imshow(img_train), plt.title('org_img'), plt.axis('off')
plt.subplot(1, 3, 2), plt.imshow(temp_img), plt.title('temp_img'), plt.axis('off')
plt.subplot(1, 3, 3), plt.imshow(Result_img), plt.title('result_img'), plt.axis('off')
plt.show()

函式功能解說

FeatureMatching :特徵匹配的類別,初始化方法__init__用來進行特徵匹配的準備工作,包括使用SIFT算法檢測並計算特徵點,使用FLANN特徵匹配器進行特徵匹配等。

_extract_features 方法:抽取圖片的特徵,這裡使用SIFT算法。

_match_features 方法:進行特徵匹配,這裡使用FLANN特徵匹配器。

_detect_corner_points 方法:檢測匹配到的角點,計算透視變換矩陣。

_frontal_keypoints 方法:根據透視變換矩陣,將圖片轉換成正面視角。

match 方法:實際進行特徵匹配,並根據透視變換將圖片轉換成正面視角。

在主程式中,載入原圖、樣本圖,並使用 FeatureMatching 進行特徵匹配。接著,使用 _frontal_keypoints 將匹配的圖片轉換成正面視角,然後使用 rotate_image 方法進行影像旋轉,最後顯示原始圖片、樣本圖片和結果圖片。

rotate_image 方法:這是一個影像旋轉的函式,使用 OpenCV 的 cv2.getRotationMatrix2D cv2.warpAffine 函數進行旋轉。

FeatureMatching 類別初始化 (__init__ 方法)

def __init__(self, query_image='data/query.jpg'):
# 初始化 SIFT 物件
self.sift = cv2.SIFT_create()
# 讀取模板圖片(query_image)
self.img_query = cv2.imread(query_image, 0)
if self.img_query is None:
print("Could not find train image " + query_image)
raise SystemExit
# 取得模板圖片的形狀(高度和寬度)
self.shape_query = self.img_query.shape[:2]
# 用 SIFT 算法提取模板圖片的關鍵點和描述符
self.key_query, self.desc_query = self.sift.detectAndCompute(self.img_query, None)
# 使用 FLANN 特徵匹配器
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
self.flann = cv2.FlannBasedMatcher(index_params, search_params)

在 __init__ 方法中,這是 FeatureMatching 類別的初始化部分。以下是詳細說明:

  1. self.sift = cv2.SIFT_create(): 創建了一個 SIFT 物件,用於後續的特徵提取。
  2. self.img_query = cv2.imread(query_image, 0): 讀取指定路徑的模板圖片(query_image),並以灰度模式(0 表示灰度)讀入。
  3. if self.img_query is None: ...: 檢查是否成功讀取模板圖片,如果未成功,則輸出錯誤信息並退出程式。
  4. self.shape_query = self.img_query.shape[:2]: 取得模板圖片的形狀,即高度和寬度,存儲在 self.shape_query 中。
  5. self.key_query, self.desc_query = self.sift.detectAndCompute(self.img_query, None): 使用 SIFT 算法檢測並計算模板圖片的關鍵點和描述符。

self.key_query:存儲關鍵點

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.