介绍使用Clojure的OpenCV开发

来源:互联网 发布:js获取img标签的src 编辑:程序博客网 时间:2024/04/24 23:43

介绍使用Clojure的OpenCV开发

作为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 optgit clone https://github.com/opencv/opencv.gitcd opencvgit checkout 2.4mkdir buildcd buildcmake -DBUILD_SHARED_LIBS=OFF ........make -j8# optional# make install

安装Leiningen

如果你安装了桌面Java支持的OpenCV,唯一的需求是安装Leiningeng,它允许你管理整个CLJ项目的生命周期。

可获得的安装指南,下面的几步非常的简单。
1. 下载脚本。
2. 替换成你的路径$PATH(参考:如果/bin在你的path里,它将是一个很好的选择)
3. 设置脚本执行权限。(例如:chmod 0755 /bin/lein)
如果你工作在Windos平台,使用这个介绍
你现在已经有OpenCV库和一个完整安装Clojure的基础环境。现在所需要做的就是配置Clojure环境与OpenCV库进行交互。

安装localrepo Leiningen插件

指令集(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。
你不需要做任何事情来安装插件,因为它会在第一时间发起一个任务,会自动从远程仓库下载。

安装Java特定的库作为本地仓库

如果你使用标准文档在你的电脑中安装的OpenCV,你会在build目录找到如下的两个库:

  • 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-247.jar库。

cd ~/optmkdir clj-opencvcd clj-opencvcp ~/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文件。

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

安装jar包

我们现在已经手动帮助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的交互了。

创建一个CLJ项目

使用lein在命令行新建一个CLJ项目。

# cd in the directory where you work with your development projects (e.g. ~/devel)lein new simple-sampleGenerating 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,你需要加倍检查你所做的事情了。

对OpenCV进行REPLing

现在进入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) #<Point {0.0, 0.0}>

在这里我们创建了二维的opencv实例。甚至所有的java包也包括OpenCV的接口,对ClJ
都是可用的,惹人烦的前缀点。实例构造限制使用包的全名。

幸运的是CLJ提供一个非常简单的方式解决了这个惹人烦的问题,就是直接导入需要的类。

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

我们甚至可以检查类的实例,鉴定一个符号值是否是所指的java类。

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

如果我们现在要使用opencv的Rect类来创建一个长方形,我们需要继续完全限定他们的构造函数,即使它来自于相同的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.Sizeuser=> (def r1 (Rect. p1 p2))#'user/r1user=> r1#<Rect {0, 0, 100x100}>user=> (class r1)org.opencv.core.Rectuser=> (instance? org.opencv.core.Rect r1)trueuser=> (Size. 100 100)#<Size 100x100>user=> (def sq-100 (Size. 100 100))#'user/sq-100user=> (class sq-100)org.opencv.core.Sizeuser=> (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提供给你一个很容易查找java文档的方式。

 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());  }}

添加注入到项目里

在编码之前,我们愿意消除交启动一个新的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练习:

  • 创建一个5x10的矩阵,所有的元素都初始化为0.
  • 将第二行的每个元素值都值为1.
  • 将第六列的每个元素值为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语言之间的匹配(在Scala混合范式编程语言)。

退出REPL输入(exit),ctr-D或者(quit)。

 user=> (exit) Bye for now!

动态加载模糊化处理一个图片

在下一个例子中我们将学习如何通过REPL使用openCV的方法,动态加载模糊化一个图片:

  • Highgui类的静态imread方法读取一个影像文件。
  • Highgui类的静态imwrite方法写入一个影像文件。
  • Imgproc类的静态GaussianBlur方法实现原始图片的模糊化。

我们还是使用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!

下面就是新的被模糊的lean照片。
这里写图片描述

下一阶段

这个教程仅仅介绍了非常基础的环境设置,并可以使用CLJ REPL与OpenCV进行互动。

我建议Clojure新手去阅读Clojure Java Interop chapter 在这里你可以学到操作还没有被封装到Clojure里面的Java库,更加地道的方式使用Clojure。

OpenCV Java API没有封装highgui依赖的QT功能模块(例如,nameWindow和imshow)。如果你要通过REPL去创建一个窗口并在上面显示一个图片。在这一刻你会丢弃你所用于的一切,因为Java Swing可以很好的解决这个。

License

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

0 0
原创粉丝点击