camalian 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/gpr-push.yml +30 -0
- data/.github/workflows/ruby.yml +32 -0
- data/.github/workflows/rubygems-push.yml +29 -0
- data/.gitignore +2 -1
- data/.overcommit.yml +33 -0
- data/.rubocop.yml +33 -0
- data/.tool-versions +1 -0
- data/.travis.yml +17 -0
- data/Gemfile +5 -0
- data/README.md +5 -1
- data/Rakefile +5 -3
- data/camalian.gemspec +16 -15
- data/lib/camalian.rb +16 -14
- data/lib/camalian/color.rb +28 -26
- data/lib/camalian/image.rb +24 -22
- data/lib/camalian/palette.rb +17 -6
- data/lib/camalian/quantization/histogram.rb +26 -0
- data/lib/camalian/version.rb +3 -1
- data/test/assets/palette.png +0 -0
- data/test/test_color.rb +14 -11
- data/test/test_helper.rb +4 -0
- data/test/test_palette.rb +14 -12
- metadata +44 -19
- data/lib/chunky_png_patch/color.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bdc6f4d61421eac9f0017baf9e6f290c37e47b633ee4533e18d00883fd162b54
|
4
|
+
data.tar.gz: 5cb477be826b4c0706a9385ce31699a34aab8b27c66c5c57140d1818650ca60e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 912bcad8d3db3362d2997877fe42b9d2df01c806b9bbfe965463d5df8e530dab55f716820f414e44910e5818ff86ac1ac256f8c9d1a2d760ca75d38c69492dcc
|
7
|
+
data.tar.gz: fe6989ee96c21f12482857c26aed89f7db69e6d1a41616c9aaed3a11732ccf76c8376c6711b49dc375cbb8d29adfef1ff09ba3ea3c5780c4f70cdce0431041eb
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: Github Packages Push
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
push:
|
6
|
+
tags:
|
7
|
+
- '*'
|
8
|
+
jobs:
|
9
|
+
build:
|
10
|
+
name: Build + Publish
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby 2.6
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.6.x
|
19
|
+
|
20
|
+
- name: Publish to GPR
|
21
|
+
run: |
|
22
|
+
mkdir -p $HOME/.gem
|
23
|
+
touch $HOME/.gem/credentials
|
24
|
+
chmod 0600 $HOME/.gem/credentials
|
25
|
+
printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
26
|
+
gem build *.gemspec
|
27
|
+
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
28
|
+
env:
|
29
|
+
GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
|
30
|
+
OWNER: ${{ github.repository_owner }}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: build
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ master ]
|
15
|
+
|
16
|
+
jobs:
|
17
|
+
test:
|
18
|
+
runs-on: ubuntu-16.04
|
19
|
+
strategy:
|
20
|
+
matrix:
|
21
|
+
ruby: [ '2.4', '2.5', '2.6', '2.7' ]
|
22
|
+
name: Ruby ${{ matrix.ruby }}
|
23
|
+
steps:
|
24
|
+
- uses: actions/checkout@v2
|
25
|
+
- uses: actions/setup-ruby@v1
|
26
|
+
with:
|
27
|
+
ruby-version: ${{ matrix.ruby }}
|
28
|
+
- name: Install dependencies
|
29
|
+
run: bundle install
|
30
|
+
- name: Run tests
|
31
|
+
run: bundle exec rake
|
32
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
name: Ruby Gems Push
|
2
|
+
|
3
|
+
on:
|
4
|
+
workflow_dispatch:
|
5
|
+
push:
|
6
|
+
tags:
|
7
|
+
- '*'
|
8
|
+
jobs:
|
9
|
+
build:
|
10
|
+
name: Build + Publish
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- name: Set up Ruby 2.6
|
16
|
+
uses: actions/setup-ruby@v1
|
17
|
+
with:
|
18
|
+
ruby-version: 2.6.x
|
19
|
+
|
20
|
+
- name: Publish to RubyGems
|
21
|
+
run: |
|
22
|
+
mkdir -p $HOME/.gem
|
23
|
+
touch $HOME/.gem/credentials
|
24
|
+
chmod 0600 $HOME/.gem/credentials
|
25
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
26
|
+
gem build *.gemspec
|
27
|
+
gem push *.gem
|
28
|
+
env:
|
29
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
data/.gitignore
CHANGED
data/.overcommit.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Use this file to configure the Overcommit hooks you wish to use. This will
|
2
|
+
# extend the default configuration defined in:
|
3
|
+
# https://github.com/sds/overcommit/blob/master/config/default.yml
|
4
|
+
#
|
5
|
+
# At the topmost level of this YAML file is a key representing type of hook
|
6
|
+
# being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
|
7
|
+
# customize each hook, such as whether to only run it on certain files (via
|
8
|
+
# `include`), whether to only display output if it fails (via `quiet`), etc.
|
9
|
+
#
|
10
|
+
# For a complete list of hooks, see:
|
11
|
+
# https://github.com/sds/overcommit/tree/master/lib/overcommit/hook
|
12
|
+
#
|
13
|
+
# For a complete list of options that you can use to customize hooks, see:
|
14
|
+
# https://github.com/sds/overcommit#configuration
|
15
|
+
#
|
16
|
+
# Uncomment the following lines to make the configuration take effect.
|
17
|
+
|
18
|
+
PreCommit:
|
19
|
+
RuboCop:
|
20
|
+
enabled: true
|
21
|
+
on_warn: fail # Treat all warnings as failures
|
22
|
+
|
23
|
+
# TrailingWhitespace:
|
24
|
+
# enabled: true
|
25
|
+
# exclude:
|
26
|
+
# - '**/db/structure.sql' # Ignore trailing whitespace in generated files
|
27
|
+
#
|
28
|
+
#PostCheckout:
|
29
|
+
# ALL: # Special hook name that customizes all hooks of this type
|
30
|
+
# quiet: true # Change all post-checkout hooks to only display output on failure
|
31
|
+
#
|
32
|
+
# IndexTags:
|
33
|
+
# enabled: true # Generate a tags file with `ctags` each time HEAD changes
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
2
|
+
# configuration file. It makes it possible to enable/disable
|
3
|
+
# certain cops (checks) and to alter their behavior if they accept
|
4
|
+
# any parameters. The file can be placed either in your home
|
5
|
+
# directory or in some project directory.
|
6
|
+
#
|
7
|
+
# RuboCop will start looking for the configuration file in the directory
|
8
|
+
# where the inspected file is and continue its way up to the root directory.
|
9
|
+
#
|
10
|
+
# See https://docs.rubocop.org/rubocop/configuration
|
11
|
+
|
12
|
+
AllCops:
|
13
|
+
NewCops: enable
|
14
|
+
|
15
|
+
Naming/MethodParameterName:
|
16
|
+
AllowedNames:
|
17
|
+
- r
|
18
|
+
- g
|
19
|
+
- b
|
20
|
+
|
21
|
+
Metrics/MethodLength:
|
22
|
+
Max: 50
|
23
|
+
|
24
|
+
Metrics/PerceivedComplexity:
|
25
|
+
Max: 25
|
26
|
+
|
27
|
+
Metrics/CyclomaticComplexity:
|
28
|
+
Max: 15
|
29
|
+
|
30
|
+
Metrics/AbcSize:
|
31
|
+
Max: 20
|
32
|
+
IgnoredMethods:
|
33
|
+
- build_components
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.7.1
|
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.7
|
4
|
+
- 2.6
|
5
|
+
- 2.5
|
6
|
+
- 2.4
|
7
|
+
|
8
|
+
before_script:
|
9
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
10
|
+
- chmod +x ./cc-test-reporter
|
11
|
+
- ./cc-test-reporter before-build
|
12
|
+
|
13
|
+
script:
|
14
|
+
- bundle exec rake test
|
15
|
+
|
16
|
+
after_script:
|
17
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# Camalian
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/camalian.svg)](https://badge.fury.io/rb/camalian)
|
4
|
+
![Ruby](https://github.com/nazarhussain/camalian/workflows/build/badge.svg?branch=master)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/5495a2c122469d81b6c5/maintainability)](https://codeclimate.com/github/nazarhussain/camalian/maintainability)
|
6
|
+
|
3
7
|
Ruby gem to extract color palettes from images and play with their saturation
|
4
8
|
|
5
9
|
## Installation
|
6
10
|
|
7
11
|
Add this line to your application's Gemfile:
|
8
12
|
|
9
|
-
gem 'camalian', '~> 0.0
|
13
|
+
gem 'camalian', '~> 0.1.0'
|
10
14
|
|
11
15
|
And then execute:
|
12
16
|
|
data/Rakefile
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
2
4
|
|
3
5
|
require 'rake/testtask'
|
4
6
|
|
@@ -6,5 +8,5 @@ Rake::TestTask.new do |t|
|
|
6
8
|
t.libs << 'test'
|
7
9
|
end
|
8
10
|
|
9
|
-
desc
|
10
|
-
task :
|
11
|
+
desc 'Run tests'
|
12
|
+
task default: :test
|
data/camalian.gemspec
CHANGED
@@ -1,27 +1,28 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'camalian/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'camalian'
|
8
9
|
spec.version = Camalian::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
10
|
+
spec.authors = ['Nazar Hussain']
|
11
|
+
spec.email = ['nazarhussain@gmail.com']
|
12
|
+
spec.description = 'Library used to deal with colors and images'
|
13
|
+
spec.summary = 'Library used to deal with colors and images. You can extract colors from images.'
|
14
|
+
spec.homepage = 'https://github.com/nazarhussain/camalian'
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
|
-
spec.files = `git ls-files`.split(
|
17
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
|
21
|
-
spec.
|
22
|
+
spec.add_dependency 'chunky_png', '~> 1.3', '>= 1.3.14'
|
22
23
|
|
23
|
-
spec.
|
24
|
-
spec.
|
25
|
-
spec.add_dependency "oily_png", "~> 1.2.0"
|
24
|
+
spec.add_development_dependency 'minitest', '~> 5.14', '>= 5.14.2'
|
25
|
+
spec.add_development_dependency 'rake', '~> 13.0', '>= 13.0.1'
|
26
26
|
|
27
|
+
spec.required_ruby_version = '>= 2.4'
|
27
28
|
end
|
data/lib/camalian.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
-
|
2
|
-
require "tempfile"
|
3
|
-
require "open-uri"
|
4
|
-
require "cocaine"
|
5
|
-
require "chunky_png_patch/color"
|
1
|
+
# frozen_string_literal: true
|
6
2
|
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
|
3
|
+
require 'chunky_png'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
require 'camalian/version'
|
8
|
+
require 'camalian/color'
|
9
|
+
require 'camalian/palette'
|
10
|
+
require 'camalian/image'
|
11
|
+
require 'camalian/quantization/histogram'
|
12
|
+
|
13
|
+
module Camalian # :nodoc:
|
14
|
+
QUANTIZATION_HISTOGRAM = 'histogram'
|
11
15
|
|
12
|
-
module Camalian
|
13
16
|
class << self
|
14
17
|
def options
|
15
|
-
convert = `which convert`.strip
|
16
18
|
@options ||= {
|
17
|
-
|
18
|
-
|
19
|
+
color_count: 8,
|
20
|
+
quantization: Camalian::QUANTIZATION_HISTOGRAM
|
19
21
|
}
|
20
22
|
end
|
21
23
|
|
22
|
-
def load(image_path)
|
24
|
+
def load(image_path)
|
23
25
|
Image.new(image_path)
|
24
26
|
end
|
25
27
|
end
|
data/lib/camalian/color.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Camalian
|
4
|
+
# Camalian color object
|
2
5
|
class Color
|
3
|
-
|
4
6
|
attr_reader :r, :g, :b, :h, :s, :l, :hsv
|
5
7
|
|
6
|
-
def initialize(
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def initialize(r, g, b)
|
9
|
+
build_components(r, g, b)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_hex(hex_value)
|
13
|
+
r, g, b = extract_rgb(hex_value)
|
14
|
+
Color.new(r, g, b)
|
13
15
|
end
|
14
16
|
|
15
17
|
def to_s
|
@@ -21,7 +23,7 @@ module Camalian
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def distance(color)
|
24
|
-
[(
|
26
|
+
[(h - color.h) % 360, (color.h - h) % 360].min
|
25
27
|
end
|
26
28
|
|
27
29
|
def extract_rgb(color_hash)
|
@@ -35,7 +37,7 @@ module Camalian
|
|
35
37
|
|
36
38
|
private
|
37
39
|
|
38
|
-
def build_components(r,g,b)
|
40
|
+
def build_components(r, g, b)
|
39
41
|
@r = r
|
40
42
|
@g = g
|
41
43
|
@b = b
|
@@ -50,21 +52,21 @@ module Camalian
|
|
50
52
|
|
51
53
|
@l = (cmax + cmin) / 2.0
|
52
54
|
|
53
|
-
if delta
|
55
|
+
if delta.zero?
|
54
56
|
@h = 0
|
55
57
|
elsif cmax == ri
|
56
58
|
@h = 60 * (((gi - bi) / delta) % 6)
|
57
59
|
elsif cmax == gi
|
58
|
-
@h = 60 * (((bi - ri)/ delta) + 2)
|
60
|
+
@h = 60 * (((bi - ri) / delta) + 2)
|
59
61
|
elsif cmax == bi
|
60
|
-
@h = 60 * (((ri - gi)/ delta) + 4)
|
62
|
+
@h = 60 * (((ri - gi) / delta) + 4)
|
61
63
|
end
|
62
64
|
|
63
|
-
if
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
@s = if delta.zero?
|
66
|
+
0
|
67
|
+
else
|
68
|
+
delta / (1 - (2 * @l - 1).abs)
|
69
|
+
end
|
68
70
|
|
69
71
|
@h = @h.round(2)
|
70
72
|
@s = (@s * 100).round(2)
|
@@ -72,22 +74,22 @@ module Camalian
|
|
72
74
|
|
73
75
|
# HSV Calculation
|
74
76
|
# Hue calculation
|
75
|
-
if delta
|
77
|
+
if delta.zero?
|
76
78
|
@hsv = [0]
|
77
79
|
elsif cmax == ri
|
78
80
|
@hsv = [60 * (((gi - bi) / delta) % 6)]
|
79
81
|
elsif cmax == gi
|
80
|
-
@hsv = [60 * (((bi - ri)/ delta) + 2)]
|
82
|
+
@hsv = [60 * (((bi - ri) / delta) + 2)]
|
81
83
|
elsif cmax == bi
|
82
|
-
@hsv = [60 * (((ri - gi)/ delta) + 4)]
|
84
|
+
@hsv = [60 * (((ri - gi) / delta) + 4)]
|
83
85
|
end
|
84
86
|
|
85
87
|
# Saturation calculation
|
86
|
-
if
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
@hsv << if cmax.zero?
|
89
|
+
0
|
90
|
+
else
|
91
|
+
delta / cmax
|
92
|
+
end
|
91
93
|
|
92
94
|
# Value calculation
|
93
95
|
@hsv << cmax
|
data/lib/camalian/image.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Camalian
|
4
|
+
# Load image into Camalian
|
4
5
|
class Image
|
5
6
|
attr_accessor :src_file_path
|
6
7
|
|
@@ -8,29 +9,30 @@ module Camalian
|
|
8
9
|
@src_file_path = file_path
|
9
10
|
end
|
10
11
|
|
11
|
-
def prominent_colors(count=Camalian.options[:color_count]
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
def prominent_colors(count = Camalian.options[:color_count],
|
13
|
+
quantization: Camalian.options[:quantization],
|
14
|
+
optimal: true)
|
15
|
+
image = ::ChunkyPNG::Image.from_file(@src_file_path)
|
16
|
+
|
17
|
+
colors = image.pixels.map do |val|
|
18
|
+
Color.new(
|
19
|
+
ChunkyPNG::Color.r(val),
|
20
|
+
ChunkyPNG::Color.g(val),
|
21
|
+
ChunkyPNG::Color.b(val)
|
22
|
+
)
|
21
23
|
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
colors << Color.new(c[0],c[1],c[2])
|
24
|
+
|
25
|
+
quantize = Object.const_get("Camalian::Quantization::#{quantization.capitalize}").new
|
26
|
+
|
27
|
+
palette = quantize.process(colors, count)
|
28
|
+
|
29
|
+
retry_count = 1
|
30
|
+
while !optimal && palette.size < count
|
31
|
+
palette = quantize.process(colors, count + retry_count)
|
32
|
+
retry_count += 1
|
32
33
|
end
|
33
|
-
|
34
|
+
|
35
|
+
palette
|
34
36
|
end
|
35
37
|
end
|
36
38
|
end
|
data/lib/camalian/palette.rb
CHANGED
@@ -1,26 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Camalian
|
4
|
+
# Collection of colors with some useful features
|
2
5
|
class Palette < Array
|
3
|
-
|
4
6
|
def sort_by_lightness
|
5
|
-
Palette.new(
|
7
|
+
Palette.new(sort_by(&:l).reverse)
|
6
8
|
end
|
7
9
|
|
8
10
|
def sort_by_hue
|
9
|
-
Palette.new(
|
11
|
+
Palette.new(sort_by(&:h).reverse)
|
10
12
|
end
|
11
13
|
|
12
14
|
def sort_similar_colors
|
13
|
-
Palette.new(
|
15
|
+
Palette.new(sort_by(&:hsv))
|
14
16
|
end
|
15
17
|
|
16
18
|
def sort_by_saturation
|
17
|
-
Palette.new(
|
19
|
+
Palette.new(sort_by(&:s).reverse)
|
20
|
+
end
|
21
|
+
|
22
|
+
def average_color
|
23
|
+
r = map(&:r).inject(&:+)
|
24
|
+
g = map(&:g).inject(&:+)
|
25
|
+
b = map(&:b).inject(&:+)
|
26
|
+
size = self.size
|
27
|
+
|
28
|
+
Color.new(r / size, g / size, b / size)
|
18
29
|
end
|
19
30
|
|
20
31
|
def light_colors(limit1, limit2)
|
21
32
|
min = [limit1, limit2].min
|
22
33
|
max = [limit1, limit2].max
|
23
|
-
table =
|
34
|
+
table = dup
|
24
35
|
Palette.new(table.delete_if { |color| color.l > max or color.l < min })
|
25
36
|
end
|
26
37
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Camalian
|
4
|
+
module Quantization
|
5
|
+
class Histogram # :nodoc:
|
6
|
+
def process(colors, count)
|
7
|
+
bucket_size = (255.0 / count).ceil
|
8
|
+
buckets = {}
|
9
|
+
|
10
|
+
colors.each do |color|
|
11
|
+
key = bucket_key(color, bucket_size)
|
12
|
+
buckets[key] ||= Palette.new
|
13
|
+
buckets[key].push(color)
|
14
|
+
end
|
15
|
+
|
16
|
+
Palette.new(buckets.map { |_, value| value.average_color })
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def bucket_key(color, bucket_size)
|
22
|
+
"#{color.r / bucket_size}:#{color.g / bucket_size}:#{color.b / bucket_size}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/camalian/version.rb
CHANGED
data/test/assets/palette.png
CHANGED
Binary file
|
data/test/test_color.rb
CHANGED
@@ -1,29 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
1
4
|
require 'minitest/autorun'
|
2
5
|
require 'minitest/spec'
|
3
6
|
require 'camalian'
|
4
7
|
|
5
8
|
describe Camalian::Color do
|
6
9
|
before do
|
7
|
-
@color = Camalian::Color.new(120,255,30)
|
10
|
+
@color = Camalian::Color.new(120, 255, 30)
|
8
11
|
end
|
9
12
|
|
10
|
-
describe
|
11
|
-
it
|
12
|
-
@color.to_hex.must_equal
|
13
|
+
describe 'Color initialized with 120, 255, 30 rgb values' do
|
14
|
+
it 'hex value must be #78ff1e' do
|
15
|
+
_(@color.to_hex).must_equal '#78ff1e'
|
13
16
|
end
|
14
17
|
|
15
|
-
it
|
16
|
-
[@color.h.to_i, @color.s.to_i, @color.l.to_i].must_equal [96, 100, 55]
|
18
|
+
it 'hsl color components must ' do
|
19
|
+
_([@color.h.to_i, @color.s.to_i, @color.l.to_i]).must_equal [96, 100, 55]
|
17
20
|
end
|
18
21
|
|
19
|
-
it
|
20
|
-
@color.hsv.map(&:to_i).must_equal [96, 88, 100]
|
22
|
+
it 'hsv color components must ' do
|
23
|
+
_(@color.hsv.map(&:to_i)).must_equal [96, 88, 100]
|
21
24
|
end
|
22
25
|
end
|
23
26
|
|
24
|
-
describe
|
25
|
-
it
|
26
|
-
Camalian::Color.new(7, 7, 7).to_hex.must_equal
|
27
|
+
describe 'initialized with 1 integer rgb value' do
|
28
|
+
it 'must have leading zero' do
|
29
|
+
_(Camalian::Color.new(7, 7, 7).to_hex).must_equal '#070707'
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
data/test/test_helper.rb
ADDED
data/test/test_palette.rb
CHANGED
@@ -1,28 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
1
4
|
require 'minitest/autorun'
|
2
5
|
require 'minitest/spec'
|
3
6
|
require 'camalian'
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
end
|
8
|
+
PALLET_IMAGE_COLORS = %w[#4dd915 #49cc23 #45c031 #41b43f #3da84d #399c5b #359069 #318478 #2d7886 #296c94 #2560a2
|
9
|
+
#2154b0 #1d48be #193ccc #1530db].freeze
|
8
10
|
|
9
11
|
describe Camalian::Palette do
|
10
12
|
before do
|
11
|
-
@image = Camalian
|
12
|
-
@palette = @image.prominent_colors(15)
|
13
|
+
@image = Camalian.load(File.join(File.dirname(__FILE__), 'assets/palette.png'))
|
14
|
+
@palette = @image.prominent_colors(15, optimal: false)
|
13
15
|
end
|
14
16
|
|
15
|
-
describe
|
16
|
-
it
|
17
|
-
@palette.size.must_equal 15
|
17
|
+
describe 'palette with 15 colors extracted' do
|
18
|
+
it 'must have 15 colors' do
|
19
|
+
_(@palette.size).must_equal 15
|
18
20
|
end
|
19
21
|
|
20
|
-
it
|
21
|
-
@palette.sort_similar_colors.map(&:to_hex).must_equal
|
22
|
+
it 'sort similar colors in order' do
|
23
|
+
_(@palette.sort_similar_colors.map(&:to_hex)).must_equal PALLET_IMAGE_COLORS
|
22
24
|
end
|
23
25
|
|
24
|
-
it
|
25
|
-
@palette.light_colors(0, 40).map(&:to_hex).must_equal %
|
26
|
+
it 'color with intensity 0-40 works well' do
|
27
|
+
_(@palette.light_colors(0, 40).map(&:to_hex)).must_equal %w[#2560a2 #296c94 #2d7886 #318478 #359069]
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
metadata
CHANGED
@@ -1,57 +1,75 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: camalian
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nazar Hussain
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: chunky_png
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
17
20
|
- - ">="
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
22
|
+
version: 1.3.14
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
24
30
|
- - ">="
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
32
|
+
version: 1.3.14
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
34
|
+
name: minitest
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - "~>"
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
34
|
-
|
39
|
+
version: '5.14'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 5.14.2
|
43
|
+
type: :development
|
35
44
|
prerelease: false
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
37
46
|
requirements:
|
38
47
|
- - "~>"
|
39
48
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
49
|
+
version: '5.14'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 5.14.2
|
41
53
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
54
|
+
name: rake
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
44
56
|
requirements:
|
45
57
|
- - "~>"
|
46
58
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
48
|
-
|
59
|
+
version: '13.0'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 13.0.1
|
63
|
+
type: :development
|
49
64
|
prerelease: false
|
50
65
|
version_requirements: !ruby/object:Gem::Requirement
|
51
66
|
requirements:
|
52
67
|
- - "~>"
|
53
68
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
69
|
+
version: '13.0'
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 13.0.1
|
55
73
|
description: Library used to deal with colors and images
|
56
74
|
email:
|
57
75
|
- nazarhussain@gmail.com
|
@@ -59,7 +77,14 @@ executables: []
|
|
59
77
|
extensions: []
|
60
78
|
extra_rdoc_files: []
|
61
79
|
files:
|
80
|
+
- ".github/workflows/gpr-push.yml"
|
81
|
+
- ".github/workflows/ruby.yml"
|
82
|
+
- ".github/workflows/rubygems-push.yml"
|
62
83
|
- ".gitignore"
|
84
|
+
- ".overcommit.yml"
|
85
|
+
- ".rubocop.yml"
|
86
|
+
- ".tool-versions"
|
87
|
+
- ".travis.yml"
|
63
88
|
- Gemfile
|
64
89
|
- LICENSE.txt
|
65
90
|
- README.md
|
@@ -69,10 +94,11 @@ files:
|
|
69
94
|
- lib/camalian/color.rb
|
70
95
|
- lib/camalian/image.rb
|
71
96
|
- lib/camalian/palette.rb
|
97
|
+
- lib/camalian/quantization/histogram.rb
|
72
98
|
- lib/camalian/version.rb
|
73
|
-
- lib/chunky_png_patch/color.rb
|
74
99
|
- test/assets/palette.png
|
75
100
|
- test/test_color.rb
|
101
|
+
- test/test_helper.rb
|
76
102
|
- test/test_palette.rb
|
77
103
|
homepage: https://github.com/nazarhussain/camalian
|
78
104
|
licenses:
|
@@ -86,16 +112,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
86
112
|
requirements:
|
87
113
|
- - ">="
|
88
114
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
115
|
+
version: '2.4'
|
90
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
117
|
requirements:
|
92
118
|
- - ">="
|
93
119
|
- !ruby/object:Gem::Version
|
94
120
|
version: '0'
|
95
|
-
requirements:
|
96
|
-
|
97
|
-
rubyforge_project:
|
98
|
-
rubygems_version: 2.4.5
|
121
|
+
requirements: []
|
122
|
+
rubygems_version: 3.0.3
|
99
123
|
signing_key:
|
100
124
|
specification_version: 4
|
101
125
|
summary: Library used to deal with colors and images. You can extract colors from
|
@@ -103,4 +127,5 @@ summary: Library used to deal with colors and images. You can extract colors fro
|
|
103
127
|
test_files:
|
104
128
|
- test/assets/palette.png
|
105
129
|
- test/test_color.rb
|
130
|
+
- test/test_helper.rb
|
106
131
|
- test/test_palette.rb
|
@@ -1,14 +0,0 @@
|
|
1
|
-
module ChunkyPNG::Color
|
2
|
-
# See http://en.wikipedia.org/wiki/Hue#Computing_hue_from_RGB
|
3
|
-
def self.hue(pixel)
|
4
|
-
r, g, b = r(pixel), g(pixel), b(pixel)
|
5
|
-
return 0 if r == b and b == g
|
6
|
-
((180 / Math::PI * Math.atan2((2 * r) - g - b, Math.sqrt(3) * (g - b))) - 90) % 360
|
7
|
-
end
|
8
|
-
|
9
|
-
# The modular distance, as the hue is circular
|
10
|
-
def self.distance(pixel, poxel)
|
11
|
-
hue_pixel, hue_poxel = hue(pixel), hue(poxel)
|
12
|
-
[(hue_pixel - hue_poxel) % 360, (hue_poxel - hue_pixel) % 360].min
|
13
|
-
end
|
14
|
-
end
|