--- title: JavaCVで画像処理 tags: ["Java", "JavaCV", "OpenCV"] categories: ["Programming", "Java", "org", "bytedeco", "javacpp", "opencv"] date: 2015-01-29T14:51:45Z updated: 2015-01-29T14:51:45Z --- JavaでOpenCVが使いたくなったので調べてみた。 次の2通りあるっぽい * [OpenCVの公式Javaラッパー](http://docs.opencv.org/doc/tutorials/introduction/desktop_java/java_dev_intro.html) * [JavaCV](https://github.com/bytedeco/javacv) セットアップの手軽さ(Mavenだけで使える)を重視し、JavaCVを使う。 OpenCVはセットアップが面倒なイメージがあったが、JavaCV使うとさくっと使える。 JavaCVは[JavaCPP](https://github.com/bytedeco/javacpp)というC++のソースから自動生成してできるブリッジのようなもので作られているみたい。 ### pom.xmlの設定 ネイティブライブラリを選ぶために、`classifier`を指定する必要があるが、Mavenのprofile機能で切り替えられる。 ``` xml 4.0.0 com.example hello-javacv jar 1.0.0-SNAPSHOT hello-javacv 0.10 2.4.10-${javacv.version} org.bytedeco javacv ${javacv.version} org.bytedeco.javacpp-presets opencv ${opencv.version} org.bytedeco.javacpp-presets opencv ${opencv.version} ${classifier} maven-compiler-plugin 1.8 1.8 macosx-x86_64 mac x86_64 macosx-x86_64 linux-x86_64 unix amd64 linux-x86_64 windows-x86_64 windows amd64 windows-x86_64 windows-x86 windows x86 windows-x86 ``` ### 簡単な画像処理プログラミング とりあえず画像をリサイズ(1/2)する例 ``` java package com.example; import java.net.URISyntaxException; import java.nio.file.Paths; import static org.bytedeco.javacpp.opencv_core.*; import static org.bytedeco.javacpp.opencv_highgui.*; import static org.bytedeco.javacpp.opencv_imgproc.*; public class App { public static void main(String[] args) throws URISyntaxException { String filepath = args.length > 0 ? args[0] : Paths.get( App.class.getResource("/lena.png").toURI()).toString(); resize(filepath); } public static void resize(String filepath) { IplImage source = cvLoadImage(filepath, CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR); System.out.println("path = " + filepath); System.out.println("image = " + source); if (source != null) { IplImage dest = cvCreateImage(cvSize(source.width() / 2, source.height() / 2), source.depth(), source.nChannels()); cvResize(source, dest, CV_INTER_NN); cvSaveImage("half-" + Paths.get(filepath).getFileName().toString(), dest); cvReleaseImage(source); cvReleaseImage(dest); } } } ``` 定番のlenaで実行 ``` console $ mvn compile exec:java -Dexec.mainClass=com.example.App -Dexec.args=lena.png ``` ![image](https://qiita-image-store.s3.amazonaws.com/0/1852/428eb7c4-e88d-2295-c43b-79d98909384c.png) できた。 [ソース](https://github.com/making/hello-cv) ### 顔検出 OpenCV定番の顔検出をやってみる。今度はOpenCV2系のC++用APIを使ってみる。 ``` java package com.example; import static org.bytedeco.javacpp.opencv_core.*; import static org.bytedeco.javacpp.opencv_highgui.*; import static org.bytedeco.javacpp.opencv_objdetect.*; import java.net.URISyntaxException; import java.nio.file.Paths; public class App { public static void main(String[] args) throws URISyntaxException { String filepath = args.length > 0 ? args[0] : Paths.get( App.class.getResource("/lena.png").toURI()).toString(); faceDetect(filepath); } public static void faceDetect(String filepath) throws URISyntaxException { String classifierName = Paths.get( App.class.getResource("/haarcascade_frontalface_default.xml") .toURI()).toString(); CascadeClassifier faceDetector = new CascadeClassifier(classifierName); System.out.println("load " + filepath); Mat source = imread(filepath); Rect faceDetections = new Rect(); faceDetector.detectMultiScale(source, faceDetections); int numOfFaces = faceDetections.limit(); System.out.println(numOfFaces + " faces are detected!"); for (int i = 0; i < numOfFaces; i++) { Rect r = faceDetections.position(i); rectangle(source, new Point(r.x(), r.y()), new Point(r.x() + r.width(), r.y() + r.height()), new Scalar(0, 0, 255, 0)); } imwrite("faces.png", source); } } ``` 実行 ``` console $ mvn compile exec:java -Dexec.mainClass=com.example.App -Dexec.args=lena.png ``` ![image](https://qiita-image-store.s3.amazonaws.com/0/1852/03fc19c2-8829-686d-132f-c9eee3b0bf1b.png) できた。 [ソース](https://github.com/making/hello-cv/tree/facedetect) ### Duke化 ちょっと遊んでみましょう。 赤枠で囲む代わりにDukeっぽくしてみます。 あと、`org.bytedeco.javacpp.opencv_core.Mat`クラスを使って直接読み書きするのはJavaプログラミングとしては応用し辛くなるので、 `java.awt.image.BufferedImage`を介するようにする。 ``` java package com.example; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; import static org.bytedeco.javacpp.opencv_core.*; import static org.bytedeco.javacpp.opencv_objdetect.*; public class App { public static void main(String[] args) throws URISyntaxException, IOException { String filepath = args.length > 0 ? args[0] : Paths.get( App.class.getResource("/lena.png").toURI()).toString(); faceDetect(filepath); } public static void faceDetect(String filepath) throws URISyntaxException, IOException { String classifierName = Paths.get( App.class.getResource("/haarcascade_frontalface_default.xml") .toURI()).toString(); CascadeClassifier faceDetector = new CascadeClassifier(classifierName); System.out.println("load " + filepath); Mat source = Mat.createFrom(ImageIO.read(new File(filepath))); // BufferedImage -> Mat Rect faceDetections = new Rect(); faceDetector.detectMultiScale(source, faceDetections); int numOfFaces = faceDetections.limit(); System.out.println(numOfFaces + " faces are detected!"); for (int i = 0; i < numOfFaces; i++) { Rect r = faceDetections.position(i); ducker(source, r); } BufferedImage image = source.getBufferedImage(); // Mat -> BufferedImage try (OutputStream out = Files.newOutputStream(Paths .get("duked-faces.png"))) { ImageIO.write(image, "png", out); } } public static void ducker(Mat source, Rect r) { int x = r.x(), y = r.y(), h = r.height(), w = r.width(); // make the face Duke rectangle(source, new Point(x, y), new Point(x + w, y + h / 2), new Scalar(0, 0, 0, 0), -1, CV_AA, 0); rectangle(source, new Point(x, y + h / 2), new Point(x + w, y + h), new Scalar(255, 255, 255, 0), -1, CV_AA, 0); circle(source, new Point(x + h / 2, y + h / 2), (w + h) / 12, new Scalar(0, 0, 255, 0), -1, CV_AA, 0); } } ``` 実行 ``` console $ mvn compile exec:java -Dexec.mainClass=com.example.App -Dexec.args=lena.png ``` ![image](https://qiita-image-store.s3.amazonaws.com/0/1852/c87fb02f-3e6b-a694-d7da-9cfdc2adc5c8.png) できた。 [ソース](https://github.com/making/hello-cv/tree/duker) これで色々遊べますね。 [ここのリファレンス](http://book.mynavi.jp/support/pc/opencv2/c3/index.html)がほぼそのまま使えます。ありがたい。