画像から特徴量を抽出し、透視変換行列を導出して画像を変形する

入力画像中に目的画像がある場合に、入力画像を目的画像にフィットするように透視変換行列を使って変形させる方法
※opencv-python 3.4にて動作確認済み

サンプル画像

今回は例として目的画像をレトロな写真とし、入力画像は目的画像をPIXUSでL判印刷して適当な角度で撮影したものを使用している。この工程によって用紙の変形や照明の反射が映りこむなどの外乱を発生させられる。

目的画像 showa-0.JPG
入力画像 showa-1.jpg
変換結果 aligned_00000.jpg

ソースコード

  • img1: 目的画像
  • img2: 入力画像
  • max_pts:検出する特徴量の最大数
  • good_match_rate:全体の特徴量から透視変換行列に使用する割合(上位から取得する)
import cv2

def alignImages(img1, img2,
                max_pts=500, good_match_rate=0.15, min_match=10):
    # 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
    detector = cv2.ORB_create(max_pts)
    # find the keypoints and descriptors with SIFT
    kp1, des1 = detector.detectAndCompute(
        cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY), None
    )
    kp2, des2 = detector.detectAndCompute(
        cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY), 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, None, flags=2))

        # Use homography
        height, width, channels = img1.shape
        dst_img = cv2.warpPerspective(img2, h, (width, height))
        return dst_img, h
    else:
        return img1, np.zeros((3, 3))

各工程の解説

[1] ORBを用いて特徴量を検出する

SIFTやSURFなど特徴量検出器はいろいろ種類があるが、速度を重視してORBを選択した。ORB以外の検出器を利用したい場合は下記を参照。

Feature Detection and Description
https://docs.opencv.org/3.0-beta/modules/features2d/doc/feature_detection_and_description.html

[2] 検出した特徴量の比較をしてマッチングをする

目的画像の特徴量des1と、入力画像の特徴量des2を比較してマッチングする。今回は総当たりのBasics of Brute-Force Matcherを使用しているが、FLANNなど他のマッチング方法を利用したい場合は下記を参照。

Feature Matching
https://docs.opencv.org/3.0-beta/doc/py_tutorials/py_feature2d/py_matcher/py_matcher.html

[3] 十分な特徴量が集まったらそれを使って入力画像を変形する

マッチングした特徴量を変換し、目的画像特徴点src_ptsと入力画像特徴点dst_ptsを用意する。findHomography()ではRANSACを用いて透視変換行列hを導出している。RANSACにより使用された特徴量の情報はmaskに格納されている。最後warpPerspective()に入力画像と先ほど得られたhを入力して画像を変換する。

参考(特徴量の可視化)
a.jpg

findHomography
https://docs.opencv.org/3.0-beta/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#findhomography

warpPerspective
https://docs.opencv.org/3.0-beta/modules/imgproc/doc/geometric_transformations.html#warpperspective

以上

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.