OpenCV  4.10.0
开源计算机视觉
正在加载...
正在搜索...
无匹配项
Clojure 版 OpenCV 开发简介

前序教程: 在 Eclipse 中使用 OpenCV Java
后续教程: Android 开发简介

原作者Mimmo Cosenza
兼容性OpenCV >= 3.0
警告
本教程可能包含过时的信息。

自 OpenCV 2.4.4 起,OpenCV 使用与 Android 开发几乎相同的界面支持桌面 Java 开发。

Clojure 是 Java 虚拟机托管的当代 LISP 方言,它提供与底层 JVM 的完全互操作性。这意味着我们甚至应该能够将 Clojure REPL(读评估打印循环)用作底层 OpenCV 引擎的交互式可编程界面。

在本教程中我们将做什么

本教程将帮助您设置一个基本的 Clojure 环境,以便在可完全编程的 CLojure REPL 中交互式学习 OpenCV。

教程源代码

您可以在 OpenCV 存储库的 samples/java/clojure/simple-sample 文件夹中找到样本的可运行源代码。安装 OpenCV 和 Clojure(如教程中所述)后,发布以下命令以从命令行运行样本。

cd path/to/samples/java/clojure/simple-sample
lein run

引言

有关在桌面 Java 中安装支持 OpenCV 的详细说明,请参见 相应教程

如果赶时间,以下是 Mac OS X 中安装 OpenCV 的最小快速入门指南

注意
我假设您已安装 xcodejdkCmake
cd ~/
mkdir opt
git clone https://github.com/opencv/opencv.git
cd opencv
git checkout 2.4
mkdir build
cd build
cmake -DBUILD_SHARED_LIBS=OFF ..
...
...
make -j8
# 可选
# make install

安装 Leiningen

一旦使用桌面 Java 安装 OpenCV,您还需要安装 Leiningeng,以便管理 CLJ 项目的整个生命周期。

可用的 安装指南 非常易于遵循

  1. 下载脚本
  2. 将其放在你的 $PATH(如果它在你的路径中,那么 cf./bin 是一个好的选择。)下
  3. 设置脚本为可执行。(即 chmod 755/bin/lein)。

如果你在 Windows 上工作,请遵循 此说明

你现在有了 OpenCV 库和一个完全安装好的 Clojure 基础环境。现在需要做的是配置 Clojure 环境以与 OpenCV 库交互。

安装 localrepo Leiningen 插件

Leiningen 原生支持的一组命令(在 Leiningen 语法中称为任务)可以通过各种插件非常轻松地扩展。其中之一是 lein-localrepo 插件,它允许将任何 jar 库作为伪像安装到你的机器的本地 maven 存储库中(通常在你的用户名 /.m2/repository 目录中)。

我们将使用此 lein 插件将 Java 和 Clojure 使用 opencv 库所需的 opencv 组件添加到本地 maven 存储库中。

一般而言,如果你仅要在项目基础上使用一个插件,那么可以将其直接添加到由 lein 创建的 CLJ 项目中。

相反,当你希望某个插件对你的用户名空间中的任何 CLJ 项目都可用时,你可以将其添加到 /.lein/ 目录中的 profiles.clj 中。

lein-localrepo 插件对我来说在需要调用由 Java 接口封装的本地库的其他 CLJ 项目中很有用。所以我决定让任何 CLJ 项目都可以使用它

mkdir ~/.lein

在 /.lein 目录中创建一个名为 profiles.clj 的文件,并复制以下内容

{:user {:plugins [[lein-localrepo "0.5.2"]]}}

在这里我们说 lein-localrepo 插件的版本 "0.5.2" 将对 lein 创建的任何 CLJ 项目的 :user 配置文件可用。

你不需要做任何其他事情来安装插件,因为它将在你首次执行任何 lein 任务时自动从远程存储库下载。

将特定的 Java 库安装为本地存储库

如果你按照在你的计算机上安装 OpenCV 的标准文档进行操作,你应该在构建 OpenCV 的目录中找到以下两个库

  • build/bin/opencv-247.jar Java 库
  • build/lib/libopencv_java247.dylib 本机库(或者在你使用 GNU/Linux 操作系统构建 OpenCV 时为 .so)

这是 JVM 与 OpenCV 交互所需的唯一 OpenCV 库。

拆分所需的 opencv 库

创建一个新目录来存储上述两个库。首先将 opencv-247.jar 库复制到其中。

cd ~/opt
mkdir clj-opencv
cd clj-opencv
cp ~/opt/opencv/build/bin/opencv-247.jar .

第一个库已完成。

现在,为了能够将 libopencv_java247.dylib 共享本机库添加到本地 maven 存储库,我们首先需要将其打包为 jar 文件。

