Full Code of LunarLogic/gauguin for AI

master 4067ee267499 cached
40 files
41.8 KB
12.7k tokens
105 symbols
1 requests
Download .txt
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
================================================
[![Build Status](https://travis-ci.org/LunarLogic/gauguin.svg?branch=master)](https://travis-ci.org/LunarLogic/gauguin)
[![Code Climate](https://codeclimate.com/github/LunarLogic/gauguin/badges/gpa.svg)](https://codeclimate.com/github/LunarLogic/gauguin)
[![Test Coverage](https://codeclimate.com/github/LunarLogic/gauguin/badges/coverage.svg)](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?

![Black and white image](http://gauguin.lunarlogic.io/assets/gray_and_black-b6871d86ef45c8740bf898233b0a588c.png)

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

Download .txt
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
Download .txt
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": "[![Build Status](https://travis-ci.org/LunarLogic/gauguin.svg?branch=master)](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.

Copied to clipboard!