How to Install OpenCV on Heroku (with ffmpeg support)

Too Long; Did not Read:

Update heroku to use this buildpack URL: https://github.com/slobdell/heroku-buildpack-python-opencv

True Story Follows:

Recently I’ve been playing around with Heroku, and I’ve been trying to create a web application that uses OpenCV on the backend. Initially there were quite a few problems to even get this working at all, but I did find a custom buildpack that worked quite well. Sort of.

This buildpack worked by offloading pre-compiled OpenCV binaries to heroku’s environment. This had to hapen because OpenCV takes forever to compile and install, and a 15 minute build timeout on Heroku will prevent an easy solution to getting OpenCV deployed (and that’s also a really long time anyway). However, using the buildpack mentioned above, I ended up having a few random problems. For instance, I couldn’t get libmemcached installed (which is otherwise trivial in Heroku’s default buildpack), and I couldn’t include any python module in my requirements.txt file that used bz2 compression. This was all acceptable to work with until I got to the point where I actually tried to do some stuff with OpenCV…I was playing around with videos so I needed ffmpeg support, but the version of OpenCV from the buildpack was not compiled with that support enabled. So, at this point I had to learn and figure out how to replicate the work to make a custom buildpack.  And this is how it felt: azLM7op_700b

Context: Minimal understanding of integrating the buildback

A custom buildpack can be specified for Heroku using:

heroku config:set BUILDPACK_URL=https://github.com/slobdell/heroku-buildpack-python-opencv-scipy

In the default buildpacks, there are a lot of extra files and options because of automated and dynamic configurations, but if you’re using a custom buildpack, then there’s not really a need to be dynamic or infer any options because you’re specifying exactly what they are. When you deploy to Heroku, a virtual machine will be created using the specifications from the above Github URL. You can learn more about Heroku custom buildbacks here.

Getting Started: Create an empty Virtual Machine

The nice thing about this tutorial is that presumably, if you copy and paste from start to finish you should end up with the exact same results given that the startpoint should be identical. I grabbed an empty Vagrant template from https://github.com/ejholmes/vagrant-heroku. So for these steps:

  • Download VirtualBox (in order to run virtual machines)
  • Download Vagrant (in order for VirtualBox to open the heroku box machine below)
  • Create a file somewhere called “VagrantFile” with the following lines:
    •                 Vagrant::Config.run do |config|
                      config.vm.box = "heroku"
                      config.vm.box_url = "https://s3.amazonaws.com/lobbdawg/heroku.box"
                      end
    • Note that the URL I have in the configuration is different from the one in the repo’s README. At the time of this writing I copied the file to Amazon so that this tutorial couldn’t change over time.
  • Run “vagrant up”
  • Open VirtualBox and there should now be a new machine running 64 bit Ubuntu 10.04 that you can start

Login

Open VirtualBox and there should now be a new machine. Before you start, you’ll need to up the available RAM in the machine, otherwise the OpenCV compilation will end up failing. I went ahead and changed it from 512 to 2045 MB (settings->system). Start the machine and login with credentials:
Username: vagrant
Password: vagrant

Also note that the shell wasn’t visible at first, so like most problems, you can fix by turning it off and turning it back on

Install some stuff before OpenCV

Ubuntu 10.04’s version of python is 2.6, but we probably want python 2.7. Without it, you can’t do list comprehensions with dictionaries, and there are some things left out of the collections module. So unless you like to go to the gym and put on a couple quarters instead of stacking 45’s, you shouldn’t skip these steps:

  • sudo apt-get install libbz2-dev

    (lesson learned from using the other buildpack; without it you might have issues with pip for certain modules)

  • Download Python 2.7.8 from here
  • wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgz
    tar -xzvf Python-2.7.8.tgz
    cd Python-2.7.8
    ./configure --enable-shared --enable-static
         

    (IMPORTANT! The extra options to compile with a shared object are needed for compiling OpenCV with ffmpeg support)

  • make
  • sudo make install
  • sudo ldconfig

    (I had to run this because running python threw an error about not being able to find libpython2.7.so.1.0)

  • python --version

    (should give the correct version of 2.7.8 now)

Now if we want to install anything via pip, we also need to verify that it’s installing for Python2.7 and not Python2.6.