本机库必须复制到一个目录布局中,它的名称模仿了你的操作系统和架构。我正在使用具有 X86 64 位架构的 Mac OS X。因此,我的布局如下

mkdir -p native/macosx/x86_64

将 libopencv_java247.dylib 库复制到 x86_64 目录中。

cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/

如果你从不同的 OS/架构对运行 OpenCV,这里有一个你可以选择的映射总结。

OS
Mac OS X -> macosx
Windows -> windows
Linux -> linux
SunOS -> solaris
架构
amd64 -> x86_64
x86_64 -> x86_64
x86 -> x86
i386 -> x86
arm -> arm
sparc -> sparc

将本机库打包为 jar

接下来,你需要使用 jar 命令从目录中创建一个新 jar 文件,将本机库打包到 jar 文件中。

jar -cMf opencv-native-247.jar native

请注意,ehe M 选项指示 jar 命令不要为构件创建一个 MANIFEST 文件。

你的目录布局应如下所示

tree
.
|__ native
| |__ macosx
| |__ x86_64
| |__ libopencv_java247.dylib
|
|__ opencv-247.jar
|__ opencv-native-247.jar
3 个目录,3 个文件

本地安装 jar

我们现在可以借助 lein-localrepo 插件将两个 jar 作为构件添加到本地 maven 知识库中。

lein localrepo install opencv-247.jar opencv/opencv 2.4.7

在这里,localrepo install 任务从 opencv-247.jar 库中创建 opencv/opencv maven 构件的 2.4.7. 发行版,然后将其安装到本地 maven 知识库中。然后,opencv/opencv 构件将可供任何 maven 兼容项目使用(Leiningen 在内部基于 maven)。

对先前包装在新 jar 文件中的本机库执行相同操作。

lein localrepo install opencv-native-247.jar opencv/opencv-native 2.4.7

请注意,两个构件的 groupId 为 opencv,相同。现在,我们可以创建一个新的 CLJ 项目来开始与 OpenCV 交互。

创建一个项目

使用终端中的 lein new 任务创建一个新的 CLJ 项目。

# cd 进入你处理开发项目的目录(例如 ~/devel)
lein new simple-sample
根据“default”模板生成名为 simple-sample 的项目。
要查看其他模板(app、lein 插件等),请尝试“lein help new”。

上述任务创建了以下 simple-sample 目录布局

tree simple-sample/
simple-sample/
|__ LICENSE
|__ README.md
|__ doc
| |__ intro.md
|
|__ project.clj
|__ resources
|__ src
| |__ simple_sample
| |__ core.clj
|__ test
|__ simple_sample
|__ core_test.clj
6 个目录,6 个文件

我们需要将这两个 opencv 工件作为新创建项目的依赖项添加。打开 project.clj 并按如下所示修改其依赖项部分

(defproject simple-sample "0.1.0-SNAPSHOT"
description "FIXME: 写入描述"
url "http://example.com/FIXME"
license {:name "Eclipse 公共许可证"
url "http://www.eclipse.org/legal/epl-v10.html"}
dependencies [[org.clojure/clojure "1.5.1"]
[opencv/opencv "2.4.7"] ; 添加的行
[opencv/opencv-native "2.4.7"]]) ; 添加的行

请注意 Clojure 编程语言同样是一个 jar 工件。这就是 Clojure 被称为托管语言的原因。

要验证一切无误,请执行 lein deps 任务。您第一次运行 lein 任务时,它需要一些时间才能下载所需的所有依赖项,然后再执行该任务本身。

cd simple-sample
lein deps
...

deps 任务从 project.clj 和 / .lein / profiles.clj 文件读取并合并 simple-sample 项目的所有依赖项,并验证本地 maven 存储库中是否已缓存这些依赖项。如果任务返回的内容中没有无法检索两个新工件的信息,则表示您的安装正确,否则请返回并仔细检查您是否做好了所有操作。

使用 OpenCV 命令行

现在,请在 simple-sample 目录中使用 cd,然后执行下面的 lein 任务

cd simple-sample
lein repl
...
...
nREPL 服务器已在主机 127.0.0.1 上的端口 50907 启动
REPL-y 0.3.0
Clojure 1.5.1
文档: (doc function-name-here)
(find-doc "part-of-name-here")
来源: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
退出:Control+D 或 (exit) 或 (quit)
结果:存储在变量 *1、*2、*3 中,异常存储在 *e 中
user=>

您可以通过发出要评估的任何 CLJ 表达式来立即与 REPL 进行交互。

user=> (+ 41 1)
42
user=> (println "Hello, OpenCV!")
Hello, OpenCV!
nil
user=> (defn foo [] (str "bar"))
#'user/foo
user=> (foo)
"bar"

