在C++/Python/Java/ObjectC中使用OpenCV,详细配置踩坑记录!!

前言

 作为OpenCV的高频使用者,我在目前主流语言中都使用过OpenCV,并且在Windows/ Linux/ Mac OS上均有过配置经验,为了让自己踩过的坑不至于白踩,现将这些过程和踩过的坑都记录下来,希望能对需要的同学有点帮助。OpenCV的话就不过多介绍了,做图像处理的童鞋应该都比较喜欢用这个库,毕竟更新多,维护多,文档多,且C++写的运行速度快,并且功能丰富,4版本封装了很多深度学习相关的库,避免重复造轮子,提高生产效率。下面进入正题,每种语言在不同平台(根据实际情况)下的配置过程和例子都会给出。

C++

Windows下配置

Windows下使用OpenCV最方便的是在Visual Studio中,首先去官网上面下载OpenCV for windows的包,check here,选择windows即可。比如下载最新的OpenCV4.1.1,文件的名字是opencv-4.1.1-vc14_vc15.exe,其中vc14,vc15代表的是VS2015和VS2017版本号。下载完成之后双击解压即可。

我发现这个网上教程非常多,可以看这儿

Mac OS下配置

(1) Mac下配置非常简单,打开终端,运行
brew install opencv
(PS:但是注意!这个命令是不支持java版本的,如果需要在mac下使用java+opencv,建议跳到下面的java部分。)
(2) 静静地等待安装完毕,需要一段时间。这个时间,可以去下载一个Xcode。
使用上面这个命令安装好OpenCV之后,头文件和相关的静态库是默认安装在/usr/local/Cellar/opencv/4.1.0_2/下的,但这个命令还很贴心的将头文件和静态库软连接到/usr/local/include/opencv4这个下面,方便后面的配置。

(3)mac下C++使用OpenCV
方案1:命令行使用
export PKG_CONFIG_PATH=/usr/local/Cellar/opencv/4.1.0_2/lib/pkgconfig/
然后随便打开一个OpenCV的sample,里面有写好的Makefile文件,直接cmake…&&make就行。
方案2:【推荐】Xcode下使用
打开xcode,新建macOS->command line tool,选择语言C++,新建project后按照下图去配置。注意左边那些*.dylib 在/usr/local/Cellar/opencv/4.1.0_2/lib下可以找到。

配置完成之后,就可以在Xcode中愉快的使用C++和OpenCV了~

Centos下配置

有两种方法:

一、利用centos的Repository

sudo yum install opencv opencv-devel opencv-python pkg-config --modversion opencv 2.4.5
这个办法也许不能安装最新的版本,得看repository有哪个版本的,我这边装的就是2.4.5,现在已经出到4.1.0了

二、从源码编译

1.下载OpenCV源码:check here 注意,选择source
2.解压文件并进入文件夹:

Python

python跨平台,三种操作系统共用一种方法,只是注意,最好使用安装Anaconda使用虚拟环境,具体步骤如下,打开终端,输入:
conda create -n tf python=3.6 #创建一个名为tf的python3的虚拟环境 conda activate tf pip install opencv-contrib-python-headless #添加headless则不会编译图形学相关的库,减少出错
搞定!

Java

由于笔者只在linux和os上使用过java,所以这部分没有windows的配置经验~

Mac OS下

之前说过,mac下安装opencv,只需要brew install opencv即可,但这样只会按默认方式去安装,不会生成java相关的jar包和动态库,因此mac os下想要顺利使用java+opencv的环境,需要进行如下操作:
brew install ant brew edit opencv
在打开的文件中把-DBUILD_opencv_java=OFF改为-DBUILD_opencv_java=ON
接着运行
brew install --build-from-source opencv
全部完成之后就能在如下的目录中发现下图所示的东西:
在C++/Python/Java/ObjectC中使用OpenCV,详细配置踩坑记录!!_第1张图片

如果使用的编译器是IDEA,则需要在run->Edit configurations->VM options中加入路径:
-Djava.library.path="/usr/local/Cellar/opencv/4.1.0_2/share/OpenCV/java/opencv"
这个主要是为了让编译器可以找到动态库所在的位置,jar包则按照一般jar包的使用规则去引入就可以。
测试程序如下:

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Scalar;

import static java.lang.System.*;


public class OpenCVTest {
    public static void main(String []args)
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        System.out.println("Welcome to OpenCV " + Core.VERSION);
        Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0));
        System.out.println("OpenCV Mat: " + m);
        Mat mr1 = m.row(1);
        mr1.setTo(new Scalar(1));
        Mat mc5 = m.col(5);
        mc5.setTo(new Scalar(5));
        out.println("OpenCV Mat data:\n" + m.dump());
    }
}

centos 下

配置

  1. CentOS7.0虽然自带JDK1.7和1.8,运行“java -version”命令也可以看到版本信息,但是jdk的安装环境不全,比如缺少tool.jar和dt.jar等,这就导致“javac”等这样的命令即便配置了环境变量也不能用,所以要重新安装jdk,并且配置环境变量。
    首先查看可以安装的jdk版本:
    yum search java-1.7
    安装1.8这一款:
    yum -y install java-1.8.0-openjdk-devel.x86_64
    然后配置环境变量:
    vim /etc/profile export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.141-2.6.10.1.el7_3.x86_64 export PATH=$JAVA_HOME/bin:$PATH export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
  2. 在安装了jdk并且export JAVA_HOME=“你的jdk路径之后”,再从源码编译opencv即可,具体步骤如下:
  • 下载opencv源码包并解压:
