介紹OpenCV開發(fā)與Clojure

2018-08-26 10:43 更新

從OpenCV 2.4.4開始,OpenCV支持桌面Java開發(fā),使用與Android開發(fā)幾乎相同的界面。

Clojure是由Java虛擬機(jī)托管的當(dāng)代LISP方言,它提供與底層JVM的完全互操作性。這意味著我們甚至可以使用Clojure REPL(Read Eval Print Loop)作為底層OpenCV引擎的交互式可編程接口。

我們將在本教程中做什么

本教程將幫助您在完全可編程的CLojure REPL中設(shè)置交互式學(xué)習(xí)OpenCV的基本Clojure環(huán)境。

教程源代碼

您可以samples/java/clojure/simple-sample在OpenCV存儲庫的文件夾中找到可運(yùn)行的示例源代碼。按照教程中的說明安裝OpenCV和Clojure后,請執(zhí)行以下命令以從命令行運(yùn)行示例。

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

前言

有關(guān)使用桌面Java支持安裝OpenCV的詳細(xì)說明,請參閱相應(yīng)的教程。

如果你是匆忙,這里是一個(gè)最小的快速入門指南在Mac OS X上安裝OpenCV:

注意
我假設(shè)你已經(jīng)安裝了xcodejdkCmake。
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

一旦您安裝OpenCV與桌面java支持,唯一的其他要求是安裝Leiningeng,它允許您管理CLJ項(xiàng)目的整個(gè)生命周期。

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

  1. 下載腳本
  2. 將它放在$ PATH上(如果你的路徑是cf./bin是一個(gè)不錯(cuò)的選擇)
  3. 將腳本設(shè)置為可執(zhí)行。(即chmod 755 / bin / lein)。

如果您在Windows上工作,請按照此說明進(jìn)行操作

您現(xiàn)在擁有OpenCV庫和完全安裝的基本Clojure環(huán)境?,F(xiàn)在需要配置Clojure環(huán)境與OpenCV庫進(jìn)行交互。

安裝localrepo Leiningen插件

由Leiningen本人支持的一組命令(Leiningen的任務(wù))可以通過各種插件輕松擴(kuò)展。其中一個(gè)是lein-localrepo插件,允許在機(jī)器的本地maven存儲庫中安裝任何jar lib作為工件(通常位于用戶名的/.m2/repository目錄中)。

我們將使用這個(gè)lein插件來添加到本地的maven存儲庫,Java和Clojure需要使用opencv lib的opencv組件。

一般來說,如果您只想在項(xiàng)目基礎(chǔ)上使用插件,則可以直接添加到由lein創(chuàng)建的CLJ項(xiàng)目中。

相反,當(dāng)您希望插件可用于用戶名空間中的任何CLJ項(xiàng)目時(shí),可以將其添加到/ .lein /目錄中的profiles.clj中。

lein-localrepo插件對于我在其他CLJ項(xiàng)目中將是有用的,我需要調(diào)用Java接口包裝的本地庫。所以我決定把它提供給任何CLJ項(xiàng)目:

mkdir?/ .lein

在/ .lein目錄中創(chuàng)建一個(gè)名為profiles.clj的文件,并將其復(fù)制到以下內(nèi)容中:

{:user {:plugins [[lein-localrepo“0.5.2”]]}}

這里我們說,lein-localrepo插件的版本“0.5.2”將可用于由lein創(chuàng)建的任何CLJ項(xiàng)目的用戶配置文件。

您不需要做任何其他安裝插件,因?yàn)樗鼘⒃谀状伟l(fā)布任何lein任務(wù)時(shí)從遠(yuǎn)程存儲庫自動下載。

將java特定的lib安裝為本地存儲庫

如果您在計(jì)算機(jī)上遵循用于安裝OpenCV的標(biāo)準(zhǔn)文檔,您應(yīng)該在構(gòu)建OpenCV的目錄下找到以下兩個(gè)lib:

  • build / bin / opencv-247.jar java lib
  • build / lib / libopencv_java247.dylib本機(jī)庫(或.so中,您構(gòu)建OpenCV是GNU / Linux操作系統(tǒng))

它們是JVM與OpenCV交互所需的唯一opencv庫。

拆分所需的opencv庫

創(chuàng)建一個(gè)新的目錄以存儲在上述兩個(gè)庫中。首先將其復(fù)制到opencv-247.jar lib中。

cd?/ opt
mkdir clj-opencv
cd clj-opencv
cp?/ opt / opencv / build / bin / opencv-247.jar。

首先完成

