My Tribute to Steve Ballmer

These days Microsoft is often being hammered in both the news and in Open Source communities across the globe, so on behalf of the Clojure community I would like to submit a small tribute to the man at the wheel, Steve Ballmer. First we’ll write a small lisp macro and use that for some simplified java interop.

Preface

Microsoft makes good money, but they are going through tough times. So to make life a little happier at Camp Microsoft, I’ve decided to write a little Image to Ascii Art converter. Demonstrating, among other things, the power of macros. I don’t think I’ll get any arguments, that when you conjoin clojure and ascii-art, you get claskii art – So I’ve named this project appropriately

Processing an image

To convert an image to ascii we need to look at it as a bunch of colored pixels, converting them to characters one at a time. Disregarding the details of which image-holder we will use, we always find ourselves doing some tedious java-interop when working with Java Classes. Here’s a quick example of how to get the brighest color from a pixel:

(defn get-pixel [img x y]
  (let [color (.getRGB img x y)
        red   (.getRed color)
        green (.getGreen color)
        blue  (.getBlue color)]
    (apply max [red green blue])))

Very simple right? Yes, and very boring. What I would like to be able to do, is just get at the fields more directly, like:

claskii> (def image (ImageIO/read (File. "steve-ballmer.jpg")))
#'image
claskii> (get-properties (Color. (.getRGB image 10 10)) .getRed .getBlue)
[8 60]

Which of course isn’t possible, because as soon as .getRed is evaluated I’ll get a Symbol not defined error– Enter Macros! A recommended first step when writing macros is:

  • Dont. If you can manage to solve the job without macros, you’re better off that way. Since macros generate code at read time, they’re more complex than regular code and thus more error-prone.

The second advice is

  • Write out the code that you want the macro to emit, before writing the macro

Writing your first Lisp Macro

In our case we want get-properties to expand into something like

(get-properties object .getRed .getBlue) >>> [(.getRed object) (.getBlue object)]

So to make this happen we need to walk through our method-list (ie. .getRed .getBlue) and return a sequence which results from applying the methods to the first argument, the object:

(defmacro get-properties [obj & properties]
  (for [property properties]
    (property obj)))

Because Lisp is homoiconic we treat code exactly like data in that (.getRed obj) is nothing more than a list whos first item is .getRed and the second is obj. To see what our macro expands to, call

claskii> (macroexpand-1 `(get-properties (Color. (.getRGB image 10 10)) .getRed .getBlue))
(nil nil)

Not what we wanted! The reason is, that we haven’t taken control of evaluation, using the macro-characters. I recommend you checkout them all before proceeding, as these puppies give you a lot of power, but also make macro-definitions a little hard on the eyes. The backquote stops evaluation, while allowing us to prefix items with a tilde ~ for evaluation, ~@ for splicing ~@(list 1 2 3) => 1 2 3.

(defmacro get-properties [obj & properties]
  `(vector
    ~@(for [property properties]
        (property obj))))

Unfortunately when you check that using macroexpand-1, you’ll see the exact same result as above. The reason is, that when we’re passing .getRed as a symbol and symbols are similar to :keywords in that they are a function of their arguments. When you’re calling the symbol on the object you get nil in return. But why is the symbol being evaluated?

The splicing ~@ forces evaluation within its body, so we need to add an extra backquote:

(defmacro get-properties [obj & properties]
  `(vector
    ~@(for [property properties]
        `(property obj))))

Then check it

