-
-
Save waffleau/6e074573d4bece3f7a007ed8536512b5 to your computer and use it in GitHub Desktop.
def self.extract_dominant_colors(image_path, quantity=5, threshold=0.01) | |
image = MiniMagick::Image.open(image_path) | |
# Get image histogram | |
result = image.run_command('convert', image_path, '-format', '%c', '-colors', quantity, '-depth', 8, 'histogram:info:') | |
# Extract colors and frequencies from result | |
frequencies = result.scan(/([0-9]+)\:/).flatten.map { |m| m.to_f } | |
hex_values = result.scan(/(\#[0-9ABCDEF]{6,8})/).flatten | |
total_frequencies = frequencies.reduce(:+).to_f | |
# Create frequency/color pairs [frequency, hex], | |
# sort by frequency, | |
# ignore fully transparent colours | |
# select items over frequency threshold (1% by default), | |
# extract hex values, | |
# return desired quantity | |
frequencies | |
.map.with_index { |f, i| [f / total_frequencies, hex_values[i]] } | |
.sort { |r| r[0] } | |
.reject { |r| r[1].end_with?('FF') } | |
.select { |r| r[0] > threshold } | |
.map { |r| r[1][0..6] } | |
.slice(0, quantity) | |
end |
Thanks for this!
L20 seems to not sort correctly for me. I think it should be .sort { |a, b| b[0] - a[0] }
Also, if the image does not have any transparency, L21 would needlessly reject any color with a 100% blue channel. I think that line should be .reject { |r| r[1].size == 9 && r[1].ends_with?('FF') }
Hello,
is it possible for you to explain how your line :
image.run_command('convert', image_path, '-format', '%c', '-colors', quantity, '-depth', 8, 'histogram:info:')
is working ?
I've used your amazing code for an app which work on images. But, your script is not grouping pixels which are almost same colors. Is it possible to change a config do to that ?
Thanks in advance !
hey @chridubois,
That line creates a histogram, which is a way of analyzing the colors present in an image. You can read more about it here, but the short explanation of that line is that it returns a list that describes how many pixels in the image are a particular color.
The colors
argument should do what you're asking for. If you ask for fewer colors, it will group pixels that are similar. If you ask for more colors, it will give a more detailed breakdown.
From my testing, it's unfortunately a bit hard to get things exactly right. For example, you might ask for 4 colors, and it will only return 3 — even though there are indeed 4 colors present in the image. I don't know enough about ImageMagick to understand why that would be. However I suspect there are ways to get around this issue — maybe you can posterize the image before getting a histogram.
Anyway, the tl;dr of it all is: adjust the quantity
argument to be a smaller number.
Shouldn't it reject values ending in 00
instead of FF
?
You can change line
8
fromto