huespace 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/huespace/median_cut.rb +80 -0
- data/lib/huespace.rb +65 -0
- metadata +74 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6422aedbcf443c23efb849ff2543a1872f120a82d2e68d7a8fc34b0ee4f518f3
|
4
|
+
data.tar.gz: 2353233ed144e5283f833b8e9c7512999a1655c70881d6bac7d13f8ce33743ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 50c80164bc380eea51cd27f0798930988334333e327da22abcc639b1711d2eeb89cf40e1033f1da2fcaa90ea01341b9fc0f7e587e54175332c02d34a401a9053
|
7
|
+
data.tar.gz: 726acd864d746d4d64176f07f8ead52ac69c2d647fab10340803e095ba42ab917ac807e3c4d869d735d8acc337375efb57f81416f3f31b10a6c4c6061faf7c47
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Huespace
|
2
|
+
class MedianCut
|
3
|
+
SplitInfo = Struct.new(:range, :group_index, :color_index, keyword_init: true)
|
4
|
+
|
5
|
+
def self.process(colors, count, hist)
|
6
|
+
colors = colors.uniq # Remove duplicate colors
|
7
|
+
groups = [colors]
|
8
|
+
limit = [count, colors.size].min
|
9
|
+
|
10
|
+
loop do
|
11
|
+
break if groups.size >= limit
|
12
|
+
|
13
|
+
split_info = determine_split(groups)
|
14
|
+
group1, group2 = split_group(groups[split_info.group_index], split_info)
|
15
|
+
groups.delete_at(split_info.group_index) # Remove group that we split by
|
16
|
+
groups << group1 unless group1.empty?
|
17
|
+
groups << group2 unless group2.empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
palette = []
|
21
|
+
groups.sort_by! { |group| -calc_sort_score(group, hist) }
|
22
|
+
groups.each do |group|
|
23
|
+
palette << average_color(group)
|
24
|
+
end
|
25
|
+
|
26
|
+
palette[0...count]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def self.determine_split(groups)
|
32
|
+
stats = []
|
33
|
+
|
34
|
+
groups.each_with_index do |group, index|
|
35
|
+
reds = group.map { |el| el[0] }
|
36
|
+
greens = group.map { |el| el[1] }
|
37
|
+
blues = group.map { |el| el[2] }
|
38
|
+
|
39
|
+
ranges = []
|
40
|
+
ranges << SplitInfo.new(group_index: index, range: reds.max - reds.min, color_index: 0)
|
41
|
+
ranges << SplitInfo.new(group_index: index, range: greens.max - greens.min, color_index: 1)
|
42
|
+
ranges << SplitInfo.new(group_index: index, range: blues.max - blues.min, color_index: 2)
|
43
|
+
|
44
|
+
stats << ranges.max_by(&:range)
|
45
|
+
end
|
46
|
+
|
47
|
+
stats.max_by(&:range)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.split_group(group, split_info)
|
51
|
+
colors = group.sort_by { |pixel| pixel[split_info.color_index] }
|
52
|
+
|
53
|
+
median_index = colors.size / 2
|
54
|
+
|
55
|
+
group1 = colors[0..(median_index - 1)]
|
56
|
+
group2 = colors[median_index..-1]
|
57
|
+
|
58
|
+
[group1, group2]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Score for sorting colors by dominance
|
62
|
+
# For each group we calculate the sum of how many of each pixel from the group was in the original image
|
63
|
+
def self.calc_sort_score(group, hist)
|
64
|
+
score = 0
|
65
|
+
group.each do |pixel|
|
66
|
+
score += hist[Huespace.get_pixel_index(pixel)]
|
67
|
+
end
|
68
|
+
|
69
|
+
score
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.average_color(colors)
|
73
|
+
average_r = colors.map { |pixel| pixel[0]}.sum() / colors.size()
|
74
|
+
average_g = colors.map { |pixel| pixel[1]}.sum() / colors.size()
|
75
|
+
average_b = colors.map { |pixel| pixel[2]}.sum() / colors.size()
|
76
|
+
|
77
|
+
[average_r, average_g, average_b]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/huespace.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Huespace
|
4
|
+
require "mini_magick"
|
5
|
+
require "huespace/median_cut.rb"
|
6
|
+
|
7
|
+
# Returns a palette of representative colors
|
8
|
+
def Huespace.get_palette(image_source, n_colors)
|
9
|
+
pixels = Huespace.load_image(image_source)
|
10
|
+
|
11
|
+
sampled_pixels, hist = Huespace.sample_pixels(pixels)
|
12
|
+
Huespace::MedianCut.process(sampled_pixels, n_colors, hist)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Returns most colorful color
|
16
|
+
# This is achieved by sorting the palette using the following formula:
|
17
|
+
# (max + min) * (max - min)) / max
|
18
|
+
# max and min represent one of the rgb values
|
19
|
+
# More about this here: http://changingminds.org/explanations/perception/visual/colourfulness.htm
|
20
|
+
def Huespace.get_most_colorful_color(image_source)
|
21
|
+
colors = Huespace.get_palette(image_source, 6)
|
22
|
+
|
23
|
+
return colors.sort_by { |color| ((color.max + color.min) * (color.max - color.min)) / color.max }.last()
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the dominant color
|
27
|
+
def Huespace.get_dominant_color(image_source)
|
28
|
+
colors = Huespace.get_palette(image_source, 5)
|
29
|
+
|
30
|
+
return colors[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def Huespace.load_image(image_source)
|
35
|
+
begin
|
36
|
+
image = MiniMagick::Image.open(image_source)
|
37
|
+
rescue TypeError
|
38
|
+
# Extract from stream
|
39
|
+
image = MiniMagick::Image.read(image_source) #
|
40
|
+
rescue StandardError => e
|
41
|
+
raise "Invalid URL!"
|
42
|
+
end
|
43
|
+
|
44
|
+
image.get_pixels.flatten(1)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Quality determines the step between chosen pixels
|
48
|
+
# Higher number = Lower quality
|
49
|
+
def Huespace.sample_pixels(pixels, quality=10)
|
50
|
+
sampled_pixels = [];
|
51
|
+
hist = Hash.new(0)
|
52
|
+
|
53
|
+
(0..pixels.length - 1).step(quality).each do |i|
|
54
|
+
sampled_pixels << pixels[i] unless (pixels[i][0] > 250 && pixels[i][1] > 250 && pixels[i][2] > 250) # Skip white pixels
|
55
|
+
hist[get_pixel_index(pixels[i])] += 1
|
56
|
+
end
|
57
|
+
|
58
|
+
[sampled_pixels, hist]
|
59
|
+
end
|
60
|
+
|
61
|
+
# Determines the index for the histogram
|
62
|
+
def Huespace.get_pixel_index(pixel)
|
63
|
+
return pixel[0] << 10 + pixel[1] << 5 + pixel[2]
|
64
|
+
end
|
65
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: huespace
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dino Tognon, Ivan Božić
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-01-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mini_magick
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.11.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.11.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.2'
|
41
|
+
description: "Huespace can extract a color palette from local images, url-s or images
|
42
|
+
as byte-streams. \n Besides palettes, it can also extract the
|
43
|
+
most dominant color and the most colorful color!"
|
44
|
+
email: dino.tognon@arsfutura.co
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- lib/huespace.rb
|
50
|
+
- lib/huespace/median_cut.rb
|
51
|
+
homepage:
|
52
|
+
licenses:
|
53
|
+
- MIT
|
54
|
+
metadata: {}
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options: []
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 2.5.7
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubygems_version: 3.0.9
|
71
|
+
signing_key:
|
72
|
+
specification_version: 4
|
73
|
+
summary: Extract a color palette from any image
|
74
|
+
test_files: []
|