Detecting Cats

By Nicolas Modrzyk

It is a beautiful Friday afternoon, you know you should be doing something useful, and you keep teasing with that simple code to detect cats in pictures.

Let’s say you have a picture of all your cats, ready to jump for food.

The goal is to redraw their faces as they are recognized, and add the total number of cats, as shown below:

This example uses Origami, a Clojure wrapper around OpenCV, as usual with the recent examples found on this blog.

The trained classifier to recognize the cats is already existing in the OpenCV sources, and this will be used as is.

The catsdetector code will be using solely origami as a dependencies.

(ns catsdetector
    [opencv3.colors.rgb :as rgb]
    [opencv3.core :refer :all]))

The detector itself is created from using the haarcascade_frontalcatface xml definition.

We also create two helper functions, one to draw how many cats were found, and the other to re-draw cat faces by changing their colormap via a submat.

(def detector
(defn add-label! [buffer rects]
  (put-text! buffer (str (count (.toArray rects) ) " cat(s) " )
     (new-point 30 100) FONT_HERSHEY_PLAIN 2 rgb/magenta-2 2))
(defn draw-rects! [buffer rects]
   (doseq [r (.toArray rects)]
     (-> buffer
      (submat r)
      (apply-color-map! COLORMAP_WINTER)
      (copy-to (submat buffer r))))

(defn detect-cats! [mat]
  (let [  rects (new-matofrect) ]
   (.detectMultiScale detector mat rects)
   (-> mat
    (draw-rects! rects)
    (add-label! rects))))

The detect-cats function is the main piece, where we use the detector to give us rectangles matching cat faces. Once we have them, we call the helper function to draw those rectangles and write the total number of cats.

To script things a little bit, you can add a main function to the namespace that will read the mat from the input file, and write a file with the updated cat faces.

(defn -main[ & args ]
  (if-let [ c (first args)]
  (let [input (-> (str c) (imread))
        output (or (second args) "output.png")]
   (println "Reading: " c)
   (println "Writing: " output)
   (-> input
     (imwrite output)))
     (println "need input file ...")))

Voila. Now you know what you should be doing this afternoon.

About the Author

Nicolas Modrzyk has over 12 years of IT experience in the United States and Asia and is currently CTO of an international consulting company in Tokyo, Japan. An author of two other published books, Nicolas began working with Clojure five years ago and loves helping customers reach their goals in many languages  When not bringing new ideas to customers, he spends time with his two fantastic daughters Mei and Manon, and playing live music internationally.

For more, check out Nicolas' book Java Image Processing Recipes: With OpenCV and JVM.