|
|
|
@ -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 <http://clojure.org/>`_ 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 <http://docs.opencv.org/2.4.4-beta/doc/tutorials/introduction/desktop_java/java_dev_intro.html>`_. |
|
|
|
|
|
|
|
|
|
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 <https://developer.apple.com/xcode/>`_, |
|
|
|
|
`jdk <http://www.oracle.com/technetwork/java/javase/downloads/index.html>`_ |
|
|
|
|
and `Cmake <http://www.cmake.org/cmake/resources/software.html>`_. |
|
|
|
|
|
|
|
|
|
.. 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 <https://github.com/technomancy/leiningen>`_ which allows |
|
|
|
|
you to manage the entire life cycle of your CLJ projects. |
|
|
|
|
|
|
|
|
|
The available `installation guide <https://github.com/technomancy/leiningen#installation>`_ is very easy to be followed: |
|
|
|
|
|
|
|
|
|
1. `Download the script <https://raw.github.com/technomancy/leiningen/stable/bin/lein>`_ |
|
|
|
|
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 <https://github.com/technomancy/leiningen#windows>`_ |
|
|
|
|
|
|
|
|
|
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 <https://github.com/kumarshantanu/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 <http://docs.opencv.org/java/>`_ you can find the |
|
|
|
|
full OpenCV Java API. |
|
|
|
|
|
|
|
|
|
.. code:: clojure |
|
|
|
|
|
|
|
|
|
user=> (org.opencv.core.Point. 0 0) |
|
|
|
|
#<Point {0.0, 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 |
|
|
|
|
#<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 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) |
|
|
|
|
#<Rect {0, 0, 100x100}> |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
#<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 |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
#<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: |
|
|
|
|
|
|
|
|
|
.. 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 <http://docs.opencv.org/2.4.4-beta/doc/tutorials/introduction/desktop_java/java_dev_intro.html>`_. |
|
|
|
|
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)) |
|
|
|
|
#<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 |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
#<Mat Mat [ 512*512*CV_8UC3, isCont=true, isSubmat=false, nativeObj=0x7f9ab3054c40, dataAddr=0x19fea9010 ]> |
|
|
|
|
|
|
|
|
|
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 <http://clojure.org/java_interop>`_ 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. |