PythonでのOpenCVのサンプルは結構あるけどJavaは少なめだったのでJavaCVの使い方をメモ用に書いておきます。
以下の関連記事です。
- JavaCV(OpenCV)で画像のリサイズ
- JavaCV(OpenCV)で画像のRGBを取得する
- JavaCV(OpenCV)でパノラマ合成(Stitching)
- JavaCV(OpenCV)で画像をグレースケール化
- JavaCV(OpenCV)で画像を結合する(VConcat, HConcat)
- JavaCV(OpenCV)で画像を2値化(白黒化)する
- JavaCV(OpenCV)で画像の切り出し
環境
- 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です。
- 元画像
- 草1
- 草2
まずは草1、草2のマッチングした個数ですが、実行した結果は0個でした。
ジャンル的には同じ草でも形状が違うのでマッチングされませんでした。
続いて元画像と草1、元画像と草2のマッチング個数は以下となりました。
こちらは元画像から切り取った画像なので見事マッチングしました。
- 元画像と草1
match count = 43 - 元画像と草2
match count = 23
コメント