ORB特徴量を用いて、ホモグラフィ変換を行う


この記事では、ORB特徴量を用いて、入力画像を目的画像にフィットするような形に変換(ホモグラフィ変換)する方法について書きます。

# はじめに
この記事は、"[画像から特徴量を抽出し、透視変換行列を導出して画像を変形する](https://qiita.com/ka10ryu1/items/bd05aed321a7a154d8a1)"を参考に、
自分の環境(OpenCV3+Python3)で動かしてみた結果とプログラムについて記述します。
# ホモグラフィ変換とは
ホモグラフィ変換とは、平面を射影変換を用いて別の平面に射影することで、アフィン変換と違い長方形を、台形に変形させるなどの変換ができます。
# 手順
- Step1 目的画像と入力画像を用意
- Step2 各画像に対してORB特徴量を抽出する
- Step3 検出した特徴量から画像間のマッチングを行う
- Step4 特徴量を用いて変換行列を求め,画像の変換を行う

# プログラム
OpenCVには```cv2::findHomography```という関数があり、オプションでRANSACを使って2つの画像を関連付けるホモグラフィ行列を見つけることができます。取得した行列は変数hに格納されるので、応用することもできます。
私は、特徴点が取りにくい画像を変換させたいときにエッジを強調させた画像をマスク画像として使用し、特徴点を取得、行列の取得をして、元画像を変換させるといった活用をしています。
また、結果がうまく行かなっかた場合は、使用する特徴量の割合(```good_match_rate```の値)を変えることで解決できることがあります。

・各変数の説明
- ```max_pts```: 取得する特徴量の最大数
- ```good_match_rate```: 取得した特徴量の内、ホモグラフィ変換に使用する特徴量の割合
- ```min_match```: 最低限必要な特徴点の数
- ```des1```: img1から取得した特徴量
- ```des2```: img2から取得した特徴量

```py
import numpy as np
import cv2
from matplotlib import pyplot as plt

im1 = cv2.imread('./image1',0) # queryImage
im2 = cv2.imread('./image2',0) # trainImage

def alignImages(img1, img2,
               max_pts, good_match_rate, min_match):
   # https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_feature2d/py_feature_homography/py_feature_homography.html#feature-homography

   # [1] ORBを用いて特徴量を検出する
   # Initiate ORB detector
   orb = cv2.ORB_create(max_pts)
   # find the keypoints and descriptors with SIFT
   kp1, des1 = orb.detectAndCompute(img1,None)
   kp2, des2 = orb.detectAndCompute(img2,None)
   bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)

   # [2] 検出した特徴量の比較をしてマッチングをする
   # Match descriptors.
   matches = bf.match(des1, des2)

   # Sort them in the order of their distance.
   matches = sorted(matches, key=lambda x: x.distance)
   good = matches[:int(len(matches) * good_match_rate)]

   # [3] 十分な特徴量が集まったらそれを使って入力画像を変形する
   if len(good) > min_match:
       src_pts = np.float32(
           [kp1[m.queryIdx].pt for m in good]).reshape(-1, 1, 2)
       dst_pts = np.float32(
           [kp2[m.trainIdx].pt for m in good]).reshape(-1, 1, 2)
       # Find homography
       h, mask = cv2.findHomography(dst_pts, src_pts, cv2.RANSAC)

       cv2.imwrite('draw_match.jpg', cv2.drawMatches(
       img1, kp1, img2, kp2, matches[:10], None, flags=2))

       # Use homography
       height, width = img1.shape
       dst_img = cv2.warpPerspective(img2, h, (width, height))
       plt.imshow(dst_img, 'gray'),plt.show()
       cv2.imwrite('kekka-ab.png',dst_img)
       return dst_img, h
   else:
       plt.imshow(img1, 'gray'),plt.show()
       return img1, np.zeros((3, 3))

alignImages(im1,im2,500,0.5,10)
```

# 実行結果例
![index1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406018/64f33510-e1b5-9eda-2903-7b27359521e5.jpeg)     ![index2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406018/66fee621-4716-e9df-644d-c927b254396b.jpeg)

![draw_match.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406018/d449e545-0921-1283-206d-b732c187caf2.jpeg)

![kekka_lena.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406018/5609315f-8c2e-0cc5-4a3b-a7b95e1cf14f.png)




# 参考サイト
[画像から特徴量を抽出し、透視変換行列を導出して画像を変形する](https://qiita.com/ka10ryu1/items/bd05aed321a7a154d8a1)