imagga 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +28 -1
- data/imagga.gemspec +1 -0
- data/lib/imagga.rb +8 -3
- data/lib/imagga/client.rb +12 -34
- data/lib/imagga/commands.rb +42 -0
- data/lib/imagga/core_client.rb +25 -0
- data/lib/imagga/exceptions.rb +3 -0
- data/lib/imagga/image.rb +59 -2
- data/lib/imagga/image_or_url_parametizer.rb +21 -0
- data/lib/imagga/options.rb +74 -0
- data/lib/imagga/parametizer.rb +18 -0
- data/lib/imagga/rank_color_parametizer.rb +17 -0
- data/lib/imagga/resolution_parametizer.rb +17 -0
- data/lib/imagga/result_builder.rb +27 -0
- data/lib/imagga/version.rb +1 -1
- data/spec/fixtures/crop_response.txt +62 -0
- data/spec/fixtures/extract_response.txt +253 -0
- data/spec/fixtures/rank_response.txt +12 -0
- data/spec/integration/crop_spec.rb +60 -0
- data/spec/integration/extract_spec.rb +51 -0
- data/spec/integration/rank_spec.rb +103 -0
- data/spec/lib/commands_spec.rb +70 -0
- data/spec/lib/core_client_spec.rb +43 -0
- data/spec/lib/crop_info_spec.rb +15 -0
- data/spec/lib/crop_options_spec.rb +24 -0
- data/spec/lib/crop_result_builder_spec.rb +4 -0
- data/spec/lib/extraction_options_spec.rb +16 -51
- data/spec/lib/image_crop_spec.rb +34 -0
- data/spec/lib/image_or_url_parametizer_spec.rb +65 -0
- data/spec/lib/parametizer_spec.rb +42 -0
- data/spec/lib/rank_color_parametizer_spec.rb +27 -0
- data/spec/lib/rank_color_spec.rb +51 -0
- data/spec/lib/rank_options_spec.rb +28 -0
- data/spec/lib/rank_result_builder_spec.rb +1 -12
- data/spec/lib/resolution_parametizer_spec.rb +60 -0
- metadata +63 -28
- data/lib/imagga/extract_options.rb +0 -74
- data/lib/imagga/extract_result_builder.rb +0 -11
- data/lib/imagga/rank_result_builder.rb +0 -9
- data/spec/lib/client_spec.rb +0 -352
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2ViMjRjNTFlZmE3YTNmYzc4ZTEwMjlmZDg5NjJiNzRkNGRjOGViNg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NGMyOTRhMzEzMTkzZmQ5MjJmMTkxZGJiOTBiYzViM2IwZTE0Njk1Mg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZDc1MWI4N2NmOGQ0N2FlZTQ2NjJjZDRkMzA0ZDhiYzAyNjcwMmNlNmI0OGY3
|
10
|
+
MjY2ZjVlOTcwMzhiZWRmMmI0N2YyYjU1MDQxYWU4MWI3YjM5ZjM2MzU2NjI4
|
11
|
+
MDAwNTQ5NGUxMDRmZWE4MmRjNTkzMGE2OTljZGIwM2ZiZWQ2MTg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YTc1ODMzZGVlOTgyZDAwM2JlMmZiNjNiMzNlMWZkMWYyNDdmNGY2MGY4NTJh
|
14
|
+
MjE4ZDI2M2FiNzRhOTNmMWY0N2FjYTQ0YWJmNTFlMWQ0ZTNmNjY3YzMxZWNh
|
15
|
+
MzlhYTkwMmQ5NTgwZTJiZjRhNWUzNDBiNTRhZjZmM2U5YTJkNTM=
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
[![Code Climate](https://codeclimate.com/github/martkaru/imagga.png)](https://codeclimate.com/github/martkaru/imagga)
|
1
2
|
[![Build Status](https://travis-ci.org/martkaru/imagga.png?branch=master)](https://travis-ci.org/martkaru/imagga)
|
2
3
|
|
3
4
|
# Imagga
|
@@ -56,7 +57,10 @@ Check the results, for example:
|
|
56
57
|
Multi-color search:
|
57
58
|
|
58
59
|
client.rank(
|
59
|
-
|
60
|
+
colors: [
|
61
|
+
Imagga::RankColor.new(percent: 100, r: 82, g: 37, b: 43) # use r g b values
|
62
|
+
Imagga::RankColor.new(percent: 100, hex: '#336699') # or use hex
|
63
|
+
],
|
60
64
|
type: 'object',
|
61
65
|
dist: 6000,
|
62
66
|
count: 10
|
@@ -64,6 +68,29 @@ Multi-color search:
|
|
64
68
|
puts "Distance of #%i is %.4f" % [similarity.id, similarity.dist] # Distance of #333 is 3581.5500
|
65
69
|
end
|
66
70
|
|
71
|
+
Image crop suggestions:
|
72
|
+
|
73
|
+
client.crop(
|
74
|
+
[
|
75
|
+
'http://image1', # Use urls
|
76
|
+
Imagga::Image.new(url: 'http://image2') # or image object
|
77
|
+
],
|
78
|
+
resolutions: ['100x40','50x100'], # '100,40,50,100' or '100x40,50x100' instead of array would work also
|
79
|
+
no_scaling: true
|
80
|
+
).each do |crop_info|
|
81
|
+
puts crop_info.url
|
82
|
+
crop_info.croppings.each do |cropping|
|
83
|
+
puts cropping.info
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
## Contributors
|
88
|
+
|
89
|
+
martkaru
|
90
|
+
|
91
|
+
jprosevear
|
92
|
+
|
93
|
+
|
67
94
|
## Contributing
|
68
95
|
|
69
96
|
1. Fork it
|
data/imagga.gemspec
CHANGED
data/lib/imagga.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
require "imagga/version"
|
2
2
|
require "imagga/exceptions"
|
3
|
-
require "imagga/extract_result_builder"
|
4
|
-
require "imagga/rank_result_builder"
|
5
3
|
require "imagga/image"
|
6
|
-
require "imagga/
|
4
|
+
require "imagga/result_builder"
|
5
|
+
require "imagga/parametizer"
|
6
|
+
require "imagga/image_or_url_parametizer"
|
7
|
+
require "imagga/rank_color_parametizer"
|
8
|
+
require "imagga/resolution_parametizer"
|
9
|
+
require "imagga/commands"
|
10
|
+
require "imagga/core_client"
|
11
|
+
require "imagga/options"
|
7
12
|
require "imagga/client"
|
data/lib/imagga/client.rb
CHANGED
@@ -1,42 +1,20 @@
|
|
1
|
-
require "httparty"
|
2
|
-
require "json"
|
3
|
-
|
4
1
|
module Imagga
|
5
|
-
class Client
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(opts={})
|
11
|
-
@api_key = opts[:api_key] || raise_missing(:api_key)
|
12
|
-
@api_secret = opts[:api_secret] || raise_missing(:api_secret)
|
13
|
-
@base_uri = opts[:base_uri] || raise_missing(:base_uri)
|
14
|
-
@service_path = '/colorsearchserver.php'
|
15
|
-
|
16
|
-
Imagga::Client.base_uri @base_uri
|
17
|
-
end
|
18
|
-
|
19
|
-
def extract(urls_or_images, additional_options={})
|
20
|
-
options = extract_options(urls_or_images, additional_options)
|
21
|
-
result = JSON.parse(self.class.post(service_path, body: options))
|
22
|
-
raise_if_request_failed!(result)
|
23
|
-
ExtractResultBuilder.new.build_from(result)
|
24
|
-
end
|
25
|
-
|
26
|
-
def rank(opts)
|
27
|
-
result = JSON.parse(self.class.post(service_path, body: rank_options(opts)))
|
28
|
-
raise_if_request_failed!(result)
|
29
|
-
RankResultBuilder.new.build_from(result)
|
2
|
+
class Client < CoreClient
|
3
|
+
def extract(urls_or_images, options={})
|
4
|
+
options.merge!(ImageOrUrlParametizer.new.parametrize(urls_or_images))
|
5
|
+
ExtractResultBuilder.new.build_from(super(options))
|
30
6
|
end
|
31
7
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
8
|
+
def rank(options={})
|
9
|
+
colors = options.delete(:colors) { raise_missing('colors') }
|
10
|
+
options.merge!(RankColorParametizer.new.parametrize(colors))
|
11
|
+
RankResultBuilder.new.build_from(super(options))
|
36
12
|
end
|
37
13
|
|
38
|
-
def
|
39
|
-
|
14
|
+
def crop(urls_or_images, options={})
|
15
|
+
options.merge!(ImageOrUrlParametizer.new.build_urls(urls_or_images))
|
16
|
+
options.merge!(ResolutionParametizer.new.parametrize(options.fetch(:resolutions)))
|
17
|
+
CropResultBuilder.new.build_from(super(options))
|
40
18
|
end
|
41
19
|
end
|
42
20
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "httparty"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Imagga
|
5
|
+
class BaseCommand
|
6
|
+
include HTTParty
|
7
|
+
include Imagga::Exceptions
|
8
|
+
|
9
|
+
attr_reader :api_key, :api_secret, :base_uri
|
10
|
+
|
11
|
+
def initialize(api_key, api_secret, base_uri)
|
12
|
+
@api_key, @api_secret, @base_uri = api_key, api_secret, base_uri
|
13
|
+
self.class.base_uri @base_uri
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute(options)
|
17
|
+
JSON.parse(self.class.post(service_path, body: args(options))).tap do |result|
|
18
|
+
raise_if_request_failed!(result)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def args(options)
|
23
|
+
options_class.new(api_key, api_secret).options(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def options_class; BaseOptions; end
|
27
|
+
end
|
28
|
+
|
29
|
+
class ExtractCommand < BaseCommand
|
30
|
+
def service_path; '/colorsearchserver.php'; end
|
31
|
+
def options_class; ExtractOptions; end
|
32
|
+
end
|
33
|
+
|
34
|
+
class RankCommand < ExtractCommand
|
35
|
+
def options_class; RankOptions; end
|
36
|
+
end
|
37
|
+
|
38
|
+
class CropCommand < BaseCommand
|
39
|
+
def service_path; '/extractionrestserver.php'; end
|
40
|
+
def options_class; CropOptions; end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Imagga
|
2
|
+
class CoreClient
|
3
|
+
include Imagga::Exceptions
|
4
|
+
attr_reader :api_key, :api_secret, :base_uri
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
@api_key = opts[:api_key] || raise_missing(:api_key)
|
8
|
+
@api_secret = opts[:api_secret] || raise_missing(:api_secret)
|
9
|
+
@base_uri = opts[:base_uri] || raise_missing(:base_uri)
|
10
|
+
end
|
11
|
+
|
12
|
+
def extract(options={})
|
13
|
+
ExtractCommand.new(api_key, api_secret, base_uri).execute(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def rank(options={})
|
17
|
+
RankCommand.new(api_key, api_secret, base_uri).execute(options)
|
18
|
+
end
|
19
|
+
|
20
|
+
def crop(options={})
|
21
|
+
CropCommand.new(api_key, api_secret, base_uri).execute(options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/imagga/exceptions.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module Imagga
|
2
2
|
module Exceptions
|
3
|
+
|
3
4
|
def raise_missing(attribute)
|
4
5
|
raise ArgumentError, "%s is missing" % attribute.to_s
|
5
6
|
end
|
@@ -9,10 +10,12 @@ module Imagga
|
|
9
10
|
raise Imagga::ClientException.new(result['error_code'].to_i), result['error_message'], caller[0..-1]
|
10
11
|
end
|
11
12
|
end
|
13
|
+
|
12
14
|
end
|
13
15
|
|
14
16
|
class ClientException < StandardError
|
15
17
|
attr_accessor :error_code
|
18
|
+
|
16
19
|
def initialize(error_code)
|
17
20
|
@error_code = error_code
|
18
21
|
super
|
data/lib/imagga/image.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
require 'color'
|
2
|
+
|
1
3
|
module Imagga
|
2
4
|
class Image
|
3
5
|
include Imagga::Exceptions
|
4
6
|
attr_accessor :url, :id
|
5
7
|
|
6
8
|
def initialize(opts)
|
7
|
-
@url
|
8
|
-
@id
|
9
|
+
@url = opts[:url] || raise_missing(:url)
|
10
|
+
@id = opts[:id] || 0
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
@@ -64,6 +66,20 @@ module Imagga
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
69
|
+
class RankColor
|
70
|
+
attr_accessor :percent, :r, :g, :b, :hex
|
71
|
+
def initialize(opts)
|
72
|
+
@percent = opts.fetch(:percent)
|
73
|
+
if @hex = opts[:hex]
|
74
|
+
color = Color::RGB.from_html(@hex)
|
75
|
+
@r, @g, @b = color.red, color.green, color.blue
|
76
|
+
else
|
77
|
+
@r, @g, @b = opts[:r], opts[:g], opts[:b]
|
78
|
+
@hex = Color::RGB.new(r, g, b).html
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
67
83
|
class RankSimilarity < ImageInfoBase
|
68
84
|
def self.fields
|
69
85
|
%w(id dist)
|
@@ -71,4 +87,45 @@ module Imagga
|
|
71
87
|
|
72
88
|
attr_accessor *fields
|
73
89
|
end
|
90
|
+
|
91
|
+
class CropInfo < ImageInfoBase
|
92
|
+
def self.fields
|
93
|
+
%w(url)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.crop_fields
|
97
|
+
%w(croppings)
|
98
|
+
end
|
99
|
+
|
100
|
+
def initialize(opts={})
|
101
|
+
super({'url' => opts['url']})
|
102
|
+
self.class.crop_fields.each do |field|
|
103
|
+
send("#{field}=", build_image_crops(opts[field]))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def build_image_crops(image_crop_nodes)
|
108
|
+
image_crop_nodes && image_crop_nodes.map { |crop_node| ImageCrop.new(crop_node) }
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_accessor *(fields + crop_fields)
|
112
|
+
end
|
113
|
+
|
114
|
+
class ImageCrop < ImageInfoBase
|
115
|
+
def self.fields
|
116
|
+
%w(target_width target_height x1 y1 x2 y2)
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_accessor *fields
|
120
|
+
|
121
|
+
%w(target_width target_height x1 y1 x2 y2).each do |field|
|
122
|
+
define_method("#{field}=") do |value|
|
123
|
+
instance_variable_set("@#{field}", value.to_i)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def info
|
128
|
+
"target: (%i,%i), crop: (%i, %i) to (%i, %i)" % [target_width, target_height, x1, y1, x2, y2]
|
129
|
+
end
|
130
|
+
end
|
74
131
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Imagga
|
2
|
+
class ImageOrUrlParametizer
|
3
|
+
include Parametizer
|
4
|
+
|
5
|
+
def parametrize(urls_or_images)
|
6
|
+
options = build_urls(urls_or_images)
|
7
|
+
if (ids = build_ids(urls_or_images))[:ids]
|
8
|
+
options.merge!(ids)
|
9
|
+
end
|
10
|
+
options
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_urls(urls_or_images)
|
14
|
+
{ urls: [urls_or_images].flatten.map{ |o| o.url rescue o }.join(',') }
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_ids(urls_or_images)
|
18
|
+
{ ids: build_comma_separated_string(urls_or_images, :id, 0) }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Imagga
|
2
|
+
class BaseOptions
|
3
|
+
include Parametizer
|
4
|
+
attr_accessor :api_key, :api_secret, :version
|
5
|
+
|
6
|
+
def initialize(api_key, api_secret)
|
7
|
+
@version = '1.0'
|
8
|
+
@api_key = api_key || raise_missing(:api_key)
|
9
|
+
@api_secret = api_secret || raise_missing(:api_secret)
|
10
|
+
end
|
11
|
+
|
12
|
+
def base_options
|
13
|
+
{ v: version, api_key: api_key }
|
14
|
+
end
|
15
|
+
|
16
|
+
def sign(options)
|
17
|
+
sorted_options_string = options.keys.sort.map do |key|
|
18
|
+
"%s=%s" % [key.to_s, options[key]]
|
19
|
+
end.join('') << api_secret
|
20
|
+
Digest::MD5.hexdigest(sorted_options_string)
|
21
|
+
end
|
22
|
+
|
23
|
+
def options(opts={})
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ExtractOptions < BaseOptions
|
29
|
+
def options(opts={})
|
30
|
+
opts.merge!(base_options).merge!(method: method)
|
31
|
+
opts.merge!(build_boolean_options(opts, boolean_fields))
|
32
|
+
opts.merge!(sig: sign(opts))
|
33
|
+
end
|
34
|
+
|
35
|
+
def method
|
36
|
+
'imagga.colorsearch.extract'
|
37
|
+
end
|
38
|
+
|
39
|
+
def boolean_fields
|
40
|
+
[:extract_overall_colors, :extract_object_colors]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class RankOptions < BaseOptions
|
45
|
+
include Imagga::Exceptions
|
46
|
+
|
47
|
+
def options(opts={})
|
48
|
+
opts.merge!(base_options).merge!(
|
49
|
+
method: method,
|
50
|
+
color_vector: opts.delete(:color_vector),
|
51
|
+
type: (opts.delete(:type) { raise_missing('type') }).to_s,
|
52
|
+
dist: (opts.delete(:dist) { raise_missing('dist') }).to_s,
|
53
|
+
count: (opts.delete(:count) { raise_missing('count') }).to_s
|
54
|
+
)
|
55
|
+
opts.merge!(sig: sign(opts))
|
56
|
+
end
|
57
|
+
|
58
|
+
def method
|
59
|
+
'imagga.colorsearch.rank'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class CropOptions < BaseOptions
|
64
|
+
def options(opts={})
|
65
|
+
options = base_options.merge(opts).merge(method: method)
|
66
|
+
options.merge!(build_boolean_options(opts, :no_scaling))
|
67
|
+
options.merge!(sig: sign(options))
|
68
|
+
end
|
69
|
+
|
70
|
+
def method
|
71
|
+
'imagga.process.crop'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Imagga
|
2
|
+
module Parametizer
|
3
|
+
def build_comma_separated_string(objects, field, default_to=nil)
|
4
|
+
field_values = [objects].flatten.map{ |o| o.send(field) rescue default_to }
|
5
|
+
return if field_values.uniq == [default_to]
|
6
|
+
field_values.join(',')
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_boolean_options(options, keys)
|
10
|
+
[keys].flatten.inject({}) do |result, key|
|
11
|
+
if options.keys.include?(key) && (value = [true, '1', 1].include?(options[key]) ? 1 : 0)
|
12
|
+
result[key] = value
|
13
|
+
end
|
14
|
+
result
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Imagga
|
2
|
+
class RankColorParametizer
|
3
|
+
include Parametizer
|
4
|
+
|
5
|
+
def parametrize(rank_color_or_colors)
|
6
|
+
{ color_vector: build_vector(rank_color_or_colors) }
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_vector(rank_color_or_colors)
|
10
|
+
[rank_color_or_colors].flatten.map{ |o| build_rank_color_string(o) rescue o }.join(',')
|
11
|
+
end
|
12
|
+
|
13
|
+
def build_rank_color_string(rank_color)
|
14
|
+
"%i,%i,%i,%i" % [rank_color.percent, rank_color.r, rank_color.g, rank_color.b]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|