From: Mimmmo Cosenza Date: Wed, 11 Dec 2013 12:30:52 +0000 (+0100) Subject: add tutorial content X-Git-Tag: accepted/tizen/6.0/unified/20201030.111113~1314^2~712^2~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1c639befe636e4be32fad5b49af4a19251fa89cb;p=platform%2Fupstream%2Fopencv.git add tutorial content --- diff --git a/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst new file mode 100644 index 0000000..248abdf --- /dev/null +++ b/doc/tutorials/introduction/clojure_dev_intro/clojure_dev_intro.rst @@ -0,0 +1,728 @@ +.. _clojure_dev_intro: + +Introduction to OpenCV Development with Clojure +*********************************************** + +As of OpenCV 2.4.4, OpenCV supports desktop Java development using +nearly the same interface as for Android development. + +`Clojure `_ is a contemporary LISP dialect hosted +by the Java Virtual Machine and it offers a complete interoperability +with the underlying JVM. This means that we should even be able to use +the Clojure REPL (Read Eval Print Loop) as and interactive programmable +interface to the underlying OpenCV engine. + +What we'll do in this tutorial +============================== + +This tutorial will help you in setting up a basic Clojure environment +for interactively learning OpenCV within the fully programmable +CLojure REPL. + +Tutorial source code +-------------------- + +You can find a runnable source code of the sample in the +:file:`samples/java/clojure/simple-sample` folder of the OpenCV +repository. After having installed OpenCV and Clojure as explained in +the tutorial, issue the following command to run the sample from the +command line. + +.. code:: bash + + cd path/to/samples/java/clojure/simple-sample + lein run + +Preamble +======== + +For detailed instruction on installing OpenCV with desktop Java support +refer to the `corresponding tutorial `_. + +If you are in hurry, here is a minimum quick start guide to install +OpenCV on Mac OS X: + + NOTE 1: I'm assuming you already installed + `xcode `_, + `jdk `_ + and `Cmake `_. + +.. code:: bash + + cd ~/ + mkdir opt + git clone https://github.com/Itseez/opencv.git + cd opencv + git checkout 2.4 + mkdir build + cd build + cmake -DBUILD_SHARED_LIBS=OFF .. + ... + ... + make -j8 + # optional + # make install + +Install Leiningen +================= + +Once you installed OpenCV with desktop java support the only other +requirement is to install +`Leiningeng `_ which allows +you to manage the entire life cycle of your CLJ projects. + +The available `installation guide `_ is very easy to be followed: + +1. `Download the script `_ +2. Place it on your ``$PATH`` (cf. ``~/bin`` is a good choice if it is + on your ``path``.) +3. Set the script to be executable. (i.e. ``chmod 755 ~/bin/lein``). + +If you work on Windows, follow `this instruction `_ + +You now have both the OpenCV library and a fully installed basic Clojure +environment. What is now needed is to configure the Clojure environment +to interact with the OpenCV library. + +Install the localrepo Leiningen plugin +======================================= + +The set of commands (tasks in Leiningen parlance) natively supported by +Leiningen can be very easily extended by various plugins. One of them is +the `lein-localrepo `_ +plugin which allows to install any jar lib as an artifact in the local +maven repository of your machine (typically in the ``~/.m2/repository`` +directory of your username). + +We're going to use this ``lein`` plugin to add to the local maven +repository the opencv components needed by Java and Clojure to use the +opencv lib. + +Generally speaking, if you want to use a plugin on project base only, it +can be added directly to a CLJ project created by ``lein``. + +Instead, when you want a plugin to be available to any CLJ project in +your username space, you can add it to the ``profiles.clj`` in the +``~/.lein/`` directory. + +The ``lein-localrepo`` plugin will be useful to me in other CLJ +projects where I need to call native libs wrapped by a Java interface. +So I decide to make it available to any CLJ project: + +.. code:: bash + + mkdir ~/.lein + +Create a file named ``profiles.clj`` in the ``~/.lein`` directory and +copy into it the following content: + +.. code:: clojure + + {:user {:plugins [[lein-localrepo "0.5.2"]]}} + +Here we're saying that the version release ``"0.5.2"`` of the +``lein-localrepo`` plugin will be available to the ``:user`` profile for +any CLJ project created by ``lein``. + +You do not need to do anything else to install the plugin because it +will be automatically downloaded from a remote repository the very first +time you issue any ``lein`` task. + +Install the java specific libs as local repository +================================================== + +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: + +- 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) + +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 the ``opencv-247.jar`` lib. + +.. code:: bash + + 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 the ``libopencv_java247.dylib`` shared 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 architecture. I'm using a Mac OS +X with a X86 64 bit architecture. So my layout will be the following: + +.. code:: bash + + mkdir -p native/macosx/x86_64 + +Copy into the ``x86_64`` directory the ``libopencv_java247.dylib`` lib. + +.. code:: bash + + 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. + +.. code:: bash + + 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 + +Package the native lib as a jar +------------------------------- + +Next you need to package the native lib in a jar file by using the +``jar`` command to create a new jar file from a directory. + +.. code:: bash + + jar -cMf opencv-native-247.jar native + +Note that ehe ``M`` option instructs the ``jar`` command to not create +a MANIFEST file for the artifact. + +Your directories layout should look like the following: + +.. code:: bash + + tree + . + |__ native + |   |__ macosx + |   |__ x86_64 + |   |__ libopencv_java247.dylib + | + |__ opencv-247.jar + |__ opencv-native-247.jar + + 3 directories, 3 files + +Locally install the jars +------------------------ + +We are now ready to add the two jars as artifacts to the local maven +repository with the help of the ``lein-localrepo`` plugin. + +.. code:: bash + + 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.jar`` lib and +then installs it into the local maven repository. The ``opencv/opencv`` +artifact 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. + +.. code:: bash + + 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 the ``lein new`` task from the +terminal. + +.. code:: bash + + # 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 following ``simple-sample`` directories +layout: + +.. code:: bash + + 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 + +We need to add the two ``opencv`` artifacts as dependencies of the newly +created project. Open the ``project.clj`` and modify its dependencies +section as follows: + +.. code:: bash + + (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 the ``lein deps`` task. The +very first time you run a ``lein`` task it will take sometime to +download all the required dependencies before executing the task +itself. + +.. code:: bash + + cd simple-sample + lein deps + ... + +The ``deps`` task reads and merges from the ``project.clj`` and the +``~/.lein/profiles.clj`` files all the dependencies of the +``simple-sample`` project 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. + +REPLing with OpenCV +------------------- + +Now ``cd`` in the ``simple-sample`` directory and issue the following +``lein`` task: + +.. code:: bash + + 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=> + +You can immediately interact with the REPL by issuing any CLJ expression +to be evaluated. + +.. code:: clojure + + 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 the +``lein repl`` task automatically loads all the project dependencies, you +still need to load the opencv native library to be able to interact with +the OpenCV. + +.. code:: clojure + + 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: `Here `_ you can find the + full OpenCV Java API. + +.. code:: clojure + + user=> (org.opencv.core.Point. 0 0) + # + +Here we created a two dimensions opencv ``Point`` 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 +the ``Point.`` instance constructors with the fully qualified package +name. + +Fortunately CLJ offer a very easy way to overcome this annoyance by +directly importing the ``Point`` class. + +.. code:: clojure + + 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 + +We can even inspect the class of an instance and verify if the value of +a symbol is an instance of a ``Point`` java class. + +.. code:: clojure + + user=> (class p1) + org.opencv.core.Point + user=> (instance? org.opencv.core.Point p1) + true + +If we now want to use the opencv ``Rect`` class to create a rectangle, +we again have to fully qualify its constructor even if it leaves in +the same ``org.opencv.core`` package of the ``Point`` class. + +.. code:: clojure + + user=> (org.opencv.core.Rect. p1 p2) + # + +Again, the CLJ importing facilities is very handy and let you to map +more symbols in one shot. + +.. code:: clojure + + 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 + +Obviously you can call methods on instances as well. + +.. code:: clojure + + user=> (.area r1) + 10000.0 + user=> (.area sq-100) + 10000.0 + +Or modify the value of a member field. + +.. code:: clojure + + 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 + +If you find yourself not remembering a OpenCV class behavior, the +REPL gives you the opportunity to easily search the corresponding +javadoc documention: + +.. code:: clojure + + user=> (javadoc Rect) + "http://www.google.com/search?btnI=I%27m%20Feeling%20Lucky&q=allinurl:org/opencv/core/Rect.html" + +Mimic the OpenCV Java Tutorial Sample in the REPL +------------------------------------------------- + +Let's now try to port to Clojure the `opencv 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. + +.. code:: 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()); + } + + } + +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. + +.. code:: clojure + + user=> (exit) + Bye for now! + +Then open your ``project.clj`` file and edit it as follows: + +.. code:: clojure + + (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 the ``lein repl`` task + +.. code:: bash + + 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=> + +Import the interested OpenCV java interfaces. + +.. code:: clojure + + 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 + +.. code:: clojure + + 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 + +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-D`` or ``(quit)`` at the REPL +prompt. + +.. code:: clojure + + 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: + +- the ``imread`` static method from the ``Highgui`` class to read an + image from a file +- the ``imwrite`` static method from the ``Highgui`` class to write an + image to a file +- the ``GaussianBlur`` static method from the ``Imgproc`` class to + apply to blur the original image + +We're also going to use the ``Mat`` class which is returned from the +``imread`` method and accpeted as the main argument to both the +``GaussianBlur`` and the ``imwrite`` methods. + +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. + +.. image:: images/lena.png + :alt: Original Image + :align: center + +.. code:: bash + + 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: + +.. code:: clojure + + 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 the ``resources/images/lena.png`` file. + +.. code:: clojure + + user=> (def lena (Highgui/imread "resources/images/lena.png")) + #'user/lena + user=> lena + # + +As you see, by simply evaluating the ``lena`` symbol we know that +``lena.png`` is a ``512x512`` matrix of ``CV_8UC3`` elements type. Let's +create a new ``Mat`` instance of the same dimensions and elements type. + +.. code:: clojure + + user=> (def blurred (Mat. 512 512 CvType/CV_8UC3)) + #'user/blurred + user=> + +Now apply a ``GaussianBlur`` filter using ``lena`` as the source matrix +and ``blurred`` as the destination matrix. + +.. code:: clojure + + user=> (Imgproc/GaussianBlur lena blurred (Size. 5 5) 3 3) + nil + +As a last step just save the ``blurred`` matrix in a new image file. + +.. code:: clojure + + user=> (Highgui/imwrite "resources/images/blurred.png" blurred) + true + user=> (exit) + Bye for now! + +Following is the new blurred image of Lena. + +.. image:: images/blurred.png + :alt: Blurred Image + :align: center + +Next Steps +========== + +This tutorial only introduces the very basic environment set up to be +able to interact with OpenCV in a CLJ REPL. + +I recommend any Clojure newbie to read the `Clojure Java Interop chapter `_ to get all you need to know +to interoperate with any plain java lib that has not been wrapped in +Clojure to make it usable in a more idiomatic and functional way within +Clojure. + +The OpenCV Java API does not wrap the ``highgui`` module +functionalities depending on ``Qt`` (e.g. ``namedWindow`` and +``imshow``. If you want to create windows and show images into them +while interacting with OpenCV from the REPL, at the moment you're left +at your own. You could use Java Swing to fill the gap. + + +License +------- + +Copyright © 2013 Giacomo (Mimmo) Cosenza aka Magomimmo + +Distributed under the BSD 3-clause License, the same of OpenCV. diff --git a/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png b/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png new file mode 100644 index 0000000..c8aeea8 Binary files /dev/null and b/doc/tutorials/introduction/clojure_dev_intro/images/blurred.png differ diff --git a/doc/tutorials/introduction/clojure_dev_intro/images/lena.png b/doc/tutorials/introduction/clojure_dev_intro/images/lena.png new file mode 100644 index 0000000..68342fa Binary files /dev/null and b/doc/tutorials/introduction/clojure_dev_intro/images/lena.png differ