作为OpenCV2.4.4。OpenCV支持桌面Java开发使用和Android开发非常相似的接口。
Clojure是运行在Java虚拟机上的现代LISP方言,提供与底层JVM的一个完整互动操作。这就意味着这我们甚至可以使用Clojure REPL(Read Eval print Loop)作为互动程序的界面与底层的OpenCV引擎交互。
这个教程将帮助你建立一个基于Clojure的开发环境,在完全可编程Clojure REPL内部,交互学习OpenCV。
在OpenCV仓库(repository)里的例子(sample)里面你能找到一个可运行的源代码在samples/java/clojure/simple-sample
里。安装完OpenCV和Clojure以后再解释这个教程,在命令行中使用下面的命令运行例子。
cd path/to/samples/java/clojure/simple-sample
lein run
详细的介绍安装OpenCV支持桌面Java的请参考相关教程。
中文的有 Linux环境安装OpenCV、使用GCC和CMake编译OpenCV、OpenCV的Java开发介绍
如果你很忙,这里有一个精简版的快速开始教程,用于在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
如果你安装了桌面Java支持的OpenCV,唯一的需求是安装Leiningeng,它允许你管理整个CLJ项目的生命周期。
可获得的安装指南,下面的几步非常的简单。
1. 下载脚本。
2. 替换成你的路径$PATH(参考:如果/bin在你的path里,它将是一个很好的选择)
3. 设置脚本执行权限。(例如:chmod 0755 /bin/lein)
如果你工作在Windos平台,使用这个介绍
你现在已经有OpenCV库和一个完整安装Clojure的基础环境。现在所需要做的就是配置Clojure环境与OpenCV库进行交互。
指令集(Leiningen说法任务)由Leinigen原生态支持,可以非常简单的通过这种插件进行扩展。众多插件中的Leinlocalrepo插件允许在你的机器上安装任何jar包,人为的作为本地maven仓库。(一般在你用户名下的/.m2/repository目录)
我们计划使用lei插件添加opencv组件所需要的Java和Clojure库,添加到本的maven仓库中。
通常来说,如果你仅想使用一个项目基础插件,lein能直接添加一个CLJ项目。
相反,当你要一个插件成为用户名空间中的任何CLJ项目生效,你可以添加他到profiles.clj在/.lein/directory/目录。
lein-localrepo插件会有助于我在其他CLj项目中,调用被Java接口封装的本地库。因此我决定提给给任何CLJ项目:
mkdir ~/.lein
在/.lein目录创建一个叫profiles.clj的文件,并拷入如下内容:
{:user {:plugins [[lein-localrepo "0.5.2"]]}}
这里我们说的lein-localrepo插件发布版本号为“0.5.2”,将被lein有效的用于所有CLJ项目中的 :user profile。
你不需要做任何事情来安装插件,因为它会在第一时间发起一个任务,会自动从远程仓库下载。
如果你使用标准文档在你的电脑中安装的OpenCV,你会在build目录找到如下的两个库:
the build/lib/libopencv_java247.dylib native lib (or .so in you built OpenCV a GNU/Linux OS)
他们是JVM与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文件。
原生库需要拷贝到一个带有操作系统架构的文件夹目录中。我使用的是Mac OS X x86_64架构。所以我需要创建如下的目录。
mkdir -p native/macosx/x86_64
将libopencv_java247.dylib文件拷贝到x86_64目录中。
cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/
如果你在不同的操作系统和CPU架构中运行OpenCV,这里有一个汇总表你可以在里面进行选择。
OS
Mac OS X -> macosx
Windows -> windows
Linux -> linux
SunOS -> solaris
Architectures
amd64 -> x86_64
x86_64 -> x86_64
x86 -> x86
i386 -> x86
arm -> arm
sparc -> sparc
下一步你需要包装一个原生库到jar文件中,使用jar命令创建一个新的jar文件。
jar -cMf opencv-native-247.jar native
注意命令的‘M’选项参数,不会手动的创建MANIFEST文件。
你的目录层次结构看起来应该是这个样子:
tree
.
|__ native
| |__ macosx
| |__ x86_64
| |__ libopencv_java247.dylib
|
|__ opencv-247.jar
|__ opencv-native-247.jar
3 directories, 3 files
我们现在已经手动帮助lein-localrepo插件添加了两个jar包到maven的仓库。
lein localrepo install opencv-247.jar opencv/opencv 2.4.7
这里的localrepo安装任务创建了2.4.7。释放来自opencv-2.4.7.jar的maven artifact到opencv/opencv目录,并且添加到了maven仓库中。opencv/opencv artifact将被有效的用于maven编译项目(Leiningen内部是基于maven的)。
对先前包装成jar文件的本地库做相同的事情。
lein localrepo install opencv-native-247.jar opencv/opencv-native 2.4.7
注意组ID,opencv,两个artifacts是相同的。我们现在真的要创建一个CLJ项目开始OpenCV的交互了。
使用lein在命令行新建一个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
我们需要添加两个opencv artifacts作为新建项目的依赖。打开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 artifact。这就是为什么叫做托管语言。
去验证lein deps 任务每件事情都正确。首先需要你运行lein任务,他会在执行自身任务之前下载所有的依赖。
cd simple-sample
lein deps
...
deps任务读取、合并simple-sample项目的project.clj和/.lein/profiles.clj文件所有的依赖,验证他们已经真的存储在maven repository里面了。如果返回错误消息还没有收到你正确安装的两个新的artifacts,你需要加倍检查你所做的事情了。
现在进入simple-sample目录运行如下的lei任务:
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=>
你可以立刻使用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 API。
user=> (org.opencv.core.Point. 0 0)
#
在这里我们创建了二维的opencv实例。甚至所有的java包也包括OpenCV的接口,对ClJ
都是可用的,惹人烦的前缀点。实例构造限制使用包的全名。
幸运的是CLJ提供一个非常简单的方式解决了这个惹人烦的问题,就是直接导入需要的类。
user=> (import 'org.opencv.core.Point)
org.opencv.core.Point
user=> (def p1 (Point. 0 0))
#'user/p1
user=> p1
#
user=> (def p2 (Point. 100 100))
#'user/p2
我们甚至可以检查类的实例,鉴定一个符号值是否是所指的java类。
user=> (class p1)
org.opencv.core.Point
user=> (instance? org.opencv.core.Point p1)
true
如果我们现在要使用opencv的Rect类来创建一个长方形,我们需要继续完全限定他们的构造函数,即使它来自于相同的org.opencv.core包里的类。
user=> (org.opencv.core.Rect. p1 p2)
#
CLJ的再次引入能力是非常方便的,让你瞬间映射更多的符号。
user=> (import '[org.opencv.core Point Rect Size])
org.opencv.core.Size
user=> (def r1 (Rect. p1 p2))
#'user/r1
user=> r1
#
user=> (class r1)
org.opencv.core.Rect
user=> (instance? org.opencv.core.Rect r1)
true
user=> (Size. 100 100)
#
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
#
user=> (set! (.width sq-100) 10)
10
user=> (set! (.height sq-100) 10)
10
user=> (.area sq-100)
100.0
如果你们没能记住OpenCV类的功能。REPL提供给你一个很容易查找java文档的方式。
user=> (javadoc Rect)
"http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html"
现在尝试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());
}
}
在编码之前,我们愿意消除交启动一个新的REPL互所需要加载opencv原生库的时间。
首先退出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原生库,这样我们就不需要手动加载他啦。
返回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练习:
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))
#
user=> (def mc5 (.col m 5))
#'user/mc5
user=> (.setTo mc5 (Scalar. 5 0))
#
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语言之间的匹配(在Scala混合范式编程语言)。
退出REPL输入(exit),ctr-D或者(quit)。
user=> (exit)
Bye for now!
在下一个例子中我们将学习如何通过REPL使用openCV的方法,动态加载模糊化一个图片:
我们还是使用Mat类,它是通过imread方法返回的矩阵,并且可以作为主参数的方式接收GaussianBlur和imwrite方法。
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
现在从读取 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,它是512x512的矩阵,属于CV_8UC3元素类型。让我们创建并实现一个新的矩阵,并使用相同的尺寸和元素类型。
user=> (def blurred (Mat. 512 512 CvType/CV_8UC3))
#'user/blurred
user=>
现在使用高斯模糊滤波器作用于lena的原始图片,会产生一个模糊的目标矩阵。
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!
这个教程仅仅介绍了非常基础的环境设置,并可以使用CLJ REPL与OpenCV进行互动。
我建议Clojure新手去阅读Clojure Java Interop chapter 在这里你可以学到操作还没有被封装到Clojure里面的Java库,更加地道的方式使用Clojure。
OpenCV Java API没有封装highgui依赖的QT功能模块(例如,nameWindow和imshow)。如果你要通过REPL去创建一个窗口并在上面显示一个图片。在这一刻你会丢弃你所用于的一切,因为Java Swing可以很好的解决这个。
Copyright © 2013 Giacomo (Mimmo) Cosenza aka Magomimmo
Distributed under the BSD 3-clause License, the same of OpenCV.
出处:http://docs.opencv.org/master/d7/d1e/tutorial_clojure_dev_intro.html