前言
OpenCV (Open Source Computer Vision Library) 是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它主要由 C++ 语言编写,同时提供了Python、Ruby、MATLAB、Java等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法,同时一个使用CUDA的GPU接口也于2010年9月开始实现。
该库拥有 2500 多个优化算法,其中包括一套全面的经典和最先进的计算机视觉和机器学习算法。这些算法可用于检测和识别人脸、识别对象、对视频中的人类行为进行分类、跟踪摄像机移动、跟踪移动对象、提取对象的 3D 模型、从立体摄像机生成 3D 点云、将图像拼接在一起生成整个场景的高分辨率图像,从图像数据库中查找相似图像,从使用闪光灯拍摄的图像中删除红眼,跟踪眼睛运动,识别风景,并建立标记以覆盖其增强现实等。
除了谷歌、微软、英特尔、IBM、索尼、等老牌公司,还有许多初创公司,它们广泛使用OpenCV。
我们可以通过以下网址获取各个版本的 OpenCV ,OpenCV最新版本已经进入 4.x.x 了。
OpenCV环境搭建
下面分别展示了Windows和Mac环境下的OpenCV搭建,在Linux环境下搭建和Mac环境下类似,如有机会,我会在进行补充。
本文基于 Java + IDEA + OpenCV 环境的搭建和使用来进行说明。
Windows环境下的OpenCV环境搭建
首先下载 OpenCV for Windows 版本,我这儿下载了4.1.1 版本的OpenCV。
我们将这个exe运行,将OpenCV安装在一个方便寻找的目录下即可。
至此,OpenCV算是在Windows环境上安装了,下面我们来进行开发环境搭建。
PS:如果想获取OpenCV最新版进行安装,可以通过OpenCV的 GitHub 获取最新版源码,在通过CMake工具进行编译生成OpenCV Lib,这块大家可以查阅相关资料,不在过多叙述。
我们打开OpenCV的安装目录,在 build/java
可以看到 opencv-411.jar
和 x64、x86
两个文件夹,这两个文件夹里有两个dll文件,都叫opencv_java411.dll
,这分别是64位系统和32系统需要使用的动态链接库文件,我们如果想在Java环境下使用OpenCV,在JVM启动时,必须将dll文件加载进去,然后才能使用opencv-411.jar
里提供的Java接口方法。
我们来看简单测试下,我们用IDEA新建一个Java项目test,同时需要引入上面的dll文件和jar文件。
新建一个Java项目:
引入必要文件:
这儿要注意两个文件同时选中,引入后如下图效果,Native Library 可以被正确加载到。否则在启动后会出现 java.lang.UnsatisfiedLinkError
错误。
然后我们测试一下,代码如下:
1 | public class Main { |
可以看到正确输出内容。
这儿要注意System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
这句话,在使用OpenCV时,需要调用此语句以加载原生Library。
PS:上面的文件引入过程,dll和jar也是可以分别引入的,也是相当于引入到了JVM运行环境中。本质上是没有区别的,但一起引入方便理解,即opencv_java411.dll
是为opencv-411.jar
”服务“ 的。
显然我们实际的项目是没有这么简单的,而且很多是Web (SpringBoot)项目,使用Maven管理,这种直接引入对我们的管理十分不方便。
对于jar包部分,我们可以使用如下配置引入:
1 | <!-- https://mvnrepository.com/artifact/org.bytedeco/opencv --> |
对于dll文件,我们有三种引入方式:
按照上面直接添加lib到项目里
这种虽然可以正常使用,但是在Maven打包项目部署到服务器运行后仍会遇到连接不到的问题。
添加VM Options
我们可以将dll所在路径添加到VM Options里,
-Djava.library.path=D:/opencv/build/java/x64
。使用程序在项目启动时加入
理论上JVM启动后就不能在向其中加入参数了,但我们可以利用反射机制,在JVM启动时将
java.library.path
参数添加进去,代码如下: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/**
* 追加VM Options参数
* java.library.path
* 需要在启动后直接调用
* @param libraryPath
* @throws Exception
*/
public static void addLibraryDir(String libraryPath) throws Exception {
Field userPathsField = ClassLoader.class.getDeclaredField("usr_paths");
userPathsField.setAccessible(true);
String[] paths = (String[]) userPathsField.get(null);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < paths.length; i++) {
if (libraryPath.equals(paths[i])) {
continue;
}
sb.append(paths[i]).append(';');
}
sb.append(libraryPath);
System.setProperty("java.library.path", sb.toString());
//系统变量设置为空,JVM会重新加载 sys_paths 和 usr_paths
final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
sysPathsField.setAccessible(true);
sysPathsField.set(null, null);
}这个方法在项目启动时调用即可。
这个方法在JDK9 及以上版本会出现警告,因为我们这样操作是不安全的,也是不建议的,未来JDK所有非法访问操作将被拒绝。
我们通常也建议使用第二种方法引入OpenCV动态链接库dll文件。
PS:有时候我们环境搭建好了,可能仍无法使用,出现动态库dll找不到的情况,如下:
这时候需要注意Core.NATIVE_LIBRARY_NAME
这段代码,我们点进去,可以看到它使用的 LIBRARY_NAME 为 opencv_java410
,加载不到的原因是我们安装的是 OpenCV 4.1.1 版本,这时候我们使用System.loadLibrary("opencv_java411");
即可加载成功,出现这种情况的原因是我们上面Maven下载的jar包是 4.1.0 版本的(这也是目前Maven上的最高版本)。所以为防止出现问题,需要知道自己安装了什么版本的OpenCV,并建议设置为常量,方便引入。
Mac环境下的OpenCV环境搭建
OpenCV未提供Mac版本的安装程序,我们通常有以下几种安装方式:
使用Homebrew进行安装
这是获取OpenCV最快的方式,安装Homebrew后,要检查Mac系统是否安装了 XCode Command Line Tools。
在Terminal下执行如下命令
xcode-select --install
,如果系统要求安装此工具,则进行安装即可。上述步骤完成后,我们可以直接使用
brew install opencv
安装OpenCV,安装好后的文件位于/usr/local/Cellar/opencv4
文件夹下。我们使用此种方式安装,可以发现得到的OpenCV安装文件里是不包含支持Java接口部分的编译文件的。
这对我们来说十分不友好。
使用Homebrew下载源码自动编译安装
对于OpenCV,Homebrew也是可以在线自动下载源码到本地并自动进行编译安装的。
在安装Homebrew和 XCode Command Line Tools 后,使用Homebrew安装cmake工具
brew install cmake
,这是OpenCV编译需要用到的工具。同时我们需要安装Apache Ant工具,
brew install ant
,因为OpenCV编译生成Java接口文件需要用到。然后我们使用指令
brew edit opencv
,可以打开查看opencv的编译项。如下图:然后找到编译项里的
-DBUILD_opencv_java=OFF
,将其改为-DBUILD_opencv_java=ON
,然后保存配置。由上面的操作可以看到 Homebrew 的 OpenCV编译默认是不包含Java相关支持的。
我们使用
brew install --build-from-source opencv
下载源码到本地自动进行编译安装。PS:我按照上述操作后确实会从GitHub上自动下载最新版源码并进行编译,但是在编译过程中遇到了一个”has no symbol”的错误。导致始终无法make成功,也未找到相关解决办法,于是放弃了该种安装办法。
错误详情如下:
1
Linking CXX static library ../../lib/libopencv_core.a /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(hal_internal.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_clamdblas.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_clamdfft.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_core.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(hal_internal.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_clamdblas.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_clamdfft.cpp.o) has no symbols /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ranlib: file: ../../lib/libopencv_core.a(opencl_core.cpp.o) has no symbols
我在OpenCV官网上也看到别人遇到此种情况,尚未有回复。
https://answers.opencv.org/question/104758/has-no-symbol-error-on-mac/
手动编译OpenCV
我们使用OpenCV的源码来编译OpenCV,同样我们需要cmake工具,使用
brew install cmake
进行安装。安装Apache Ant工具,
brew install ant
。从OpenCV官网上下载 OpenCV的源码 Sources,我下载的是opencv-4.1.1.zip。
我们将它解压,得到源码文件。
进入到opencv源码目录
cd /Users/xxx/Desktop/opencv-4.1.1
。我们在该目录下创建一个build文件夹用于存放编译后的文件
mkdir build
。进入到build目录
cd build
。在此目录下,我们配置cmake的编译参数
cmake -DBUILD_SHARED_LIBS=OFF -D CMAKE_INSTALL_PREFIX=/Users/xxx/Applications/opencv-4.1.1 ..
PS:
CMAKE_INSTALL_PREFIX 指的是编译完成后安装的路径前缀,我们会把OpenCV安装到此目录下。
-DBUILD_SHARED_LIBS=OFF 指的是OpenCV作为一组静态库构建,不去动态依赖其它库,而是包含全部代码的编译。
我们可以使用
brew info opencv
查看OpenCV的依赖库,如果-DBUILD_SHARED_LIBS=ON 则OpenCV会依赖已经存在的库来进行编译,可能会产生问题。构建完成后我们开始进行安装,使用
make -j6
来进行任务。PS:-j6 指的是并行6个任务来进行构建,当然也可以 -j5 五个并行任务等等。
检查cmake的输出并确保java是“待构建”模块之一。如果不是的话,很可能你缺少了一个依赖关系您应该通过查看cmake输出中未找到的与java相关的工具并安装它们来进行故障排除。
完成后,我们使用
sudo make install
完成最后的安装任务。这样,在我们上面提到的安装目录里,就会找到OpenCV的安装文件,当然,也能找到Java接口部分的文件。
位于
${CMAKE_INSTALL_PREFIX} /share/java/opencv4
文件夹下,如图:
Linux环境下的OpenCV环境搭建
Linux环境下不再过多叙述,有需要的可以查看如下相关文章。
文章内也包含Windows和Mac的安装教程。
OpenCV的使用
这儿我们先来了解下OpenCV的简单使用,后面在深入了解。
基础使用
先来了解下OpenCV的基本类Mat
,它是一个图像的数据格式矩阵。用来存储图像的数据结构。
我们知道图像是由R (Red)、 G (Green) 、 B (Blue) 三原色构成,那Mat
存储这三原色数据,会是一个三维数组?
其实不是的,Mat
中是使用二维数组存储图像数据的,如何存储呢?如下图:
可以看到Mat
彩色图像的存储形式是三列当做一列,由 BGR 三个通道,存储在一个平面内,这儿彩色图的一个像素会占用3个字节。
对于灰度图,由于没有颜色要求,因此Mat的存储灰度图的格式和彩色图略有些不同,如下图:
可以看到灰度图的一个像素在Mat中会占用1个字节。
我们使用OpenCV里的 Imgcodecs
读取一张红色图片转为Mat
,然后进行输出,如下:
1 | public static void main(String[] args) throws Exception{ |
我们dump后可以看到Mat
的数据内容。
Scalar
是OpenCV里的可认为是颜色的向量类,它最多可以有四个入参。
1 | Scalar scalar = new Scalar(255,0,0,0); |
它的参数分别表示该颜色向量的 B G R 和 透明度,上面我们就创建了一个纯蓝色透明度为0(不透明)的颜色向量。
我们使用setTo
方法,可以将原来Mat
图的红色全部替换为蓝色,如下:
1 | public static void main(String[] args) throws Exception{ |
我们会生成一张蓝色图片。
人脸检测
CascadeClassifier
级联分类器是OpenCV里进行图片对象识别的检测器。
一个分类器的生成: 用一个对象的几百个样本(或者更多)作为正面例子,需要将它们缩放到相同大小;负面例子样本可用任意多张(非正面例子)和正面例子相同大小的图片即可。用它们完成分类器的训练。
而对于级联分类器,则是由若干个分类器组成,它们占用不同的权重组成,比如人脸级联分类器,要检测人脸,则需要有眼睛部分,则眼睛分类器可以作为人脸级联分类器的一部分。
我们训练特定样本后,可以得到一个分类器文件(xml),加载这个xml文件后,我们可以对一些测试样本进行检测,看它是否属于目标样本。
关于训练的内容我们后面在聊,现在OpenCV内置了一些分类器文件例子,我们来看下,如下图:
可以看到 opencv\build\etc
文件夹下有haarcascades
和lbpcascades
两种模式的分类器,我们以Haar分类器来看。
可以看到它下面的一些分类器文件demo,根据英文名称还是比较好确认它们是对于哪种类型图片进行识别的。
根据以上,我们来看一个寻找图片人脸并进行裁剪的例子。
1 |
|
我们寻找一张照片(包含人脸),可以看到成功生成人脸图。
其检测裁剪逻辑主要如下:
- 级联分类器加载样本分类器文件
haarcascade_frontalface_alt.xml
,检测该文件的正确性!faceDetector.empty()
; - 将待检测图片读为
Mat
,Imgcodecs.imread
; - 进行人脸检测
faceDetector.detectMultiScale
,该方法除了传入Mat
外,还要传入一个MatOfRect
,它是一个Rect
数组,用来存放识别返回的”感兴趣”的区域(人脸区域); - “感兴趣”的区域可以有多个,如果一个也没有,我们就可以认为这张待检测图片上不存在人脸;
- 如果有的话,我们可以根据矩形区域裁剪得到人脸。
关于这部分我们先介绍到这儿,有兴趣的可以了解下例子文件里的其他级联分类器。
总结
上面我们主要说了OpenCV的安装、开发环境的配置等内容。
对于OpenCV的安装,我们根据一些文档手册,安装还是比较简单的;开发环境的配置方面,由于OpenCV主要以C++编写、开发和使用,因此应用在Java上还是不那么方便的(需要加载原生Lib库)。
对于OpenCV的使用,这儿说的比较少,只是简单的介绍了它的一些使用,其原理也未作深入分析。
后面我们继续来看下OpenCV的一些有意思的地方(图片训练、图片识别等)。