使用 Clojure 进行 OpenCV 开发简介

  返回:OpenCV系列文章目录(持续更新中......)

上一篇:如何将OpenCV Java 与Eclipse结合使用

下一篇: 

警告
本教程可以包含过时的信息。

从 OpenCV 2.4.4 开始,OpenCV 支持使用与 Android 开发几乎相同的界面进行桌面 Java 开发

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

我们将在本教程中执行的操作

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

教程源代码

您可以在 OpenCV 存储库的文件夹中找到该示例的可运行源代码。按照教程中的说明安装 OpenCV 和 Clojure 后,发出以下命令以从命令行运行示例。

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

序言

有关安装具有桌面 Java 支持的 OpenCV 的详细说明,请参阅相应的教程”。

如果您赶时间,以下是在 Mac OS X 上安装 OpenCV 的最低快速入门指南:

注意

我假设你已经安装了 xcode、jdk 和 Cmake。

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
# optional
# make install

安装 Leiningen

一旦你安装了支持桌面 java 的 OpenCV,唯一的其他要求就是安装 Leiningeng,它允许你管理 CLJ 项目的整个生命周期。

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

  1. 下载脚本 script
  2. 建议放在你系统路径目录下的cf./bin 比较好
  3. 将脚本设置为可执行。(i.e. chmod 755/bin/lein).

如果是 Windows系统 请参照: this instruction

说明操作

现在,您已经拥有了 OpenCV 库和完全安装的基本 Clojure 环境。现在需要的是配置 Clojure 环境以与 OpenCV 库进行交互。

安装 localrepo Leiningen 插件

Leiningen 原生支持的命令集(莱宁根术语中的任务)可以很容易地通过各种插件进行扩展。其中之一是 lein-localrepo 插件,它允许将任何 jar lib 作为工件安装在您机器的本地 maven 存储库中(通常位于用户名的 /.m2/repository 目录中)。

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

一般来说,如果你只想在项目基础上使用一个插件,可以直接将其添加到lein创建的CLJ项目中。

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

ein-localrepo 插件在其他 CLJ 项目中对我有用,在这些项目中,我需要调用由 Java 接口包装的原生库。因此,我决定将其提供给任何 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 的目录下找到以下两个库:

  • the build/bin/opencv-247.jar java lib
  • the build/lib/libopencv_java247.dylib native lib (or .so in you built OpenCV a GNU/Linux OS)

它们是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
Copy into the x86_64 directory the libopencv_java247.dylib lib.
cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/

如果您从不同的操作系统/架构对运行OpenCV,以下是您可以选择的映射摘要。

操作系统

Mac OS X -> macosx

Windows -> windows

Linux -> linux

SunOS -> solaris

CPU架构

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

 注意,eheM选项指示jar命令不为工件创建MANIFEST文件

tree
.
|__ native
| |__ macosx
| |__ x86_64
| |__ libopencv_java247.dylib
|
|__ opencv-247.jar
|__ opencv-native-247.jar
3 directories, 3 files

在本地安装jar

现在,我们准备在lein-localrepo插件的帮助下将这两个jar作为工件添加到本地maven存储库中。

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

在这里,localrepo安装任务创建2.4.7。从opencv-247.jar库中释放opencv/opencvmaven工件,然后将其安装到本地maven存储库中。然后,opencv/opencv工件将可用于任何符合maven的项目(Leiningen内部基于maven)。

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

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

注意:这两个工件的groupIdopencv是相同的。现在,我们准备创建一个新的CLJ项目来开始与OpenCV交互。

创建项目

使用终端中的leinnew任务创建新的CLJ项目。

# cd in the directory where you work with your development projects (e.g. ~/devel)
lein new simple-sample

基于“默认”模板生成一个名为simple-sample的项目。
要查看其他模板(应用程序、lein插件等),请尝试“leinhelpnew”。
上述任务创建以下简单示例目录布局:

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 directories, 6 files

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

(defproject simple-sample "0.1.0-SNAPSHOT"
description "FIXME: write description"
url "http://example.com/FIXME"
license {:name "Eclipse Public License"
url "http://www.eclipse.org/legal/epl-v10.html"}
dependencies [[org.clojure/clojure "1.5.1"]
[opencv/opencv "2.4.7"] ; added line
[opencv/opencv-native "2.4.7"]]) ;added line

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

要验证一切正常,请发出leindeps任务。第一次运行lein任务时,在执行任务本身之前,需要一些时间来下载所有必需的依赖项。

cd simple-sample
lein deps
...

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

使用OpenCV进行REPLing

现在cd在simple-sample目录中并发出以下lein任务:

cd simple-sample
lein repl
...
...

nREPL服务器在主机127.0.0.1上的端口50907上启动

REPL-y 0.3.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *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的项目的主目录运行时,即使leinrepl任务自动加载所有项目依赖项,您仍然需要加载opencv原生库才能与OpenCV进行交互。

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

然后,您只需引用其类的完全限定名称即可开始与OpenCV交互。