claskii> (macroexpand-1 `(get-properties (Color. (.getRGB image 10 10)) .getRed .getBlue))
(clojure.core/vector (claskii/property claskii/obj) (claskii/property claskii/obj))

Now thats more like it! But calling it will throw an error of course, since property is not defined anywhere – Instead of taking property literally we want it evaluated to the ‘property’ var in the for-loop, and although direct evaluation would work in most-cases you always have to be on your toes when using macros, as one day a user will submit an argument, which clashes with one of yours. The solution is (gensym), which generates a unique name for your variables. Gensym can either be called directly or simply using the syntactic sugar: symbol# – A symbol with a pound sign as its suffix:

(defmacro get-properties [obj & properties]
  `(vector
    ~@(for [property# properties]
        `(~property# ~obj))))
claskii> (macroexpand-1 `(get-properties (Color. (.getRGB image 10 10)) .getRed .getBlue))
(clojure.core/vector
    (.getRed (java.awt.Color. (.getRGB claskii/image 10 10)))
    (.getBlue (java.awt.Color. (.getRGB claskii/image 10 10))))
claskii> (get-properties (Color. (.getRGB image 10 10)) .getRed .getBlue)
[8 60]

Nice! It expands like we want and it gets us the result we want! There are a couple of optimizations which are begging to be done. Firstly the object is being created once for each method argument. This is partly because the macro allows it, and partly because I’m not actually passing an object but rather the code which constructs an object. Secondly, by adding at object with the syntactic `(let [obj# ~obj]) gensym, will actually change the gensym with each run of the for-loop. So the ugly, stable and fully functioning version, using manual gensym comes out looking like so:

(defmacro get-properties [obj & properties]
  (let [target (gensym)]
    `(let [~target ~obj]
       (vector ~@(for [property properties]
                   `(~property ~target))))))

Scared yet?

I realize and admit that macro definitions look awful because we need all of our syntactic weapony rolled out in order to get the expansion we want. On the other hand, if you can look past the superficial, Lisps homoiconicity  lets us treat code exactly as data, which is just an incredible tool to be sitting with, as you can freely extend the very language itself by relatively simple means!

Well, to calm the nerves again, look at how easy java-interop has become. We have gone from:

(defn old-school [img]
  (let [w   (.getWidth img)
        h   (.getHeight img)
        r   (.getRed img)
        g   (.getGreen img)
        b   (.getBlue img)]))

To:

(def new-school [img]
     (let [[w h]   (get-properties img .getWidth .getHeight)
           [r g b] (get-properties (.getRGB img 10 10) .getRed .getGreen .getBlue)]))

Ascii-Art

So making the ascii-art itself should be very simple. This is my strategy:

  1. Define a list of ascii characters of descending density
  2. Scale the image down to ascii-output-size
  3. Look at every pixel
  4. Examine the brighest color
  5. Divide that color by the amount of characters available and pick the appropriate one
  6. Output result

So begin by defining a list which you think looks good:

(def ascii-chars [\# \A \@ \% \$ \+ \= \* \: \, \. \space])

Then pick out the peak value and convert it to an index in the above data, by simple division

(defn ascii [img x y color?]
  (let [[red green blue] (get-properties ( Color. (.getRGB img x y))
                                         .getRed .getGreen .getBlue)
        peak    (apply max [red green blue])
        idx     (if (zero? peak)
                  (dec (count ascii-chars))
                  (dec (int (+ 1/2 (* (count ascii-chars) (/ peak 255))))))
        output  (nth ascii-chars (if (pos? idx) idx 0)) ]

…And depending on the output-type selected by the user, return either that character or an html-version:

    (if color?
      (html [:span {:style (format "color: rgb(%s,%s,%s);" red green blue)} output])
      output)))

Putting it together

Now that we have a way of processing each pixel, we just need to walk them all:

(defn convert-image [uri w color?]
  (let [raw-image   (scale-image uri w)
        ascii-image (->> (for [y (range (.getHeight raw-image))
                               x (range (.getWidth  raw-image))]
                           (ascii raw-image x y color?))
                         (partition w))

So that walks every X for every Y returning the ascii-representation of each pixel. When the for-loop completes you’re sitting with a 1D stream of characters representing the image. In order to distinguish lines you need to partition the sequence, chopping it up every width-number-of-characters. Now we we’re sitting with a sequence of lines, which we can properly format:

        output      (->> ascii-image
                         (interpose (if color? "<BR/>" \newline))
                         flatten)]

The only difference between the html version and ascii at this point, is how to seperate the lines. Once that done we can flatten the sequence of safe printing.

    (if color?
      (html [:pre {:style "font-size:5pt; letter-spacing:1px;
                           line-height:4pt; font-weight:bold;"}
             output])
      (println output))))

Adjust the html-settings to your liking – I’m no Ascii artist so consider this a raw prototype which more creative people can improve upon should they want to. Anyway, now’s the time to test.

Getting Steve Ballmer

First I hit Google to get an image of the guy:

Steve Ballmer

Second, lets try and run it directly from the REPL:

claskii> (convert-image "/home/lau/Desktop/steve.jpg" 50 nil)

ASCII REPL

Since thats cooked down to only 50 characters it doesn’t really do the guy justice, so lets try the HTML renderer:

claskii> (spit "h.html" (convert-image "steve.jpg" 120 true))

ASCII HTML created using a lisp macro

Thats more like it – although I’ll admit that the A’s look bad.

Conclusion

You’ve seen how macros are functions that control evaluation and outputs code. Macros are both fun and tricky so use them carefully. The entire program weighs in at 55 lines and I’ve put it on Github: here.

About the author

Lau Jensen is the owner of Best In Class, an avid Clojure Developer well experienced in project management and also the primary author of ClojureQL. You are very welcome to follow his posts on this blog or get in touch directly on lau.jensen@bestinclass.dk

  • Great walk-through of the macro-building process. Thanks for the post!

  • missingfaktor

    Great write-up! Not convinced this calls for a macro though. Something like below would work, and is simpler:

    `(let [rgb (.getRGB img 10 10)]
    ((juxt #(.getRed %) #(.getGreen %) #(.getBlue %)) rgb))`

    I know #(.method %) is a bit ugly, but if the situation where we need to pull methods into functions (eta expansion?) occurs so often, perhaps we need some language level support for that. That will enable more general solutions, unlike this very ad hoc one.

    • LauJensen

      That is exactly why you need a macro :) But this is a contrived example, the point of the article was to demonstrate best practice in building macros.

  • Image links are broken :(

  • quax

    Not that it really matters, but it seems to me that this article was written when Steve Ballmer had already stepped down as MS CEO.

    • Its an old post that got moved onto the new server and the date reset.

  • Pingback: Clojure Gazette 1.98 | Clojure Gazette()