13

I need to know the average color from an image when I upload it to my Ruby on Rails application. Is it possible to get the average color value in HEX or in RGB to use this color later in the view that's going to display this image?

Something like:

img =  Magick::Image.read(path).first
hexVal = img.getHexValue
Benjamin Sullivan
  • 1,466
  • 1
  • 12
  • 15
Mr_Nizzle
  • 6,644
  • 12
  • 55
  • 85
  • As I asked on your [related question using JavaScript](http://stackoverflow.com/questions/5162828/how-to-get-the-average-or-main-color-from-an-image-with-javascript): "Do you want the exact pixel color only, or within a particular HSV range? (I'd suggest that the latter will serve you better.)" – Phrogz Mar 02 '11 at 05:24

6 Answers6

23

Resize the image to one pixel and get its color?

img =  Magick::Image.read(path).first
pix = img.scale(1, 1)
averageColor = pix.pixel_color(0,0)
stef
  • 14,172
  • 2
  • 48
  • 70
19

I don't think you can ask an RMagick image for its average color directly but computing such a thing isn't that difficult.

I think the easiest way would be to extract the color histogram and then use that to compute your average. You'd probably want to quantize the image first though, computing the histogram for an image with a lot of colors is not cheap and probably pointless busy work if you're just interested in an average:

total = 0
avg   = { :r => 0.0, :g => 0.0, :b => 0.0 }
img.quantize.color_histogram.each { |c, n|
    avg[:r] += n * c.red
    avg[:g] += n * c.green
    avg[:b] += n * c.blue
    total   += n
}
[:r, :g, :b].each { |comp| avg[comp] /= total }

That'll give you the average color in avg. But, the color will be in ImageMagick's internal format (i.e. the components will range from zero to Magick::QuantumRange) so you'll have to scale them down to 0-255:

[:r, :g, :b].each { |comp| avg[comp] = (avg[comp] / Magick::QuantumRange * 255).to_i }

And finally you have the RGB components in avg as integers between zero and 255 and getting the average color in hex format should be trivial. You could easily merge this into the averaging step if desired.

I could probably be cleverer with the iterators but .each is nice and clear and clarity is more important than cleverness.

You can also try with and without the quantization step and use whichever one works best for the images that you're working with.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 7
    @zarazan: That blog is dated July 5 2011, my answer was posted on March 2 2011. – mu is too short Feb 13 '13 at 22:19
  • 1
    @muistooshort If all the quantize function does is make a image less complex by taking averages of pixel colors (assuming) - wouldn't be even simpler if you just quantized the image down to color like: – sfkaos Apr 29 '13 at 03:39
  • @sfkaos: I'm guessing that the missing part of your comment is about quantizing the image down to one color. That might work and I'd guess that it would have the same effect as [stef's](http://stackoverflow.com/a/5164917/479863) "scale it down to one pixel" approach. I'm not sure what the underlying algorithms would do to the palette though, I'd want to run some tests to see what happens with real images. Neat idea though. – mu is too short Apr 29 '13 at 04:40
3

I found my solution (here), after I tested all the possibilities presented here.

def maincolor()
  img =  Magick::Image.read(self.url).first
  pix = img.scale(1, 1)
  avg_color_hex = pix.to_color(pix.pixel_color(0,0))
  return avg_color_hex
end

I hope this helps. I added the conversion to hex color by rmagick, because it's a pita with ruby ( otherwise I used sprintf to hex conversion)

tingel2k
  • 392
  • 1
  • 12
  • Unfortunately this method sometimes returns invalid hex colors. Example: "#AAC5C3F1". – rikas Mar 24 '14 at 19:26
  • Hum... I'm not sure. How can I know that? Or how can I convert the image? – rikas Mar 26 '14 at 19:20
  • 1
    Hi, please read here -> https://github.com/rmagick/rmagick/blob/master/examples/identify.rb , you can get the information about colorchannel depth. – tingel2k Mar 28 '14 at 10:05
1

According to @muistooshort - If all the quantize function does is make a image less complex by taking averages of pixel colors (assuming) - wouldn't be even simpler if you just quantized the image down to color like:

img.quantize(1,Magick::RGBColorspace).color_histogram

And just use the resulting color?

sfkaos
  • 205
  • 4
  • 10
1

Consider using the miro gem, which seems to follow "mu is too short"'s approach: https://github.com/jonbuda/miro

Benissimo
  • 1,010
  • 9
  • 14
0

Consider using the rails dominant colors gem, https://github.com/OpenGems/rails_dominant_colors

Boris BRESCIANI
  • 527
  • 7
  • 16
  • 1
    Please don't just post some tool or library as an answer. At least demonstrate [how it solves the problem](http://meta.stackoverflow.com/a/251605) in the answer itself. – Yunnosch May 03 '20 at 10:50