現(xiàn)在,為了能夠?qū)⒈镜豰aven庫中的libopencv_java247.dylib共享的本地lib添加到我們首先需要將其打包成一個(gè)jar文件。

本機(jī)庫必須被復(fù)制到一個(gè)模擬操作系統(tǒng)和體系結(jié)構(gòu)名稱的目錄布局中。我正在使用具有X86 64位架構(gòu)的Mac OS X。所以我的布局將如下所示:

mkdir -p native / macosx / x86_64

復(fù)制到x86_64目錄中的libopencv_java247.dylib lib。

cp?/ opt / opencv / build / lib / libopencv_java247.dylib native / macosx / x86_64 /

如果您從不同的OS / Architecture對運(yùn)行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

將本機(jī)lib打包成jar

接下來,您需要使用jar命令從一個(gè)目錄創(chuàng)建一個(gè)新的jar文件,將本機(jī)的lib打包到一個(gè)jar文件中。

jar -cMf opencv-native-247.jar native

請注意,ehe M選項(xiàng)指示jar命令不為該工件創(chuàng)建一個(gè)MANIFEST文件。

您的目錄布局應(yīng)如下所示:

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

在本地安裝jars

我們現(xiàn)在準(zhǔn)備在lein-localrepo插件的幫助下,將兩個(gè)jar作為工件添加到本地的maven倉庫中。

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

這里的localrepo安裝任務(wù)創(chuàng)建了2.4.7。從opencv-247.jar lib釋放opencv / opencv maven工件,然后將其安裝到本地的maven存儲庫中。然后,opencv / opencv工件將可用于任何符合maven的項(xiàng)目(Leiningen內(nèi)部基于maven)。

與之前包裝在一個(gè)新的jar文件中的native lib做同樣的事情。

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

請注意,兩個(gè)工件的groupId,opencv是相同的?,F(xiàn)在我們準(zhǔn)備開創(chuàng)一個(gè)新的CLJ項(xiàng)目,開始與OpenCV進(jìn)行互動。

創(chuàng)建一個(gè)項(xiàng)目

通過使用終端的lein新任務(wù)創(chuàng)建一個(gè)新的CLJ項(xiàng)目。

# 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`.

上述任務(wù)創(chuàng)建以下簡單示例目錄布局:

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

我們需要將兩個(gè)opencv工件添加為新創(chuàng)建的項(xiàng)目的依賴關(guān)系。打開project.clj并修改其依賴關(guān)系部分,如下所示:

(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編程語言也是一個(gè)jar工件。這就是為什么Clojure被稱為托管語言。

驗(yàn)證一切正確的問題,lein deps任務(wù)。在第一次運(yùn)行l(wèi)ein任務(wù)時(shí),在執(zhí)行任務(wù)本身之前,需要一些時(shí)間才能下載所有必需的依賴項(xiàng)。

cd simple-sample
lein deps
...

deps任務(wù)從project.clj和/ .lein / profiles.clj文件中讀取并合并所有依賴關(guān)系的簡單示例,并驗(yàn)證它們是否已被緩存在本地的maven存儲庫中。如果任務(wù)返回沒有消息,無法檢索兩個(gè)新的工件,您的安裝是正確的,否則返回并仔細(xì)檢查您做的一切正確。

REPLing with OpenCV

現(xiàn)在cd在簡單樣本目錄中,并發(fā)出以下lein任務(wù):

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

您可以立即通過發(fā)出要評估的CLJ表達(dá)式與REPL進(jìn)行交互。

user=> (+ 41 1)
42
user=> (println "Hello, OpenCV!")
Hello, OpenCV!
nil
user=> (defn foo [] (str "bar"))
#'user/foo
user=> (foo)
"bar"

當(dāng)從基于lein的項(xiàng)目的主目錄運(yùn)行時(shí),即使lein repl任務(wù)自動加載所有項(xiàng)目依賴項(xiàng),仍然需要加載opencv本機(jī)庫才能與OpenCV進(jìn)行交互。

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

然后,您可以通過引用其類的完全限定名稱開始與OpenCV進(jìn)行交互。

注意
在這里,您可以找到完整的OpenCV Java API。
user =>(org.opencv.core.Point。0 0)
#<Point {0.0,0.0}>

這里我們創(chuàng)建了一個(gè)二維opencv Point實(shí)例。即使OpenCV的Java接口中包含的所有java包都可以從CLJ REPL中立即獲得,因此在Point前綴非常煩人。具有完全限定包名稱的實(shí)例構(gòu)造函數(shù)。

幸運(yùn)的是,CLJ提供了一種非常簡單的方法來通過直接導(dǎo)入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

我們甚至可以檢查一個(gè)實(shí)例的類,并驗(yàn)證一個(gè)符號的值是否是Point java類的一個(gè)實(shí)例。

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

如果我們現(xiàn)在想使用opencv Rect類創(chuàng)建一個(gè)矩形,我們再次必須完全限定它的構(gòu)造函數(shù),即使它留在同一個(gè)Point類的org.opencv.core包中。

user=> (org.opencv.core.Rect. p1 p2)
#<Rect {0, 0, 100x100}>

再次,CLJ進(jìn)口設(shè)施非常方便,讓您可以一次性映射更多的符號。

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

顯然你也可以調(diào)用實(shí)例的方法。

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

如果您發(fā)現(xiàn)自己不記得OpenCV類的行為,REPL可以讓您有機(jī)會輕松搜索相應(yīng)的javadoc文檔:

user =>(javadoc Rect)
“http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html”

模擬REPL中的OpenCV Java教程示例

我們現(xiàn)在嘗試將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());
  }
}

向項(xiàng)目進(jìn)行補(bǔ)充

在開始編碼之前,我們希望在我們開始一個(gè)新的REPL與之交互的時(shí)候,消除對交互式加載本機(jī)opencv lib的無聊需求。

首先,通過在REPL提示符處評估(exit)表達(dá)式來停止REPL。

user=> (exit)
Bye for now!

然后打開你的project.clj文件,并按如下所示進(jìn)行編輯:

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

這里我們要說,在我們運(yùn)行REPL的時(shí)候加載opencv native lib,這樣我們再也不用記得手動執(zhí)行了。

重新運(yùn)行l(wèi)ein repl任務(wù)

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

導(dǎo)入感興趣的OpenCV java接口。

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

我們將逐漸模擬原始的OpenCV java教程:

  • 創(chuàng)建一個(gè)5x10矩陣,其所有元素初始化為0
  • 將第二行的每個(gè)元素的值更改為1
  • 將第6列的每個(gè)元素的值更改為5
  • 打印所得矩陣的內(nèi)容
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

如果你習(xí)慣了功能語言,所有這些被濫用和變異的名詞會激怒你對動詞的偏好。即使CLJ interop語法非常方便和完整,任何OOP語言和任何FP語言之間仍然存在阻抗不匹配(Scala是混合范式編程語言)。

要在REPL提示符處退出REPL類型(退出),ctr-D或(退出)。

user=> (exit)
Bye for now!

交互式加載和模糊圖像

在下一個(gè)示例中,您將學(xué)習(xí)如何使用以下OpenCV方法從REPL交互式加載和模糊和映像:

  • 來自Highgui類的imread靜態(tài)方法從文件讀取圖像
  • 來自Highgui類的imwrite靜態(tài)方法將圖像寫入文件
  • 來自Imgproc類的GaussianBlur靜態(tài)方法應(yīng)用于模糊原始圖像

我們還將使用從imread方法返回的Mat類,并將其作為GaussianBlur和imwrite方法的主要參數(shù)。

將圖像添加到項(xiàng)目中

首先,我們要將圖像文件添加到新創(chuàng)建的目錄中,用于存儲項(xiàng)目的靜態(tài)資源。

介紹OpenCV開發(fā)與Clojure

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

查看圖片

現(xiàn)在,照常啟動REPL,首先導(dǎo)入我們要使用的所有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

現(xiàn)在從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是一個(gè)512×512矩陣的CV_8UC3元素類型。我們來創(chuàng)建一個(gè)新的Mat實(shí)例,它的尺寸和元素類型相同。

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

現(xiàn)在,使用lena作為源矩陣應(yīng)用GaussianBlur濾波器,并將其模糊為目標(biāo)矩陣。

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!

以下是莉娜的新模糊形象。

介紹OpenCV開發(fā)與Clojure

下一步

本教程僅介紹了在CLJ REPL中與OpenCV進(jìn)行交互的基本環(huán)境。

我推薦任何Clojure新手閱讀Clojure 教程,以獲得所有您需要知道的任何與Clojure中未包裝的任何普通的java lib進(jìn)行互操作,以便在Clojure中以更為慣用和功能的方式使用。

OpenCV Java API不會根據(jù)Qt包裝highgui模塊的功能(例如namedWindow和imshow)。如果要在REPL中與OpenCV進(jìn)行交互時(shí)創(chuàng)建窗口并顯示圖像,那么當(dāng)您自己離開時(shí)。您可以使用Java Swing填補(bǔ)空白。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號