indico 0.2.5 → 0.3.0
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.
- 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
|