文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

从OpenCV 2.4.4开始,OpenCV支持Java桌面开发,其开发界面与Android开发几乎相同。

Clojure是由Java虚拟机托管的一种现代LISP方言,它提供与底层JVM的完全互操作性。 这意味着我们能够使用Clojure REPL(Read Eval Print Loop)作为底层OpenCV引擎的交互式可编程接口。

我们将在本教程中做什么

本教程将帮助您使用完全可编程的CLojure REPL语言,为交互式学习OpenCV设置一个基本的Clojure环境。

第 1 段(可获 2 积分)

教程源码

你可以在OpenCV资源库samples/java/clojure/simple-sample目录找到一个可运行的示例源码. 根据教程安装 OpenCV 和 Clojure后, 在终端执行下面的命令运行示例.

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

序言

关于安装支持java桌面OpenCV的详细说明参考相应的教程

如果你时间紧迫, 这里有一个小的手册,可以快速的在Mac OS X上安装OpenCV的:

注 1: 我假设你已经安装

第 2 段(可获 2 积分)

xcodejdk 和 Cmake.

cd ~/
mkdir opt
git clone https://github.com/Itseez/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. 下载脚本
  2. 把它放在你的 $PATH (如果它在你home目录内, ~/bin 是一个不错的选择)
  3. 为脚本添加执行权限. (如. chmod 755 ~/bin/lein).
第 3 段(可获 2 积分)

如果你使用的是Windows, 可以按照 这个指导

现在你既有OpenCV库也有安装完整的Clojure环境.现在你需要配置与OpenCV库交互的Clojure环境.

安装 localrepo Leiningen 插件

Leiningen支持的原生命令集 (Leiningen parlance中的任务),可以很容易地通过各种插件扩展. 其中一个是 lein-localrepo 插件,它允许你将任何jar作为构件安装到你机器上的maven仓库 (通常是你用户名下的 ~/.m2/repository 目录).

第 4 段(可获 2 积分)

我将使用lein插件将Java和Clojure需要使用的opencv库作为opencv组件安装到maven仓库.

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

相反,如果你想让工作空间内的任何CLJ项目都可以使用一个插件,你可以将它添加到 ~/.lein/目录下的profiles.clj文件内.

 lein-localrepo插件对我的其它CLJ项目来说非常有用,因为我需要通过包装过的Java接口调用本地库.因此我将让它对任何CLJ项目都可用.

第 5 段(可获 2 积分)
mkdir ~/.lein

在~/.lein目录里创建profiles.clj文件,将下面的内容复制进去:

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

它的含义是说:用户配置文件中配置的任何由lein创建的CLJ项目都可以使用"0.5.2"版本的lein-localrepo插件.

安装这个插件你不需要再做其它事情,因为当你第一次执行任何lein任务时它将会自动从远端资源库下载.

像安装本地资源库一样安装java特定的库

在你的电脑上安装OpenCV时,如果你遵循了标准文档,你将会在构建OpenCV的目录内发现下面的这两个库:

第 6 段(可获 2 积分)
  • java库 build/bin/opencv-247.jar 
  • 本地库 build/lib/libopencv_java247.dylib  (在GNU/Linux OS上构建的OpenCV是 .so )

它们是与OpenCV交互时JVM需要的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文件.

第 7 段(可获 2 积分)

这个本地库需要根据你的操作系统和系统结构复制到指定名称的目录中. 我用的是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/

如果你运行OpenCV用的是不同的OS/体系架构,你可以参照这个总结的映射.

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
第 8 段(可获 2 积分)

将本地库打成jar包

接下来,你需要在一个目录里使用jar命令创建一个新的jar文件,将本地库打进这个jar文件.

jar -cMf opencv-native-247.jar native

注意这个M参数,指定jar命令创建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仓库.

第 9 段(可获 2 积分)
lein localrepo install opencv-247.jar opencv/opencv 2.4.7

这是localrepo安装任务,根据opencv-247.jar库创建2.4.7版本的opencv/opencv 的maven组件并且安装到本地maven资源库. opencv/opencv 组件将会对任何的maven标准项目可用 (Leiningen 内部基于Maven).

将前面包装生成的新jar,按照上面的再做一遍.

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

请注意groupId, 这两个组件用的是相同的opencv. 现在我们可以创建新的CLJ项目与OpenCV进行交互.

第 10 段(可获 2 积分)

创建一个项目

在终端通过使用lein new任务创建一个新的CLJ项目.