wget https://github.com/opencv/opencv/archive/4.1.0.zip &&unzip 4.1.0.zip
cd 4.1.0 &&mkdir build && cd build
cmake -DBUILD_SHARED_LIBS=OFF _DBUILD_TEST=OFF ..
make -j8

其中cmake的编译选项又很大的灵活性,第一个是静态编译opencv,这样生成的libopencv_java410.so会比较大,但是不会依赖其他的.so,比如libopencv_ml410.so这种。具体的精简编译opencv可以看这篇博客

编译完之后就能在build/lib/下发现libopencv_java410.so文件,在build/bin目录下发现opencv410.jar文件。
运行下面的命令可以查看libopencv_java410.so的依赖的动态库情况:

ldd build/lib/libopencv_java410.so

如何打包独立的包含opencv所有依赖库的jar包?

这个是要解决一个这样的问题:我们的一个sdk,需要在线上环境下使用java+opencv,但是线上的机器无法配置OpenCV的环境。因此,需要自己将opencv的动态库打进jar包,在线上引用的时候,将动态库释放到/tmp目录或其他方便的目录下,从而可以在没有安装过OpenCV的java环境下正确运行。
(1) 将上一步生成的libopencv_java.so.4.0,以及其他的.so都放到resources文件夹下
(2)编写资源加载类:

import java.io.*;
import java.util.zip.CRC32;

public class LoadLibrary {

	private static byte[] read(InputStream ins, boolean closed) throws IOException {
		try {
			ByteArrayOutputStream bout = new ByteArrayOutputStream();
			byte[] bs = new byte[8192];
			for (int len; (len = ins.read(bs, 0, bs.length)) > 0; ) {
				bout.write(bs, 0, len);
			}
			return bout.toByteArray();
		} finally {
			if (closed) {
				ins.close();
			}
		}
	}

	public synchronized static String load(String libName, String version, boolean fload) {
		String libExtension;
		String libprefix = "";
		String nativeTempDir;
		if (System.getProperty("os.name").toLowerCase().indexOf("win") != -1) {
			libExtension = ".dll";
			if (version != null && !version.isEmpty()) {
				libExtension = "-" + version + ".dll";
			}
			nativeTempDir = System.getProperty("java.io.tmpdir");
		} else if (System.getProperty("os.name").toLowerCase().indexOf("mac os") != -1) {
			libExtension = ".dylib";
			if (version != null && !version.isEmpty()) {
				libExtension = "." + version + ".dylib";
			}
			libprefix = "lib";
			nativeTempDir = "/tmp";
		} else {
			libExtension = ".so";
			if (version != null && !version.isEmpty()) {
				libExtension = ".so." + version;
			}
			libprefix = "lib";
		    nativeTempDir = "/tmp";
		}
	    String libFullName = libprefix + libName + libExtension;
	    File extractedLibFile = new File(nativeTempDir + File.separatorChar + libFullName);

        try {
        	InputStream in = LoadLibrary.class.getClassLoader().getResourceAsStream(libFullName);
            if(in==null) {
                in =  LoadLibrary.class.getResourceAsStream("/" + libFullName);
            }
            if(in==null) {
            	throw new IOException("not exist " + libFullName);
            }
            byte[] nbuf = read(in, true);
            byte[] obuf = null;
            
            if (extractedLibFile.exists()) {
            	obuf = read(new FileInputStream(extractedLibFile), true);
            }
            
            do {
            	if (obuf != null) {
                	CRC32 crc = new CRC32();
                	crc.update(obuf);
                	long ocrc = crc.getValue();
                	crc.reset();
                	crc.update(nbuf);
                	long ncrc = crc.getValue();
                	if (ocrc == ncrc) {
                		break;
                	}
                }
            	BufferedOutputStream writer = new BufferedOutputStream(new FileOutputStream(extractedLibFile));
            	writer.write(nbuf);
            	writer.close();
            } while (false);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
	    if (fload) {
	    	System.load(extractedLibFile.toString());
	    }
	    return extractedLibFile.toString();
	}
}

这个类的功能是将resources文件夹下的资源打包释放到/tmp文件夹下,使用的作为静态语句放到类中就可以:

static
{
//参数依次为库名(已经考虑不同平台后缀不同),版本号,是否加载(该函数返回参数为资源释放的绝对路径,也可以选择只返回该路径不加载)
LoadLibrary.load("opencv_java410","",true);
}

(3)mvn package即可在target下面生成yourproject.jar,这样,其他没有编译安装opencv的平台就可以正常使用这个sdk了!
(4)注意事项:如果实际使用sdk的电脑不仅没有opencv,还没有opencv所依赖的第三方库比如jpeg,webp等,建议在cmake的时候,添加-DBUILD_JPEG=ON这样类似的标识,然后ldd 查看libopencv_java410.so是否还依赖jpeg的动态库,如果不再依赖,就表示可以在没有jpeg的平台上使用了!

总结

以上为在各个语言下各个操作系统下使用OpenCV的过程,最后,奉献上我编译的opencvjava的动态库和jar包,需要的同学可以评论一下,我给加上权限~另外有什么问题也可以评论,我踩了很多坑,但是自己已经记不全了,如果问到,可能会想起来~
下载地址:链接: https://pan.baidu.com/s/1LtLlKrH-4TSJzsswKu9rZw 提取码: 6uti

你可能感兴趣的:(C++,OpenCV,Java)