Working with opencv3 in Clojure

This short post will go all the way from compiling your own version of opencv3, with some secret information on how to get the build going. Then we will see how to create the java artefacts to use it from your favorite Java Runtime environment. Finally, we will see a few examples on how to call opencv functions from Clojure with just a wrapper.

Compiling your own opencv

It should be rather obvious but it was not for me, and opencv3. so here is what you need. There is a link deep down in the doc to refer to.

Requirements

On Linux, you probably would like to install the following packages:

# compiler
sudo apt-get install build-essential
# libraries
sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev

On Mac, install developper tools and cmake

brew install cmake

C Compilation

First clone the opencv repository.

git clone https://github.com/opencv/opencv.git

Look at the git tags and find one that suits you, we will do with 3.3.0-rc today:

git tag

Returns:

  • 3.1.0
  • 3.2.0
  • 3.2.0-rc
  • 3.3.0-rc
git checkout 3.3.0-rc

The secret is to create a buid folder and move into it.

mkdir build && cd build 

Then configure with cmake.

cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..

Finally compile and you can specifiy the number of threads to use with the -j parameter:

make -j8

This should take about an hour or so on a slow machine so let it run and do some work

Packaging the jar files

To use OpenCV you need two jar files, one generic jar file with the java interfaces, and one for the native code packaged in a jar file.

The first one has been generated and is in the bin folder:

./bin/opencv-330.jar

The second one we need to create with the short snippet below.

# Find the dylib native library

# NikoMacBook% find . -name *java*.dylib 
# ./lib/libopencv_java330.dylib
#
# Then for osx

mkdir -p native/macosx/x86_64
cp ./lib/libopencv_java330.dylib native/macosx/x86_64
jar -cMf opencv-native-osx.jar native

# or on linux
mkdir -p native/linux/x86_64
cp ./lib/libopencv_java330.so native/linux/x86_64/
jar -cMf opencv-native-linux.jar native

The path inside the native jar is very important for the library to be loaded properly:

 ├── native
 │   └── macosx
 │       └── x86_64
 │           └── libopencv_java330.dylib

Install or upload the two opencv jars

Install in your local maven repository

Maven does that for you using the install-file command.

mvn install:install-file \
-DgroupId=opencv \
-DartifactId=opencv \
-Dversion=3.3.0-rc \
-Dpackaging=jar \
-Dfile=opencv.jar

mvn install:install-file \
-DgroupId=opencv \
-DartifactId=opencv-native \
-Dversion=3.3.0-rc-osx \
-Dpackaging=jar \
-Dfile=opencv-native.jar

Deploy to your remote nexus server

Its about the same command to deploy to a remote repository. Note that you need the settings.xml file from $HOME/.m2/settings.xml to have the username/password settings for your remote repository.

# upload the main opencv jar file
mvn deploy:deploy-file -DgroupId=opencv \
-DartifactId=opencv \
-Dversion=3.3.0-rc \
-Dpackaging=jar \
-Dfile=opencv.jar \
-DrepositoryId=vendredi \
-Durl=http://hellonico.info:8081/repository/hellonico/

# upload the osx native jar file
mvn deploy:deploy-file -DgroupId=opencv \
-DartifactId=opencv-native \
-Dversion=3.3.0-rc-osx \
-Dpackaging=jar \
-Dfile=opencv-native-osx.jar \
-DrepositoryId=vendredi \
-Durl=http://hellonico.info:8081/repository/hellonico/

# upload the linux native jar file
mvn deploy:deploy-file -DgroupId=opencv \
-DartifactId=opencv-native \
-Dversion=3.3.0-rc-linux \
-Dpackaging=jar \
-Dfile=opencv-native-linux.jar \
-DrepositoryId=vendredi \
-Durl=http://hellonico.info:8081/repository/hellonico/

Use in a Clojure Project

As you have noticed, the libraries have been uploaded to the nexus repositories here:

http://hellonico.info:8081/repository/hellonico/

So they are ready to download.

Leiningen settings

The project.clj is pretty standard, except the injections which prevent you from typing the same code each time you start the REPL. Sweet.

(defproject opencv-fun "0.1.0-SNAPSHOT"
  :injections [(clojure.lang.RT/loadLibrary org.opencv.core.Core/NATIVE_LIBRARY_NAME)]
  :repositories [["vendredi" "http://hellonico.info:8081/repository/hellonico/"]]
  :dependencies [
  [org.clojure/clojure "1.8.0"]
  [org.clojure/tools.nrepl "0.2.11"]
  [proto-repl "0.3.1"]
  [opencv/opencv "3.3.0-rc"]
  [opencv/opencv-native "3.3.0-rc"]

  ])

First OpenCV Sample

Lena is always our reference, so providing the file path to the lena.png file is resources/images/lena.png, you should be able to do the java classes imports, and do some read writes with optional blur on the picture.

(import '[org.opencv.core Point Rect Mat CvType Size Scalar]
         org.opencv.imgcodecs.Imgcodecs
         org.opencv.imgproc.Imgproc)

; open close image, do not do anything
(let [lena (Imgcodecs/imread "resources/images/lena.png")]
  (Imgcodecs/imwrite "output/lena-copy.png" lena))

; load, blur and save
(let [lena (Imgcodecs/imread "resources/images/lena.png")
      blurred (Mat. 512 512 CvType/CV_8UC3)]
  (Imgproc/GaussianBlur lena blurred (Size. 9 9) 10 10)
  (Imgcodecs/imwrite "output/lena-blurred.png" blurred))

/blurred.png