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))