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