Repository: LunarLogic/gauguin
Branch: master
Commit: 4067ee267499
Files: 40
Total size: 41.8 KB
Directory structure:
gitextract_y36ydtsy/
├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── Guardfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── gauguin.gemspec
├── lib/
│ ├── gauguin/
│ │ ├── color.rb
│ │ ├── color_space/
│ │ │ ├── lab_vector.rb
│ │ │ ├── rgb_vector.rb
│ │ │ └── xyz_vector.rb
│ │ ├── color_space.rb
│ │ ├── colors_clusterer.rb
│ │ ├── colors_limiter.rb
│ │ ├── colors_retriever.rb
│ │ ├── image.rb
│ │ ├── image_recolorer.rb
│ │ ├── image_repository.rb
│ │ ├── noise_reducer.rb
│ │ ├── painting.rb
│ │ ├── palette_serializer.rb
│ │ └── version.rb
│ └── gauguin.rb
└── spec/
├── integration/
│ ├── painting_spec.rb
│ └── samples_spec.rb
├── lib/
│ └── gauguin/
│ ├── color_space/
│ │ ├── rgb_vector_spec.rb
│ │ └── xyz_vector_spec.rb
│ ├── color_spec.rb
│ ├── colors_clusterer_spec.rb
│ ├── colors_limiter_spec.rb
│ ├── colors_retriever_spec.rb
│ ├── image_recolorer_spec.rb
│ ├── image_repository_spec.rb
│ ├── image_spec.rb
│ ├── noise_reducer_spec.rb
│ ├── painting_spec.rb
│ └── palette_serializer_spec.rb
└── spec_helper.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
.DS_Store
================================================
FILE: .rspec
================================================
--color
--format documentation
================================================
FILE: .travis.yml
================================================
language: ruby
script:
- CODECLIMATE_REPO_TOKEN=edbf400c9cd2e92ef8eabf2dad1d03b0ed0cb2a83a20f12f70e4f8107c38de51 bundle exec rake
rvm:
- 2.1
notifications:
email:
- anna.slimak@lunarlogic.io
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
# Specify your gem's dependencies in gauguin.gemspec
gemspec
================================================
FILE: Guardfile
================================================
guard :rspec, cmd: 'bundle exec rspec' do
watch(%r{^spec/.+_spec\.rb$})
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
watch('spec/spec_helper.rb') { "spec" }
end
================================================
FILE: LICENSE.txt
================================================
Copyright (c) 2014 Lunar Logic Polska http://lunarlogicpolska.com
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
[](https://travis-ci.org/LunarLogic/gauguin)
[](https://codeclimate.com/github/LunarLogic/gauguin)
[](https://codeclimate.com/github/LunarLogic/gauguin)
<img src="http://gauguin.lunarlogic.io/assets/gauguin-b7a7737e8ede819b98df9d05f7df020a.png" alt="Guard Icon" align="left" />
# Gauguin
Retrieves palette of main colors, merging similar colors using [Lab color space](http://en.wikipedia.org/wiki/Lab_color_space).
## Why not just use `RMagick`?
How many colors do you recognize on the image below?

Many people would say `2`, but actually there are `1942`.
It's because of the fact that to make image more smooth, borders of the figure are not pure black but consist of many gray scale colors.
It's common that images includes very similar colors, so when you want to get useful color palette, you would need to process color histogram you get from `RMagick` yourself.
This gem was created to do this for you.
## Sample app
Sample application available here: http://gauguin.lunarlogic.io
## Requirements
Gem depends on `RMagick` which requires `ImageMagick` to be installed.
### Ubuntu
$ sudo apt-get install imagemagick
### OSX
$ brew install imagemagick
## Installation
Add this line to your application's Gemfile:
```ruby
gem 'gauguin'
```
And then execute:
$ bundle
Or install it yourself as:
$ gem install gauguin
## Usage
#### Palette
```ruby
palette = Gauguin::Painting.new("path/to/image.png").palette
```
Result for image above would be:
```ruby
{
rgb(204, 204, 204)[0.5900935269505287] => [
rgb(77, 77, 77)[7.383706620723603e-05],
rgb(85, 85, 85)[0.00012306177701206005],
# ...
rgb(219, 220, 219)[1.2306177701206005e-05],
rgb(220, 220, 220)[7.383706620723603e-05]
],
rgb(0, 0, 0)[0.40990647304947003] => [
rgb(0, 0, 0)[0.40990647304947003],
rgb(1, 1, 1)[0.007912872261875462],
# ...
rgb(64, 64, 64)[6.153088850603002e-05],
rgb(66, 66, 66)[6.153088850603002e-05]
]
}
```
Where keys are instances of `Gauguin::Color` class and values are array of instances of `Gauguin::Color` class.
#### Recolor
There is also recolor feature - you can pass original image and the calculated palette and return new image, colored only with the main colours from the palette.
```ruby
painting.recolor(palette, 'path/where/recolored/file/will/be/placed')
```
## Custom configuration
There are `4` parameters that you can configure:
- `max_colors_count` (default value is `10`) - maximum number of colors that a palette will include
- `colors_limit` (default value is `10000`) - maximum number of colors that will be considered while calculating a palette - if image has too many colors it is not efficient to calculate grouping for all of them, so only `colors_limit` of colors of the largest percentage are used
- `min_percentage_sum` (default value is `0.981`) - parameter used while calculating which colors should be ignored. Colors are sorted by percentage in descending order, then colors which percentages sums to `min_percentage_sum` are taken into consideration
- `color_similarity_threshold` (default value is `25`) - maximum distance in [Lab color space](http://en.wikipedia.org/wiki/Lab_color_space) to consider two colors as the same while grouping
To configure any of above options you can use configuration block.
For example changing `max_colors_count` would look like this:
```ruby
Gauguin.configuration do |config|
config.max_colors_count = 7
end
```
## Contributing
1. Fork it ( https://github.com/LunarLogic/gauguin/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
================================================
FILE: Rakefile
================================================
require "bundler/gem_tasks"
require 'rake'
require 'rspec/core/rake_task'
desc "Run all examples"
RSpec::Core::RakeTask.new(:spec) do |t|
t.rspec_opts = %w[--color]
end
task :default => [:spec]
================================================
FILE: gauguin.gemspec
================================================
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'gauguin/version'
Gem::Specification.new do |spec|
spec.name = "gauguin"
spec.version = Gauguin::VERSION
spec.authors = ["Ania Slimak"]
spec.email = ["anna.slimak@lunarlogic.io"]
spec.summary = %q{Tool for retrieving main colors from the image.}
spec.description = %q{Retrieves palette of main colors, merging similar colors using Lab color space.}
spec.homepage = "https://github.com/LunarLogic/gauguin"
spec.license = "MIT"
spec.files = `git ls-files -z`.split("\x0")
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_dependency "rmagick"
spec.add_development_dependency "bundler", "~> 1.7"
spec.add_development_dependency "rake", "~> 10.0"
spec.add_development_dependency "rspec", "~> 3.2"
spec.add_development_dependency "simplecov"
spec.add_development_dependency "codeclimate-test-reporter"
spec.add_development_dependency "guard-rspec"
spec.add_development_dependency "pry"
end
================================================
FILE: lib/gauguin/color.rb
================================================
module Gauguin
class Color
attr_accessor :red, :green, :blue, :percentage, :transparent
def initialize(red, green, blue, percentage = 1, transparent = false)
self.red = red
self.green = green
self.blue = blue
self.percentage = percentage
self.transparent = transparent
end
def ==(other)
self.class == other.class && self.to_key == other.to_key
end
alias eql? ==
def hash
self.to_key.hash
end
def similar?(other_color)
self.transparent == other_color.transparent &&
self.distance(other_color) < Gauguin.configuration.color_similarity_threshold
end
def distance(other_color)
(self.to_lab - other_color.to_lab).r
end
def to_lab
rgb_vector = self.to_vector
xyz_vector = rgb_vector.to_xyz
xyz_vector.to_lab
end
def to_vector
ColorSpace::RgbVector[*to_rgb]
end
def to_rgb
[self.red, self.green, self.blue]
end
def to_a
to_rgb + [self.percentage, self.transparent]
end
def self.from_a(array)
red, green, blue, percentage, transparent = array
Color.new(red, green, blue, percentage, transparent)
end
def to_key
to_rgb + [self.transparent]
end
def to_s
"rgb(#{self.red}, #{self.green}, #{self.blue})"
end
def inspect
msg = "#{to_s}[#{percentage}]"
if transparent?
msg += "[transparent]"
end
msg
end
def transparent?
self.transparent
end
end
end
================================================
FILE: lib/gauguin/color_space/lab_vector.rb
================================================
module Gauguin
module ColorSpace
class LabVector < Vector
end
end
end
================================================
FILE: lib/gauguin/color_space/rgb_vector.rb
================================================
module Gauguin
module ColorSpace
class RgbVector < Vector
MAX_VAUE = 255.0
# Observer. = 2°, Illuminant = D65
RGB_TO_XYZ = Matrix[[0.4124, 0.2126, 0.0193],
[0.3576, 0.7152, 0.1192],
[0.1805, 0.0722, 0.9505]]
def pivot!
self.each.with_index do |component, i|
self[i] = pivot(component / MAX_VAUE)
end
self
end
def to_xyz
self.pivot!
matrix = Matrix[self] * RGB_TO_XYZ
XyzVector[*matrix.row_vectors.first.to_a]
end
private
def pivot(component)
component = if component > 0.04045
((component + 0.055) / 1.055) ** 2.4
else
component / 12.92
end
component * 100.0
end
end
end
end
================================================
FILE: lib/gauguin/color_space/xyz_vector.rb
================================================
module Gauguin
module ColorSpace
class XyzVector < Vector
WHITE_REFERENCE = self[95.047, 100.000, 108.883]
EPSILON = 0.008856
KAPPA = 903.3
def to_lab
zipped = self.zip(XyzVector::WHITE_REFERENCE)
x, y, z = zipped.map do |component, white_component|
component / white_component
end
l = 116 * f(y) - 16
a = 500 * (f(x) - f(y))
b = 200 * (f(y) - f(z))
LabVector[l, a, b]
end
private
def f(x)
if x > EPSILON
x ** (1.0/3.0)
else
(KAPPA * x + 16.0) / 116.0
end
end
end
end
end
================================================
FILE: lib/gauguin/color_space.rb
================================================
require 'matrix'
require "gauguin/color_space/rgb_vector"
require "gauguin/color_space/xyz_vector"
require "gauguin/color_space/lab_vector"
================================================
FILE: lib/gauguin/colors_clusterer.rb
================================================
module Gauguin
class ColorsClusterer
def call(colors)
clusters = {}
while !colors.empty?
pivot = colors.shift
group = [pivot]
colors, pivot, group = find_all_similar(colors, pivot, group)
clusters[pivot] = group
end
update_pivots_percentages(clusters)
clusters
end
def clusters(colors)
clusters = self.call(colors)
clusters = clusters.sort_by { |color, _| color.percentage }.reverse
Hash[clusters[0...Gauguin.configuration.max_colors_count]]
end
def reversed_clusters(clusters)
reversed_clusters = {}
clusters.each do |pivot, group|
group.each do |color|
reversed_clusters[color] = pivot
end
end
reversed_clusters
end
private
def find_all_similar(colors, pivot, group)
loop do
similar_colors = colors.select { |c| c.similar?(pivot) }
break if similar_colors.empty?
group += similar_colors
colors -= similar_colors
pivot = group.sort_by(&:percentage).last
end
[colors, pivot, group]
end
def update_pivots_percentages(clusters)
clusters.each do |main_color, group|
percentage = group.inject(0) do |sum, color|
sum += color.percentage
end
main_color.percentage = percentage
end
end
end
end
================================================
FILE: lib/gauguin/colors_limiter.rb
================================================
module Gauguin
class ColorsLimiter
def call(colors)
colors_limit = Gauguin.configuration.colors_limit
if colors.count > colors_limit
colors = colors.sort_by { |key, group| key.percentage }.
reverse[0..colors_limit - 1]
end
colors
end
end
end
================================================
FILE: lib/gauguin/colors_retriever.rb
================================================
module Gauguin
class ColorsRetriever
def initialize(image)
@image = image
end
def colors
colors = {}
histogram = @image.color_histogram
image_size = @image.columns * @image.rows
histogram.each do |pixel, count|
image_pixel = @image.pixel(pixel)
red, green, blue = image_pixel.to_rgb
percentage = count.to_f / image_size
color = Gauguin::Color.new(red, green, blue, percentage,
image_pixel.transparent?)
# histogram can contain different magic pixels for
# the same colors with different opacity
if colors[color]
colors[color].percentage += color.percentage
else
colors[color] = color
end
end
colors.values
end
end
end
================================================
FILE: lib/gauguin/image.rb
================================================
require 'rmagick'
require 'forwardable'
module Gauguin
class Image
extend Forwardable
attr_accessor :image
delegate [:color_histogram, :columns, :rows, :write] => :image
def initialize(path = nil)
return unless path
list = Magick::ImageList.new(path)
self.image = list.first
end
def self.blank(columns, rows)
blank_image = Image.new
transparent_white = Magick::Pixel.new(255, 255, 255, Pixel::MAX_TRANSPARENCY)
blank_image.image = Magick::Image.new(columns, rows) do
self.background_color = transparent_white
end
blank_image
end
def pixel(magic_pixel)
Pixel.new(magic_pixel)
end
def pixel_color(row, column, *args)
magic_pixel = self.image.pixel_color(row, column, *args)
pixel(magic_pixel)
end
class Pixel
MAX_CHANNEL_VALUE = 257
MAX_TRANSPARENCY = 65535
def initialize(magic_pixel)
@magic_pixel = magic_pixel
end
def transparent?
@magic_pixel.opacity >= MAX_TRANSPARENCY
end
def to_rgb
[:red, :green, :blue].map do |color|
@magic_pixel.send(color) / MAX_CHANNEL_VALUE
end
end
end
end
end
================================================
FILE: lib/gauguin/image_recolorer.rb
================================================
module Gauguin
class ImageRecolorer
def initialize(image)
@image = image.dup
end
def recolor(new_colors)
columns = @image.columns
rows = @image.rows
new_image = Image.blank(columns, rows)
(0...columns).each do |column|
(0...rows).each do |row|
image_pixel = @image.pixel_color(column, row)
next if image_pixel.transparent?
color = Color.new(*image_pixel.to_rgb)
new_color = new_colors[color]
next unless new_color
new_image.pixel_color(column, row, new_color.to_s)
end
end
new_image
end
end
end
================================================
FILE: lib/gauguin/image_repository.rb
================================================
module Gauguin
class ImageRepository
def get(path)
Gauguin::Image.new(path)
end
end
end
================================================
FILE: lib/gauguin/noise_reducer.rb
================================================
module Gauguin
class NoiseReducer
def call(colors_clusters)
pivots = colors_clusters.keys.sort_by! { |key, group| key.percentage }.reverse
percentage_sum = 0
index = 0
pivots.each do |color|
percentage_sum += color.percentage
break if percentage_sum > Gauguin.configuration.min_percentage_sum
index += 1
end
reduced_clusters(colors_clusters, pivots, index)
end
private
def reduced_clusters(colors_clusters, pivots, cut_off_index)
reduced_pivots = pivots[0..cut_off_index]
colors_clusters.select do |c|
!c.transparent? && reduced_pivots.include?(c)
end
end
end
end
================================================
FILE: lib/gauguin/painting.rb
================================================
module Gauguin
class Painting
def initialize(path, image_repository = nil, colors_retriever = nil,
colors_limiter = nil, noise_reducer = nil,
colors_clusterer = nil, image_recolorer = nil)
@image_repository = image_repository || Gauguin::ImageRepository.new
@image = @image_repository.get(path)
@colors_retriever = colors_retriever || Gauguin::ColorsRetriever.new(@image)
@colors_limiter = colors_limiter || Gauguin::ColorsLimiter.new
@noise_reducer = noise_reducer || Gauguin::NoiseReducer.new
@colors_clusterer = colors_clusterer || Gauguin::ColorsClusterer.new
@image_recolorer = image_recolorer || Gauguin::ImageRecolorer.new(@image)
end
def palette
colors = @colors_retriever.colors
colors = @colors_limiter.call(colors)
colors_clusters = @colors_clusterer.clusters(colors)
@noise_reducer.call(colors_clusters)
end
def recolor(palette, path)
new_colors = @colors_clusterer.reversed_clusters(palette)
recolored_image = @image_recolorer.recolor(new_colors)
recolored_image.write(path)
end
end
end
================================================
FILE: lib/gauguin/palette_serializer.rb
================================================
require 'yaml'
module Gauguin
class PaletteSerializer
def self.load(value)
return unless value
value = YAML.load(value)
value = value.to_a.map do |color_key, group|
[Gauguin::Color.from_a(color_key), group]
end
Hash[value]
end
def self.dump(value)
value = value.to_a.map { |color, group| [color.to_a, group] }
value = Hash[value]
YAML.dump(value)
end
end
end
================================================
FILE: lib/gauguin/version.rb
================================================
module Gauguin
VERSION = "0.0.3"
end
================================================
FILE: lib/gauguin.rb
================================================
require "gauguin/version"
require "gauguin/color"
require "gauguin/color_space"
require "gauguin/colors_retriever"
require "gauguin/colors_limiter"
require "gauguin/colors_clusterer"
require "gauguin/noise_reducer"
require "gauguin/image_recolorer"
require "gauguin/painting"
require "gauguin/image"
require "gauguin/image_repository"
require "gauguin/palette_serializer"
module Gauguin
class << self
attr_accessor :configuration
end
def self.configure
self.configuration ||= Configuration.new
yield(configuration) if block_given?
end
class Configuration
DEFAULT_MAX_COLORS_COUNT = 10
DEFAULT_COLORS_LIMIT = 10000
DEFAULT_MIN_PERCENTAGE_SUM = 0.981
DEFAULT_COLOR_SIMILARITY_THRESHOLD = 25
attr_accessor :max_colors_count, :colors_limit,
:min_percentage_sum, :color_similarity_threshold
def initialize
@max_colors_count = DEFAULT_MAX_COLORS_COUNT
@colors_limit = DEFAULT_COLORS_LIMIT
@min_percentage_sum = DEFAULT_MIN_PERCENTAGE_SUM
@color_similarity_threshold = DEFAULT_COLOR_SIMILARITY_THRESHOLD
end
end
end
Gauguin.configure
================================================
FILE: spec/integration/painting_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe Painting do
let(:path) do
File.join("spec", "support", "pictures", file_name)
end
let(:gray) { Color.new(204, 204, 204) }
let(:black) { Color.new(0, 0, 0) }
let(:white) { Color.new(255, 255, 255) }
let(:painting) { Painting.new(path) }
describe "#palette" do
shared_examples_for "retrieves unique colors" do
it { expect(subject.count).to eq 5 }
it do
expect(subject.keys).to include(white)
end
end
subject { painting.palette }
context "unique colors in the picture" do
let(:file_name) { "unique_colors.png" }
it_behaves_like "retrieves unique colors"
end
context "not unique colors in the picture" do
let(:file_name) { "not_unique_colors.png" }
it_behaves_like "retrieves unique colors"
end
context "image has two colors but with different gradients
so actually 1942 unique colors" do
let(:file_name) { "gray_and_black.png" }
let(:values) { subject.values.flatten }
it { expect(subject.count).to eq 2 }
it { expect(values.include?(black)).to be true }
it { expect(values.include?(gray)).to be true }
end
context "transparent background" do
let(:file_name) { "transparent_background.png" }
it { expect(subject.count).to eq 1 }
it do
expect(subject.keys).to eq [Color.new(2, 0, 0)]
end
end
context "image with 10 colors" do
let(:file_name) { "10_colors.png" }
it { expect(subject.count).to eq 10 }
end
context "image with over than max_colors_count colors" do
let(:file_name) { "12_colors.png" }
it { expect(subject.count).to eq 10 }
context "image with over than colors_limit colors" do
configure(:colors_limit, 9)
configure(:max_colors_count, 12)
it "returns colors_limit colors" do
expect(subject.count).to eq 9
end
end
end
end
end
end
================================================
FILE: spec/integration/samples_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe "samples" do
def self.picture_path(file_name)
File.join("spec", "support", "pictures", file_name)
end
let(:painting) { Painting.new(picture_path(file_name)) }
def self.paths
(1..11).map { |i| picture_path(File.join("samples", "sample#{i}.png")) }
end
def self.expected_results
[
["rgb(219, 12, 38)", "rgb(255, 255, 255)"],
["rgb(168, 36, 40)", "rgb(255, 255, 255)"],
["rgb(0, 0, 0)", "rgb(204, 204, 204)"],
["rgb(154, 79, 54)", "rgb(187, 196, 201)", "rgb(236, 112, 48)", "rgb(28, 28, 64)", "rgb(92, 54, 59)"],
["rgb(254, 254, 254)", "rgb(255, 195, 13)", "rgb(60, 4, 67)"],
["rgb(2, 0, 0)"],
["rgb(148, 158, 149)", "rgb(198, 64, 63)"],
["rgb(109, 207, 246)", "rgb(237, 28, 36)", "rgb(255, 255, 255)"],
["rgb(255, 255, 255)", "rgb(87, 196, 15)"],
["rgb(240, 110, 170)", "rgb(255, 255, 255)"],
["rgb(0, 165, 19)", "rgb(0, 71, 241)", "rgb(230, 27, 49)", "rgb(249, 166, 0)", "rgb(255, 255, 255)"]
]
end
def self.samples
Hash[paths.zip(expected_results)]
end
samples.each do |sample_path, expected_result|
it "returns expected result for #{sample_path}" do
painting = Painting.new(sample_path)
expect(painting.palette.keys.map(&:to_s).sort).to eq(expected_result.sort)
end
end
end
end
================================================
FILE: spec/lib/gauguin/color_space/rgb_vector_spec.rb
================================================
require 'spec_helper'
module Gauguin::ColorSpace
describe RgbVector do
describe "#to_xyz" do
let(:red) { RgbVector[255, 0, 0] }
it "converts to lab space" do
expect(red.to_xyz).to eq(
XyzVector[41.24, 21.26, 1.9300000000000002])
end
end
end
end
================================================
FILE: spec/lib/gauguin/color_space/xyz_vector_spec.rb
================================================
require 'spec_helper'
module Gauguin::ColorSpace
describe XyzVector do
describe "#to_lab" do
let(:red) { XyzVector[41.24, 21.26, 1.9300000000000002] }
it "converts to lab space" do
expect(red.to_lab).to eq(
LabVector[53.23288178584245, 80.10930952982204, 67.22006831026425])
end
end
end
end
================================================
FILE: spec/lib/gauguin/color_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe Color do
let(:black) { Color.new(0, 0, 0) }
let(:red) { Color.new(255, 0, 0) }
describe "initialize" do
let(:red) { 1 }
let(:green) { 2 }
let(:blue) { 3 }
let(:percentage) { 0.5 }
subject { Color.new(red, green, blue, percentage) }
it { expect(subject.red).to eq red }
it { expect(subject.green).to eq green }
it { expect(subject.blue).to eq blue }
it { expect(subject.percentage).to eq percentage }
end
describe "#==" do
it "returns true for colors with the same key values" do
expect(black == Color.new(0, 0, 0)).to be true
end
it "returns false if any key value is different" do
expect(black == Color.new(0, 0, 1)).to be false
end
it "returns false for objects with different classes" do
expect(black == "black").to be false
end
end
describe "#similar?" do
context "similar colors" do
it { expect(black.similar?(Color.new(0, 0, 1))).to be true }
end
context "different colors" do
it { expect(black.similar?(red)).to be false }
end
end
describe '#distance' do
it 'returns circa 178.36 between black & red' do
expect(black.distance(red)).to be_within(0.01).of(117.34)
end
end
describe "#to_lab" do
let(:red) { 1 }
let(:green) { 2 }
let(:blue) { 3 }
subject { Color.new(red, green, blue).to_lab }
it "returns lab vector" do
rgb_vector = double
xyz_vector = double
expect(ColorSpace::RgbVector).to receive(:[]).with(red, green, blue).and_return(rgb_vector)
expect(rgb_vector).to receive(:to_xyz).and_return(xyz_vector)
expect(xyz_vector).to receive(:to_lab)
subject
end
end
describe "#to_s" do
subject { black.to_s }
it { expect(subject).to eq("rgb(0, 0, 0)") }
end
let(:color) { Color.new(1, 2, 3, 0.4, true) }
describe "#to_rgb" do
subject { color.to_rgb }
it { expect(subject).to eq([1, 2, 3]) }
end
describe "#to_key" do
subject { color.to_key }
it { expect(subject).to eq([1, 2, 3, true]) }
end
describe "#to_a" do
subject { color.to_a }
it { expect(subject).to eq([1, 2, 3, 0.4, true]) }
end
describe ".from_a" do
subject { Color.from_a([1, 2, 3, 0.4, true]) }
it { expect(subject.red).to eq(1) }
it { expect(subject.green).to eq(2) }
it { expect(subject.blue).to eq(3) }
it { expect(subject.percentage).to eq(0.4) }
it { expect(subject.transparent).to be true }
end
describe "#transparent?" do
subject { color.transparent? }
it { expect(subject).to be true }
end
describe "#hash" do
it "can be used as keys in the hash" do
hash = { Color.new(255, 255, 255) => 777 }
expect(hash[Color.new(255, 255, 255)]).to eq(777)
end
end
describe "#inspect" do
subject { color.inspect }
it { expect(subject).to eq("rgb(1, 2, 3)[0.4][transparent]")}
end
end
end
================================================
FILE: spec/lib/gauguin/colors_clusterer_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe ColorsClusterer do
let(:black) { Color.new(0, 0, 0, 0.597) }
let(:white) { Color.new(255, 255, 255, 0.4) }
let(:clusterer) { ColorsClusterer.new }
describe "call" do
subject { clusterer.call(colors) }
context "colors is empty" do
let(:colors) { [] }
it { expect(subject).to eq({}) }
end
context "colors includes similar colors" do
let(:pseudo_black) { Color.new(4, 0, 0, 0.001) }
let(:other_pseudo_black) { Color.new(5, 0, 0, 0.001) }
let(:another_pseudo_black) { Color.new(6, 0, 0, 0.001) }
let(:colors) do
[black, white, pseudo_black, other_pseudo_black,
another_pseudo_black]
end
it "make separate groups for them" do
expect(subject).to eq({
white => [white],
black => [black, pseudo_black, other_pseudo_black,
another_pseudo_black]
})
end
context do
let(:white) { Color.new(255, 255, 255, 0.3) }
let(:transparent_white) do
Color.new(255, 255, 255, 0.1, Image::Pixel::MAX_TRANSPARENCY)
end
it "make separate groups for fully transparent colors" do
colors << transparent_white
expect(subject).to eq({
white => [white],
transparent_white => [transparent_white],
black => [black, pseudo_black, other_pseudo_black,
another_pseudo_black]
})
end
end
it "updates percentage of leader of each group" do
subject
expect(white.percentage).to eq(0.4)
expect(black.percentage).to eq(0.6)
end
context "there is color with bigger percentage
than pivot in the group" do
before do
black.percentage = 0.001
other_pseudo_black.percentage = 0.597
end
it "chooses it as pivot" do
expect(subject).to eq({
white => [white],
other_pseudo_black => [black, pseudo_black,
other_pseudo_black,
another_pseudo_black]
})
end
context "pivots are similar" do
before do
other_pseudo_black.red = 30
another_pseudo_black.red = 60
end
it "merge their groups" do
expect(subject).to eq({
white => [white],
other_pseudo_black => [black, pseudo_black,
other_pseudo_black,
another_pseudo_black]
})
end
end
end
end
context "colors includes different colors" do
let(:colors) do
[black, white]
end
before do
expect(white).to receive(:similar?).
with(black).and_return(false)
end
it "make separate groups for them" do
expect(subject).to eq({
black => [black],
white => [white]
})
end
end
end
describe "#clusters" do
let(:red) { Color.new(255, 0, 0, 0.1) }
let(:colors) { [black, red, white] }
subject { clusterer.clusters(colors) }
configure(:max_colors_count, 2)
before do
expect(clusterer).to receive(:call).and_return({
black => [black],
red => [red],
white => [white]
})
end
it "returns max_colors_count most common colors" do
expect(subject).to eq({
white => [white],
black => [black]
})
end
end
describe "#reversed_clusters" do
let(:gray) { Color.new(0, 0, 10, 0.4) }
let(:clusters) do
{
white => [white],
black => [black, gray]
}
end
subject { clusterer.reversed_clusters(clusters) }
it "returns reversed clusters" do
expect(subject).to eq({
white => white,
black => black,
gray => black
})
end
end
end
end
================================================
FILE: spec/lib/gauguin/colors_limiter_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe ColorsLimiter do
describe "#limit" do
let(:limiter) { ColorsLimiter.new }
let(:colors) { [black, red, white] }
let(:white) { Color.new(255, 255, 255, 0.01) }
let(:red) { Color.new(255, 0, 0, 0.02) }
let(:black) { Color.new(0, 0, 0, 0.97) }
subject { limiter.call(colors) }
it "returns all colors" do
expect(subject).to eq([black, red, white])
end
context "colors count is greater than colors_limit" do
configure(:colors_limit, 2)
it "reduces colors to colors_limit" do
expect(subject).to eq([black, red])
end
end
end
end
end
================================================
FILE: spec/lib/gauguin/colors_retriever_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe ColorsRetriever do
let(:retriever) { ColorsRetriever.new(image) }
let(:image) do
fake = FakeImage.new
fake.magic_black_pixel = magic_black_pixel
fake.magic_white_pixel = magic_white_pixel
fake.magic_red_pixel = magic_red_pixel
fake.magic_red_little_transparent_pixel = magic_red_little_transparent_pixel
fake.pixels_repository = {
magic_white_pixel => FakeImage::Pixel.new(magic_white_pixel),
magic_red_pixel => FakeImage::Pixel.new(magic_red_pixel),
magic_black_pixel => FakeImage::Pixel.new(magic_black_pixel),
magic_red_little_transparent_pixel => FakeImage::Pixel.new(
magic_red_little_transparent_pixel)
}
fake.color_histogram = {
magic_white_pixel => 20,
magic_black_pixel => 30,
magic_red_pixel => 10
}
fake.rows = 10
fake.columns = 10
fake
end
def magic_pixel(rgb, opacity)
double(rgb: rgb, opacity: opacity)
end
let(:magic_black_pixel) { magic_pixel([0, 0, 0], 0) }
let(:magic_white_pixel) { magic_pixel([255, 255, 255], 0) }
let(:magic_red_pixel) { magic_pixel([255, 0, 0], 0) }
let(:magic_red_little_transparent_pixel) { magic_pixel([255, 0, 0], 50) }
describe "#colors" do
subject { retriever.colors.sort_by(&:percentage) }
it "returns array with colors with percentages" do
expect(subject).to eq([
Color.new(255, 0, 0, 0.1),
Color.new(255, 255, 255, 0.2),
Color.new(0, 0, 0, 0.3)
])
end
context "histogram contains different magic pixels
for the same color with different opacity" do
before do
image.color_histogram[magic_red_little_transparent_pixel] = 40
end
it "sums percentage" do
expect(subject).to eq([
Color.new(255, 255, 255, 0.2),
Color.new(0, 0, 0, 0.3),
Color.new(255, 0, 0, 0.5)
])
end
context "fully transparent colors" do
let(:magic_red_little_transparent_pixel) do
magic_pixel([255, 0, 0], Image::Pixel::MAX_TRANSPARENCY)
end
it "should be treated separately" do
expect(subject).to eq([
Color.new(255, 0, 0, 0.1),
Color.new(255, 255, 255, 0.2),
Color.new(0, 0, 0, 0.3),
Color.new(255, 0, 0, 0.4, true)
])
end
end
end
end
end
end
================================================
FILE: spec/lib/gauguin/image_recolorer_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe ImageRecolorer do
describe "#recolor" do
let(:image) do
fake = FakeImage.new
fake.pixels = pixels
fake.rows = pixels.count
fake.columns = pixels.first.count
fake.colors_to_pixels = {
white.to_s => white_pixel,
red.to_s => red_pixel,
black.to_s => black_pixel
}
fake
end
let(:pixels) do
[
[black_pixel, white_pixel, white_pixel],
[black_pixel, black_pixel, white_pixel],
[black_pixel, black_pixel, black_pixel]
]
end
let(:image_recolorer) { ImageRecolorer.new(image) }
let(:white_pixel) do
double('white', to_rgb: [255, 255, 255], transparent?: false)
end
let(:black_pixel) do
double('black', to_rgb: [0, 0, 0], transparent?: false)
end
let(:red_pixel) do
double('red', to_rgb: [255, 0, 0], transparent?: false)
end
let(:white) { Color.new(255, 255, 255) }
let(:black) { Color.new(0, 0, 0) }
let(:red) { Color.new(255, 0, 0) }
let(:new_colors) do
{
black => white,
white => black
}
end
subject { image_recolorer.recolor(new_colors) }
before do
allow(Image).to receive(:blank).and_return(image)
end
it "recolors image based on new_colors" do
expect(subject.pixels).to eq([
[white_pixel, black_pixel, black_pixel],
[white_pixel, white_pixel, black_pixel],
[white_pixel, white_pixel, white_pixel]
])
end
context "transparent pixel" do
let(:black_pixel) do
double('black', to_rgb: [0, 0, 0], transparent?: true)
end
it "stays the same" do
expect(subject.pixels).to eq([
[black_pixel, black_pixel, black_pixel],
[black_pixel, black_pixel, black_pixel],
[black_pixel, black_pixel, black_pixel]
])
end
end
context "color not present in new_colors" do
let(:pixels) do
[
[red_pixel, white_pixel, white_pixel],
[red_pixel, red_pixel, white_pixel],
[red_pixel, red_pixel, red_pixel]
]
end
it "stays the same" do
expect(subject.pixels).to eq([
[red_pixel, black_pixel, black_pixel],
[red_pixel, red_pixel, black_pixel],
[red_pixel, red_pixel, red_pixel]
])
end
end
end
end
end
================================================
FILE: spec/lib/gauguin/image_repository_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe ImageRepository do
let(:repository) { ImageRepository.new }
let(:path) { "path" }
describe "#get" do
it "returns image" do
expect(Gauguin::Image).to receive(:new).with(path)
repository.get(path)
end
end
end
end
================================================
FILE: spec/lib/gauguin/image_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe Image do
let(:path) do
File.join("spec", "support", "pictures", "gray_and_black.png")
end
let(:image) { Image.new(path) }
let(:magic_pixel) { double }
describe "initialize" do
context "path given" do
subject { Image.new(path) }
it "returns Image with magick image present" do
expect(subject.image).not_to be nil
end
end
context "empty constructor" do
subject { Image.new }
it "returns Image without magick image" do
expect(subject.image).to be nil
end
end
end
describe ".blank" do
let(:rows) { 10 }
let(:columns) { 20 }
it "returns blank image with transparent background" do
blank_image = Image.blank(rows, columns)
pixels = columns.times.map do |column|
rows.times.map do |row|
blank_image.pixel_color(column, row)
end
end.flatten
expect(pixels.all? { |p| p.transparent? }).to be true
end
end
describe "#pixel" do
it "returns new Image::Pixel" do
expect(Image::Pixel).to receive(:new)
image.pixel(magic_pixel)
end
end
describe "#pixel_color" do
subject { image.pixel_color(0, 0) }
it "returns Image::Pixel for given row and column" do
expect(subject.to_rgb).to eq([204, 204, 204])
end
end
describe Image::Pixel do
let(:pixel) { Image::Pixel.new(magic_pixel) }
describe "#transparent?" do
let(:magic_pixel) { double(opacity: opacity) }
let(:opacity) { 0 }
subject { pixel.transparent? }
it { expect(subject).to be false }
context "opacity equals MAX_TRANSPARENCY" do
let(:opacity) { Image::Pixel::MAX_TRANSPARENCY }
it { expect(subject).to be true }
end
end
describe "#to_rgb" do
let(:magic_pixel) { double(red: 65535, green: 0, blue: 0) }
subject { pixel.to_rgb }
it { expect(subject).to eq([255, 0, 0]) }
end
end
end
end
================================================
FILE: spec/lib/gauguin/noise_reducer_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe NoiseReducer do
let(:reducer) { NoiseReducer.new }
describe "#reduce" do
subject { reducer.call(colors).keys }
let(:white) { Color.new(255, 255, 255, 0.01) }
let(:red) { Color.new(255, 0, 0, 0.02) }
let(:black) { Color.new(0, 0, 0, 0.97) }
let(:colors) do
{
black => [black],
red => [red],
white => [white]
}
end
configure(:min_percentage_sum, 0.96)
it "returns only relevant colors" do
expect(subject).to eq([black])
end
context "no sum greater than min_percentage_sum" do
let(:white) { Color.new(255, 255, 255, 0.02) }
let(:red) { Color.new(255, 0, 0, 0.01) }
let(:black) { Color.new(0, 0, 0, 0.90) }
it "returns all colors" do
expect(subject).to eq([black, red, white])
end
end
context "transparent color" do
configure(:min_percentage_sum, 0.98)
before do
white.transparent = true
end
it "returns all colors except white" do
expect(subject).to eq([black, red])
end
end
end
end
end
================================================
FILE: spec/lib/gauguin/painting_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe Painting do
let(:colors) { [] }
let(:image_repository) { double(get: double('image')) }
let(:colors_retriever) { double }
let(:colors_limiter) { double }
let(:noise_reducer) { double }
let(:colors_clusterer) { double }
let(:image_recolorer) { double }
let(:clusters) { {} }
let(:painting) do
Painting.new("path", image_repository, colors_retriever,
colors_limiter, noise_reducer,
colors_clusterer, image_recolorer)
end
describe "#palette" do
it "returns hash with main colors of the image" do
expect(colors_retriever).to receive(:colors).
and_return(colors)
expect(colors_limiter).to receive(:call).with(colors).
and_return(colors)
expect(colors_clusterer).to receive(:clusters).with(colors).
and_return(clusters)
expect(noise_reducer).to receive(:call).with(clusters).
and_return(colors)
painting.palette
end
end
describe "#recolor" do
let(:palette) { {} }
let(:path) { 'path' }
let(:image) { double(write: nil) }
let(:new_colors) { {} }
it "recolors and writes the image to given path" do
expect(colors_clusterer).to receive(:reversed_clusters).
with(palette).and_return(new_colors)
expect(image_recolorer).to receive(:recolor).
with(new_colors).and_return(image)
expect(image).to receive(:write).with(path)
painting.recolor(palette, path)
end
end
end
end
================================================
FILE: spec/lib/gauguin/palette_serializer_spec.rb
================================================
require 'spec_helper'
module Gauguin
describe PaletteSerializer do
let(:palette) do
{
Color.new(255, 255, 255, 0.7, true) => [Color.new(255, 0, 0, 0.7, true)]
}
end
it "serializes palette" do
dumped = PaletteSerializer.dump(palette)
loaded = PaletteSerializer.load(dumped)
key = loaded.keys.first
value = loaded.values.first.first
expect(key.class).to eq(Color)
expect(value.class).to eq(Color)
expect(key.to_a).to eq([255, 255, 255, 0.7, true])
expect(value.to_a).to eq([255, 0, 0, 0.7, true])
end
end
end
================================================
FILE: spec/spec_helper.rb
================================================
if ENV['CODECLIMATE_REPO_TOKEN']
require "codeclimate-test-reporter"
CodeClimate::TestReporter.start
else
require 'simplecov'
SimpleCov.start
end
require 'bundler/setup'
require './lib/gauguin'
require 'pry'
Bundler.setup
RSpec.configure do |config|
end
def configure(config_option, value)
old_value = Gauguin.configuration.send(config_option)
before do
Gauguin.configuration.send("#{config_option}=", value)
end
after do
Gauguin.configuration.send("#{config_option}=", old_value)
end
end
class FakeImage
attr_accessor :magic_black_pixel, :magic_red_pixel,
:magic_white_pixel, :magic_red_little_transparent_pixel,
:pixels_repository, :color_histogram, :rows, :columns,
:pixels, :colors_to_pixels
def pixel(magic_pixel)
pixels_repository[magic_pixel]
end
def pixel_color(row, column, new_color = nil)
if new_color
new_pixel = self.colors_to_pixels[new_color]
pixels[row][column] = new_pixel
end
pixels[row][column]
end
class Pixel < Gauguin::Image::Pixel
attr_accessor :magic_pixel
def initialize(magic_pixel)
self.magic_pixel = magic_pixel
end
def to_rgb
magic_pixel.rgb
end
end
end
gitextract_y36ydtsy/
├── .gitignore
├── .rspec
├── .travis.yml
├── Gemfile
├── Guardfile
├── LICENSE.txt
├── README.md
├── Rakefile
├── gauguin.gemspec
├── lib/
│ ├── gauguin/
│ │ ├── color.rb
│ │ ├── color_space/
│ │ │ ├── lab_vector.rb
│ │ │ ├── rgb_vector.rb
│ │ │ └── xyz_vector.rb
│ │ ├── color_space.rb
│ │ ├── colors_clusterer.rb
│ │ ├── colors_limiter.rb
│ │ ├── colors_retriever.rb
│ │ ├── image.rb
│ │ ├── image_recolorer.rb
│ │ ├── image_repository.rb
│ │ ├── noise_reducer.rb
│ │ ├── painting.rb
│ │ ├── palette_serializer.rb
│ │ └── version.rb
│ └── gauguin.rb
└── spec/
├── integration/
│ ├── painting_spec.rb
│ └── samples_spec.rb
├── lib/
│ └── gauguin/
│ ├── color_space/
│ │ ├── rgb_vector_spec.rb
│ │ └── xyz_vector_spec.rb
│ ├── color_spec.rb
│ ├── colors_clusterer_spec.rb
│ ├── colors_limiter_spec.rb
│ ├── colors_retriever_spec.rb
│ ├── image_recolorer_spec.rb
│ ├── image_repository_spec.rb
│ ├── image_spec.rb
│ ├── noise_reducer_spec.rb
│ ├── painting_spec.rb
│ └── palette_serializer_spec.rb
└── spec_helper.rb
SYMBOL INDEX (105 symbols across 30 files)
FILE: lib/gauguin.rb
type Gauguin (line 14) | module Gauguin
function configure (line 19) | def self.configure
class Configuration (line 24) | class Configuration
method initialize (line 33) | def initialize
FILE: lib/gauguin/color.rb
type Gauguin (line 1) | module Gauguin
class Color (line 2) | class Color
method initialize (line 5) | def initialize(red, green, blue, percentage = 1, transparent = false)
method == (line 13) | def ==(other)
method hash (line 19) | def hash
method similar? (line 23) | def similar?(other_color)
method distance (line 28) | def distance(other_color)
method to_lab (line 32) | def to_lab
method to_vector (line 38) | def to_vector
method to_rgb (line 42) | def to_rgb
method to_a (line 46) | def to_a
method from_a (line 50) | def self.from_a(array)
method to_key (line 55) | def to_key
method to_s (line 59) | def to_s
method inspect (line 63) | def inspect
method transparent? (line 71) | def transparent?
FILE: lib/gauguin/color_space/lab_vector.rb
type Gauguin (line 1) | module Gauguin
type ColorSpace (line 2) | module ColorSpace
class LabVector (line 3) | class LabVector < Vector
FILE: lib/gauguin/color_space/rgb_vector.rb
type Gauguin (line 1) | module Gauguin
type ColorSpace (line 2) | module ColorSpace
class RgbVector (line 3) | class RgbVector < Vector
method pivot! (line 11) | def pivot!
method to_xyz (line 18) | def to_xyz
method pivot (line 26) | def pivot(component)
FILE: lib/gauguin/color_space/xyz_vector.rb
type Gauguin (line 1) | module Gauguin
type ColorSpace (line 2) | module ColorSpace
class XyzVector (line 3) | class XyzVector < Vector
method to_lab (line 9) | def to_lab
method f (line 24) | def f(x)
FILE: lib/gauguin/colors_clusterer.rb
type Gauguin (line 1) | module Gauguin
class ColorsClusterer (line 2) | class ColorsClusterer
method call (line 3) | def call(colors)
method clusters (line 20) | def clusters(colors)
method reversed_clusters (line 26) | def reversed_clusters(clusters)
method find_all_similar (line 40) | def find_all_similar(colors, pivot, group)
method update_pivots_percentages (line 54) | def update_pivots_percentages(clusters)
FILE: lib/gauguin/colors_limiter.rb
type Gauguin (line 1) | module Gauguin
class ColorsLimiter (line 2) | class ColorsLimiter
method call (line 3) | def call(colors)
FILE: lib/gauguin/colors_retriever.rb
type Gauguin (line 1) | module Gauguin
class ColorsRetriever (line 2) | class ColorsRetriever
method initialize (line 3) | def initialize(image)
method colors (line 7) | def colors
FILE: lib/gauguin/image.rb
type Gauguin (line 4) | module Gauguin
class Image (line 5) | class Image
method initialize (line 10) | def initialize(path = nil)
method blank (line 17) | def self.blank(columns, rows)
method pixel (line 26) | def pixel(magic_pixel)
method pixel_color (line 30) | def pixel_color(row, column, *args)
class Pixel (line 35) | class Pixel
method initialize (line 39) | def initialize(magic_pixel)
method transparent? (line 43) | def transparent?
method to_rgb (line 47) | def to_rgb
FILE: lib/gauguin/image_recolorer.rb
type Gauguin (line 1) | module Gauguin
class ImageRecolorer (line 2) | class ImageRecolorer
method initialize (line 3) | def initialize(image)
method recolor (line 7) | def recolor(new_colors)
FILE: lib/gauguin/image_repository.rb
type Gauguin (line 1) | module Gauguin
class ImageRepository (line 2) | class ImageRepository
method get (line 3) | def get(path)
FILE: lib/gauguin/noise_reducer.rb
type Gauguin (line 1) | module Gauguin
class NoiseReducer (line 2) | class NoiseReducer
method call (line 3) | def call(colors_clusters)
method reduced_clusters (line 19) | def reduced_clusters(colors_clusters, pivots, cut_off_index)
FILE: lib/gauguin/painting.rb
type Gauguin (line 1) | module Gauguin
class Painting (line 2) | class Painting
method initialize (line 3) | def initialize(path, image_repository = nil, colors_retriever = nil,
method palette (line 15) | def palette
method recolor (line 22) | def recolor(palette, path)
FILE: lib/gauguin/palette_serializer.rb
type Gauguin (line 3) | module Gauguin
class PaletteSerializer (line 4) | class PaletteSerializer
method load (line 5) | def self.load(value)
method dump (line 15) | def self.dump(value)
FILE: lib/gauguin/version.rb
type Gauguin (line 1) | module Gauguin
FILE: spec/integration/painting_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/integration/samples_spec.rb
type Gauguin (line 3) | module Gauguin
function picture_path (line 5) | def self.picture_path(file_name)
function paths (line 11) | def self.paths
function expected_results (line 15) | def self.expected_results
function samples (line 31) | def self.samples
FILE: spec/lib/gauguin/color_space/rgb_vector_spec.rb
type Gauguin::ColorSpace (line 3) | module Gauguin::ColorSpace
FILE: spec/lib/gauguin/color_space/xyz_vector_spec.rb
type Gauguin::ColorSpace (line 3) | module Gauguin::ColorSpace
FILE: spec/lib/gauguin/color_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/colors_clusterer_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/colors_limiter_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/colors_retriever_spec.rb
type Gauguin (line 3) | module Gauguin
function magic_pixel (line 34) | def magic_pixel(rgb, opacity)
FILE: spec/lib/gauguin/image_recolorer_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/image_repository_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/image_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/noise_reducer_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/painting_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/lib/gauguin/palette_serializer_spec.rb
type Gauguin (line 3) | module Gauguin
FILE: spec/spec_helper.rb
function configure (line 18) | def configure(config_option, value)
class FakeImage (line 30) | class FakeImage
method pixel (line 36) | def pixel(magic_pixel)
method pixel_color (line 40) | def pixel_color(row, column, new_color = nil)
class Pixel (line 48) | class Pixel < Gauguin::Image::Pixel
method initialize (line 51) | def initialize(magic_pixel)
method to_rgb (line 55) | def to_rgb
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (47K chars).
[
{
"path": ".gitignore",
"chars": 128,
"preview": "/.bundle/\n/.yardoc\n/Gemfile.lock\n/_yardoc/\n/coverage/\n/doc/\n/pkg/\n/spec/reports/\n/tmp/\n*.bundle\n*.so\n*.o\n*.a\nmkmf.log\n.D"
},
{
"path": ".rspec",
"chars": 32,
"preview": "--color\n--format documentation\n\n"
},
{
"path": ".travis.yml",
"chars": 202,
"preview": "language: ruby\nscript:\n - CODECLIMATE_REPO_TOKEN=edbf400c9cd2e92ef8eabf2dad1d03b0ed0cb2a83a20f12f70e4f8107c38de51 bundl"
},
{
"path": "Gemfile",
"chars": 92,
"preview": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in gauguin.gemspec\ngemspec\n"
},
{
"path": "Guardfile",
"chars": 189,
"preview": "guard :rspec, cmd: 'bundle exec rspec' do\n watch(%r{^spec/.+_spec\\.rb$})\n watch(%r{^lib/(.+)\\.rb$}) { |m| \"spec/li"
},
{
"path": "LICENSE.txt",
"chars": 1103,
"preview": "Copyright (c) 2014 Lunar Logic Polska http://lunarlogicpolska.com\n\nMIT License\n\nPermission is hereby granted, free of ch"
},
{
"path": "README.md",
"chars": 4141,
"preview": "[](https://travis-ci.org/LunarLogic/gauguin)\n"
},
{
"path": "Rakefile",
"chars": 201,
"preview": "require \"bundler/gem_tasks\"\n\nrequire 'rake'\nrequire 'rspec/core/rake_task'\n\ndesc \"Run all examples\"\nRSpec::Core::RakeTas"
},
{
"path": "gauguin.gemspec",
"chars": 1234,
"preview": "# coding: utf-8\nlib = File.expand_path('../lib', __FILE__)\n$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)\nrequi"
},
{
"path": "lib/gauguin/color.rb",
"chars": 1536,
"preview": "module Gauguin\n class Color\n attr_accessor :red, :green, :blue, :percentage, :transparent\n\n def initialize(red, g"
},
{
"path": "lib/gauguin/color_space/lab_vector.rb",
"chars": 82,
"preview": "module Gauguin\n module ColorSpace\n class LabVector < Vector\n end\n end\nend\n"
},
{
"path": "lib/gauguin/color_space/rgb_vector.rb",
"chars": 814,
"preview": "module Gauguin\n module ColorSpace\n class RgbVector < Vector\n MAX_VAUE = 255.0\n\n # Observer. = 2°, Illumina"
},
{
"path": "lib/gauguin/color_space/xyz_vector.rb",
"chars": 648,
"preview": "module Gauguin\n module ColorSpace\n class XyzVector < Vector\n WHITE_REFERENCE = self[95.047, 100.000, 108.883]\n\n"
},
{
"path": "lib/gauguin/color_space.rb",
"chars": 140,
"preview": "require 'matrix'\nrequire \"gauguin/color_space/rgb_vector\"\nrequire \"gauguin/color_space/xyz_vector\"\nrequire \"gauguin/colo"
},
{
"path": "lib/gauguin/colors_clusterer.rb",
"chars": 1384,
"preview": "module Gauguin\n class ColorsClusterer\n def call(colors)\n clusters = {}\n\n while !colors.empty?\n pivo"
},
{
"path": "lib/gauguin/colors_limiter.rb",
"chars": 298,
"preview": "module Gauguin\n class ColorsLimiter\n def call(colors)\n colors_limit = Gauguin.configuration.colors_limit\n\n "
},
{
"path": "lib/gauguin/colors_retriever.rb",
"chars": 813,
"preview": "module Gauguin\n class ColorsRetriever\n def initialize(image)\n @image = image\n end\n\n def colors\n colo"
},
{
"path": "lib/gauguin/image.rb",
"chars": 1221,
"preview": "require 'rmagick'\nrequire 'forwardable'\n\nmodule Gauguin\n class Image\n extend Forwardable\n attr_accessor :image\n "
},
{
"path": "lib/gauguin/image_recolorer.rb",
"chars": 639,
"preview": "module Gauguin\n class ImageRecolorer\n def initialize(image)\n @image = image.dup\n end\n\n def recolor(new_co"
},
{
"path": "lib/gauguin/image_repository.rb",
"chars": 106,
"preview": "module Gauguin\n class ImageRepository\n def get(path)\n Gauguin::Image.new(path)\n end\n end\nend\n"
},
{
"path": "lib/gauguin/noise_reducer.rb",
"chars": 678,
"preview": "module Gauguin\n class NoiseReducer\n def call(colors_clusters)\n pivots = colors_clusters.keys.sort_by! { |key, g"
},
{
"path": "lib/gauguin/painting.rb",
"chars": 1153,
"preview": "module Gauguin\n class Painting\n def initialize(path, image_repository = nil, colors_retriever = nil,\n "
},
{
"path": "lib/gauguin/palette_serializer.rb",
"chars": 439,
"preview": "require 'yaml'\n\nmodule Gauguin\n class PaletteSerializer\n def self.load(value)\n return unless value\n\n value"
},
{
"path": "lib/gauguin/version.rb",
"chars": 39,
"preview": "module Gauguin\n VERSION = \"0.0.3\"\nend\n"
},
{
"path": "lib/gauguin.rb",
"chars": 1116,
"preview": "require \"gauguin/version\"\nrequire \"gauguin/color\"\nrequire \"gauguin/color_space\"\nrequire \"gauguin/colors_retriever\"\nrequi"
},
{
"path": "spec/integration/painting_spec.rb",
"chars": 2091,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe Painting do\n let(:path) do\n File.join(\"spec\", \"support\", \"pictu"
},
{
"path": "spec/integration/samples_spec.rb",
"chars": 1426,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe \"samples\" do\n def self.picture_path(file_name)\n File.join(\"spec"
},
{
"path": "spec/lib/gauguin/color_space/rgb_vector_spec.rb",
"chars": 296,
"preview": "require 'spec_helper'\n\nmodule Gauguin::ColorSpace\n describe RgbVector do\n describe \"#to_xyz\" do\n let(:red) { Rg"
},
{
"path": "spec/lib/gauguin/color_space/xyz_vector_spec.rb",
"chars": 342,
"preview": "require 'spec_helper'\n\nmodule Gauguin::ColorSpace\n describe XyzVector do\n describe \"#to_lab\" do\n let(:red) { Xy"
},
{
"path": "spec/lib/gauguin/color_spec.rb",
"chars": 3144,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe Color do\n let(:black) { Color.new(0, 0, 0) }\n let(:red) { Color.n"
},
{
"path": "spec/lib/gauguin/colors_clusterer_spec.rb",
"chars": 4239,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe ColorsClusterer do\n let(:black) { Color.new(0, 0, 0, 0.597) }\n le"
},
{
"path": "spec/lib/gauguin/colors_limiter_spec.rb",
"chars": 690,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe ColorsLimiter do\n describe \"#limit\" do\n let(:limiter) { ColorsL"
},
{
"path": "spec/lib/gauguin/colors_retriever_spec.rb",
"chars": 2539,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe ColorsRetriever do\n let(:retriever) { ColorsRetriever.new(image) }\n "
},
{
"path": "spec/lib/gauguin/image_recolorer_spec.rb",
"chars": 2570,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe ImageRecolorer do\n describe \"#recolor\" do\n let(:image) do\n "
},
{
"path": "spec/lib/gauguin/image_repository_spec.rb",
"chars": 307,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe ImageRepository do\n let(:repository) { ImageRepository.new }\n let"
},
{
"path": "spec/lib/gauguin/image_spec.rb",
"chars": 2115,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe Image do\n let(:path) do\n File.join(\"spec\", \"support\", \"pictures"
},
{
"path": "spec/lib/gauguin/noise_reducer_spec.rb",
"chars": 1199,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe NoiseReducer do\n let(:reducer) { NoiseReducer.new }\n\n describe \"#"
},
{
"path": "spec/lib/gauguin/painting_spec.rb",
"chars": 1598,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe Painting do\n let(:colors) { [] }\n\n let(:image_repository) { doubl"
},
{
"path": "spec/lib/gauguin/palette_serializer_spec.rb",
"chars": 599,
"preview": "require 'spec_helper'\n\nmodule Gauguin\n describe PaletteSerializer do\n let(:palette) do\n {\n Color.new(255"
},
{
"path": "spec/spec_helper.rb",
"chars": 1208,
"preview": "if ENV['CODECLIMATE_REPO_TOKEN']\n require \"codeclimate-test-reporter\"\n CodeClimate::TestReporter.start\nelse\n require "
}
]
About this extraction
This page contains the full source code of the LunarLogic/gauguin GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (41.8 KB), approximately 12.7k tokens, and a symbol index with 105 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.