photomosaic 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +7 -0
- data/bin/photomosaic +5 -0
- data/diagrams/sequence.png +0 -0
- data/diagrams/sequence.uml +70 -0
- data/diagrams/structure.png +0 -0
- data/diagrams/structure.uml +21 -0
- data/lib/photomosaic.rb +12 -0
- data/lib/photomosaic/client.rb +57 -0
- data/lib/photomosaic/color/hsv.rb +27 -0
- data/lib/photomosaic/color/rgb.rb +62 -0
- data/lib/photomosaic/image.rb +113 -0
- data/lib/photomosaic/image_downloader.rb +39 -0
- data/lib/photomosaic/options.rb +80 -0
- data/lib/photomosaic/search_engine/bing.rb +15 -0
- data/lib/photomosaic/version.rb +3 -0
- data/photomosaic.gemspec +31 -0
- data/spec/fixtures/lena.png +0 -0
- data/spec/fixtures/lena_0.png +0 -0
- data/spec/fixtures/lena_1.png +0 -0
- data/spec/fixtures/lena_2.png +0 -0
- data/spec/fixtures/lena_3.png +0 -0
- data/spec/fixtures/lena_4.png +0 -0
- data/spec/fixtures/lena_5.png +0 -0
- data/spec/photomosaic/client_spec.rb +91 -0
- data/spec/photomosaic/color/hsv_spec.rb +16 -0
- data/spec/photomosaic/color/rgb_spec.rb +52 -0
- data/spec/photomosaic/image_downloader_spec.rb +74 -0
- data/spec/photomosaic/image_spec.rb +151 -0
- data/spec/photomosaic/options_spec.rb +58 -0
- data/spec/photomosaic/search_engine/bing_spec.rb +57 -0
- data/spec/photomosaic_spec.rb +7 -0
- data/spec/spec_helper.rb +17 -0
- metadata +226 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "open-uri"
|
3
|
+
require "tmpdir"
|
4
|
+
|
5
|
+
module Photomosaic
|
6
|
+
class ImageDownloader
|
7
|
+
def initialize(save_dir = tmp_dir)
|
8
|
+
@save_dir = save_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def download_images(image_url_list)
|
12
|
+
image_url_list.inject([]) do |path_list, image_url|
|
13
|
+
image_path = File.join(@save_dir, File.basename(image_url))
|
14
|
+
|
15
|
+
begin
|
16
|
+
download_image(image_url, image_path)
|
17
|
+
path_list << image_path
|
18
|
+
rescue
|
19
|
+
end
|
20
|
+
|
21
|
+
path_list
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def remove_save_dir
|
26
|
+
FileUtils.remove_entry_secure(@save_dir)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def download_image(image_url, image_path)
|
32
|
+
open(image_path, "wb+") { |f| f.puts open(image_url).read }
|
33
|
+
end
|
34
|
+
|
35
|
+
def tmp_dir
|
36
|
+
Dir.mktmpdir("photomosaic")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module Photomosaic
|
4
|
+
class Options
|
5
|
+
KEYS = %w(
|
6
|
+
api_key
|
7
|
+
base_image
|
8
|
+
color_model
|
9
|
+
colors
|
10
|
+
height
|
11
|
+
keyword
|
12
|
+
level
|
13
|
+
output_path
|
14
|
+
results
|
15
|
+
search_engine
|
16
|
+
width
|
17
|
+
)
|
18
|
+
|
19
|
+
REQUIRED_KEYS = %w(
|
20
|
+
api_key
|
21
|
+
base_image
|
22
|
+
output_path
|
23
|
+
)
|
24
|
+
|
25
|
+
def self.parse(argv)
|
26
|
+
options = default_options
|
27
|
+
parser(options).parse(argv)
|
28
|
+
options[:api_key] = api_key
|
29
|
+
check_options(options)
|
30
|
+
|
31
|
+
self.new(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def initialize(options)
|
35
|
+
@options = options
|
36
|
+
end
|
37
|
+
|
38
|
+
KEYS.each do |key|
|
39
|
+
define_method(key) { @options[key.to_sym] }
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def self.api_key
|
45
|
+
ENV["PHOTOMOSAIC_API_KEY"]
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.check_options(options)
|
49
|
+
REQUIRED_KEYS.each do |key|
|
50
|
+
raise OptionParser::MissingArgument, key unless options[key.to_sym]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.default_options
|
55
|
+
{
|
56
|
+
color_model: :rgb,
|
57
|
+
colors: 16,
|
58
|
+
height: 200,
|
59
|
+
level: 4,
|
60
|
+
results: 50,
|
61
|
+
search_engine: Photomosaic::SearchEngine::Bing,
|
62
|
+
width: 200,
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.parser(options)
|
67
|
+
OptionParser.new do |opt|
|
68
|
+
opt.on("-b", "--base_image=VAL", "Path of base image") { |val| options[:base_image] = File.expand_path(val) }
|
69
|
+
opt.on("-c", "--colors=VAL", "Number of colors") { |val| options[:colors] = val.to_i }
|
70
|
+
opt.on("-h", "--height=VAL", "Height of output image") { |val| options[:height] = val.to_i }
|
71
|
+
opt.on("-k", "--keyword=VAL", "Keyword") { |val| options[:keyword] = val }
|
72
|
+
opt.on("-l", "--level=VAL", "Color level") { |val| options[:level] = val.to_i }
|
73
|
+
opt.on("-o", "--output_path=VAL", "Path of mosaic image") { |val| options[:output_path] = File.expand_path(val) }
|
74
|
+
opt.on("-r", "--results=VAL", "Number of results") { |val| options[:results] = val.to_i }
|
75
|
+
opt.on("-w", "--width=VAL", "Width of output image") { |val| options[:width] = val.to_i }
|
76
|
+
opt.on("--hsv", "Use HSV") { |val| options[:color_model] = :hsv }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "searchbing"
|
2
|
+
|
3
|
+
module Photomosaic
|
4
|
+
module SearchEngine
|
5
|
+
class Bing
|
6
|
+
def initialize(api_key, results)
|
7
|
+
@client = ::Bing.new(api_key, results, "Image")
|
8
|
+
end
|
9
|
+
|
10
|
+
def get_image_list(keyword)
|
11
|
+
@client.search(keyword)[0][:Image].map { |image| image[:MediaUrl] }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/photomosaic.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'photomosaic/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "photomosaic"
|
8
|
+
spec.version = Photomosaic::VERSION
|
9
|
+
spec.authors = ["Daisuke Fujita"]
|
10
|
+
spec.email = ["dtanshi45@gmail.com"]
|
11
|
+
spec.summary = %q{Photomosaic Generator}
|
12
|
+
spec.description = %q{Photomosaic Generator}
|
13
|
+
spec.homepage = "https://github.com/dtan4/photomosaic"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "searchbing"
|
22
|
+
spec.add_dependency "rmagick"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
25
|
+
spec.add_development_dependency "coveralls"
|
26
|
+
spec.add_development_dependency "guard-rspec"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec", "~> 3.0.0"
|
29
|
+
spec.add_development_dependency "terminal-notifier-guard"
|
30
|
+
spec.add_development_dependency "webmock"
|
31
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "ostruct"
|
3
|
+
|
4
|
+
module Photomosaic
|
5
|
+
describe Client do
|
6
|
+
let(:api_key) { "api_key" }
|
7
|
+
let(:base_image) { fixture_path("lena.png") }
|
8
|
+
let(:color_model) { :rgb }
|
9
|
+
let(:colors) { 16 }
|
10
|
+
let(:height) { 200 }
|
11
|
+
let(:keyword) { "keyword" }
|
12
|
+
let(:level) { 4 }
|
13
|
+
let(:output_path) { tmp_path("output.png") }
|
14
|
+
let(:results) { 50 }
|
15
|
+
let(:search_engine) { SearchEngine::Bing }
|
16
|
+
let(:width) { 200 }
|
17
|
+
|
18
|
+
let(:options) do
|
19
|
+
{
|
20
|
+
api_key: api_key,
|
21
|
+
base_image: base_image,
|
22
|
+
color_model: color_model,
|
23
|
+
colors: colors,
|
24
|
+
height: height,
|
25
|
+
keyword: keyword,
|
26
|
+
level: level,
|
27
|
+
output_path: output_path,
|
28
|
+
results: results,
|
29
|
+
search_engine: search_engine,
|
30
|
+
width: width
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
let(:client) do
|
35
|
+
described_class.new("argv")
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:dispatched_images) do
|
39
|
+
[
|
40
|
+
[image],
|
41
|
+
[image],
|
42
|
+
[image]
|
43
|
+
]
|
44
|
+
end
|
45
|
+
|
46
|
+
let(:image) do
|
47
|
+
double(Photomosaic::Image)
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:image_name_list) do
|
51
|
+
[
|
52
|
+
"lena_0.png",
|
53
|
+
"lena_1.png",
|
54
|
+
"lena_2.png",
|
55
|
+
"notfound.png"
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
let(:image_path_list) do
|
60
|
+
image_name_list.map { |name| fixture_path(name) }
|
61
|
+
end
|
62
|
+
|
63
|
+
let(:image_url_list) do
|
64
|
+
image_name_list.map { |name| "http://example.com/#{name}" }
|
65
|
+
end
|
66
|
+
|
67
|
+
before do
|
68
|
+
allow(Photomosaic::Options).to receive(:parse).and_return(OpenStruct.new(options))
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#execute" do
|
72
|
+
before do
|
73
|
+
allow_any_instance_of(Photomosaic::SearchEngine::Bing).to receive(:get_image_list).and_return(image_url_list)
|
74
|
+
allow_any_instance_of(Photomosaic::ImageDownloader).to receive(:download_images).and_return(image_path_list)
|
75
|
+
allow(Photomosaic::Image).to receive(:create_mosaic_image)
|
76
|
+
allow(Photomosaic::Image).to receive(:new).with(/lena(?:_\d)?\.png/).and_return(image)
|
77
|
+
allow(Photomosaic::Image).to receive(:new).with(/notfound.png/).and_raise Magick::ImageMagickError
|
78
|
+
allow(Photomosaic::Image).to receive(:resize_to_pixel_size)
|
79
|
+
allow(image).to receive(:dispatch_images).and_return(dispatched_images)
|
80
|
+
allow(image).to receive(:posterize!)
|
81
|
+
allow(image).to receive(:reduce_colors!)
|
82
|
+
allow(image).to receive(:resize!)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should execute the program" do
|
86
|
+
expect(Photomosaic::Image).to receive(:create_mosaic_image)
|
87
|
+
client.execute
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Photomosaic
|
4
|
+
module Color
|
5
|
+
describe HSV do
|
6
|
+
describe "#calculate_distance" do
|
7
|
+
it "should calculate color distance" do
|
8
|
+
color_a = described_class.new(100, 50.0, 70.0)
|
9
|
+
color_b = described_class.new(50, 30.0, 50.0)
|
10
|
+
|
11
|
+
expect(color_a.calculate_distance(color_b)).to be_within(0.1).of(57.4)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Photomosaic
|
4
|
+
module Color
|
5
|
+
describe RGB do
|
6
|
+
describe "#calculate_distance" do
|
7
|
+
it "should calculate color distance" do
|
8
|
+
color_a = described_class.new(50, 100, 200)
|
9
|
+
color_b = described_class.new(10, 20, 30)
|
10
|
+
|
11
|
+
expect(color_a.calculate_distance(color_b)).to be_within(0.5).of(192.0)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#to_hsv" do
|
16
|
+
# cf. http://tech-unlimited.com/color.html
|
17
|
+
|
18
|
+
it "should convert (50, 50, 50) to (0, 0, 20)" do
|
19
|
+
rgb = described_class.new(50, 50, 50)
|
20
|
+
hsv = rgb.to_hsv
|
21
|
+
expect(hsv.hue).to eq 0
|
22
|
+
expect(hsv.saturation).to be_within(1).of(0)
|
23
|
+
expect(hsv.value).to be_within(1).of(20)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should convert (50, 100, 200) to (220, 75, 78)" do
|
27
|
+
rgb = described_class.new(50, 100, 200)
|
28
|
+
hsv = rgb.to_hsv
|
29
|
+
expect(hsv.hue).to eq 220
|
30
|
+
expect(hsv.saturation).to be_within(1).of(75)
|
31
|
+
expect(hsv.value).to be_within(1).of(78)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should convert (100, 50, 200) to (260, 75, 78)" do
|
35
|
+
rgb = described_class.new(100, 50, 200)
|
36
|
+
hsv = rgb.to_hsv
|
37
|
+
expect(hsv.hue).to eq 260
|
38
|
+
expect(hsv.saturation).to be_within(1).of(75)
|
39
|
+
expect(hsv.value).to be_within(1).of(78)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should convert (100, 200, 50) to (100, 75, 78)" do
|
43
|
+
rgb = described_class.new(100, 200, 50)
|
44
|
+
hsv = rgb.to_hsv
|
45
|
+
expect(hsv.hue).to eq 100
|
46
|
+
expect(hsv.saturation).to be_within(1).of(75)
|
47
|
+
expect(hsv.value).to be_within(1).of(78)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fileutils"
|
3
|
+
require "webmock/rspec"
|
4
|
+
|
5
|
+
module Photomosaic
|
6
|
+
describe ImageDownloader do
|
7
|
+
let(:downloader) do
|
8
|
+
described_class.new(tmp_dir)
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#initialize" do
|
12
|
+
context "with save directory" do
|
13
|
+
it "should set the specified directory to @save_dir" do
|
14
|
+
downloader = described_class.new(tmp_dir)
|
15
|
+
expect(downloader.instance_variable_get(:@save_dir)).to eq tmp_dir
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "without save directory" do
|
20
|
+
it "should set temporary directory to @save_dir" do
|
21
|
+
downloader = described_class.new
|
22
|
+
expect(downloader.instance_variable_get(:@save_dir)).to match(/photomosaic/)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#download_images" do
|
28
|
+
let(:image_url_list) do
|
29
|
+
[
|
30
|
+
"http://example.com/image01.jpg",
|
31
|
+
"http://example.com/image02.jpg",
|
32
|
+
"http://example.com/notfound.jpg"
|
33
|
+
]
|
34
|
+
end
|
35
|
+
|
36
|
+
before do
|
37
|
+
stub_request(:get, %r(http://example\.com/image0\d\.jpg))
|
38
|
+
.to_return(status: 200, body: "hoge")
|
39
|
+
stub_request(:get, "http://example.com/notfound.jpg")
|
40
|
+
.to_return(status: 404)
|
41
|
+
FileUtils.rm_rf(tmp_dir) if Dir.exist?(tmp_dir)
|
42
|
+
Dir.mkdir(tmp_dir)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should download listed images to temporary directory" do
|
46
|
+
downloader.download_images(image_url_list)
|
47
|
+
|
48
|
+
%w(image01.jpg image02.jpg).each do |image|
|
49
|
+
expect(File.exist?(tmp_path(image))).to be_truthy
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should return the path list" do
|
54
|
+
result = downloader.download_images(image_url_list)
|
55
|
+
expect(result).to match_array %w(image01.jpg image02.jpg).map { |image| tmp_path(image) }
|
56
|
+
end
|
57
|
+
|
58
|
+
after do
|
59
|
+
FileUtils.rm_rf(tmp_dir)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#remove_save_dir" do
|
64
|
+
before do
|
65
|
+
Dir.mkdir(tmp_dir)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should remove save directory" do
|
69
|
+
downloader.remove_save_dir
|
70
|
+
expect(Dir.exist?(tmp_dir)).to be_falsy
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fileutils"
|
3
|
+
require "tempfile"
|
4
|
+
|
5
|
+
module Photomosaic
|
6
|
+
describe Image do
|
7
|
+
let(:image_path) do
|
8
|
+
fixture_path("lena.png")
|
9
|
+
end
|
10
|
+
|
11
|
+
let(:image_path_list) do
|
12
|
+
[
|
13
|
+
fixture_path("lena_0.png"),
|
14
|
+
fixture_path("lena_1.png"),
|
15
|
+
fixture_path("lena_2.png"),
|
16
|
+
fixture_path("lena_3.png"),
|
17
|
+
fixture_path("lena_4.png"),
|
18
|
+
fixture_path("lena_5.png")
|
19
|
+
]
|
20
|
+
end
|
21
|
+
|
22
|
+
context "class methods" do
|
23
|
+
describe "#create_mosaic_image" do
|
24
|
+
let(:image_list) do
|
25
|
+
5.times.inject([]) do |image_list, _|
|
26
|
+
image_list << image_path_list.map { |path| described_class.new(path) }
|
27
|
+
image_list
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
let(:output_path) do
|
32
|
+
tmp_path("tiled_image.jpg")
|
33
|
+
end
|
34
|
+
|
35
|
+
before do
|
36
|
+
FileUtils.rm_rf(tmp_dir) if Dir.exist?(tmp_dir)
|
37
|
+
Dir.mkdir(tmp_dir)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should create the mosaic image" do
|
41
|
+
described_class.create_mosaic_image(image_list, output_path)
|
42
|
+
expect(File.exist?(output_path)).to be_truthy
|
43
|
+
end
|
44
|
+
|
45
|
+
after do
|
46
|
+
FileUtils.rm_rf(tmp_dir)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#preprocess_image" do
|
51
|
+
it "should preprocess image" do
|
52
|
+
expect_any_instance_of(described_class).to receive(:resize!).with(100, 100, true).once
|
53
|
+
expect_any_instance_of(described_class).to receive(:posterize!).with(4).once
|
54
|
+
expect_any_instance_of(described_class).to receive(:reduce_colors!).with(8).once
|
55
|
+
described_class.preprocess_image(image_path, 100, 100, 4, 8)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#resize_images" do
|
60
|
+
let(:image) do
|
61
|
+
double(:resize!)
|
62
|
+
end
|
63
|
+
|
64
|
+
let(:image_list) do
|
65
|
+
5.times.inject([]) do |image_list, _|
|
66
|
+
image_list << [image]
|
67
|
+
image_list
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should resize images" do
|
72
|
+
expect(image).to receive(:resize!).exactly(5).times
|
73
|
+
described_class.resize_images(image_list, 40, 20)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "instance methods" do
|
79
|
+
let(:image) do
|
80
|
+
described_class.new(image_path)
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#characteristic_color" do
|
84
|
+
context "by RGB" do
|
85
|
+
it "should return characteristic color in RGB" do
|
86
|
+
characteristic_color = image.characteristic_color(:rgb)
|
87
|
+
expect(characteristic_color.red).to be_within(1).of(180)
|
88
|
+
expect(characteristic_color.green).to be_within(1).of(100)
|
89
|
+
expect(characteristic_color.blue).to be_within(1).of(107)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "by HSV" do
|
94
|
+
it "should return characteristic color in HSV" do
|
95
|
+
characteristic_color = image.characteristic_color(:hsv)
|
96
|
+
expect(characteristic_color.hue).to eq 354
|
97
|
+
expect(characteristic_color.saturation).to be_within(0.5).of(44.7)
|
98
|
+
expect(characteristic_color.value).to be_within(0.5).of(70.0)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#dispatch_images" do
|
104
|
+
let(:image_list) do
|
105
|
+
image_path_list.map { |path| described_class.new(path) }
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return the map of dispatched images as a 2-dimentional array" do
|
109
|
+
images = image.dispatch_images(image_list, 8, 8)
|
110
|
+
expect(images.length).to eq 64
|
111
|
+
expect(images[0].length).to eq 64
|
112
|
+
expect(images[0][0]).to be_a described_class
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "#posterize!" do
|
117
|
+
it "should posterize itself" do
|
118
|
+
expect_any_instance_of(Magick::Image).to receive(:posterize).with(4)
|
119
|
+
expect_any_instance_of(described_class).to receive(:reload_image)
|
120
|
+
image.posterize!(4)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "#reduce_colors!" do
|
125
|
+
it "should reduce its colors" do
|
126
|
+
expect_any_instance_of(Magick::Image).to receive(:quantize).with(8)
|
127
|
+
expect_any_instance_of(described_class).to receive(:reload_image)
|
128
|
+
image.reduce_colors!(8)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "#resize!" do
|
133
|
+
context "to keep aspect ratio" do
|
134
|
+
it "should resize itself" do
|
135
|
+
expect_any_instance_of(Magick::Image).to receive(:resize_to_fit!).with(25, 50)
|
136
|
+
expect_any_instance_of(described_class).to receive(:reload_image)
|
137
|
+
image.resize!(25, 50, true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "not to keep aspect ratio" do
|
142
|
+
it "should resize itself" do
|
143
|
+
expect_any_instance_of(Magick::Image).to receive(:resize!).with(25, 50)
|
144
|
+
expect_any_instance_of(described_class).to receive(:reload_image)
|
145
|
+
image.resize!(25, 50, false)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|