You can see it when you look out your window...

In which we make OpenCV play nice with Anaconda under Windows and roll libjpeg-turbo in along the way.

You may have noticed, if you have been reading my blog, that I like to prototype in Python.  I find Python to be almost as fun as Lisp when it comes to building up a solution without knowing exactly what the solution is going to look like.  The reason I typically start in Python is the wealth of libraries; however, managing a Python install can become a real pain.  Thanks to the people over at Continuum Analytics, you can be spared most of it.

Continuum Analytics produces a fantastic distribution known as Anaconda (or Miniconda depending on the route you choose).  I won't go into all the reasons I think you should give it a try, but if you program in Python, you should check them out.  The only shortcoming I have come across is the Windows build does not include OpenCV, but we will remedy that today.  Along the way, I will also show you how to build OpenCV against libjpeg-turbo for better jpeg performance.

Things you will need:

  1. CMake: Used to generate makefiles/solutions to build OpenCV.
  2. Visual Studio: This article is geared towards VS, but you should be able to follow roughly the same steps for other compilers.
  3. OpenCV: The reason for this excursion.  I downloaded the self extracting Windows package.  You can just grab the source if you like.
  4. libjpeg-turbo: An optimized crossplatform library for working with jpegs.
  5. Anaconda (or Miniconda): My (and soon to be yours) favorite Python distribution.

 

Caution:

Make sure your versions match, i.e. if you install 64 bit Anaconda, install the 64 bit version of libjpeg-turbo and build OpenCV for 64 bits.

If you use Miniconda to make your default Python installation Python 3 (as I have done) you will need to make a Python 2 environment.  Please see here.

 To the Bat Cave!

  1. Run the self extracting OpenCV archive if necessary, and extract OpenCV to a place you will remember, e.g. C:\OpenCV
  2. Run the installer for libjpeg-turbo
  3. If your default Python install is Python 3
    1. Right click on the cmake/bin directory while holding down Shift
    2. Click "Open command window here"
    3. Activate your Python 2 environment with
      activate ENV_NAME
  4. Launch cmake-gui (from the command prompt in step 3 if necessary)
  5. Set "Where is the source code:" to the path in step 1
  6. Set "Where to build the binaries:" to where you normally build libs, e.g. C:\Libs\OpenCV
  7. Click Configure and wait for it to complete
  8. Check the Advanced checkbox
  9. Locate the option BUILD_JPEG and uncheck it
  10. Click the Add Entry button and add
    • Name: JPEG_LIBRARY
    • Type: FILEPATH
    • Value: path to the static libjpeg-turbo64 e.g. C:/libjpeg-turbo64/lib/jpeg-static.lib
  11. Click the Add Entry button and add
    • Name: JPEG_INCLUDE_DIR
    • Type: PATH
    • Value: path to the libjpeg-turbo64 include e.g. C:/libjpeg-turbo64/include
  12. If you see a message in the Configure output that says PYTHON_INCLUDE_DIR and/or PYTHON_LIBRARY are not found, you will need to set them by hand by clicking in the Value column and entering the path. On my system (Windows 8.1 Pro) these are C:/Users/lemoneer/Miniconda3/envs/python-2/include and C:/Users/lemoneer/Miniconda3/envs/python-2/libs/python27.lib respectively.
  13. Click Configure again and wait for it to complete
  14. Verify all is well in the output of Configure
  15. Click Generate
  16. Open the generated solution file in your build directory from step 6
  17. Change the build to Release
  18. Build the Solution
  19. Expand CMakeTargets in the Solution Explorer
  20. Right click on INSTALL
  21. Click Build
  22. Once the build is complete, update your path to include the OpenCV dlls
  23. You may Clean the Build at the solution level to remove intermediate files you do not need (this wont affect the install).

 

It is worth nothing that there are other build options worth considering like building the examples.  Enable/disable to your heart's content then click Configure followed by Generate and Build.

If all goes well, you will have OpenCV dlls you can use from C/C++ as well as from your Python 2 environment.

An elegant weapon, for a more civilized age.

In which we assemble a portable, Emacs based LISP development environment under Windows.  This being good for the soul.

For Carlos, happy birthday.  You will come to understand... they all do.
For Josh, you were right about Lisp.

Before we begin, let me say if you are new to Lisp, you should check out Quicklisp.  Quicklisp provides a convenient way to setup and maintain a Lisp development environment with a large set of libraries.  However, if you would like a portable Lisp development environment and/or would like to have more control over (or a better understanding of) your Emacs based setup, please read on.

What you will need:

  1. The latest version of Emacs for Windows.  Download the zip.
  2. The latest CVS snapshot of SLIME.  Download the tar archive.
  3. One or more of the following Lisp implementations:
  • CCL: Checkout the latest release version from SVN.
  • CLISP
  • ECL: You will have to compile from source.

Optional:

  • Par Edit: helps **keep parentheses balanced**

