I have compiled OpenCV many times now, since for one reason or another each time I needed to use it the previously compiled files would be lost. This was in addition done directly on the platform, which meant having the board running for 8-10 hours building everything, and not at all fun having to redo, for example for getting locked out of the board by a poor wifi AP configuration and having to re-flash the memory!
So I finally decided to do the sensible thing and cross-compile it, which means using the much higher processing power of my laptop to build the files, but for the target board’s architecture, significantly reducing the time needed (kind of like sending something to the cloud to be processed, but locally). The official OpenCV guide covers the basics, but is oriented to C++, since the files needed for Python development are not generated.
This meant that I simply would have to link to the Python libs and compile it. I merged the cross-compiler-specific instructions with the regular method I’ve followed other times and… it predictably failed. It turned out that I was trying to build OpenCV for an armhf architecture while linking it to Python libs from my native (and incompatible) x86_64, which of course meant that nothing worked.
After much trial and error I found that I had to link to the correct architecture files, and the best way to do so was copying them from the target board before the compilation. The full procedure would then look something like this:
- Install any missing packages needed and download OpenCV
- Copy the required files from the target board
- Cross-compile OpenCV
- Send the compiled library to the target board
Each stage is completed by a different script, which takes care of intermediate steps such as creating directories as well. There is also a configuration file where the board’s username, IP address and type are specified, and optionally the password too. The board type is arm-linux-gnueabihf for an armhf architecture and x86_64-linux-gnu for x86_64, there will probably be a folder under /usr/include/ with the correct name for other architectures. If the password field is filled, that is what will be used to transfer the files, if left empty the user must manually enter it each time.
The configuration file:
NAME="nanopi" ADDRESS="192.168.42.1" ARCHITECTURE="arm-linux-gnueabihf" # If the password is provided the transfer of files from/to the board will be # done automatically, otherwise it will be asked for every time it's needed. PASSWORD=""
Step 1, installing packages and downloading OpenCV:
#!/usr/bin/env bash # Install libs echo "----- Installing necessary packages" sudo apt-get update sudo apt-get install -y build-essential sshpass cmake pkg-config libjpeg-dev libtiff5-dev libjasper-dev libpng12-dev libavcodec-dev libavformat-dev libswscale-dev libv4l-dev libxvidcore-dev libx264-dev libgtk2.0-dev libgtk-3-dev libcanberra-gtk* libatlas-base-dev gfortran python2.7-dev python3-dev sudo pip install numpy sudo apt-get install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf # Download OpenCV echo "----- Downloading OpenCV" git clone https://github.com/opencv/opencv.git git clone https://github.com/opencv/opencv_contrib.git
Step 2, copying files from the board:
#!/usr/bin/env bash source scripts/config # Create folder structure echo "----- Creating python folder structure" mkdir opencv/python mkdir opencv/python/lib mkdir opencv/python/include mkdir opencv/python/include/python2.7 mkdir opencv/python/include/python3.5m cd opencv/python echo "----- Retrieving python files" if [ -n "${PASSWORD}" ]; then # Python config sshpass -p ${PASSWORD} scp ${NAME}@${ADDRESS}:/usr/include/${ARCHITECTURE}/python2.7/pyconfig.h include/python2.7/ sshpass -p ${PASSWORD} scp ${NAME}@${ADDRESS}:/usr/include/${ARCHITECTURE}/python3.5m/pyconfig.h include/python3.5m/ # Python libraries sshpass -p ${PASSWORD} scp ${NAME}@${ADDRESS}:/usr/lib/${ARCHITECTURE}/libpython*.so lib else # Python config scp ${NAME}@${ADDRESS}:/usr/include/${ARCHITECTURE}/python2.7/pyconfig.h include/python2.7/ scp ${NAME}@${ADDRESS}:/usr/include/${ARCHITECTURE}/python3.5m/pyconfig.h include/python3.5m/ # Python libraries scp ${NAME}@${ADDRESS}:/usr/lib/${ARCHITECTURE}/libpython*.so lib fi # Copy Python config files echo "----- Copying imported config files" sudo mkdir /usr/include/${ARCHITECTURE} sudo mkdir /usr/include/${ARCHITECTURE}/python2.7 sudo mkdir /usr/include/${ARCHITECTURE}/python3.5m sudo cp include/python2.7/pyconfig.h /usr/include/${ARCHITECTURE}/python2.7 sudo cp include/python3.5m/pyconfig.h /usr/include/${ARCHITECTURE}/python3.5m
Step 3, cross-compiling:
#!/usr/bin/env bash echo "----- Creating cross-compiled build directory structure" cd opencv mkdir buildCross cd buildCross mkdir installation make clean echo "----- Setting up compilation" cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=installation \ -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib/modules \ -D CMAKE_TOOLCHAIN_FILE=../platforms/linux/arm-gnueabi.toolchain.cmake ../ \ -D PYTHON2_INCLUDE_PATH=/usr/include/python2.7 \ -D PYTHON2_INCLUDE_DIR=../python/include/python2.7 \ -D PYTHON2_LIBRARY=../python/lib/libpython2.7.so \ -D PYTHON2_NUMPY_INCLUDE_DIRS=/usr/local/lib/python2.7/dist-packages/numpy/core/include \ -D PYTHON3_INCLUDE_PATH=/usr/include/python3.5m \ -D PYTHON3_INCLUDE_DIR=../python/include/python3.5m \ -D PYTHON3_LIBRARY=../python/lib/libpython3.5m.so \ -D PYTHON3_NUMPY_INCLUDE_DIRS=/usr/lib/python3/dist-packages/numpy/core/include \ -D BUILD_OPENCV_PYTHON2=ON \ -D BUILD_OPENCV_PYTHON3=ON \ -D INSTALL_PYTHON_EXAMPLES=OFF \ -D BUILD_TESTS=OFF \ -D BUILD_EXAMPLES=OFF \ -D ENABLE_NEON=ON \ -D ENABLE_VFPV3=ON \ -D WITH_TBB=ON \ -D BUILD_TBB=ON .. echo "----- Starting compilation" make -j8 make install cd installation/lib/python3.5/dist-packages/ mv cv2* cv2.so echo "----- OpenCV correctly built"
Step 4, sending compiled files to the target board:
#!/usr/bin/env bash source scripts/config # Send compiled OpenCV to embedded board echo "----- Sending compiled files to board" if [ -n "${PASSWORD}" ]; then sshpass -p ${PASSWORD} scp -r opencv/buildCross/installation/ ${NAME}@${ADDRESS}:~/ else scp -r opencv/buildCross/installation/ ${NAME}@${ADDRESS}:~/ fi
I also wrote a small script that calls all the previous ones sequentially so everything can be done in one go:
#!/usr/bin/env bash chmod +x scripts/*.sh scripts/downloadPackages.sh scripts/importFiles.sh scripts/crossCompile.sh scripts/sendOpenCV.sh echo "----- To complete installation, log into the target board and execute the command: " echo "sudo rsync -av installation/ /usr/local"
To launch this, make sure that the board and computer are both connected to the Internet and on the same network, and execute
chmod +x fullProcess.sh && ./fullProcess.sh
Once the installation folder containing the compiled library has been transferred to the board, log into it and execute
sudo rsync -av installation/ /usr/local
to copy the different files to the corresponding places.
If when trying to import cv2 there is an error about not finding the corresponding GLIBXX version in the board as the one used to compile, try upgrading g++ to the appropiate version. For example, for GLIBCXX_3.4.22 execute
sudo apt upgrade g++-6
The complete process will vary depending on processing speed, Internet connection and packages already present, but in my case took just under 30 minutes from launching the script to receiving the folder in the board.