iT邦幫忙

2024 iThome 鐵人賽

DAY 12
1

為了計算兩張圖的相機的姿態,我們需要知道兩張圖內共同的資訊,這個資訊就是特徵點 (feature point)。特徵點指的是圖片中的一些有特別代表性的像素,可以在多張圖片中找到對應的位置。然而有代表性是一個很抽象的概念,因此我們需要一些實際的方法來找到這些特徵點。

在設計特徵點的演算法時,我們會希望這些特徵點有以下特性:

  1. 有代表性:這些特徵點應該是圖片中的一些特殊的地方,例如某些物體角落、邊緣等等。
  2. 穩定:這些特徵點應該在不同的光線、角度下都能被找到。

那麼我們如何找到這些角落、邊緣呢?常見的方式是計算圖片的梯度 (image gradient),也就是計算圖片中每個像素的亮度變化。這樣我們就可以找到一些亮度變化較大的地方,這些地方通常就是我們所謂的特徵點。

以下是一個計算圖片梯度的函數:

def compute_gradients(image):
    image = image.astype(np.float32) / 255.0
    image_dx = image[:, 1:] - image[:, :-1]
    image_dy = image[1:, :] - image[:-1, :]
    image_dx = np.pad(image_dx, ((0, 0), (0, 1)), mode="constant")
    image_dy = np.pad(image_dy, ((0, 1), (0, 0)), mode="constant")
    grad = np.sqrt(image_dx ** 2 + image_dy ** 2)
    return grad

會得到以下結果

gradient

實作上會利用這些梯度來找到特徵點,例如在某個像素點上,如果這個像素點的梯度是局部最大或最小,那麼這個像素點就有可能是特徵點。

在電腦視覺中,有很多種特徵點的演算法,例如 SIFT、SURF、ORB 等等。這些演算法的原理各有不同,但基本上都是透過一些特殊的濾波器來找到圖片中的特徵點,SIFT 由於其穩定性被廣泛使用,而 ORB 則是運行更快速。

同時,我們希望這些特徵點能夠被描述,這樣我們才能在不同的圖片中找到相同的特徵點。這個描述子 (descriptor) 通常是一個向量,可以用來比較兩個特徵點的相似度。

以下是範例程式碼,使用 OpenCV 的 SIFT 特徵點演算法:

from pathlib import Path
import numpy as np
from PIL import Image
from scipy.spatial.transform import Rotation
import cv2
import matplotlib.pyplot as plt

from dataset import TUMRGBD     # Our dataset class


def detcet_features(image, type="sift", nfeatures=1000):
    if type == "sift":
        sift = cv2.SIFT_create(nfeatures=nfeatures)
        keypoints, descriptors = sift.detectAndCompute(image, None)
    elif type == "orb":
        orb = cv2.ORB_create(nfeatures=nfeatures)
        keypoints, descriptors = orb.detectAndCompute(image, None)
    else:
        raise ValueError(f"Unknown feature type: {type}")
    return keypoints, descriptors


def main():
    dataset = TUMRGBD("data/rgbd_dataset_freiburg2_desk")

    frames = []    
    # Get the first two valid frames
    for i in range(0, len(dataset), 100):
        x = dataset[i]
        if x is None:
            continue
        frames.append(x)
        if len(frames) == 2:
            break
        
    rgb1 = cv2.imread(frames[0]["rgb_path"])
    rgb1 = cv2.cvtColor(rgb1, cv2.COLOR_BGR2RGB)
    gray1 = cv2.cvtColor(rgb1, cv2.COLOR_BGR2GRAY)
    
    rgb2 = cv2.imread(frames[1]["rgb_path"])
    rgb2 = cv2.cvtColor(rgb2, cv2.COLOR_BGR2RGB)
    gray2 = cv2.cvtColor(rgb2, cv2.COLOR_BGR2GRAY)
    
    keypoints1, descriptors1 = detcet_features(gray1)
    keypoints2, descriptors2 = detcet_features(gray2)
    
    print(f"Detected {len(keypoints1)} keypoints in frame 1")
    print(f"Detected {len(keypoints2)} keypoints in frame 2")
    
    # Draw the keypoints using opencv
    img1 = cv2.drawKeypoints(rgb1, keypoints1, None)
    img2 = cv2.drawKeypoints(rgb2, keypoints2, None)
    
    fig, ax = plt.subplots(1, 2, figsize=(10, 5), tight_layout=True)
    ax[0].axis("off")
    ax[0].imshow(img1)
    ax[1].axis("off")
    ax[1].imshow(img2)
    plt.show()


if __name__ == "__main__":
    main()

會得到以下結果:

keypoints

在這個程式中,我們使用了 OpenCV 的 SIFT 特徵點演算法,並且找到了兩張圖片中的特徵點。在這個例子中,我們找到了 1000 個特徵點,並且用 OpenCV 的 drawKeypoints 函數來畫出這些特徵點。

keypoints1內存的是多個 cv2.KeyPoint 的物件,也就是每個特徵點,我們可以用 keypoints1[0].pt 來取得第一個特徵點的座標。

descriptors1descriptors2 是特徵點的描述子,是一個 (1000, 128) 的陣列,其中 1000 是特徵點的數量,128 是描述子的維度。SIFT 的描述子是 128 維的,ORB 的描述子是 32 維的。我們可以用這些描述子來比較兩個特徵點的相似度,這樣我們就可以找到兩張圖片中相同的特徵點,進而計算相機的姿態。


上一篇
Day10: 數據集讀取
下一篇
Day12: 特徵點匹配
系列文
3D 重建實戰:使用 2D 圖片做相機姿態估計與三維空間重建30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
mros
iT邦新手 5 級 ‧ 2024-09-26 09:29:42

還記得大學修課手刻特徵點算法超難寫,我已經是選比 SIFT 簡單的算法了...

我要留言

立即登入留言