clarification 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e64a471ed5071485e467ef49e82658ab0b349c63
4
- data.tar.gz: 92f9619926fa087af02ac0b1ab7cbe88606d9b29
3
+ metadata.gz: 14dba1413119dc7a3ad0e944dfcbd825b40eb326
4
+ data.tar.gz: 205ad9143ccb00f1b5f9777383358a2b9abd0870
5
5
  SHA512:
6
- metadata.gz: 9de55bd0c81de343006762c2cdee4b6420f77d7a9746a97ca2d251a1d0184c3165c63c9beceab886bd96f6e1df2de02077370c9e0db26de11df122f380fac246
7
- data.tar.gz: 576bc98bf184e7f9ca6b1d23352707dd6a807f19c5b2b15aece2b125d945f4e1af8aa788b872f8ac4a8f68a76b37bd3b884ab621dc8bea5691804314f2b3e7f7
6
+ metadata.gz: 31c796e17b2e9942b4f85340f979a7c08d268b6615b74ea234aca305cefc71e09810790f9d138f30ef72ba9d7e4742d3400049fb27e0cb83dbdbba8b5c047b0a
7
+ data.tar.gz: a93bab9d28a78f65105dd7efe3286b8db22843c16781d0db017a6932fe8dca53a0f609949761cc08a6e107ed00f455c7a96a7079663dc366cce4d12d94f3f3df
data/.gitignore CHANGED
@@ -7,6 +7,6 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
-
10
+ .DS_Store
11
11
  # rspec failure tracking
12
12
  .rspec_status