pip --version

should output something about how it’s configured for 2.6. To fix this:

wget https://bootstrap.pypa.io/get-pip.py
sudo python get-pip.py
cd /usr/local/bin
sudo ln -sf pip2.7 pip

Honestly I’m not sure if the next steps are entirely necessary. I’m pretty sure you can just wait and include these in your requirements.txt file for when you deploy to Heroku, but I went ahead and installed Numpy now. It’s used in the OpenCV compilation, and using “sudo apt-get install python-numpy” will install for Python 2.6:

sudo pip install numpy
sudo pip install virtualenv
sudo pip install virtualenvwrapper

Install OpenCV

There are a few tutorials on the internets about how to install OpenCV with ffmpeg support, but remember to keep in mind that you are using 64 bit Ubuntu so compilation will end up failing if you don’t add a few options when you’re downloading some of the dependencies. The tutorial I used that worked is here: A Comprehensive Guide to Installing and Configuring OpenCV. If you don’t want to click the link though, here’s a list of all the commands you need to run:

sudo apt-get remove ffmpeg x264 libx264-dev

sudo apt-get update

sudo apt-get install build-essential checkinstall git cmake libfaac-dev libjack-jackd2-dev libmp3lame-dev libopencore-amrnb-dev libopencore-amrwb-dev libsdl1.2-dev libtheora-dev libva-dev libvdpau-dev libvorbis-dev libx11-dev libxfixes-dev libxvidcore-dev texi2html yasm zlib1g-dev

sudo apt-get install libgstreamer0.10-0 libgstreamer0.10-dev gstreamer0.10-tools gstreamer0.10-plugins-base libgstreamer-plugins-base0.10-dev gstreamer0.10-plugins-good gstreamer0.10-plugins-ugly gstreamer0.10-plugins-bad gstreamer0.10-ffmpeg

sudo apt-get install libgtk2.0-0 libgtk2.0-dev

sudo apt-get install libjpeg8 libjpeg8-dev

cd ~
mkdir src

cd ~/src

wget ftp://ftp.videolan.org/pub/videolan/x264/snapshots/x264-snapshot-20120528-2245-stable.tar.bz2

tar xvf x264-snapshot-20120528-2245-stable.tar.bz2

cd x264-snapshot-20120528-2245-stable

./configure --enable-shared --enable-pic
make
sudo make install

cd ~/src
wget http://ffmpeg.org/releases/ffmpeg-0.11.1.tar.bz2
tar xvf ffmpeg-0.11.1.tar.bz2
cd ffmpeg-0.11.1

./configure --enable-gpl --enable-libfaac --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libxvid --enable-nonfree --enable-postproc --enable-version3 --enable-x11grab --enable-shared --enable-pic
make
sudo make install

cd ~/src

wget http://www.linuxtv.org/downloads/v4l-utils/v4l-utils-0.8.8.tar.bz2

tar xvf v4l-utils-0.8.8.tar.bz2
cd v4l-utils-0.8.8
make
sudo make install

cd ~/src
sudo apt-get install unzip
wget http://sourceforge.net/projects/opencvlibrary/files/opencv-unix/2.4.10/opencv-2.4.10.zip
unzip opencv-2.4.10.zip
cd opencv-2.4.10
mkdir build
cd build

cmake -D CMAKE_BUILD_TYPE=RELEASE -D BUILD_NEW_PYTHON_SUPPORT=ON -D BUILD_EXAMPLES=ON -D PYTHON_EXECUTABLE=/usr/local/bin/python -D PYTHONG_INCLUDE_DIR=/usr/local/include/python2.7 -D PYTHON_LIBRARY=/usr/local/lib/libpython2.7.so -D PYTHON_PACKAGES_PATH=/usr/local/lib/python2.7/site-packages -D PYTHON_NUMPY_INCLUDE_DIR=/usr/local/lib/python2.7/site-packages/numpy/core/include ..
make
sudo make install

Edit:

With the above setup, you’ll get a compile error from line 50 of cl2cpp.cmake. After some googling around for this problem, I just deleted line 50 and continued the install. It worked fine.

That last step is going to take a while: compiling To get OpenCV working on your system you might need to edit your ~/.bashrc file and add:

