Cross compiling Opencv 3 for ARM

I recently wanted to play around with the Opencv library on a Raspberry Pi. I ran into some issues to get the right version installed, so I decided that the only way forward, was to compile the latest version from source. This article is the result of having to research and scour the web for the relevant information. There is no one source, that had all the answers for my particular case, so I decided to document the process, in case someone else might find it usefull.

The standard way to install Opencv on the Raspberry Pi, would be the following:

sudo apt-get install libopencv-dev

This, however, installs and older, version 2 variety of the library. I wanted to stick with the more modern version 3, hence the reason for this whole excersize.

First of all, the Opencv library is huge. Compiling it on the Pi itself will take many hours, so this was obviously not an option. It has to be cross compiled from a host PC. My desktop PC runs Ubuntu 14.10, but these instructions should also relevant for newer versions.

Before we start. there is a bit of work to do to get all the pieces in the right place.

Download raspberry pi sysroot:
rsync -rl --delete-after --safe-links pi@xxx.xxx.xxx.xxx:/{lib,usr} /opt/arm/RaspberryPi/sysroot
Replace the x’s above, with your pi’s ip address. However, this method might not be practical. On my Pi, the /usr directory alone, was almost 2.5Gb. rsync would have take all day to copy it. An alternative method, is to put the SD card into a reader and mount the SD card directly on the host pc. Copy the /lib and /usr directories to your sysroot path. When you are done building OpenCV, you can just copy them back to the SD card.

****Edit libc.so
****Fix libz link ../../../lib/arm-linux-gnueabihf/libz.so.1.2.8
****Fix libpng.so.0 link ln -s ../../../lib/arm-linux-gnueabihf/libpng12.so.0 libpng12.so.0
****Install gstreamer1.0

Get the Raspberry PI tools repository, which contains the compiler:
git clone https://github.com/raspberrypi/tools.git
In the subdirectory arm-bcm2708/ you will find different toolchains. I used gcc-linaro-arm-linux-gnueabihf-raspbian-x64, since my Ubuntu PC is 64bit. You should choocse the one that fits your architecture.

Point path to compiler in Raspberry pi tools
Make sure that the toolchain you selected, is added to your $PATH environment variable. In my case, I find it easier to add to my .bashrc file. Open it in your favorite text editor and add:
PATH=$PATH:/opt/arm/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
to the end of the file. Make sure the path refelcts your environment.

Configure OpenCV
Now we are ready to configure OpenCV for the build. We are going to use a platform build configuration, so find the file opencv/platforms/linux/arm-gnueabi.toolchain.cmake adn save it as pi.cmake. We will modify pi.cmake for our purpose. This could be done by ccmake, but I prefer to modified the file directly.

Find lines:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mthumb -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")

Change to:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi -march=armv6 -mfloat-abi=hard -mfpu=vfp")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi -march=armv6 -mfloat-abi=hard -mfpu=vfp")

I removed the lines:

set(FLOAT_ABI_SUFFIX "")
if (NOT SOFTFP)
set(FLOAT_ABI_SUFFIX "hf")
endif()

and replaced it with:
set(FLOAT_ABI_SUFFIX "hf")

Find the lines:
find_program(CMAKE_C_COMPILER NAMES arm-linux-gnueabi${FLOAT_ABI_SUFFIX}-gcc-${GCC_COMPILER_VERSION})
find_program(CMAKE_CXX_COMPILER NAMES arm-linux-gnueabi${FLOAT_ABI_SUFFIX}-g++-${GCC_COMPILER_VERSION})
Remove the version specifier so it looks like:
find_program(CMAKE_C_COMPILER NAMES arm-linux-gnueabi${FLOAT_ABI_SUFFIX}-gcc)
find_program(CMAKE_CXX_COMPILER NAMES arm-linux-gnueabi${FLOAT_ABI_SUFFIX}-g++)

Add archiver location
I had some problems initially, with the first lot of libraries that needs to be built. It turned out to be because cmake is unable to find the archiver, to create the libraries. To fix it, I had to tell cmake explicity where to find it. I am not entirely sure why this is needed, but if you have the same issue, the add the following line to pi.cmake:
set(CMAKE_AR /opt/arm/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/arm-linux-gnueabihf-ar CACHE FILEPATH "Archiver")
Only add this if if you get build error.

Set Raspberry pi sysroot path:
set(ARM_LINUX_SYSROOT /opt/arm/RaspberryPi/sysroot CACHE PATH "ARM cross compilation system root")

Create build directory
mkdir build
cd build

Configure
cmake \
-D CMAKE_TOOLCHAIN_FILE=../platforms/linux/pi.cmake \
-D CMAKE_BUILD_TYPE=RELEASE \
-D CMAKE_INSTALL_PREFIX=/opt/arm/RaspberryPi/sysroot \
-D INSTALL_C_EXAMPLES=OFF \
-D INSTALL_PYTHON_EXAMPLES=OFF \
-D BUILD_EXAMPLES=OFF ..

Build
make -j4
The -j4 is very handy if you happen to have a multi-core cpu. The 4 tells make, to use 4 cores for the build, to drastically reduce the build time.

Install
sudo make install
This step will copy all the library and include files, to your Raspberry Pi sysroot directory. You can now copy the directories back to the Pi.

This exercise, is an example of cross compiling, the act of using a host platform, to build files that will be used or executed on another platform, different to the host environment. In this case, we used an Intel x64 based system, to build files suited to the ARM platform.