当从基于 lein 的项目的 home 目录运行时,即使 lein repl 任务会自动加载所有项目依赖项,您仍然需要加载 opencv 本机库才能与 OpenCV 进行交互。

user=> (clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)
nil

然后,您可以仅通过引用其类的完全限定名称来开始与 OpenCV 交互。

注意
此处您可以找到完整的 OpenCV Java API。
user=> (org.opencv.core.Point. 0 0)
#<Point {0.0, 0.0}>

在这里,我们创建了一个二维 OpenCV Point 实例。即使所有包含在opencv Java 接口中的 Java 包均可从 CLJ REPL 立即获得,但用完全限定的包名称为 Point 预先指定实例构造函数非常烦人。

幸运的是,CLJ 提供了一种非常简单的方法来直接导入 Point 类来克服这个烦恼。

user=> (import 'org.opencv.core.Point)
org.opencv.core.Point
user=> (def p1 (Point. 0 0))
#'user/p1
user=> p1
#<Point {0.0, 0.0}>
user=> (def p2 (Point. 100 100))
#'user/p2

我们甚至可以检查一个实例的类,并验证一个符号的值是否属于 Point Java 类的实例。

user=> (class p1)
org.opencv.core.Point
user=> (instance? org.opencv.core.Point p1)
true

如果我们现在想使用 opencv Rect 类来创建一个矩形,即使它位于 Point 类的同一个 org.opencv.core 包中,我们也必须再次完全限定其构造函数。

user=> (org.opencv.core.Rect. p1 p2)
#<Rect {0, 0, 100x100}>

同样,CLJ 导入功能非常方便,它允许你在一次映射更多符号。

user=> (import '[org.opencv.core Point Rect Size])
org.opencv.core.Size
user=> (def r1 (Rect. p1 p2))
#'user/r1
user=> r1
#<Rect {0, 0, 100x100}>
user=> (class r1)
org.opencv.core.Rect
user=> (instance? org.opencv.core.Rect r1)
true
user=> (Size. 100 100)
#<Size 100x100>
user=> (def sq-100 (Size. 100 100))
#'user/sq-100
user=> (class sq-100)
org.opencv.core.Size
user=> (instance? org.opencv.core.Size sq-100)
true

显然你也可以调用实例中的方法。

user=> (.area r1)
10000.0
user=> (.area sq-100)
10000.0

或者修改成员字段的值。

user=> (set! (.x p1) 10)
10
user=> p1
#<Point {10.0, 0.0}>
user=> (set! (.width sq-100) 10)
10
user=> (set! (.height sq-100) 10)
10
user=> (.area sq-100)
100.0

如果你发现自己无法记住 OpenCV 类行为,REPL 让你有机会轻松搜索相应的 javadoc 文档

user=> (javadoc Rect)
"http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html"

在 REPL 中模仿 OpenCV Java 教程示例

现在让我们尝试在 Clojure 中移植OpenCV Java 教程示例。与其在源文件中编写它,不如在 REPL 中对其进行评估。

以下是引用的示例的原始 Java 源代码。

import org.opencv.core.Mat;
import org.opencv.core.CvType;
import org.opencv.core.Scalar;
class SimpleSample {
static{ System.loadLibrary("opencv_java244"); }
public static void main(String[] args) {
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));
System.out.println("OpenCV Mat data:\n" + m.dump());
}
}
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3

将注入添加到项目

在开始编码之前,我们希望消除无聊的需要:在任何时候启动一个新的 REPL 以便与本机 opencv 库交互时,都要交互式地加载本机 opencv 库。

首先,通过在 REPL 提示符处计算 (exit) 表达式来停止 REPL。

user=> (exit)
现在再见!

然后打开你的 project.clj 文件并如下编辑

(defproject simple-sample "0.1.0-SNAPSHOT"
...
injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)])

这里我们说,在任何时候以这种方式运行 REPL 时,加载本机 opencv 库,这样我们不再需要记住手动执行该操作。

重新运行 lein repl 任务

lein repl
在主机 127.0.0.1 上的端口 51645 启动 nREPL 服务器
REPL-y 0.3.0
Clojure 1.5.1
文档: (doc function-name-here)
(find-doc "part-of-name-here")
来源: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
退出:Control+D 或 (exit) 或 (quit)
结果:存储在变量 *1、*2、*3 中,异常存储在 *e 中
user=>

导入所需的 OpenCV java 接口。

