imagga 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +28 -1
  3. data/imagga.gemspec +1 -0
  4. data/lib/imagga.rb +8 -3
  5. data/lib/imagga/client.rb +12 -34
  6. data/lib/imagga/commands.rb +42 -0
  7. data/lib/imagga/core_client.rb +25 -0
  8. data/lib/imagga/exceptions.rb +3 -0
  9. data/lib/imagga/image.rb +59 -2
  10. data/lib/imagga/image_or_url_parametizer.rb +21 -0
  11. data/lib/imagga/options.rb +74 -0
  12. data/lib/imagga/parametizer.rb +18 -0
  13. data/lib/imagga/rank_color_parametizer.rb +17 -0
  14. data/lib/imagga/resolution_parametizer.rb +17 -0
  15. data/lib/imagga/result_builder.rb +27 -0
  16. data/lib/imagga/version.rb +1 -1
  17. data/spec/fixtures/crop_response.txt +62 -0
  18. data/spec/fixtures/extract_response.txt +253 -0
  19. data/spec/fixtures/rank_response.txt +12 -0
  20. data/spec/integration/crop_spec.rb +60 -0
  21. data/spec/integration/extract_spec.rb +51 -0
  22. data/spec/integration/rank_spec.rb +103 -0
  23. data/spec/lib/commands_spec.rb +70 -0
  24. data/spec/lib/core_client_spec.rb +43 -0
  25. data/spec/lib/crop_info_spec.rb +15 -0
  26. data/spec/lib/crop_options_spec.rb +24 -0
  27. data/spec/lib/crop_result_builder_spec.rb +4 -0
  28. data/spec/lib/extraction_options_spec.rb +16 -51
  29. data/spec/lib/image_crop_spec.rb +34 -0
  30. data/spec/lib/image_or_url_parametizer_spec.rb +65 -0
  31. data/spec/lib/parametizer_spec.rb +42 -0
  32. data/spec/lib/rank_color_parametizer_spec.rb +27 -0
  33. data/spec/lib/rank_color_spec.rb +51 -0
  34. data/spec/lib/rank_options_spec.rb +28 -0
  35. data/spec/lib/rank_result_builder_spec.rb +1 -12
  36. data/spec/lib/resolution_parametizer_spec.rb +60 -0
  37. metadata +63 -28
  38. data/lib/imagga/extract_options.rb +0 -74
  39. data/lib/imagga/extract_result_builder.rb +0 -11
  40. data/lib/imagga/rank_result_builder.rb +0 -9
  41. data/spec/lib/client_spec.rb +0 -352
@@ -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
- color_vector: '20,82,37,43,30,20,30,10', # vector of colors (percentage, r, g, b)
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
@@ -18,6 +18,7 @@ Gem::Specification.new do |gem|
18
18
 
19
19
  gem.add_dependency "httparty", "~> 0.10"
20
20
  gem.add_dependency "json", "~> 1.7"
21
+ gem.add_dependency "color", "~> 1.4.1"
21
22
 
22
23
  gem.add_development_dependency "rspec", "~> 2.12"
23
24
  gem.add_development_dependency "rake"
@@ -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/extract_options"
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"
@@ -1,42 +1,20 @@
1
- require "httparty"
2
- require "json"
3
-
4
1
  module Imagga
5
- class Client
6
- include HTTParty
7
- include Imagga::Exceptions
8
- attr_reader :api_key, :api_secret, :base_uri, :service_path
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
- private
33
-
34
- def extract_options(urls_or_images, additional_options)
35
- ExtractOptions.new(api_key, api_secret).options(urls_or_images, additional_options)
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 rank_options(opts)
39
- RankOptions.new(api_key, api_secret).options(opts)
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
+
@@ -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
@@ -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 = opts[:url] || raise_missing(:url)
8
- @id = opts[:id] || 0
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