export LD_LIBRARY_PATH=/usr/local/lib

then apply:

source ~/.bashrc

Veryify everything works:

python -c "import numpy; import cv2"

If there were no errors you’re good so far.

Tar the pre-compiled stuff to export it to Heroku

At this point I just used the existing template from the initial buildpack I went off of. Don’t judge. When I untarred the file that the author initially created I found that the contents of the file were basically a few of the contents from /usr/local moved to .heroku. So I simply created a temporary folder and copied the contents of bin, include, lib, man, and share from /usr/local. I also found that I needed to copy /usr/lib into the tar file as well because of some shared objects that OpenCV depended on. I’m not sure if all of these were actually necessary, but I’m not going to overcomplicate something that already works. Don’t judge. So what I did:

cd ~
mkdir .heroku
mkdir .heroku/vendor
cp -r /usr/local/bin .heroku/vendor/
cp -r /usr/local/include .heroku/vendor/
cp -r /usr/local/man .heroku/vendor/
cp -r /usr/local/share .heroku/vendor/
cp -r /usr/local/lib .heroku/vendor/
cp -r /usr/lib/lib* .heroku/vendor/lib/
tar -czvf file_for_heroku.tar.gz .heroku

Tar the contents of .heroku, transfer it to some external URL, and use that URL in the buildpack. At this point, I had to fiddle with some of the settings in the buildpack to get everything working properly. You can checkout the resulting repository on git for details.

The End

  • Thieu

    I’ve tried to create an app with your buildpack and it seems to be too big by itself for the 300M per push limit of heroku.

    I did:
    heroku create opcvtest –buildpack https://github.com/slobdell/heroku-buildpack-python-opencv
    then added an empty file for good measure and then pushed and it gave me this

    Counting objects: 3, done.

    Writing objects: 100% (3/3), 240 bytes | 0 bytes/s, done.

    Total 3 (delta 0), reused 0 (delta 0)

    remote: Compressing source files… done.

    remote: Building source:

    remote:

    remote: —–> Fetching custom buildpack https://github.com/slobdell/heroku-buildpack-python-opencv… done

    remote: —–> Python + OpenCV + Numpy + Scipy app detected

    remote: —–> Generating environment

    remote: Fetching…

    remote: Unpacking…

    remote: —–> Creating environment variables.

    remote: —–> Buildpack installed.

    remote:

    remote: —–> Discovering process types

    remote: Procfile declares types -> web

    remote:

    remote: —–> Compressing…

    remote: ! Compiled slug size: 313.2MB is too large (max is 300MB).

    remote: ! See: http://devcenter.heroku.com/articles/slug-size

    remote: ! Push failed: slug archive could not be created

    remote: ! Please try pushing again.

    remote: ! If the problem persists, see http://help.heroku.com/ and provide Request ID a7b9c28e-8b94-4048-aca7-1a440e2ccf7e.

    remote:

    remote:

    remote: Verifying deploy…

    remote:

    remote: ! Push rejected to opcvtest.

    remote:

    Did I do anything wrong?
    Thanks

    • Scott Benedict Lobdell

      I haven’t messed with this in a while, I’ll see if I can play around with it tomorrow with any luck. I vaguely recall having to trim things down a bit to stay under the limit

      • Thieu

        Awesome thanks.

        As a wild guess, did they upgrade their stack and maybe we should specify an older stack at the creation of the app?

        • Scott Benedict Lobdell

          That might be the case, but if I recall, the build pack was 200 something MB, so I had to ensure that the app I was deploying was less than 50 MB or something like that

          • Thieu

            Ok.
            Now, the buildpack (including base stack) without any app is something like 313MB.

  • Alex McConnell

    Any idea how applicable this might be today? With Ubuntu 14 and IDK what going on with Heroku. Python and OpenCV versions are close enough to what I want though.

    • Scott Benedict Lobdell

      No idea, the last time I messed with this was about the time this blog post was published 🙂

  • blipton

    Is the idea that openCV can run faster when on a PaaS? Not sure how cost effective it is in comparison, but I’ve read Digital Ocean is commonly used for doing number crunching (like for cascade training).

    BTW, looking at the github repo, it’s not entirely clear how to use it… any pointers?