data/README.md CHANGED
@@ -2,10 +2,13 @@
2
2
  [![Build Status](https://travis-ci.org/nicholalexander/clarification.svg?branch=master)](https://travis-ci.org/nicholalexander/clarification)
3
3
  [![Codacy Badge](https://api.codacy.com/project/badge/Grade/cb0dd6cce7ec48a191696780951c5efe)](https://www.codacy.com/app/nicholalexander/clarification?utm_source=github.com&utm_medium=referral&utm_content=nicholalexander/clarification&utm_campaign=Badge_Grade)
4
4
 
5
-
6
5
  # Clarification
7
6
 
8
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/clarification`. To experiment with that code, run `bin/console` for an interactive prompt.
7
+ An unofficial Ruby wrapper for the [Clarifai](http://clarifai.com) API. This currently uses v2 of the API and was partially created because other Ruby gems are using v1 which is being depricated in late 2017.
8
+
9
+ Clarifai is an awesome service and hopefully this gem makes it easier to use it and to use AI image tagging in your Ruby apps. Please let me know if there's something you'd like to see but don't or if you see something you wish you didn't. Thanks!
10
+
11
+ This gem is currently in development and there is lots likely to change so please be aware!
9
12
 
10
13
  ## Installation
11
14
 
@@ -25,18 +28,20 @@ Or install it yourself as:
25
28
 
26
29
  ## Configuration
27
30
 
28
- Configure the gem as you would normally.
31
+ Configure the gem as you would normally configure a wonderful happy gem.
29
32
 
30
33
  ```ruby
31
34
  Clarification.configure do |config|
32
35
  config.api_key = 'a_big_secret_you_got_from_clarifai'
33
- config.end_points = [:food, :general]
36
+ config.default_public_models = [:food, :general]
34
37
  end
35
38
  ```
36
39
 
37
40
  This should also work nicely inside a rails initializer.
38
41
 
39
- Each endpoint initialized in your configuration will be called. _So any predictions using a client configured with :food and :general will make two API requests._
42
+ **Each endpoint initialized in your configuration will be called by default from the client. So any predictions using a client configured with `:food` and `:general` will make two API requests.**
43
+
44
+ This will change when [Workflow](https://clarifai.com/developer/guide/workflow#workflow) is implemented in the gem.
40
45
 
41
46
  ## Usage
42
47
 
@@ -47,7 +52,7 @@ response = client.predict(some_public_url_of_an_image)
47
52
 
48
53
  ### Prediction
49
54
 
50
- The response object returned from the prediction is a hash containing a nicely parsed result for each of the models called. In our example, we initialized with two models so we will have response[:food] and response[:general]. Each of those model results have an object which has three pieces of relevant information: status, concepts, and response_json.
55
+ The response object returned from the prediction is a hash containing a nicely parsed result for each of the models called. In the above configuration example we have two models. So we will have response[:food] and response[:general] after asking for a prediction. Each of those model results have an object which has three pieces of relevant information: status, concepts, and response_json.
51
56
 
52
57
  Thusly you can do things like this:
53
58
 
@@ -59,16 +64,18 @@ response[:food].concepts.each do |concept|
59
64
  end
60
65
  ```
61
66
 
62
- ```ruby
63
- response[:general].response_json
64
- ```
65
-
66
67
  ```ruby
67
68
  if response[:food].status.code == 10000
68
69
  puts response[:food].status.description
69
70
  end
70
71
  ```
71
72
 
73
+ If you want the actual api response, it's here:
74
+
75
+ ```ruby
76
+ response[:general].response_json
77
+ ```
78
+
72
79
  All the objects are OpenStructs currently, but I suspect this will change shortly.
73
80
 
74
81
  As a convenience, the client also maintains the parsed response in the last_response variable.
@@ -78,6 +85,37 @@ client.predict(some_public_url_of_an_image)
78
85
  client.last_response #=> {:food => Objectifier...}
79
86
  ```
80
87
 
88
+ ### Search
89
+
90
+ The client has a search object which can be used to index images with the Clarifai application identified by the API key.
91
+
92
+ Thusly you can do like this:
93
+
94
+ ```ruby
95
+ image_array = [ url_to_picture_of_kitten, url_to_picture_of_pizza, url_to_picture_of_drake]
96
+ client.search.index_images(image_array)
97
+ ```
98
+
99
+ Once you have your images indexed, you can go and search them by concepts that might be in your picture as identified by Clarifai's general prediction model.
100
+
101
+ ```ruby
102
+ results = client.search.by_concept('cat')
103
+ results.hits.each do |hit|
104
+ puts hit.url if hit.score > .90
105
+ end
106
+ ```
107
+
108
+ The search client also saves the last search and searched concept as a matter of convenience.
109
+
110
+ ```ruby
111
+ client.search.last_search.hits.count
112
+ client.search.last_search.concept
113
+ ```
114
+
115
+ Go and do likewise.
116
+
117
+ ### Train
118
+
81
119
  ## TODO's
82
120
 
83
121
  Lots and lots of things. Amongst them:
@@ -86,6 +124,8 @@ Lots and lots of things. Amongst them:
86
124
  * error handling
87
125
  * predict multiple images per call
88
126
  * predict video
127
+ * better testing.
128
+ * documentation
89
129
  * use workflows
90
130
  * searching
91
131
  * training custom models
@@ -94,15 +134,16 @@ Lots and lots of things. Amongst them:
94
134
 
95
135
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
96
136
 
97
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
137
+ The repo intentionally includes an api key in bin/console to make experimenting easy and quick. If you plan on making a bunch of requests, you can easily sign up for [your own free key](https://clarifai.com/signup/).
98
138
 
99
139
  ## Contributing
100
140
 
101
141
  Bug reports and pull requests are welcome on GitHub at https://github.com/nicholalexander/clarification. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
102
142
 
103
- https://codeclimate.com/github/nicholalexander/clarification
104
- https://www.codacy.com/app/nicholalexander/clarification
105
-
143
+ You can also see various code reporting / CI info here:
144
+ [travis ci](https://travis-ci.org/nicholalexander/clarification)
145
+ [codacy](https://www.codacy.com/app/nicholalexander/clarification)
146
+ [code climate](https://codeclimate.com/github/nicholalexander/clarification)
106
147
 
107
148
  ## License
108
149
 
data/bin/console CHANGED
@@ -2,19 +2,44 @@
2
2
 
3
3
  require "bundler/setup"
4
4
  require "clarification"
5
+ require "yaml"
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
8
9
 
9
10
  Clarification.configure do |config|
10
11
  config.api_key = 'f7cc628178994e16b2470ae739ef927a'
11
- config.default_models = [:food]
12
+ config.default_public_models = [:food, :general]
12
13
  end
13
14
 
14
- @target_url = 'https://www.cicis.com/media/1138/pizza_trad_pepperoni.png'
15
+ @images = YAML.load_file("spec/support/images.yml")
16
+
17
+ @urls = [ @images[:mountains], @images[:kitten], @images[:drake] ]
18
+
19
+ # models = [:apparel,
20
+ # :celebrity,
21
+ # :color,
22
+ # :demographics,
23
+ # :face_detection,
24
+ # :face_embedding,
25
+ # :focus,
26
+ # :food,
27
+ # :general,
28
+ # :general_embedding,
29
+ # :logo,
30
+ # :moderation,
31
+ # :nsfw,
32
+ # :travel,
33
+ # :wedding ]
15
34
 
16
35
  @client = Clarification::Client.new
17
- @response = @client.predict(@target_url)
36
+ # @client.set_models(models)
37
+ # @response = @client.predict(@target_url)
38
+
39
+ # @client.search.index_image
40
+ # @client.search.by_concepts
41
+ # @client.search.by_images
42
+
18
43
 
19
44
  # (If you use this, don't forget to add pry to your Gemfile!)
20
45
  require "pry"
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["nichol.alexander@gmail.com"]
11
11
 
12
12
  spec.summary = %q{An unofficial API wrapper for the Clarifai Image and Video Recognition API.}
13
- spec.description = %q{Alpha Version - configure, access Clarifai set end points, interrogate responses.}
13
+ spec.description = %q{Alpha Version - Predict and Search using Clarifai.}
14
14
  spec.homepage = "https://github.com/nicholalexander/clarification"
15
15
  spec.license = "MIT"
16
16
 
data/lib/clarification.rb CHANGED
@@ -1,9 +1,15 @@
1
1
  require "clarification/client"
2
+ require "clarification/concept"
2
3
  require "clarification/configuration"
3
4
  require "clarification/end_points"
4
5
  require "clarification/enrich"
6
+ require "clarification/hit"
5
7
  require "clarification/objectifier"
8
+ require "clarification/response"
9
+ require "clarification/response/search_response"
6
10
  require "clarification/requester"
11
+ require "clarification/requester/search_requester"
12
+ require "clarification/search"
7
13
  require "clarification/version"
8
14
 
9
15
  require 'net/http'
@@ -1,23 +1,28 @@
1
1
  module Clarification
2
2
  class Client
3
3
 
4
- attr_reader :active_models, :last_response
4
+ attr_reader :active_public_models, :last_response, :search
5
5
 
6
6
  def initialize
7
7
  raise "No Configuration Found." if Clarification.configuration.nil?
8
- @active_models = Clarification.configuration.default_models
8
+ @active_public_models = Clarification.configuration.default_public_models
9
9
  @last_response = nil
10
+ @search = Search.new
10
11
  end
11
12
 
12
13
  def predict(url)
13
- requester = Requester.new(@active_models)
14
+ requester = Requester.new(@active_public_models)
14
15
  response = requester.get(url)
15
16
  @last_response = Enrich.new(response).run
16
17
  return @last_response
17
18
  end
18
19
 
19
20
  def set_models(model_array)
20
- @active_models = model_array
21
+ if model_array.class != Array
22
+ raise "Clarification expects an array of models."
23
+ end
24
+
25
+ @active_public_models = model_array
21
26
  end
22
27
 
23
28
  end
@@ -0,0 +1,4 @@
1
+ module Clarification
2
+ class Concept < Struct.new(:name, :value)
3
+ end
4
+ end
@@ -1,10 +1,10 @@
1
1
  module Clarification
2
2
  class Configuration
3
- attr_accessor :api_key, :default_models
3
+ attr_accessor :api_key, :default_public_models
4
4
 
5
5
  def initialize
6
6
  @api_key = 'YOUR_API_KEY'
7
- @default_models = []
7
+ @default_public_models = []
8
8
  end
9
9
 
10
10
  end
@@ -1,5 +1,6 @@
1
1
  module Clarification
2
- BASE_URL = 'https://api.clarifai.com/v2/models/'.freeze
2
+ BASE_URL = 'https://api.clarifai.com/v2/'.freeze
3
+
3
4
 
4
5
  PUBLIC_MODELS = {
5
6
  apparel: 'e0be3b9d6a454f0493ac3a30784001ff',
@@ -12,7 +12,6 @@ module Clarification
12
12
  @response_array.each do |model_name, raw_response |
13
13
  @results[model_name] = Objectifier.new(raw_response.body)
14
14
  end
15
-
16
15
  return @results
17
16
  end
18
17
 
@@ -0,0 +1,4 @@
1
+ module Clarification
2
+ class Hit < Struct.new(:score, :url, :created_at)
3
+ end
4
+ end
@@ -1,21 +1,67 @@
1
1
  module Clarification
2
2
  class Objectifier
3
3
 
4
- attr_reader :response_json, :status, :concepts
4
+ attr_reader :response_json, :status, :concepts, :error
5
5
 
6
6
  def initialize(response)
7
7
  @response_json = JSON.parse(response)
8
8
  @status = OpenStruct.new(@response_json["status"])
9
9
  @concepts = []
10
-
10
+ @error = nil
11
+
11
12
  build_concept_objects
13
+
12
14
  end
13
15
 
14
16
  def build_concept_objects
15
- @response_json["outputs"][0]["data"]["concepts"].each do |concept|
16
- @concepts << OpenStruct.new(concept)
17
+
18
+ begin
19
+ concepts = @response_json.fetch("outputs").fetch(0).fetch("data", nil).fetch("concepts", nil)
20
+ rescue StandardError => e
21
+ @error = e
22
+ return
23
+ end
24
+
25
+ if concepts
26
+ concepts.each do |concept_hash|
27
+
28
+ name = concept_hash["name"]
29
+ value = concept_hash["value"]
30
+
31
+ concept = Concept.new(name, value)
32
+
33
+ @concepts << concept
34
+ end
35
+ else
36
+ data = @response_json.fetch("outputs").fetch(0).fetch("data", nil)
37
+ unless data.empty?
38
+ case @response_json['outputs'][0]['model']['name']
39
+ when "focus"
40
+ # concept.class = concept::focus
41
+ focus = OpenStruct.new(data["focus"])
42
+ regions = []
43
+ data["regions"].each do |region|
44
+ regions << OpenStruct.new(region)
45
+ end
46
+ focus_hash = {focus: focus, regions: regions}
47
+ @concepts << OpenStruct.new(focus_hash)
48
+ @concepts.first.name = "focus"
49
+ when "color"
50
+ # concept.class = concept::color
51
+ data["colors"].each do |color|
52
+ @concepts << OpenStruct.new(color)
53
+ end
54
+ when "general_embedding"
55
+ # concept.class = concept::embedding
56
+ @concepts << OpenStruct.new(data["embeddings"][0])
57
+ @concepts.first.name = "embedding"
58
+ end
59
+ end
17
60
  end
61
+
18
62
  end
19
63
 
20
64
  end
21
65
  end
66
+
67
+
@@ -13,6 +13,8 @@ module Clarification
13
13
  return response
14
14
  end
15
15
 
16
+ private
17
+
16
18
  def get_with_model(target_url, model)
17
19
  uri = uri_builder(model)
18
20
  body = body_builder(target_url)
@@ -44,14 +46,14 @@ module Clarification
44
46
 
45
47
  def uri_builder(model)
46
48
  model_key = Clarification::PUBLIC_MODELS[model]
47
- url = "#{Clarification::BASE_URL}#{model_key}/outputs"
49
+ url = "#{Clarification::BASE_URL}models/#{model_key}/outputs"
48
50
  uri = URI.parse(url)
49
51
  end
50
52
 
51
53
  def request_builder(uri, body)
52
54
  request = Net::HTTP::Post.new(uri)
53
55
  request.content_type = "application/json"
54
- request["Authorization"] = "Key f7cc628178994e16b2470ae739ef927a"
56
+ request["Authorization"] = "Key #{Clarification.configuration.api_key}"
55
57
  request.body = JSON.dump(body)
56
58
 
57
59
  return request
@@ -61,10 +63,20 @@ module Clarification
61
63
  request_options = {
62
64
  use_ssl: uri.scheme == "https",
63
65
  }
64
-
65
66
  return request_options
66
67
  end
67
68
 
69
+ def get_response(uri, body)
70
+ request = request_builder(uri, body)
71
+ options = options_builder(uri)
72
+
73
+ response = Net::HTTP.start(uri.hostname, uri.port, options) do |http|
74
+ http.request(request)
75
+ end
76
+
77
+ return response
78
+
79
+ end
68
80
 
69
81
 
70
82
  end
@@ -0,0 +1,70 @@
1
+ module Clarification
2
+ class SearchRequester < Clarification::Requester
3
+
4
+ def initialize
5
+ end
6
+
7
+ def index(target_urls)
8
+ uri = uri_builder("inputs")
9
+ body = body_builder(target_urls)
10
+ response = get_response(uri, body)
11
+ return response
12
+ end
13
+
14
+ def get_results_for_concept(concept)
15
+ uri = uri_builder("searches")
16
+ body = concept_body_builder(concept)
17
+ response = get_response(uri, body)
18
+ return response
19
+ end
20
+
21
+ private
22
+
23
+ def body_builder(target_urls)
24
+ # build individual inputs
25
+ inputs = []
26
+ target_urls.each do |url|
27
+ inputs << {
28
+ data: {
29
+ image: {
30
+ url: url
31
+ }
32
+ }
33
+ }
34
+ end
35
+
36
+ # wrap for api
37
+ {
38
+ inputs: inputs
39
+ }
40
+ end
41
+
42
+ def concept_body_builder(concept)
43
+ {
44
+ query: {
45
+ ands: [
46
+ {
47
+ output: {
48
+ data: {
49
+ concepts: [
50
+ {
51
+ name: "#{concept}"
52
+ }
53
+ ]
54
+ }
55
+ }
56
+ }
57
+ ]
58
+ }
59
+ }
60
+
61
+ end
62
+
63
+
64
+ def uri_builder(path)
65
+ url = "#{Clarification::BASE_URL}#{path}"
66
+ URI.parse(url)
67
+ end
68
+
69
+ end
70
+ end
@@ -0,0 +1,4 @@
1
+ module Clarification
2
+ class Response
3
+ end
4
+ end
@@ -0,0 +1,27 @@
1
+ module Clarification
2
+
3
+ class SearchResponse < Response
4
+
5
+ attr_reader :hits, :raw_response, :concept
6
+
7
+ def initialize(raw_response, concept)
8
+ @hits = []
9
+ @concept = concept
10
+ @raw_response = JSON.parse(raw_response)
11
+
12
+ parse_raw_response
13
+ end
14
+
15
+ private
16
+ def parse_raw_response
17
+ @raw_response['hits'].each do |hit|
18
+ score = hit["score"]
19
+ url = hit["input"]["data"]["image"]["url"]
20
+ created_at = hit["input"]["created_at"]
21
+ @hits << Hit.new(score, url, created_at)
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+
@@ -0,0 +1,29 @@
1
+ module Clarification
2
+ class Search
3
+
4
+ attr_reader :last_search
5
+
6
+ def initialize
7
+ @last_search = nil
8
+ end
9
+
10
+ def index_images(image_array)
11
+ search_requester = Clarification::SearchRequester.new
12
+ search_requester.index(image_array)
13
+ # need some useful response here
14
+ end
15
+
16
+ def by_images(_image_array)
17
+ raise "Not Implemented"
18
+ end
19
+
20
+ def by_concept(concept)
21
+ search_requester = Clarification::SearchRequester.new
22
+ raw_response = search_requester.get_results_for_concept(concept)
23
+ structured_response = Clarification::SearchResponse.new(raw_response.body, concept)
24
+ @last_search = structured_response
25
+ return structured_response
26
+ end
27
+
28
+ end
29
+ end
@@ -1,3 +1,3 @@
1
1
  module Clarification
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: clarification
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nichol Alexander
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-09-20 00:00:00.000000000 Z
11
+ date: 2017-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -136,8 +136,7 @@ dependencies:
136
136
  - - ">="
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0'
139
- description: Alpha Version - configure, access Clarifai set end points, interrogate
140
- responses.
139
+ description: Alpha Version - Predict and Search using Clarifai.
141
140
  email:
142
141
  - nichol.alexander@gmail.com
143
142
  executables: []
@@ -158,11 +157,17 @@ files:
158
157
  - clarification.gemspec
159
158
  - lib/clarification.rb
160
159
  - lib/clarification/client.rb
160
+ - lib/clarification/concept.rb
161
161
  - lib/clarification/configuration.rb
162
162
  - lib/clarification/end_points.rb
163
163
  - lib/clarification/enrich.rb
164
+ - lib/clarification/hit.rb
164
165
  - lib/clarification/objectifier.rb
165
166
  - lib/clarification/requester.rb
167
+ - lib/clarification/requester/search_requester.rb
168
+ - lib/clarification/response.rb
169
+ - lib/clarification/response/search_response.rb
170
+ - lib/clarification/search.rb
166
171
  - lib/clarification/version.rb
167
172
  homepage: https://github.com/nicholalexander/clarification
168
173
  licenses: