If you followed the standard documentation for installing OpenCV on your computer, you should find the following two libs under the directory where you built OpenCV:
• thebuild/bin/opencv-247.jarjava lib
• thebuild/lib/libopencv_java247.dylibnative lib (or.soin you built OpenCV a GNU/Linux OS) They are the only opencv libs needed by the JVM to interact with OpenCV.
Take apart the needed opencv libs
Create a new directory to store in the above two libs. Start by copying into it theopencv-247.jarlib.
cd ~/opt
mkdir clj-opencv cd clj-opencv
cp ~/opt/opencv/build/bin/opencv-247.jar .
First lib done.
Now, to be able to add thelibopencv_java247.dylibshared native lib to the local maven repository, we first need to package it as a jar file.
The native lib has to be copied into a directories layout which mimics the names of your operating system and archi-tecture. I’m using a Mac OS X with a X86 64 bit archiarchi-tecture. So my layout will be the following:
mkdir -p native/macosx/x86_64
1.9. Introduction to OpenCV Development with Clojure 59
Copy into thex86_64directory thelibopencv_java247.dyliblib.
cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/
If you’re running OpenCV from a different OS/Architecture pair, here is a summary of the mapping you can choose from.
Package the native lib as a jar
Next you need to package the native lib in a jar file by using thejarcommand to create a new jar file from a directory.
jar -cMf opencv-native-247.jar native
Note that eheMoption instructs thejarcommand to not create a MANIFEST file for the artifact.
Your directories layout should look like the following:
tree
We are now ready to add the two jars as artifacts to the local maven repository with the help of thelein-localrepo plugin.
lein localrepo install opencv-247.jar opencv/opencv 2.4.7
Here the localrepo install task creates the 2.4.7. release of the opencv/opencv maven artifact from the opencv-247.jarlib and then installs it into the local maven repository. Theopencv/opencvartifact will then be available to any maven compliant project (Leiningen is internally based on maven).
Do the same thing with the native lib previously wrapped in a new jar file.
lein localrepo install opencv-native-247.jar opencv/opencv-native 2.4.7
Note that the groupId,opencv, of the two artifacts is the same. We are now ready to create a new CLJ project to start interacting with OpenCV.
Create a project
Create a new CLJ project by using thelein newtask from the terminal.
# 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‘.
The above task creates the followingsimple-sampledirectories layout:
tree simple-sample/
We need to add the twoopencvartifacts as dependencies of the newly created project. Open theproject.cljand modify its dependencies section as follows:
(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
Note that The Clojure Programming Language is a jar artifact too. This is why Clojure is called an hosted language.
To verify that everything went right issue thelein depstask. The very first time you run aleintask it will take sometime to download all the required dependencies before executing the task itself.
cd simple-sample lein deps ...
Thedepstask reads and merges from theproject.cljand the~/.lein/profiles.cljfiles all the dependencies of thesimple-sampleproject and verifies if they have already been cached in the local maven repository. If the task returns without messages about not being able to retrieve the two new artifacts your installation is correct, otherwise go back and double check that you did everything right.
1.9. Introduction to OpenCV Development with Clojure 61
REPLing with OpenCV
Nowcdin thesimple-sampledirectory and issue the followingleintask:
cd simple-sample lein repl ...
...
nREPL server started on port 50907 on host 127.0.0.1 REPL-y 0.3.0 Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e user=>
You can immediately interact with the REPL by issuing any CLJ expression to be evaluated.
user=> (+ 41 1) 42
user=> (println "Hello, OpenCV!") Hello, OpenCV!
nil
user=> (defn foo [] (str "bar"))
#’user/foo user=> (foo)
"bar"
When ran from the home directory of a lein based project, even if thelein repltask automatically loads all the project dependencies, you still need to load the opencv native library to be able to interact with the OpenCV.
user=> (clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME) nil
Then you can start interacting with OpenCV by just referencing the fully qualified names of its classes.
NOTE 2:Hereyou can find the full OpenCV Java API.
user=> (org.opencv.core.Point. 0 0)
#<Point {0.0, 0.0}>
Here we created a two dimensions opencvPoint instance. Even if all the java packages included within the java interface to OpenCV are immediately available from the CLJ REPL, it’s very annoying to prefix thePoint.instance constructors with the fully qualified package name.
Fortunately CLJ offer a very easy way to overcome this annoyance by directly importing thePointclass.
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
We can even inspect the class of an instance and verify if the value of a symbol is an instance of aPointjava class.
user=> (class p1) org.opencv.core.Point
user=> (instance? org.opencv.core.Point p1) true
If we now want to use the opencvRectclass to create a rectangle, we again have to fully qualify its constructor even if it leaves in the sameorg.opencv.corepackage of thePointclass.
user=> (org.opencv.core.Rect. p1 p2)
#<Rect {0, 0, 100x100}>
Again, the CLJ importing facilities is very handy and let you to map more symbols in one shot.
user=> (import ’[org.opencv.core Point Rect Size]) org.opencv.core.Size
user=> (def r1 (Rect. p1 p2))
#’user/r1
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
Obviously you can call methods on instances as well.
user=> (.area r1) 10000.0
user=> (.area sq-100) 10000.0
Or modify the value of a member field.
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
If you find yourself not remembering a OpenCV class behavior, the REPL gives you the opportunity to easily search the corresponding javadoc documention:
user=> (javadoc Rect)
"http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html"
1.9. Introduction to OpenCV Development with Clojure 63
Mimic the OpenCV Java Tutorial Sample in the REPL
Let’s now try to port to Clojure theopencv java tutorial sample. Instead of writing it in a source file we’re going to evaluate it at the REPL.
Following is the original Java source code of the cited sample.
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());
}
}
Add injections to the project
Before start coding, we’d like to eliminate the boring need of interactively loading the native opencv lib any time we start a new REPL to interact with it.
First, stop the REPL by evaluating the(exit)expression at the REPL prompt.
user=> (exit) Bye for now!
Then open yourproject.cljfile and edit it as follows:
(defproject simple-sample "0.1.0-SNAPSHOT"
...
:injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)])
Here we’re saying to load the opencv native lib anytime we run the REPL in such a way that we have not anymore to remember to manually do it.
Rerun thelein repltask
lein repl
nREPL server started on port 51645 on host 127.0.0.1 REPL-y 0.3.0 Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=>
Import the interested OpenCV java interfaces.
user=> (import ’[org.opencv.core Mat CvType Scalar]) org.opencv.core.Scalar
We’re going to mimic almost verbatim the original OpenCV java tutorial to:
• create a 5x10 matrix with all its elements intialized to 0
• change the value of every element of the second row to 1
• change the value of every element of the 6th column to 5
• print the content of the obtained matrix
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))
If you are accustomed to a functional language all those abused and mutating nouns are going to irritate your preference for verbs. Even if the CLJ interop syntax is very handy and complete, there is still an impedance mismatch between any OOP language and any FP language (bein Scala a mixed paradigms programming language).
To exit the REPL type(exit),ctr-Dor(quit)at the REPL prompt.
user=> (exit) Bye for now!
Interactively load and blur an image
In the next sample you will learn how to interactively load and blur and image from the REPL by using the following OpenCV methods:
• theimreadstatic method from theHighguiclass to read an image from a file
• theimwritestatic method from theHighguiclass to write an image to a file
• theGaussianBlurstatic method from theImgprocclass to apply to blur the original image
We’re also going to use theMatclass which is returned from theimreadmethod and accpeted as the main argument to both theGaussianBlurand theimwritemethods.
1.9. Introduction to OpenCV Development with Clojure 65
Add an image to the project
First we want to add an image file to a newly create directory for storing static resources of the project.
mkdir -p resources/images
cp ~/opt/opencv/doc/tutorials/introduction/desktop_java/images/lena.png resource/images/
Read the image
Now launch the REPL as usual and start by importing all the OpenCV classes we’re going to use:
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
Now read the image from theresources/images/lena.pngfile.
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 ]>
As you see, by simply evaluating thelenasymbol we know thatlena.pngis a512x512matrix ofCV_8UC3elements type. Let’s create a newMatinstance of the same dimensions and elements type.
user=> (def blurred (Mat. 512 512 CvType/CV_8UC3))
#’user/blurred user=>
Now apply aGaussianBlurfilter usinglenaas the source matrix andblurredas the destination matrix.
user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3) nil
As a last step just save theblurredmatrix in a new image file.
user=> (Highgui/imwrite "resources/images/blurred.png" blurred) true
user=> (exit) Bye for now!
Following is the new blurred image of Lena.
1.9. Introduction to OpenCV Development with Clojure 67