介绍使用Clojure的OpenCV开发

介绍使用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 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. 下载脚本。
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 ~/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文件。

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-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,你需要加倍检查你所做的事情了。

对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)
 #

在这里我们创建了二维的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"

使用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))
 #
 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的方法,动态加载模糊化一个图片:

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

我们还是使用Mat类,它是通过imread方法返回的矩阵,并且可以作为主参数的方式接收GaussianBlur和imwrite方法。

向项目中添加一个图片

首先我们需要添加一个图片到新创建的项目资源目录中。
介绍使用Clojure的OpenCV开发_第1张图片

 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照片。
介绍使用Clojure的OpenCV开发_第2张图片

下一阶段

这个教程仅仅介绍了非常基础的环境设置,并可以使用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

你可能感兴趣的:(OpenCV,clojure,opencv)