注意
在这里,您可以找到完整的OpenCVJavaAPI。

user=> (org.opencv.core.Point. 0 0)
#<Point {0.0, 0.0}>

在这里,我们创建了一个二维opencvPoint实例。即使OpenCV的java接口中包含的所有java包都可以立即从CLJREPL获得,但作为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

我们甚至可以检查实例的类,并验证符号的值是否是Pointjava类的实例。

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

如果我们现在想使用opencvRect类来创建一个矩形,我们再次必须完全限定它的构造函数,即使它留在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
Or modify the value of a member field.
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中模拟OpenCVJava教程示例

现在,让我们尝试将OpenCVJava教程示例移植到Clojure。我们不是将其写入源文件,而是在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());
}
}

​

向项目添加注入

在开始编码之前,我们希望消除在任何时候启动新的REPL以交互方式加载原生opencv库以交互方式与之交互的无聊需求。

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

user=> (exit)

Bye for now!

然后打开project.clj文件并对其进行编辑,如下所示:

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

在这里,我们说的是,每当我们运行REPL时,都要加载opencv原生库,这样我们就不必再记住手动执行此操作了。

重新运行leinrepl任务

lein repl
nREPL server started on port 51645 on host 127.0.0.1
REPL-y 0.3.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=>
Import the interested OpenCV java interfaces.
user=> (import '[org.opencv.core Mat CvType Scalar])

nREPL服务器在主机127.0.0.1上的端口51645上启动

REPL-y 0.3.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=>
Import the interested OpenCV java interfaces.
user=> (import '[org.opencv.core Mat CvType Scalar])
org.opencv.core.Scalar

我们将几乎逐字逐句地模仿原始的OpenCVjava教程:

创建一个5x10矩阵,其所有元素初始化为0
将第二行的每个元素的值更改为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互操作语法非常方便和完整,任何OOP语言和任何FP语言(beinScala是一种混合范式编程语言)之间仍然存在阻抗不匹配。

要退出REPL,请在REPL提示符下键入(exit)、ctr-D或(quit)

user=> (exit)
Bye for now!

以交互方式加载和模糊图像

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

Highgui类中的imreadstatic方法,用于从文件中读取图像
Highgui类中的imwritestatic方法,用于将图像写入文件
Imgproc类中的GaussianBlur静态方法,用于模糊原始图像

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

将图像添加到项目

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

lena.png

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

阅读图片

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

lein repl
nREPL server started on port 50624 on host 127.0.0.1
REPL-y 0.3.0
Clojure 1.5.1
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (import '[org.opencv.core Mat Size CvType]
'[org.opencv.imgcodecs Imgcodecs]
'[org.opencv.imgproc Imgproc])
org.opencv.imgproc.Imgproc
Now read the image from the resources/images/lena.png file.
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是512x512的CV_8UC3元素类型的矩阵。让我们创建一个具有相同维度和元素类型的新Mat实例。

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

现在应用一个GaussianBlur滤镜,使用lena作为源矩阵,模糊作为目标矩阵。

user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3)
nil
As a last step just save the blurred matrix in a new image file.
user=> (Highgui/imwrite "resources/images/blurred.png" blurred)
true
user=> (exit)
Bye for now!

以下是莉娜的新模糊图像。

blurred.png

后续步骤

本教程仅介绍为能够在CLJREPL中与OpenCV交互而设置的最基本环境。

我建议任何Clojure新手阅读ClojureJavaInterop一章,了解与任何未包含在Clojure中的普通Java库进行互操作所需的所有知识,以使其在Clojure中以更惯用和更实用的方式使用。

OpenCVJavaAPI不会根据Qt包装highgui模块功能(例如namedWindow和imshow.如果您想在从REPL与OpenCV交互时创建窗口并在其中显示图像,那么目前您只能自己动手了。您可以使用JavaSwing来填补空白。

许可证:

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

根据BSD3条款许可证分发。


参考文献:

1、《Introduction to OpenCV Development with Clojure》Mimmo Cosenza

相关推荐

  1. 使用OpenCV进行简单图像分割的3个步骤

    2024-03-20 06:44:04       13 阅读
  2. OpenCV 使用方形棋盘进行相机校准

    2024-03-20 06:44:04       30 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-20 06:44:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-20 06:44:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-20 06:44:04       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-20 06:44:04       20 阅读

热门阅读

  1. 创建React+Ts项目

    2024-03-20 06:44:04       16 阅读
  2. vue 重新渲染dom

    2024-03-20 06:44:04       19 阅读
  3. 诗意人生三层: 爱层、富足金字塔层、苟且层

    2024-03-20 06:44:04       12 阅读
  4. python 计算两个字符串最长子串超级加速版

    2024-03-20 06:44:04       23 阅读
  5. 10 Internet基本服务(3)

    2024-03-20 06:44:04       14 阅读
  6. 低代码开发与数据可视化

    2024-03-20 06:44:04       19 阅读
  7. 为什么要减少Http的请求以及如何减少Http请求

    2024-03-20 06:44:04       22 阅读