Detecting Cats

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.

/catfamily.jpg

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

/output.png

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
  (:require
    [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
  (->
   "XML/haarcascade_frontalcatface.xml"
   (clojure.java.io/resource)
   (.getPath)
   (new-cascadeclassifier)))

(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))))
      buffer)

(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
     (detect-cats!)
     (imwrite output)))
     (println "need input file ...")))

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