indico 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +41 -1
- data/indico.gemspec +1 -0
- data/lib/indico/errors.rb +3 -0
- data/lib/indico/helper.rb +12 -14
- data/lib/indico/image.rb +109 -0
- data/lib/indico/multi.rb +47 -0
- data/lib/indico/settings.rb +9 -0
- data/lib/indico/version.rb +1 -1
- data/lib/indico.rb +34 -17
- data/spec/data/happy.png +0 -0
- data/spec/data/happy64.txt +1 -0
- data/spec/indico_batch_spec.rb +47 -10
- data/spec/indico_spec.rb +72 -13
- data/spec/spec_helper.rb +9 -0
- metadata +23 -2
data/README.md
CHANGED
@@ -24,7 +24,7 @@ For API key registration and setup, checkout our [quickstart guide](http://docs.
|
|
24
24
|
|
25
25
|
Full Documentation
|
26
26
|
------------
|
27
|
-
Detailed documentation and further code examples are available at [indico.
|
27
|
+
Detailed documentation and further code examples are available at [indico.readme.io](http://indico.readme.io/v2.0/docs/ruby)
|
28
28
|
|
29
29
|
Examples
|
30
30
|
---------
|
@@ -72,6 +72,46 @@ Each `Indico` method has a corresponding batch method for analyzing many example
|
|
72
72
|
=> [0.9899001220871786, 0.005709885173415242]
|
73
73
|
```
|
74
74
|
|
75
|
+
|
76
|
+
Calling multiple APIs with a single function
|
77
|
+
---------
|
78
|
+
There are two multiple API functions `predict_text` and `predict_image` (and their batch counterparts). These functions are similar to the existing api functions, but take in an additional `apis` argument as an array of strings of API names (defaults to all existing apis). `predict_text` accepts a list of existing text APIs and vice versa for `predict_image`.
|
79
|
+
|
80
|
+
Accepted text API names: `text_tags, political, sentiment, language`
|
81
|
+
|
82
|
+
Accepted image API names: `fer, facial_features, image_features`
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
> require 'indico'
|
86
|
+
|
87
|
+
=> true
|
88
|
+
|
89
|
+
> Indico.api_key = "YOUR_API_KEY"
|
90
|
+
|
91
|
+
=> "YOUR_API_KEY"
|
92
|
+
|
93
|
+
> Indico.predict_text("Best day ever", ["sentiment", "language"])
|
94
|
+
|
95
|
+
=> {"sentiment"=>0.9899001220871786, "language"=>{"Swedish"=>0.0022464881013042294, "Vietnamese"=>9.887170914498351e-05, ...}}
|
96
|
+
|
97
|
+
> Indico.batch_predict_text(["Best day ever", "Worst day ever"], ["sentiment", "language"])
|
98
|
+
|
99
|
+
=> {"sentiment"=>[0.9899001220871786, 0.005709885173415242], "language"=>[{"Swedish"=>0.0022464881013042294, "Vietnamese"=>9.887170914498351e-05, "Romanian"=>0.00010661175919993216, ...}, {"Swedish"=>0.4924352805804646, "Vietnamese"=>0.028574824174911372, "Romanian"=>0.004185623723173551, "Dutch"=>0.000717033819689362, "Korean"=>0.0030093489153785826, ...}]}
|
100
|
+
|
101
|
+
> test_face = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
|
102
|
+
|
103
|
+
=> [[[0.66, 0.99, 0.03], [0.42, 0.72, 0.86], [0.95, 0.44, 0.61], [0.39, 0.57, 0.4], [0.06, 0.52, 0.43], [0.11, 0.09, 0.78], [0.35, 0.69, 0.32], [0.44, 0.5, 0.26], [0.71, 0.75, 0.64], [0.91, 0.92, 0.14], [0.71, 0.98, 0.02], ..]]
|
104
|
+
|
105
|
+
> Indico.predict_image(test_face, ["fer", "facial_features"])
|
106
|
+
|
107
|
+
=> {"facial_features"=>[0.0, -0.026176479280200796, 0.20707644777495776, ...], "fer"=>{"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}}
|
108
|
+
|
109
|
+
> Indico.batch_predict_image([test_face, test_face], ["fer", "facial_features"])
|
110
|
+
|
111
|
+
=> {"facial_features"=>[[0.0, -0.026176479280200796, 0.20707644777495776, ...], [0.0, -0.026176479280200796, 0.20707644777495776, ...]], "fer"=>[{"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}, {"Angry"=>0.08877494466353497, "Sad"=>0.3933999409104264, "Neutral"=>0.1910612654566151, "Surprise"=>0.0346146405941845, "Fear"=>0.17682159820518667, "Happy"=>0.11532761017005204}]}
|
112
|
+
```
|
113
|
+
|
114
|
+
|
75
115
|
## Contributing
|
76
116
|
|
77
117
|
1. Fork it ( https://github.com/[my-github-username]/indico/fork )
|
data/indico.gemspec
CHANGED
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'inifile', '~> 3.0.0'
|
22
|
+
spec.add_runtime_dependency 'oily_png', '~> 1.2.0'
|
22
23
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
23
24
|
spec.add_development_dependency 'rake'
|
24
25
|
spec.add_development_dependency 'rspec'
|
data/lib/indico/helper.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative './errors'
|
2
2
|
|
3
3
|
module Indico
|
4
4
|
private
|
@@ -14,24 +14,22 @@ module Indico
|
|
14
14
|
def self.api_handler(data, api, config)
|
15
15
|
server = nil
|
16
16
|
api_key = nil
|
17
|
-
|
18
|
-
server = config[:cloud]
|
19
|
-
api_key = config[:api_key]
|
20
|
-
end
|
17
|
+
|
21
18
|
d = {}
|
22
19
|
d['data'] = data
|
23
|
-
data_dict = JSON.dump(d)
|
24
20
|
|
25
|
-
|
26
|
-
|
21
|
+
unless config.nil?
|
22
|
+
server = config[:cloud]
|
23
|
+
api_key = config[:api_key]
|
24
|
+
apis = config["apis"]
|
25
|
+
d = d.merge(config)
|
27
26
|
end
|
28
27
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
28
|
+
url = url_join((server or Indico.config['cloud']), api) +
|
29
|
+
(apis ? "?apis=" + apis.join(",") : "")
|
32
30
|
|
33
|
-
response = make_request(
|
34
|
-
add_api_key_to_header(api_key))
|
31
|
+
response = make_request(url, JSON.dump(d),
|
32
|
+
add_api_key_to_header((api_key or Indico.config['auth'])))
|
35
33
|
|
36
34
|
|
37
35
|
results = JSON.parse(response.body)
|
@@ -65,7 +63,7 @@ module Indico
|
|
65
63
|
if api_key.nil?
|
66
64
|
raise ArgumentError, 'api key is required'
|
67
65
|
end
|
68
|
-
headers =
|
66
|
+
headers = HEADERS
|
69
67
|
headers['X-ApiKey'] = api_key
|
70
68
|
headers
|
71
69
|
end
|
data/lib/indico/image.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'oily_png'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Indico
|
5
|
+
def self.preprocess(image, size, batch)
|
6
|
+
# image is [[f,f,f,f, ...], ..] or [[[f,f,f],[f,f,f],[f,f,f]], ...]
|
7
|
+
if batch
|
8
|
+
# Batch Request
|
9
|
+
im_array = Array.new
|
10
|
+
|
11
|
+
# process each image
|
12
|
+
image.each do |_image|
|
13
|
+
im_array.push(preprocess(_image, size, false))
|
14
|
+
end
|
15
|
+
|
16
|
+
return im_array
|
17
|
+
end
|
18
|
+
|
19
|
+
if image.class == String
|
20
|
+
decoded_image = handle_string_input(image)
|
21
|
+
elsif image.class == Array
|
22
|
+
decoded_image = handle_array_input(image)
|
23
|
+
else
|
24
|
+
raise Exception.new("Image input must be nested array of pixels, filename, or base64 string")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Resize and export base64 encoded string
|
28
|
+
return decoded_image.resize(size, size).to_data_url.gsub("data:image/png;base64," ,"")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.handle_string_input(str)
|
32
|
+
# Handles string input
|
33
|
+
if File.file?(str)
|
34
|
+
# Handling File Inputs
|
35
|
+
return ChunkyPNG::Image.from_file(str)
|
36
|
+
end
|
37
|
+
|
38
|
+
regex = %r{^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$}.match(str)
|
39
|
+
# Base 64 handling
|
40
|
+
if regex == nil
|
41
|
+
raise Exception.new("String is not a valid absolute filepath or base64 string")
|
42
|
+
else
|
43
|
+
return ChunkyPNG::Image.from_data_url("data:image/png;base64," + str.gsub("data:image/png;base64," ,""))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.handle_array_input(image)
|
48
|
+
# Handles properly formatting and loading array of pixels
|
49
|
+
# Single Request
|
50
|
+
warn "Warning! Array input as image will be deprecated in the next major release.\n Consider using filepaths or base64 encoded strings"
|
51
|
+
|
52
|
+
dimens = get_dimension(image)
|
53
|
+
isFloat = array_contains_float(image, dimens)
|
54
|
+
|
55
|
+
if dimens.size > 3
|
56
|
+
raise Exception.new("Nested array of image must be [[p, p, ...]] or [[[r,g,b], [r,g,b], ...]]")
|
57
|
+
end
|
58
|
+
|
59
|
+
decoded_image = ChunkyPNG::Image.new(dimens[0], dimens[1])
|
60
|
+
|
61
|
+
# Manually loading the pixels into the ChunkyImage object
|
62
|
+
(0..dimens[0] - 1).each do |x|
|
63
|
+
(0..dimens[1] - 1).each do |y|
|
64
|
+
pixel = image[x][y]
|
65
|
+
if pixel.is_a?(Array)
|
66
|
+
if isFloat
|
67
|
+
pixel.map! {|a| (255 * a).to_i}
|
68
|
+
end
|
69
|
+
decoded_image.set_pixel(x,y, ChunkyPNG::Color.rgb(pixel[0], pixel[1], pixel[2]))
|
70
|
+
else
|
71
|
+
if isFloat
|
72
|
+
pixel = (pixel * 255).to_i
|
73
|
+
end
|
74
|
+
decoded_image.set_pixel(x,y, ChunkyPNG::Color.rgb(pixel, pixel, pixel))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return decoded_image
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def self.get_dimension(array)
|
84
|
+
return [] unless array.is_a?(Array)
|
85
|
+
return [array.size] + get_dimension(array[0])
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.array_contains_float(array, dimens)
|
89
|
+
# Determines whether or not the array contains floats
|
90
|
+
if not array.is_a?(Array)
|
91
|
+
return array.class == Float
|
92
|
+
end
|
93
|
+
elem = array[0]
|
94
|
+
(0..dimens.size - 2).each do |i|
|
95
|
+
elem = elem[0]
|
96
|
+
end
|
97
|
+
|
98
|
+
return elem.class == Float
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.get_rgb(value)
|
102
|
+
# Returns Integer encoding of RGB value used by ChunkyPNG
|
103
|
+
return [
|
104
|
+
ChunkyPNG::Color.r(value),
|
105
|
+
ChunkyPNG::Color.g(value),
|
106
|
+
ChunkyPNG::Color.b(value)
|
107
|
+
]
|
108
|
+
end
|
109
|
+
end
|
data/lib/indico/multi.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative './errors'
|
2
|
+
|
3
|
+
module Indico
|
4
|
+
CLIENT_TO_SERVER = {
|
5
|
+
"political" => "political",
|
6
|
+
"sentiment" => "sentiment",
|
7
|
+
"language" => "language",
|
8
|
+
"text_tags" => "texttags",
|
9
|
+
"fer" => "fer",
|
10
|
+
"facial_features" => "facialfeatures",
|
11
|
+
"image_features" => "imagefeatures"
|
12
|
+
}
|
13
|
+
|
14
|
+
SERVER_TO_CLIENT = CLIENT_TO_SERVER.invert
|
15
|
+
def self.multi(data, type, apis, allowed, batch = false, config)
|
16
|
+
converted_apis = Array.new
|
17
|
+
apis.each { |api|
|
18
|
+
if not allowed.include? api
|
19
|
+
fail api + " is not a valid api for " + type + " requests. Please use: " + allowed.join(", ")
|
20
|
+
else
|
21
|
+
converted_apis.push(CLIENT_TO_SERVER[api])
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
if config.nil?
|
26
|
+
config = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
config["apis"] = converted_apis
|
30
|
+
response = api_handler(data, batch ? "apis/batch" : "apis", config)
|
31
|
+
results = handle_multi(response)
|
32
|
+
|
33
|
+
results
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.handle_multi(results)
|
37
|
+
converted_results = Hash.new
|
38
|
+
results.each do |key, value|
|
39
|
+
if value.is_a?(Hash) && value.has_key?("results")
|
40
|
+
converted_results[SERVER_TO_CLIENT[key]] = value["results"]
|
41
|
+
else
|
42
|
+
raise IndicoError, 'unexpected result from ' + key + '\n\t' + value.fetch("error", "")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
converted_results
|
46
|
+
end
|
47
|
+
end
|
data/lib/indico/settings.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
require 'inifile'
|
2
|
+
require_relative 'version'
|
3
|
+
|
4
|
+
HEADERS = { 'Content-Type' => 'application/json',
|
5
|
+
'Accept' => 'application/json',
|
6
|
+
'client-lib' => 'ruby',
|
7
|
+
'version-number' => Indico::VERSION }
|
8
|
+
# APIS
|
9
|
+
TEXT_APIS = ["political", "sentiment", "language", "text_tags"]
|
10
|
+
IMAGE_APIS = ["fer", "facial_features", "image_features"]
|
2
11
|
|
3
12
|
module Indico
|
4
13
|
private
|
data/lib/indico/version.rb
CHANGED
data/lib/indico.rb
CHANGED
@@ -1,16 +1,13 @@
|
|
1
1
|
require 'indico/version'
|
2
2
|
require 'indico/helper'
|
3
|
+
require 'indico/image'
|
4
|
+
require 'indico/multi'
|
3
5
|
require 'indico/settings'
|
4
6
|
require 'uri'
|
5
7
|
require 'json'
|
6
8
|
require 'net/https'
|
7
9
|
|
8
10
|
module Indico
|
9
|
-
HEADERS = { 'Content-Type' => 'application/json',
|
10
|
-
'Accept' => 'application/json',
|
11
|
-
'client-lib' => 'ruby',
|
12
|
-
'version-number' => '0.2.5' }
|
13
|
-
|
14
11
|
def self.api_key
|
15
12
|
config['auth']
|
16
13
|
end
|
@@ -67,27 +64,47 @@ module Indico
|
|
67
64
|
api_handler(test_text, 'texttags/batch', config)
|
68
65
|
end
|
69
66
|
|
70
|
-
def self.fer(
|
71
|
-
api_handler(
|
67
|
+
def self.fer(test_image, config = nil)
|
68
|
+
api_handler(preprocess(test_image, 48, false), 'fer', config)
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.batch_fer(test_image, config = nil)
|
72
|
+
api_handler(preprocess(test_image, 48, true), 'fer/batch', config)
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.facial_features(test_image, config = nil)
|
76
|
+
api_handler(preprocess(test_image, 48, false), 'facialfeatures', config)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.batch_facial_features(test_image, config = nil)
|
80
|
+
api_handler(preprocess(test_image, 48, true), 'facialfeatures/batch', config)
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.image_features(test_image, config = nil)
|
84
|
+
api_handler(preprocess(test_image, 64, false), 'imagefeatures', config)
|
72
85
|
end
|
73
86
|
|
74
|
-
def self.
|
75
|
-
api_handler(
|
87
|
+
def self.batch_image_features(test_image, config = nil)
|
88
|
+
api_handler(preprocess(test_image, 64, true), 'imagefeatures/batch', config)
|
76
89
|
end
|
77
90
|
|
78
|
-
def self.
|
79
|
-
|
91
|
+
def self.predict_image(face, apis = IMAGE_APIS, config = nil)
|
92
|
+
api_hash = {"apis" => apis}
|
93
|
+
multi(preprocess(face, 48, false), "image", apis, IMAGE_APIS, config ? config.update(api_hash) : api_hash)
|
80
94
|
end
|
81
95
|
|
82
|
-
def self.
|
83
|
-
|
96
|
+
def self.predict_text(test_text, apis = TEXT_APIS, config = nil)
|
97
|
+
api_hash = {"apis" => apis}
|
98
|
+
multi(test_text, "text", apis, TEXT_APIS, config ? config.update(api_hash) : api_hash)
|
84
99
|
end
|
85
100
|
|
86
|
-
def self.
|
87
|
-
|
101
|
+
def self.batch_predict_image(face, apis, config = nil)
|
102
|
+
api_hash = {"apis" => apis}
|
103
|
+
multi(preprocess(face, 48, true), "image", apis, IMAGE_APIS, true, config ? config.update(api_hash) : api_hash)
|
88
104
|
end
|
89
105
|
|
90
|
-
def self.
|
91
|
-
|
106
|
+
def self.batch_predict_text(test_text, apis, config = nil)
|
107
|
+
api_hash = {"apis" => apis}
|
108
|
+
multi(test_text, "text", apis, TEXT_APIS, true, config ? config.update(api_hash) : api_hash)
|
92
109
|
end
|
93
110
|
end
|
data/spec/data/happy.png
ADDED
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
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
|
data/spec/indico_batch_spec.rb
CHANGED
@@ -21,7 +21,7 @@ describe Indico do
|
|
21
21
|
it 'should access a private cloud' do
|
22
22
|
expected_keys = Set.new(%w(Conservative Green Liberal Libertarian))
|
23
23
|
data = ['Guns don\'t kill people.', ' People kill people.']
|
24
|
-
|
24
|
+
|
25
25
|
# for mocking: use http instead of https to route requests to our public cloud
|
26
26
|
Indico.cloud_protocol = 'http://'
|
27
27
|
response = Indico.batch_political(data, @config_private_cloud)
|
@@ -104,26 +104,63 @@ describe Indico do
|
|
104
104
|
it 'should tag face with correct facial expression' do
|
105
105
|
expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
|
106
106
|
test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
107
|
-
|
107
|
+
silent_warnings do
|
108
|
+
response = Indico.batch_fer([test_face, test_face], @config)
|
108
109
|
|
109
|
-
|
110
|
-
|
110
|
+
expect(Set.new(response[0].keys)).to eql(expected_keys)
|
111
|
+
expect(Set.new(response[1].keys)).to eql(expected_keys)
|
112
|
+
end
|
111
113
|
end
|
112
114
|
|
113
115
|
it 'should tag face with correct facial features' do
|
114
116
|
test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
115
|
-
|
116
|
-
|
117
|
-
|
117
|
+
silent_warnings do
|
118
|
+
response = Indico.batch_facial_features([test_face, test_face], @config)
|
119
|
+
expect(response[0].length).to eql(48)
|
120
|
+
expect(response[1].length).to eql(48)
|
121
|
+
end
|
118
122
|
end
|
119
123
|
|
120
124
|
it 'should tag image with correct image features' do
|
121
125
|
test_image = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
122
|
-
|
123
|
-
|
124
|
-
|
126
|
+
silent_warnings do
|
127
|
+
response = Indico.batch_image_features([test_image, test_image], @config)
|
128
|
+
expect(response[0].length).to eql(2048)
|
129
|
+
expect(response[1].length).to eql(2048)
|
130
|
+
end
|
125
131
|
end
|
126
132
|
|
133
|
+
it "should respond with all text apis called in batch" do
|
134
|
+
expected_keys = Set.new(TEXT_APIS)
|
135
|
+
response = Indico.batch_predict_text(["Worst movie ever."], TEXT_APIS)
|
136
|
+
|
137
|
+
expect(response.class).to eql(Hash)
|
138
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should respond with all image apis called in batch" do
|
142
|
+
test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
|
143
|
+
expected_keys = Set.new(IMAGE_APIS)
|
144
|
+
silent_warnings do
|
145
|
+
response = Indico.batch_predict_image([test_image], IMAGE_APIS)
|
146
|
+
|
147
|
+
expect(response.class).to eql(Hash)
|
148
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should respond with all image apis called on int array in batch" do
|
153
|
+
test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)}}}
|
154
|
+
expected_keys = Set.new(IMAGE_APIS)
|
155
|
+
silent_warnings do
|
156
|
+
response = Indico.batch_predict_image([test_image], IMAGE_APIS)
|
157
|
+
|
158
|
+
expect(response.class).to eql(Hash)
|
159
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
127
164
|
# Uncomment when frontend updated to accept image urls
|
128
165
|
# it "should accept image urls" do
|
129
166
|
# test_image = 'http://icons.iconarchive.com/icons/oxygen-icons.org/' +
|
data/spec/indico_spec.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'set'
|
3
|
+
require 'oily_png'
|
3
4
|
|
4
5
|
describe Indico do
|
5
6
|
before do
|
@@ -18,7 +19,7 @@ describe Indico do
|
|
18
19
|
it 'should tag text with correct political tags' do
|
19
20
|
expected_keys = Set.new(%w(Conservative Green Liberal Libertarian))
|
20
21
|
data = 'Guns don\'t kill people. People kill people.'
|
21
|
-
|
22
|
+
|
22
23
|
# for mocking: use http to redirect requests to our public cloud endpoint
|
23
24
|
Indico.cloud_protocol = 'http://'
|
24
25
|
response = Indico.political(data, @config)
|
@@ -94,33 +95,91 @@ describe Indico do
|
|
94
95
|
it 'should tag face with correct facial expression' do
|
95
96
|
expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
|
96
97
|
test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
98
|
+
silent_warnings do
|
99
|
+
response = Indico.fer(test_face)
|
100
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
101
|
+
end
|
101
102
|
end
|
102
103
|
|
103
104
|
it 'should tag face with correct facial features' do
|
104
105
|
test_face = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
silent_warnings do
|
107
|
+
response = Indico.facial_features(test_face)
|
108
|
+
expect(response.length).to eql(48)
|
109
|
+
end
|
108
110
|
end
|
109
111
|
|
110
112
|
it 'should tag image with correct image features' do
|
111
113
|
test_image = Array.new(48) { Array.new(48) { rand(100) / 100.0 } }
|
112
|
-
|
113
|
-
|
114
|
-
|
114
|
+
silent_warnings do
|
115
|
+
response = Indico.image_features(test_image)
|
116
|
+
expect(response.length).to eql(2048)
|
117
|
+
end
|
115
118
|
end
|
116
119
|
|
117
120
|
it "should tag rgb image with correct image features" do
|
118
121
|
test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
|
119
|
-
|
122
|
+
silent_warnings do
|
123
|
+
response = Indico.image_features(test_image)
|
124
|
+
expect(response.length).to eql(2048)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should be able to load image from path" do
|
129
|
+
expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
|
130
|
+
response = Indico.fer(File.dirname(__FILE__) + "/data/happy.png")
|
131
|
+
|
132
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
133
|
+
expect(response["Happy"]).to be > 0.5
|
134
|
+
end
|
120
135
|
|
121
|
-
|
136
|
+
it "should be able to load image from b64" do
|
137
|
+
expected_keys = Set.new(%w(Angry Sad Neutral Surprise Fear Happy))
|
138
|
+
response = Indico.fer(File.open(File.dirname(__FILE__) + "/data/happy64.txt", 'rb') { |f| f.read })
|
139
|
+
|
140
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
141
|
+
expect(response["Happy"]).to be > 0.5
|
122
142
|
end
|
123
143
|
|
144
|
+
it "should respond with all text apis called" do
|
145
|
+
expected_keys = Set.new(TEXT_APIS)
|
146
|
+
response = Indico.predict_text("Worst movie ever.", TEXT_APIS)
|
147
|
+
|
148
|
+
expect(response.class).to eql(Hash)
|
149
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should respond with all text apis called by default" do
|
153
|
+
expected_keys = Set.new(TEXT_APIS)
|
154
|
+
response = Indico.predict_text("Worst movie ever.")
|
155
|
+
|
156
|
+
expect(response.class).to eql(Hash)
|
157
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should respond with all image apis called" do
|
161
|
+
test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)/100.0}}}
|
162
|
+
expected_keys = Set.new(IMAGE_APIS)
|
163
|
+
silent_warnings do
|
164
|
+
response = Indico.predict_image(test_image, IMAGE_APIS)
|
165
|
+
|
166
|
+
expect(response.class).to eql(Hash)
|
167
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should respond with all image apis called on int array" do
|
172
|
+
test_image = Array.new(48){Array.new(48){Array.new(3){rand(100)}}}
|
173
|
+
expected_keys = Set.new(IMAGE_APIS)
|
174
|
+
silent_warnings do
|
175
|
+
response = Indico.predict_image(test_image, IMAGE_APIS)
|
176
|
+
|
177
|
+
expect(response.class).to eql(Hash)
|
178
|
+
expect(Set.new(response.keys)).to eql(expected_keys)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
|
124
183
|
# Uncomment when frontend updated to accept image urls
|
125
184
|
# it 'should accept image urls' do
|
126
185
|
# response = Indico.image_features('http://icons.iconarchive.com/icons/' +
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
require 'indico'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
def silent_warnings
|
5
|
+
old_stderr = $stderr
|
6
|
+
$stderr = StringIO.new
|
7
|
+
yield
|
8
|
+
ensure
|
9
|
+
$stderr = old_stderr
|
10
|
+
end
|
2
11
|
|
3
12
|
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
13
|
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: indico
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2015-
|
15
|
+
date: 2015-06-11 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: inifile
|
@@ -30,6 +30,22 @@ dependencies:
|
|
30
30
|
- - ~>
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 3.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: oily_png
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.0
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 1.2.0
|
33
49
|
- !ruby/object:Gem::Dependency
|
34
50
|
name: bundler
|
35
51
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,11 +112,16 @@ files:
|
|
96
112
|
- Rakefile
|
97
113
|
- indico.gemspec
|
98
114
|
- lib/indico.rb
|
115
|
+
- lib/indico/errors.rb
|
99
116
|
- lib/indico/helper.rb
|
117
|
+
- lib/indico/image.rb
|
118
|
+
- lib/indico/multi.rb
|
100
119
|
- lib/indico/settings.rb
|
101
120
|
- lib/indico/version.rb
|
102
121
|
- spec/config/.indicorc.test
|
103
122
|
- spec/config/.indicorc.test.2
|
123
|
+
- spec/data/happy.png
|
124
|
+
- spec/data/happy64.txt
|
104
125
|
- spec/indico_batch_spec.rb
|
105
126
|
- spec/indico_spec.rb
|
106
127
|
- spec/settings_spec.rb
|