Monday, January 28, 2013

Processing Images with Imagemagick, Scala, and Heroku

Imagemagick is a popular and powerful image processing tool.  You can use it from any JVM language such as Java and Scala by using im4java.  The im4java library is not a pure Java library.  Rather, it wraps the command-line Imagemagick program.  Fortunately, Imagemagick is popular enough to be available through cloud computing platforms such as Heroku.

I'm working on a badges application.  I have two image processing requirements:

  • Resize an uploaded badge to small, meduim, and large with conversion to PNG
  • Create gray versions of badges in each size to represent unearned badges
My requirements are simple, but it's nice that Imagemagick gives me a lot of headroom should more complex requirements come down the pipeline in the future.

These features were easy to implement in Scala using the following code:

object BadgeImageProcessor {

  def resize(originalImage: BufferedImage, size: Int): BufferedImage = {

    val cmd = new ConvertCmd
    val s2b = new Stream2BufferedImage()
    cmd.setOutputConsumer(s2b)

    def createResizeCommands(): IMOperation = {
      val op = new IMOperation()
      op.addImage()
      op.resize(size, size)
      op.addImage("png:-")
      return op
    }

    cmd.run(createResizeCommands, originalImage)
    s2b.getImage()
  }

  def grayscale(originalImage: BufferedImage): BufferedImage = {
    val cmd = new ConvertCmd
    val s2b = new Stream2BufferedImage()
    cmd.setOutputConsumer(s2b)

    def createGrayScaleCommands(): IMOperation = {
      val op = new IMOperation()
      op.addImage()
      op.colorspace("gray")
      op.addImage("png:-")
      return op
    }

    cmd.run(createGrayScaleCommands, originalImage)
    s2b.getImage()
  }
}

The program flow is straight-forward:

  • Create an imageMagick comand that outputs a BufferedImage from a reponse stream
  • Define a closure to apply operations to the original buffered image and output the response as a PNG BufferedImage
  • Run the operations against the original image, and return the resulting BufferedImage
The program is straight-forward, but the code is repetitive.  If I was using Java, I would eliminate the repetition by creating an abstract image processor taking polymorphic processing commands.  With Scala, I use higher-order functions.  This results in less coding and avoids object abstraction:

object BadgeImageProcessor {

  def resize(originalImage: BufferedImage, size: Int): BufferedImage = {
    def toNewSize(op: IMOperation, size: Int) = {
      op.resize(size, size)
    }    
    processImageMagick(originalImage, toNewSize(_, size))
  }

  def grayscale(originalImage: BufferedImage): BufferedImage = {
    def toGray(op: IMOperation) = {
      op.colorspace("gray")
    }
    processImageMagick(originalImage, toGray(_))
  }

  private def processImageMagick(originalImage: BufferedImage, commands: IMOperation => Unit): BufferedImage = {
    val cmd = new ConvertCmd
    val s2b = new Stream2BufferedImage()
    cmd.setOutputConsumer(s2b)

    def createGrayScaleCommands(): IMOperation = {
      val op = new IMOperation()
      op.addImage()
      commands(op)
      op.addImage("png:-")
      return op
    }

    cmd.run(createGrayScaleCommands, originalImage)
    s2b.getImage()
  }
}


 Functional programming allows you to pass a function into another function.  The method "processImageMagick(...)" takes two parameters;  the image to process and a function that takes an IMOperation and returns nothing special (Unit).  The magic is the ability to specify a placeholder (the underscore character) to represent the undefined IMOperation, allowing processImageMagick(...) to define the IMOperation and call the "toGray(_)" or toNewSize(_)" methods.

Since I'm only performing one ImageMagick operation in each of my methods (resize and grayscale), I can further simplify the code by in-lining the closures:


  def resize(originalImage: BufferedImage, size: Int): BufferedImage = {
    processImageMagick(originalImage, _.resize(size, size))
  }

  def grayscale(originalImage: BufferedImage): BufferedImage = {
    processImageMagick(originalImage, _.colorspace("gray"))
  }

...I could probably eliminate the need for closures altogether by allowing processImageMagick(...) to take any number of commands, but I'll save that for another day.

The next step is to move the method "processImageMagick(...)" to a utility object, but hopefully you get the idea on how to use higher-order functions to eliminate repetitive boilerplate code without introducing object patterns.

Thursday, January 3, 2013

Success Using Scala to Create a DSL

Update (March):  I've given up on the idea of creating DSL's for every business domain I work in.  I've concluded that Scala is not that scalable (at least not yet).  The original January post follows:

It took three rounds of making mistakes, but I finally have something elegant.

The primary goal was to represent a route between to airports using natural language.  For example, I wanted to be able to write the following line of code:

val routeFromDetroitToPhilly = "DTW" to "PHL"

I also wanted my model code to be pristine.  I wanted to separate any syntactic sugar from my model code, and to avoid any circular dependencies between model objects.  

Here are my model objects:

case class Airport(code: String)
case class Route(origin: Airport, destination: Airport)

A route links origin and destination Airports.  The Airport is only dependent on String.  This is good.  There was problem introduced, however, when I tried to use the "to" keyword to create a Route from two Airports.  You can see the issue if I re-write the desired code in a way that exposes some of the magic:


val routeFromDetroitToPhilly = Airport("DTW").to(Airport("PHL"))

My initial attempts to implement the "to" keyword involved introducing a circular dependency between Airport and Route:


case class Airport(code: String) {
  def to(destination: Airport) = Route(this, destination)
}
case class Route(origin: Airport, destination: Airport)

This worked, but is nasty.  Route depends on Airport and Airport depends on Route.  My first attempt was even worse.  I tried adding the "to" method for route creation though inheritance.  Not a good idea.  I'm too ashamed to publish that failed attempt.

I finally figured out that I could put my syntactic sugar in a model helper class:

object ModelHelper {

  class AirportRouteBuilder(origin: Airport) {
    def to(destination: Airport): Route = {
      Route(origin, destination)
    }
  }
  
  implicit def stringToAirport(code: String) = Airport(code)
  
  implicit def stringToAirportRouteBuilder(airportCode: String) = new AirportRouteBuilder(Airport(airportCode))
  
  implicit def airportToAirportRouteBuilder(airport: Airport) = new AirportRouteBuilder(airport)
}

Now when I write this code:


val routeFromDetroitToPhilly = "DTW" to "PHL"

...Scala resolves the code as follows:
  • The function "to(...)" is invalid for the String "DTW", but we have an implicit method "stringToAirportRouteBuilder()" that creates an AiportRouteBuilder object that has a "to(...)" method
  • Call "stringToAirportRouteBuilder("DTW")" to create an AirportRouteBuilder object
  • Call "StringToAirport("PHL")" to create the Airport object needed for "AirportRouteBuilder.to(...)"
  • Call the "to()" method on AirportRouteBuilder to construct a new Route object that links the origin and destination airports
This is pretty mind-blowing stuff.  It seems too complex and magical, but I believe it's a way of thinking that we can get used to.  It's like eating ethnic food for the first time or meeting an ugly person for the first time.  After the initial shock, you get used to it if you allow yourself the opportunity to appreciate.