0%

OpenCV for Java环境搭建

前言

众所周知OpenCV是一个基于C/C++开发的功能强大的计算机视觉开源框架,官方提供了Windows(含Java封装库)、Android、iOS这几种环境下的已编译好的库。 一般情况下如果想在桌面操作系统中使用OpenCV,可以直接使用C/C++调用编译好的动态/静态库。 如果想要通过Java来使用OpenCV,可以先将相关功能用C/C++开发后编译为JNI库、再由Java程序调用(IPC、RPC方式在此不讨论)。 也可以直接使用OpenCV的Java封装库(封装了对OpenCV的JNI调用),这样可以直接用Java进行OpenCV相关的开发,后面这种方式对于大多数Java开发同学来说更为友好,效率上也没有太大的差别。

但官方只提供了Windows下的Java封装库,Linux、MacOS下的Java封装库就要自己动手丰衣足食了,想要在Java后端开发中使用OpenCV,就得自己编译下Linux(服务器使用)、MacOS(本机开发使用)下的Java封装库和对应的OpenCV动态链接库了。

编译及结果

OpenCV 2.4.x 版本的Java封装库编译官方文档在:https://docs.opencv.org/2.4/doc/tutorials/introduction/desktop_java/java_dev_intro.html 。

编译过程也比较简单不再赘述,这里直接提供编译好的Jar包和动态库。 注意编译机器上的Java版本都是Java8,编出来的Jar包低版本的Java可能不能使用(没有测试过)。

Linux版本

如果应用要部署到服务器上,那当然只能用Linux版本的动态库和Jar包了。Jar包可以通过该url获取(尽量保证长期有效):http://cdn.spirithy.com/code/opencv2413-java/linux/opencv-2413.jar

so动态链接库可以通过该url获取(尽量保证长期有效,方便直接用Docker的ADD指令):http://cdn.spirithy.com/code/opencv2413-java/linux/libopencv_java2413.so

MacOS

Mac下是使用的XCode自带的CLang来编译的,Jar包可以通过该url获取(尽量保证长期有效):http://cdn.spirithy.com/code/opencv2413-java/macos/opencv-2413.jar但请注意,经测试MacOS下直接用Linux的Jar包也是完全OK的、证明两个平台下的Jar包其实是没有区别的,所以直接用opencv-java-x86_64-linux的Jar包更方便。

dylib动态链接库可以通过该url获取(尽量保证长期有效):http://cdn.spirithy.com/code/opencv2413-java/macos/libopencv_java2413.dylib 。

使用

上面编译出来的动态链接库中已经包含了OpenCV的所有函数,所以Java应用在使用时只需要依赖这一个动态链接库、及Java封装库即可,非常的方便。

在项目中引入jar包

直接引入上述的Maven依赖即可,MacOS、Linux下都可以用opencv-java-x86_64-linux的Jar包。

依赖动态库

需要在Java参数中指定OpenCV动态库所在的目录的路径,这样才能加载到动态链接库。

本机环境(MacOS)

例如在MacOS下进行开发,在Idea中,“Run”-“Edit Configurations”-“VM options”,加入以下参数即可指定动态库的目录。

1
-Djava.library.path=/Users/xxxx/opencv2413-java/macos

服务器环境

需要将动态库部署到服务器上,或者添加到Docker镜像中,然后需要在启动Java应用时用"-Djava.library.path"指定动态库目录。 具体实现这里也没有实践过需要读者自行尝试。

代码编写

Java使用OpenCV的封装库,和在C/C++中使用OpenCV,在代码上是稍有不同的,但基本可以把代码“平移过来”。 需要额外注意的只有一点:在Java进程中、使用OpenCV之前需要显式地加载动态库。 所以如果是Spring boot应用可以在Application.java的静态代码块中执行加载。示例如下:

1
2
3
4
5
6
7
8
9
10
11
public class Application {

//加载OpenCV动态链接库
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args)...
}
}

代码示例

下面提供一个从C++代码人工转换过来的Java使用OpenCV的示例,功能为“从一张白底图片中抠出包含主要内容的最小图片”,也就是“抠图片中的物体”,代码比较简单而且应该还有需要调优的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.xxxx;

import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Rect;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

/**
* @author Spirit
*/
public class Sample {

static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}

public static void getSplitedPicture(String dirPath, String fileName) {
Mat image = Highgui.imread(dirPath + fileName);

Mat imageBackup = image.clone();

//灰度
Imgproc.cvtColor(image, image, Imgproc.COLOR_BGR2GRAY);

//二值
double[] data = image.get(0, 0);
int thres = (int)(data[0] - 5);

Imgproc.threshold(image, image, thres, 255, Imgproc.THRESH_BINARY);

//反色
Core.bitwise_not(image, image);

//检测轮廓
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Imgproc.findContours(image, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

//查找最大区域
double maxArea = 0;
int maxAreaIndex = -1;
for(int i=0; i<contours.size(); i++)
{
double tmparea = Math.abs(Imgproc.contourArea(contours.get(i)));

if(tmparea > maxArea)
{
maxArea = tmparea;
maxAreaIndex = i;
}
}

//裁剪
if (maxAreaIndex > 0) {
Rect rect = Imgproc.boundingRect(contours.get(maxAreaIndex));

Mat result = imageBackup.submat(rect);

Highgui.imwrite(dirPath + fileName.substring(0, fileName.lastIndexOf(".")) + "_result.jpg", result);
}
}

public static void main(String[] args) {
getSplitedPicture("/Users/xxxx/Downloads/", "cup.jpeg");
}
}