Add next and previous navigation links to all tutorials
[platform/upstream/opencv.git] / doc / tutorials / introduction / clojure_dev_intro / clojure_dev_intro.markdown
1 Introduction to OpenCV Development with Clojure {#tutorial_clojure_dev_intro}
2 ===============================================
3
4 @prev_tutorial{tutorial_java_eclipse}
5 @next_tutorial{tutorial_android_dev_intro}
6
7
8 As of OpenCV 2.4.4, OpenCV supports desktop Java development using nearly the same interface as for
9 Android development.
10
11 [Clojure](http://clojure.org/) is a contemporary LISP dialect hosted by the Java Virtual Machine and
12 it offers a complete interoperability with the underlying JVM. This means that we should even be
13 able to use the Clojure REPL (Read Eval Print Loop) as and interactive programmable interface to the
14 underlying OpenCV engine.
15
16 What we'll do in this tutorial
17 ------------------------------
18
19 This tutorial will help you in setting up a basic Clojure environment for interactively learning
20 OpenCV within the fully programmable CLojure REPL.
21
22 ### Tutorial source code
23
24 You can find a runnable source code of the sample in the `samples/java/clojure/simple-sample` folder
25 of the OpenCV repository. After having installed OpenCV and Clojure as explained in the tutorial,
26 issue the following command to run the sample from the command line.
27 @code{.bash}
28 cd path/to/samples/java/clojure/simple-sample
29 lein run
30 @endcode
31 Preamble
32 --------
33
34 For detailed instruction on installing OpenCV with desktop Java support refer to the @ref tutorial_java_dev_intro "corresponding
35 tutorial".
36
37 If you are in hurry, here is a minimum quick start guide to install OpenCV on Mac OS X:
38
39 @note
40 I'm assuming you already installed [xcode](https://developer.apple.com/xcode/),
41 [jdk](http://www.oracle.com/technetwork/java/javase/downloads/index.html) and
42 [Cmake](http://www.cmake.org/cmake/resources/software.html).
43
44 @code{.bash}
45 cd ~/
46 mkdir opt
47 git clone https://github.com/opencv/opencv.git
48 cd opencv
49 git checkout 2.4
50 mkdir build
51 cd build
52 cmake -DBUILD_SHARED_LIBS=OFF ..
53 ...
54 ...
55 make -j8
56 # optional
57 # make install
58 @endcode
59 Install Leiningen
60 -----------------
61
62 Once you installed OpenCV with desktop java support the only other requirement is to install
63 [Leiningeng](https://github.com/technomancy/leiningen) which allows you to manage the entire life
64 cycle of your CLJ projects.
65
66 The available [installation guide](https://github.com/technomancy/leiningen#installation) is very
67 easy to be followed:
68
69 -#  [Download the script](https://raw.github.com/technomancy/leiningen/stable/bin/lein)
70 -#  Place it on your $PATH (cf. \~/bin is a good choice if it is on your path.)
71 -#  Set the script to be executable. (i.e. chmod 755 \~/bin/lein).
72
73 If you work on Windows, follow [this instruction](https://github.com/technomancy/leiningen#windows)
74
75 You now have both the OpenCV library and a fully installed basic Clojure environment. What is now
76 needed is to configure the Clojure environment to interact with the OpenCV library.
77
78 Install the localrepo Leiningen plugin
79 --------------------------------------
80
81 The set of commands (tasks in Leiningen parlance) natively supported by Leiningen can be very easily
82 extended by various plugins. One of them is the
83 [lein-localrepo](https://github.com/kumarshantanu/lein-localrepo) plugin which allows to install any
84 jar lib as an artifact in the local maven repository of your machine (typically in the
85 \~/.m2/repository directory of your username).
86
87 We're going to use this lein plugin to add to the local maven repository the opencv components
88 needed by Java and Clojure to use the opencv lib.
89
90 Generally speaking, if you want to use a plugin on project base only, it can be added directly to a
91 CLJ project created by lein.
92
93 Instead, when you want a plugin to be available to any CLJ project in your username space, you can
94 add it to the profiles.clj in the \~/.lein/ directory.
95
96 The lein-localrepo plugin will be useful to me in other CLJ projects where I need to call native
97 libs wrapped by a Java interface. So I decide to make it available to any CLJ project:
98 @code{.bash}
99 mkdir ~/.lein
100 @endcode
101 Create a file named profiles.clj in the \~/.lein directory and copy into it the following content:
102 @code{.clojure}
103 {:user {:plugins [[lein-localrepo "0.5.2"]]}}
104 @endcode
105 Here we're saying that the version release "0.5.2" of the lein-localrepo plugin will be available to
106 the :user profile for any CLJ project created by lein.
107
108 You do not need to do anything else to install the plugin because it will be automatically
109 downloaded from a remote repository the very first time you issue any lein task.
110
111 Install the java specific libs as local repository
112 --------------------------------------------------
113
114 If you followed the standard documentation for installing OpenCV on your computer, you should find
115 the following two libs under the directory where you built OpenCV:
116
117 -   the build/bin/opencv-247.jar java lib
118 -   the build/lib/libopencv_java247.dylib native lib (or .so in you built OpenCV a GNU/Linux OS)
119
120 They are the only opencv libs needed by the JVM to interact with OpenCV.
121
122 ### Take apart the needed opencv libs
123
124 Create a new directory to store in the above two libs. Start by copying into it the opencv-247.jar
125 lib.
126 @code{.bash}
127 cd ~/opt
128 mkdir clj-opencv
129 cd clj-opencv
130 cp ~/opt/opencv/build/bin/opencv-247.jar .
131 @endcode
132 First lib done.
133
134 Now, to be able to add the libopencv_java247.dylib shared native lib to the local maven repository,
135 we first need to package it as a jar file.
136
137 The native lib has to be copied into a directories layout which mimics the names of your operating
138 system and architecture. I'm using a Mac OS X with a X86 64 bit architecture. So my layout will be
139 the following:
140 @code{.bash}
141 mkdir -p native/macosx/x86_64
142 @endcode
143 Copy into the x86_64 directory the libopencv_java247.dylib lib.
144 @code{.bash}
145 cp ~/opt/opencv/build/lib/libopencv_java247.dylib native/macosx/x86_64/
146 @endcode
147 If you're running OpenCV from a different OS/Architecture pair, here is a summary of the mapping you
148 can choose from.
149 @code{.bash}
150 OS
151
152 Mac OS X -> macosx
153 Windows  -> windows
154 Linux    -> linux
155 SunOS    -> solaris
156
157 Architectures
158
159 amd64    -> x86_64
160 x86_64   -> x86_64
161 x86      -> x86
162 i386     -> x86
163 arm      -> arm
164 sparc    -> sparc
165 @endcode
166 ### Package the native lib as a jar
167
168 Next you need to package the native lib in a jar file by using the jar command to create a new jar
169 file from a directory.
170 @code{.bash}
171 jar -cMf opencv-native-247.jar native
172 @endcode
173 Note that ehe M option instructs the jar command to not create a MANIFEST file for the artifact.
174
175 Your directories layout should look like the following:
176 @code{.bash}
177 tree
178 .
179 |__ native
180 |   |__ macosx
181 |       |__ x86_64
182 |           |__ libopencv_java247.dylib
183 |
184 |__ opencv-247.jar
185 |__ opencv-native-247.jar
186
187 3 directories, 3 files
188 @endcode
189 ### Locally install the jars
190
191 We are now ready to add the two jars as artifacts to the local maven repository with the help of the
192 lein-localrepo plugin.
193 @code{.bash}
194 lein localrepo install opencv-247.jar opencv/opencv 2.4.7
195 @endcode
196 Here the localrepo install task creates the 2.4.7. release of the opencv/opencv maven artifact from
197 the opencv-247.jar lib and then installs it into the local maven repository. The opencv/opencv
198 artifact will then be available to any maven compliant project (Leiningen is internally based on
199 maven).
200
201 Do the same thing with the native lib previously wrapped in a new jar file.
202 @code{.bash}
203 lein localrepo install opencv-native-247.jar opencv/opencv-native 2.4.7
204 @endcode
205 Note that the groupId, opencv, of the two artifacts is the same. We are now ready to create a new
206 CLJ project to start interacting with OpenCV.
207
208 ### Create a project
209
210 Create a new CLJ project by using the lein new task from the terminal.
211 @code{.bash}
212 # cd in the directory where you work with your development projects (e.g. ~/devel)
213 lein new simple-sample
214 Generating a project called simple-sample based on the 'default' template.
215 To see other templates (app, lein plugin, etc), try `lein help new`.
216 @endcode
217 The above task creates the following simple-sample directories layout:
218 @code{.bash}
219 tree simple-sample/
220 simple-sample/
221 |__ LICENSE
222 |__ README.md
223 |__ doc
224 |   |__ intro.md
225 |
226 |__ project.clj
227 |__ resources
228 |__ src
229 |   |__ simple_sample
230 |       |__ core.clj
231 |__ test
232     |__ simple_sample
233         |__ core_test.clj
234
235 6 directories, 6 files
236 @endcode
237 We need to add the two opencv artifacts as dependencies of the newly created project. Open the
238 project.clj and modify its dependencies section as follows:
239 @code{.bash}
240 (defproject simple-sample "0.1.0-SNAPSHOT"
241 description "FIXME: write description"
242 url "http://example.com/FIXME"
243 license {:name "Eclipse Public License"
244 url "http://www.eclipse.org/legal/epl-v10.html"}
245 dependencies [[org.clojure/clojure "1.5.1"]
246                  [opencv/opencv "2.4.7"] ; added line
247                  [opencv/opencv-native "2.4.7"]]) ;added line
248 @endcode
249 Note that The Clojure Programming Language is a jar artifact too. This is why Clojure is called an
250 hosted language.
251
252 To verify that everything went right issue the lein deps task. The very first time you run a lein
253 task it will take sometime to download all the required dependencies before executing the task
254 itself.
255 @code{.bash}
256 cd simple-sample
257 lein deps
258 ...
259 @endcode
260 The deps task reads and merges from the project.clj and the \~/.lein/profiles.clj files all the
261 dependencies of the simple-sample project and verifies if they have already been cached in the local
262 maven repository. If the task returns without messages about not being able to retrieve the two new
263 artifacts your installation is correct, otherwise go back and double check that you did everything
264 right.
265
266 ### REPLing with OpenCV
267
268 Now cd in the simple-sample directory and issue the following lein task:
269 @code{.bash}
270 cd simple-sample
271 lein repl
272 ...
273 ...
274 nREPL server started on port 50907 on host 127.0.0.1
275 REPL-y 0.3.0
276 Clojure 1.5.1
277     Docs: (doc function-name-here)
278           (find-doc "part-of-name-here")
279   Source: (source function-name-here)
280  Javadoc: (javadoc java-object-or-class-here)
281     Exit: Control+D or (exit) or (quit)
282  Results: Stored in vars *1, *2, *3, an exception in *e
283
284 user=>
285 @endcode
286 You can immediately interact with the REPL by issuing any CLJ expression to be evaluated.
287 @code{.clojure}
288 user=> (+ 41 1)
289 42
290 user=> (println "Hello, OpenCV!")
291 Hello, OpenCV!
292 nil
293 user=> (defn foo [] (str "bar"))
294 #'user/foo
295 user=> (foo)
296 "bar"
297 @endcode
298 When ran from the home directory of a lein based project, even if the lein repl task automatically
299 loads all the project dependencies, you still need to load the opencv native library to be able to
300 interact with the OpenCV.
301 @code{.clojure}
302 user=> (clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)
303 nil
304 @endcode
305 Then you can start interacting with OpenCV by just referencing the fully qualified names of its
306 classes.
307
308 @note
309 [Here](https://docs.opencv.org/3.4/javadoc/index.html) you can find the full OpenCV Java API.
310
311 @code{.clojure}
312 user=> (org.opencv.core.Point. 0 0)
313 #<Point {0.0, 0.0}>
314 @endcode
315 Here we created a two dimensions opencv Point instance. Even if all the java packages included
316 within the java interface to OpenCV are immediately available from the CLJ REPL, it's very annoying
317 to prefix the Point. instance constructors with the fully qualified package name.
318
319 Fortunately CLJ offer a very easy way to overcome this annoyance by directly importing the Point
320 class.
321 @code{.clojure}
322 user=> (import 'org.opencv.core.Point)
323 org.opencv.core.Point
324 user=> (def p1 (Point. 0 0))
325 #'user/p1
326 user=> p1
327 #<Point {0.0, 0.0}>
328 user=> (def p2 (Point. 100 100))
329 #'user/p2
330 @endcode
331 We can even inspect the class of an instance and verify if the value of a symbol is an instance of a
332 Point java class.
333 @code{.clojure}
334 user=> (class p1)
335 org.opencv.core.Point
336 user=> (instance? org.opencv.core.Point p1)
337 true
338 @endcode
339 If we now want to use the opencv Rect class to create a rectangle, we again have to fully qualify
340 its constructor even if it leaves in the same org.opencv.core package of the Point class.
341 @code{.clojure}
342 user=> (org.opencv.core.Rect. p1 p2)
343 #<Rect {0, 0, 100x100}>
344 @endcode
345 Again, the CLJ importing facilities is very handy and let you to map more symbols in one shot.
346 @code{.clojure}
347 user=> (import '[org.opencv.core Point Rect Size])
348 org.opencv.core.Size
349 user=> (def r1 (Rect. p1 p2))
350 #'user/r1
351 user=> r1
352 #<Rect {0, 0, 100x100}>
353 user=> (class r1)
354 org.opencv.core.Rect
355 user=> (instance? org.opencv.core.Rect r1)
356 true
357 user=> (Size. 100 100)
358 #<Size 100x100>
359 user=> (def sq-100 (Size. 100 100))
360 #'user/sq-100
361 user=> (class sq-100)
362 org.opencv.core.Size
363 user=> (instance? org.opencv.core.Size sq-100)
364 true
365 @endcode
366 Obviously you can call methods on instances as well.
367 @code{.clojure}
368 user=> (.area r1)
369 10000.0
370 user=> (.area sq-100)
371 10000.0
372 @endcode
373 Or modify the value of a member field.
374 @code{.clojure}
375 user=> (set! (.x p1) 10)
376 10
377 user=> p1
378 #<Point {10.0, 0.0}>
379 user=> (set! (.width sq-100) 10)
380 10
381 user=> (set! (.height sq-100) 10)
382 10
383 user=> (.area sq-100)
384 100.0
385 @endcode
386 If you find yourself not remembering a OpenCV class behavior, the REPL gives you the opportunity to
387 easily search the corresponding javadoc documentation:
388 @code{.clojure}
389 user=> (javadoc Rect)
390 "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html"
391 @endcode
392 ### Mimic the OpenCV Java Tutorial Sample in the REPL
393
394 Let's now try to port to Clojure the @ref tutorial_java_dev_intro "OpenCV Java tutorial sample".
395 Instead of writing it in a source file we're going to evaluate it at the REPL.
396
397 Following is the original Java source code of the cited sample.
398 @code{.java}
399 import org.opencv.core.Mat;
400 import org.opencv.core.CvType;
401 import org.opencv.core.Scalar;
402
403 class SimpleSample {
404
405   static{ System.loadLibrary("opencv_java244"); }
406
407   public static void main(String[] args) {
408     Mat m = new Mat(5, 10, CvType.CV_8UC1, new Scalar(0));
409     System.out.println("OpenCV Mat: " + m);
410     Mat mr1 = m.row(1);
411     mr1.setTo(new Scalar(1));
412     Mat mc5 = m.col(5);
413     mc5.setTo(new Scalar(5));
414     System.out.println("OpenCV Mat data:\n" + m.dump());
415   }
416
417 }
418 @endcode
419
420 ### Add injections to the project
421
422 Before start coding, we'd like to eliminate the boring need of interactively loading the native
423 opencv lib any time we start a new REPL to interact with it.
424
425 First, stop the REPL by evaluating the (exit) expression at the REPL prompt.
426 @code{.clojure}
427 user=> (exit)
428 Bye for now!
429 @endcode
430 Then open your project.clj file and edit it as follows:
431 @code{.clojure}
432 (defproject simple-sample "0.1.0-SNAPSHOT"
433   ...
434 injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)])
435 @endcode
436 Here we're saying to load the opencv native lib anytime we run the REPL in such a way that we have
437 not anymore to remember to manually do it.
438
439 Rerun the lein repl task
440 @code{.bash}
441 lein repl
442 nREPL server started on port 51645 on host 127.0.0.1
443 REPL-y 0.3.0
444 Clojure 1.5.1
445     Docs: (doc function-name-here)
446           (find-doc "part-of-name-here")
447   Source: (source function-name-here)
448  Javadoc: (javadoc java-object-or-class-here)
449     Exit: Control+D or (exit) or (quit)
450  Results: Stored in vars *1, *2, *3, an exception in *e
451
452 user=>
453 @endcode
454 Import the interested OpenCV java interfaces.
455 @code{.clojure}
456 user=> (import '[org.opencv.core Mat CvType Scalar])
457 org.opencv.core.Scalar
458 @endcode
459 We're going to mimic almost verbatim the original OpenCV java tutorial to:
460
461 -   create a 5x10 matrix with all its elements initialized to 0
462 -   change the value of every element of the second row to 1
463 -   change the value of every element of the 6th column to 5
464 -   print the content of the obtained matrix
465
466 @code{.clojure}
467 user=> (def m (Mat. 5 10 CvType/CV_8UC1 (Scalar. 0 0)))
468 #'user/m
469 user=> (def mr1 (.row m 1))
470 #'user/mr1
471 user=> (.setTo mr1 (Scalar. 1 0))
472 #<Mat Mat [ 1*10*CV_8UC1, isCont=true, isSubmat=true, nativeObj=0x7fc9dac49880, dataAddr=0x7fc9d9c98d5a ]>
473 user=> (def mc5 (.col m 5))
474 #'user/mc5
475 user=> (.setTo mc5 (Scalar. 5 0))
476 #<Mat Mat [ 5*1*CV_8UC1, isCont=false, isSubmat=true, nativeObj=0x7fc9d9c995a0, dataAddr=0x7fc9d9c98d55 ]>
477 user=> (println (.dump m))
478 [0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
479   1, 1, 1, 1, 1, 5, 1, 1, 1, 1;
480   0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
481   0, 0, 0, 0, 0, 5, 0, 0, 0, 0;
482   0, 0, 0, 0, 0, 5, 0, 0, 0, 0]
483 nil
484 @endcode
485
486 If you are accustomed to a functional language all those abused and mutating nouns are going to
487 irritate your preference for verbs. Even if the CLJ interop syntax is very handy and complete, there
488 is still an impedance mismatch between any OOP language and any FP language (bein Scala a mixed
489 paradigms programming language).
490
491 To exit the REPL type (exit), ctr-D or (quit) at the REPL prompt.
492 @code{.clojure}
493 user=> (exit)
494 Bye for now!
495 @endcode
496
497 ### Interactively load and blur an image
498
499 In the next sample you will learn how to interactively load and blur and image from the REPL by
500 using the following OpenCV methods:
501
502 -   the imread static method from the Highgui class to read an image from a file
503 -   the imwrite static method from the Highgui class to write an image to a file
504 -   the GaussianBlur static method from the Imgproc class to apply to blur the original image
505
506 We're also going to use the Mat class which is returned from the imread method and accepted as the
507 main argument to both the GaussianBlur and the imwrite methods.
508
509 ### Add an image to the project
510
511 First we want to add an image file to a newly create directory for storing static resources of the
512 project.
513
514 ![](images/lena.png)
515 @code{.bash}
516 mkdir -p resources/images
517 cp ~/opt/opencv/doc/tutorials/introduction/desktop_java/images/lena.png resource/images/
518 @endcode
519 ### Read the image
520
521 Now launch the REPL as usual and start by importing all the OpenCV classes we're going to use:
522 @code{.clojure}
523 lein repl
524 nREPL server started on port 50624 on host 127.0.0.1
525 REPL-y 0.3.0
526 Clojure 1.5.1
527     Docs: (doc function-name-here)
528           (find-doc "part-of-name-here")
529   Source: (source function-name-here)
530  Javadoc: (javadoc java-object-or-class-here)
531     Exit: Control+D or (exit) or (quit)
532  Results: Stored in vars *1, *2, *3, an exception in *e
533
534 user=> (import '[org.opencv.core Mat Size CvType]
535                '[org.opencv.imgcodecs Imgcodecs]
536                '[org.opencv.imgproc Imgproc])
537 org.opencv.imgproc.Imgproc
538 @endcode
539 Now read the image from the resources/images/lena.png file.
540 @code{.clojure}
541 user=> (def lena (Highgui/imread "resources/images/lena.png"))
542 #'user/lena
543 user=> lena
544 #<Mat Mat [ 512*512*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x7f9ab3054c40, dataAddr=0x19fea9010 ]>
545 @endcode
546 As you see, by simply evaluating the lena symbol we know that lena.png is a 512x512 matrix of
547 CV_8UC3 elements type. Let's create a new Mat instance of the same dimensions and elements type.
548 @code{.clojure}
549 user=> (def blurred (Mat. 512 512 CvType/CV_8UC3))
550 #'user/blurred
551 user=>
552 @endcode
553 Now apply a GaussianBlur filter using lena as the source matrix and blurred as the destination
554 matrix.
555 @code{.clojure}
556 user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3)
557 nil
558 @endcode
559 As a last step just save the blurred matrix in a new image file.
560 @code{.clojure}
561 user=> (Highgui/imwrite "resources/images/blurred.png" blurred)
562 true
563 user=> (exit)
564 Bye for now!
565 @endcode
566 Following is the new blurred image of Lena.
567
568 ![](images/blurred.png)
569
570 Next Steps
571 ----------
572
573 This tutorial only introduces the very basic environment set up to be able to interact with OpenCV
574 in a CLJ REPL.
575
576 I recommend any Clojure newbie to read the [Clojure Java Interop
577 chapter](http://clojure.org/java_interop) to get all you need to know to interoperate with any plain
578 java lib that has not been wrapped in Clojure to make it usable in a more idiomatic and functional
579 way within Clojure.
580
581 The OpenCV Java API does not wrap the highgui module functionalities depending on Qt (e.g.
582 namedWindow and imshow. If you want to create windows and show images into them while interacting
583 with OpenCV from the REPL, at the moment you're left at your own. You could use Java Swing to fill
584 the gap.
585
586 ### License
587
588 Copyright © 2013 Giacomo (Mimmo) Cosenza aka Magomimmo
589
590 Distributed under the BSD 3-clause License, the same of OpenCV.