And we are off!

  1. Extract Emacs to a location of your choice, e.g. emacs-24.3 (tip include the version in the directory names so you can tell which version you are running.)
  2. Create a directory named 'home' in the root of your emacs directory, e.g. emacs-24.3/home
  3. Create a directory named 'bin' in the root of 'home', e.g. emacs-24.3/home/bin
  4. Deploy Lisp implementation(s)
    • CLISP
      • Extract to a directory in bin, e.g. emacs-24.3/home/bin/clisp-2.49
    • ECL
      • Compile from source and install to a directory in bin, e.g. emacs-24.3/home/bin/ecl-13.5.1
    • CCL
      • Checkout from SVN to a directory in bin, e.g. emacs-24.3/home/bin/ccl
      • Tip: keep CCL up to date between version releases with SVN's update command.
      • Modify ccl/level-1/linux-files.lisp line 939 #+windows-target to set HOME from the 'HOME' environment variable.  This allows us to control where CCL writes files reducing debris and making it portable.
        #+windows-target
        ;; Matthew Witherwax (lemoneer) 01MAY2013
        ;; Modified the Windows target to use the value of the HOME environment
        ;; variable if it exists; otherwise, it falls back to the OS user database.
        ;; This allows CCL to be run portably under Windows without leaving debris
        ;; behind.
        (or
         (getenv "HOME")
         (dolist (k '(#||"HOME"||# "USERPROFILE"))
           (with-native-utf-16-cstrs ((key k))
             (let* ((p (#__wgetenv key)))
           (unless (%null-ptr-p p)
             (return (get-foreign-namestring p)))))))
        
  5. Create the file site-start.el in site-lisp in the root of your Emacs directory, e.g. emacs-24.3/, and insert the code below. This allows us to set the ‘HOME’ environment variable when we launch Emacs.
    ; remove etc from the path
        (defvar %~dp0
            (substring data-directory 0
            (- (length data-directory) 4)))
        ; create appened home/
        (defvar home-dir (concat %~dp0 "home/"))
        (setenv "HOME" home-dir)
    
  6. Extract the latest slime csv snapshot to site-lisp, e.g. emacs-24.3/site-lisp/slime-2013-12-12
  7. Optional: Copy paredit to /lisp in the root of your Emacs directory, e.g. emacs-24.3/lisp
  8. Launch Emacs by double clicking runemacs.exe in the bin directory of the Emacs directory, e.g. emacs-24.3/bin, and type C-x C-f ~/.emacs to open the Emacs config file.   Insert the following:
    (set-language-environment "utf-8")
    
    ;;; Add slime
    (add-to-list 'load-path "~/site-lisp/slime-2013-12-12")  ;or wherever you put it
    
    ;;; CCL Note that if you save a heap image, the character
    ;;; encoding specified on the command line will be preserved,
    ;;; and you won't have to specify the -K utf-8 any more.
    
    ;; SLIME and CLISP will complain about a missing temp directory without this
    (setq temporary-file-directory (expand-file-name "~/temp"))
    
    ;; Path to clisp full so we can build the call
    (setq path-clisp (expand-file-name "~/bin/clisp-2.49/full/"))
    
    ;;; Configure available Lisp
    ;;; Leave out the one(s) you did not install
    (setq slime-lisp-implementations
    	`(
    		(ccl (,(expand-file-name "~/bin/ccl/wx86cl64.exe") "-K utf-8"))
    		(clisp (,(replace-regexp-in-string "@" path-clisp "@lisp.exe") 
    			,(replace-regexp-in-string "@" path-clisp "-B@") 
    			,(replace-regexp-in-string "@" path-clisp "-M@lispinit.mem")
    			"-ansi"))
    		(ecl (,(expand-file-name "~/bin/ecl-13.5.1/ecl.exe")))))
    
    ;;; setup slime
    (require 'slime)
    (setq slime-net-coding-system 'utf-8-unix)
    (slime-setup '(slime-fancy))
    
    ;;; setup paredit if you installed it
    (autoload 'paredit-mode "paredit"
      "Minor mode for pseudo-structurally editing Lisp code." t)
    (add-hook 'emacs-lisp-mode-hook       (lambda () (paredit-mode +1)))
    (add-hook 'lisp-mode-hook             (lambda () (paredit-mode +1)))
    (add-hook 'lisp-interaction-mode-hook (lambda () (paredit-mode +1)))
    (add-hook 'scheme-mode-hook           (lambda () (paredit-mode +1)))
    
    ;;; This is disabled to stop paredit from running in the REPL
    ;;; Feel free to uncomment this to turn it back on
    
    ;;;; (add-hook 'slime-repl-mode-hook (lambda () (paredit-mode +1)))
    ;;;; ;; Stop SLIME's REPL from grabbing DEL,
    ;;;; ;; which is annoying when backspacing over a '('
    ;;;; (defun override-slime-repl-bindings-with-paredit ()
    ;;;; (define-key slime-repl-mode-map
    ;;;; 	(read-kbd-macro paredit-backward-delete-key) nil))
    ;;;; (add-hook 'slime-repl-mode-hook 'override-slime-repl-bindings-with-paredit)
    
    ;;; bonus split the screen in to two side by side windows
    (defun 2-windows-vertical-to-horizontal ()
      (if (and
           (= (length (window-list)) 2) ; only split if there are 2 windows
           (window-combined-p)) ; and they are vertical
          (progn
    	(delete-window)
    	(split-window-horizontally))))
    
    (add-hook 'emacs-startup-hook '2-windows-vertical-to-horizontal)
    
    (when (display-graphic-p)
      (add-to-list 'default-frame-alist '(left . 0))
      (add-to-list 'default-frame-alist '(top . 0))
    )
    
    (w32-send-sys-command 61488)
    
  9. Create a batch file called runslime.bat and insert the following command.  This will allow us to launch Emacs with slime and your default Lisp running.
    @ECHO OFF
    C:\emacs-24.3\bin\runemacs.exe --eval=(slime)

 If you followed all the steps, when you run runslime.bat, you should see something like this

Take this REPL, brother, and may it serve you well.