Averaging Image Color Using RMagick

Written by

In the past I’ve using chunky_png for image manipulation. I’ve needed this as part of my pixel curtain project. The thing about chunky_png is that it’s just not very fast. If you need fast you’re talking about C, and C libraries. The problem is that those libraries often don’t feel very ruby like. One of those big, C like libraries is RMagick. In the past I’ve been hesitant to use it because it’s BIG. However, I’m hear to tell you it’s not really that bad. You came here from code, right? Check this out:

def average_color(image)
  total = 0
  avg = { :r => 0.0, :g => 0.0, :b => 0.0 }

  image.quantize.color_histogram.each do |c,n|
    avg[:r] += n * c.red
    avg[:g] += n * c.green
    avg[:b] += n * c.blue
    total += n

  avg.each_key do |c| 
    avg[c] /= total
    avg[c] = (avg[c] / QuantumRange * 255).to_i

  return "rgb(#{avg[:r]},#{avg[:g]},#{avg[:b]})"

What you’re looking at is a function to average the color of a given picture. Most of the code should be pretty clear, but let me walk you through it just to sure. We start off by initializing all an average array with each of our components. Next we take the image and quanitze it. Quantizing it means that we’re knocking down the complexity. By default this brings the number of colors to 256, this is for speed but feel free to change the quantization to fit your situation.

Next we call color_histogram on the quantized image. This is a really neat function that returns a hash of how often each color appears within a given image. What does that mean? Well imagine we’ve got a color, say orange (a great color!). Orange appears in 3000 pixels. So something like image.quantize.color_histogram[:orange] would return 3000. Colors are really stored by their RGB and components. What all of this ends up translating to is that within the loop we add the red, green, and blue components, and keep track of the total number of pixels we’ve seen.

Next, we loop through the r, g, and b components, divide by the total (makes sense, this is an average) and then translates the color into its location from 0 to 255. Why’s that? So we can take the color and return the rgb string representation. This is one of many available RMagick representations.

So there it is, computing average colors in RMagick.

Comments or Questions? Contact Nick @nixterrimus on twitter.

Nick is a software engineer, geek, web enthusaist, open source contributor, home automation tinkerer, ocean admirer and all around general optimist living in San Francisco. Want to get in touch about professional matters? Nick Rowe is also available on LinkedIn.