JavaCV(OpenCV)で特徴点マッチング

orgプログラミング

PythonでのOpenCVのサンプルは結構あるけどJavaは少なめだったのでJavaCVの使い方をメモ用に書いておきます。
以下の関連記事です。

環境

  • Windows10 Pro
  • Spring Boot 2.5.3
  • JDK 11

Spring Bootなのは特に意味はないです。

pom.xml


pomでJavaCVのjarを読み込みます。

<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv-platform -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bytedeco/javacv -->
<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv</artifactId>
    <version>1.5.6</version>
</dependency>

 

OpenCVで画像読み込み

JavaのOpenCVで画像をいじるときはMatにする必要があります。
Pythonでいうところの

cv2.imread("img.jpg");

です。
Javaでは以下のように書きます。

// 画像読み込み
Path img1 = Paths.get("C:\\gomibako\\1.jpg");
Mat mat1 = imread(img1.toFile().getAbsolutePath());

特徴点の特徴記述子取得

特徴点を取得します。
特徴点を抽出するアルゴリズムはいくつかあるようですがAKAZEが良さそうなのでこちらを使用していきます。

AKAZE akaze = AKAZE.create();

KeyPointVector keypoints1 = new KeyPointVector();
Mat descriptors1 = new Mat();
akaze.detect(mat1, keypoints1);
akaze.compute(mat1, keypoints1, descriptors);

特徴点によるマッチング

今回はブルートフォース(総当たり)マッチングを使用します。
画像2枚をMat型で読み込みAKAZEでそれぞれ特徴点を抽出します。
その後、両画像の特徴点をマッチングします。
マッチング結果がmatchVectorに格納されるので各特徴点が一致しているかをfor文で確認し、一致していそうな場合にmatchをインクリメントしています。
その個数によって一致してるかしていないかを判断していきます。

全体ソースは以下
実行が楽なのでテストクラスで作ってます。

import static org.bytedeco.opencv.global.opencv_imgcodecs.*;

import java.nio.file.Path;
import java.nio.file.Paths;

import org.bytedeco.opencv.opencv_core.DMatch;
import org.bytedeco.opencv.opencv_core.DMatchVector;
import org.bytedeco.opencv.opencv_core.DMatchVectorVector;
import org.bytedeco.opencv.opencv_core.KeyPointVector;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_features2d.AKAZE;
import org.bytedeco.opencv.opencv_features2d.BFMatcher;
import org.bytedeco.opencv.opencv_features2d.DescriptorMatcher;
import org.junit.jupiter.api.Test;

class OpenCVUtilTest {

    // AKAZEで特徴点を抽出
    private static AKAZE akaze = AKAZE.create();

    // Brute Force Matching
    private static BFMatcher matcher = new BFMatcher(DescriptorMatcher.BRUTEFORCE);

    @Test
    void test() {

        // 画像読み込み & 特徴点の特徴記述子取得
        Path img1 = Paths.get("C:\\gomibako\\1.jpg");
        Mat mat1 = imread(img1.toFile().getAbsolutePath());
        KeyPointVector keypoints1 = new KeyPointVector();
        Mat descriptors1 = new Mat();
        detectAndCompute(mat1, keypoints1, descriptors1);

        Path img2 = Paths.get("C:\\gomibako\\org.jpg");
        Mat mat2 = imread(img2.toFile().getAbsolutePath());
        KeyPointVector keypoints2 = new KeyPointVector();
        Mat descriptors2 = new Mat();
        detectAndCompute(mat2, keypoints2, descriptors2);

        // 特徴点マッチング実行
        DMatchVectorVector matchVector = new DMatchVectorVector();
        matcher.knnMatch(descriptors1, descriptors2, matchVector, 2, null, false);

        // 値を小さくすると厳しめのマッチングになる
        // 甘め(0.9とか)に設定した場合、全く無関係の画像もマッチングしてしまうので注意
        float threshold = 0.5f;

        int match = 0;
        for (long i = 0; i < matchVector.size(); i++) {
            DMatchVector vector = matchVector.get(i);
            if (vector.size() == 2) {  // 2枚分の特徴量が取得できた場合のみ
                DMatch m = vector.get(0);
                DMatch n = vector.get(1);
                if (m.distance() < threshold * n.distance()) {
                    match++;
                }
            }
        }

        System.out.println("match count = " + match);

        if (match > 10) {
            System.out.println("大体同じ画像かも");
        }
    }

    private static void detectAndCompute(Mat mat, KeyPointVector keyPoint, Mat descriptors) {
        akaze.detect(mat, keyPoint);
        akaze.compute(mat, keyPoint, descriptors);
    }

}

 

実行してみる

使用画像は以下の3枚
元画像とその画像から切り取った草1、草2です。

  • 元画像

org

  • 草1

草1

  • 草2

草2

 

まずは草1、草2のマッチングした個数ですが、実行した結果は0個でした。
ジャンル的には同じ草でも形状が違うのでマッチングされませんでした。
続いて元画像と草1、元画像と草2のマッチング個数は以下となりました。
こちらは元画像から切り取った画像なので見事マッチングしました。

  • 元画像と草1
    match count = 43
  • 元画像と草2
    match count = 23

コメント

タイトルとURLをコピーしました