# cd in the directory where you work with your development projects (e.g. ~/devel)
lein new simple-sample
Generating a project called simple-sample based on the 'default' template.
To see other templates (app, lein plugin, etc), try `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 directories, 6 files
第 11 段(可获 2 积分)

我们需要为新创建的项目添加依赖的两个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被称为宿主语言.

第 12 段(可获 2 积分)

执行lein deps任务可以检验所有的是否都正常.第一次执行lein任务时,在执行它自己的任务之前它将花一些时间去下载所有的依赖.

cd simple-sample
lein deps
...

deps任务读取并合并projet.clj和 ~/.lein/profiles.clj 文件内simple-sample项目所有的依赖,并验证这些依赖在maven本地仓库是否已经有缓存.如果任务没有返回关于无法获取两个新组件的消息,那么你的安装就是正确的,否则回去仔细检查你做的一切。

第 13 段(可获 2 积分)

与OpenCV交互执行

现在进入到simple-sample 并执行下面的 lein任务:

cd simple-sample
lein repl
...
...
nREPL server started on port 50907 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=>

你现在可以与REPL交互执行任何 CLJ可计算的表达式.

第 14 段(可获 2 积分)
user=> (+ 41 1)
42
user=> (println "Hello, OpenCV!")
Hello, OpenCV!
nil
user=> (defn foo [] (str "bar"))
#'user/foo
user=> (foo)
"bar"

当在lein创建的项目的home目录内运行时,尽管lein replw任务自动的加载了所有的依赖,你仍然需要加载opencv本地库才可以与OpenCV进行交互.

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

然后你就可以通过引用类的完整名称开始与OpenCV交互。

提示 2:

第 15 段(可获 2 积分)

这里你可以找到OpenCV Java API.

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

这里我们创建了一个opencv的二维点实例.尽管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
第 16 段(可获 2 积分)

我们甚至可以检查实例的类型,并验证它的值是否是Point类的实例.

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

如果现在我们想使用opencv Rect类来创建一个矩形,尽管Point类和它在同一个org.opencv.core包中,但我们必须完全遵循Rect的构造函数.

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
第 17 段(可获 2 积分)

很明显你可以正常的使用实例调用方法.

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

或修改member的属性值.

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"
第 18 段(可获 2 积分)

在REPL中模仿OpenCV的Java教程示例

现在开始通过opencv java教程示例进入到Coljure世界中,我们在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());
  }

}
第 19 段(可获 2 积分)

向项目中添加注入

在开始编码之前,我们希望消除每当我们开始一个新的REPL交互时都需要加载本地opencv lib的烦人要求。

首先,通过在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本地库,这样我们就不用手动更改了。

第 20 段(可获 2 积分)

重新运行lein repl任务

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=>

导入感兴趣的OpenCV java接口。

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

我们几乎在逐字模仿原来的OpenCV java教程:

第 21 段(可获 2 积分)
  • 创建一个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
第 22 段(可获 2 积分)

如果你习惯于功能性语言,所有那些被滥用和变异的名词都会激发你对动词的偏好。 即使CLJ互操作语法非常方便和完整,在任何OOP语言和任何FP语言(bein Scala混合范式编程语言)之间仍然存在阻抗不匹配。

要退出REPL可以在REPL提示符输入(exit)),也可以使用ctr-D或(quit))。

user=> (exit)
Bye for now!

交互式加载和模糊图像

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

第 23 段(可获 2 积分)
  • 来自Highgui类的imread静态方法从文件读取图像
  • 来自Highgui类的imwrite静态方法将图像写入文件
  • 来自Imgproc类的GaussianBlur静态方法用于模糊原始图像

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

向项目中添加图像

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

Original Image

mkdir -p resources/images
cp ~/opt/opencv/doc/tutorials/introduction/desktop_java/images/lena.png resource/images/
第 24 段(可获 2 积分)

读取图像

现在像往常一样启动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.highgui Highgui]
               '[org.opencv.imgproc Imgproc])
org.opencv.imgproc.Imgproc
第 25 段(可获 2 积分)

现在从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是一个512x512矩阵的CV_8UC3元素类型。 让我们创建一个具有相同尺寸和元素类型的新Mat实例。

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

现在应用GaussianBlur滤波器使用lena作为源矩阵和模糊后的目标矩阵。

第 26 段(可获 2 积分)
user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3)
nil

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

user=> (Highgui/imwrite "resources/images/blurred.png" blurred)
true
user=> (exit)
Bye for now!

以下是Lena的新的模糊图像。

Blurred Image

下一步

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

我建议任何Clojure新手阅读Clojure Java Interop chapter 章节,以获得所有你需要知道的以一种更符合规范、更实用的方式,与那些没有被包装在Clojure中的纯Java库互操作。

第 27 段(可获 2 积分)

OpenCV Java API没有包装基于 Qt 的highgui模块功能(例如namedWindow和imshow)。当你一个人时如果你想创建窗口并在与REPL交互时显示图像。你可以使用Java Swing填补空白。

License

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

与OpenCV一样,按照 BSD 3-clause License分发。

第 28 段(可获 2 积分)

文章评论