user=> (import '[org.opencv.core Mat CvType Scalar])
org.opencv.core.Scalar

我们将几乎逐字逐句地模仿原始的 OpenCV java 教程,以

  • 创建一个元素全部初始化为 0 的 5x10 矩阵
  • 将第二行的每个元素的值更改为 1
  • 将第 6 列的每个元素的值更改为 5
  • 打印获得的矩阵的内容
user=> (def m (Mat. 5 10 CvType/CV_8UC1 (Scalar. 0 0)))
#'user/m
user=> (def mr1 (.row m 1))
#'user/mr1
user=> (.setTo mr1 (Scalar. 1 0))
#<Mat Mat [ 1*10*CV_8UC1, isCont=true, isSubmat=true, nativeObj=0x7fc9dac49880, dataAddr=0x7fc9d9c98d5a ]>
user=> (def mc5 (.col m 5))
#'user/mc5
user=> (.setTo mc5 (Scalar. 5 0))
#<Mat Mat [ 5*1*CV_8UC1, isCont=false, isSubmat=true, nativeObj=0x7fc9d9c995a0, dataAddr=0x7fc9d9c98d55 ]>
user=> (println (.dump m))
[0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
1, 1, 1, 1, 1, 5, 1, 1, 1, 1;
0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
0, 0, 0, 0, 0, 5, 0, 0, 0, 0]
nil

如果你习惯了函数式语言,所有那些被滥用和变异的名词都会令你喜爱的动词变得焦躁。即使 CLJ 交互操作语法非常方便且完整,任何面向对象语言和任何 FP 语言之间仍然存在阻抗失配(Scala 是混合范例的编程语言)。

要退出 REPL,在 REPL 提示符处输入 (exit)、ctr-D 或 (quit)。

user=> (exit)
现在再见!

交互式加载和模糊图像

在下一个示例中,你将学习如何使用以下 OpenCV 方法,从 REPL 交互式加载和模糊图像

  • Highgui 类中的 imread 静态方法,用于从文件中读取图像
  • Highgui 类的 imwrite 静态方法,用于将图像写入文件
  • Imgproc 类的 GaussianBlur 静态方法,用于向原始图像应用模糊

我们还将使用 imread 方法返回并被 GaussianBlur 和 imwrite 方法接受为主要参数的 Mat 类。

将图像添加到项目

首先,我们要将图像文件添加到新创建的目录中,以存储项目的静态资源。

mkdir -p resources/images
cp ~/opt/opencv/doc/tutorials/introduction/desktop_java/images/lena.png resource/images/

读取图像

现在像往常一样启动 REPL,并首先导入我们要使用的所有 OpenCV 类

lein repl
nREPL 服务器在主机 127.0.0.1 上的 50624 端口启动
REPL-y 0.3.0
Clojure 1.5.1
文档: (doc function-name-here)
(find-doc "part-of-name-here")
来源: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
退出:Control+D 或 (exit) 或 (quit)
结果:存储在变量 *1、*2、*3 中,异常存储在 *e 中
user=> (import '[org.opencv.core Mat Size CvType]
'[org.opencv.imgcodecs Imgcodecs]
'[org.opencv.imgproc Imgproc])
org.opencv.imgproc.Imgproc

现在从 resources/images/lena.png 文件中读取图像。

user=> (def lena (Highgui/imread "resources/images/lena.png"))
#'user/lena
user=> lena
#<Mat Mat [ 512*512*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x7f9ab3054c40, dataAddr=0x19fea9010 ]>

正如你所见,通过简单地计算 lena 符号,我们知道 lena.png 是一个 CV_8UC3 元素类型的 512x512 矩阵。让我们创建一个具有相同维度和元素类型的 Mat 新实例。

user=> (def blurred (Mat. 512 512 CvType/CV_8UC3))
#'user/blurred
user=>

现在使用 lena 作为源矩阵和 blurred 作为目标矩阵应用 GaussianBlur 滤镜。

user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3)
nil

最后一步,只要将模糊矩阵保存在一个新图像文件中。

user=> (Highgui/imwrite "resources/images/blurred.png" blurred)
true
user=> (exit)
现在再见!

以下是模糊的 Lena 新图像。

后续步骤

本教程仅介绍了非常基本的环境设置,以便能够在 CLJ REPL 中与 OpenCV 进行交互。

我建议任何 Clojure 新手阅读Clojure Java 互操作章节,以了解与任何普通的 Java 库进行互操作所需的所有知识。这些库尚未在 Clojure 中封装,以使其能够以更习惯的 Clojure 函数式方式在 Clojure 中使用。

OpenCV Java API 不封装依赖于 Qt 的 highgui 模块功能(例如 namedWindow 和 imshow)。如果你想在从 REPL 与 OpenCV 进行交互时创建窗口并在其中显示图像,那么目前你只能自己完成了。你可以使用 Java Swing 来填补空白。

许可证

版权所有 © 2013 Giacomo (Mimmo) Cosenza 又名 Magomimmo

根据 BSD 3 条款许可证分发。