deepstack 1.0.0 → 1.3.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
  SHA256:
3
- metadata.gz: dc0409e409422c065962aa065244f20658c764c904462935936b05c1950e771a
4
- data.tar.gz: 074e0f2e9f309f72aaa402ef5e1b3bd469da172bbb60679a2a59b218f4a2a7ac
3
+ metadata.gz: be874725c205c1b4c704649e99506f362fc711eeff315d9616deb112f7caecd5
4
+ data.tar.gz: f3a54bde1b2d8a533e9af9c58154a9004c7133f3e383d8e612a91d6052187fe1
5
5
  SHA512:
6
- metadata.gz: '0923fd828175f78a7bc90a40623c6ed7ec8ce8681876ab340697309c9557bdbf2551f10567b4df4dd861c2121e2cd37c62b47f06803e08f8cc0b1a5739e68d89'
7
- data.tar.gz: b9be9f1439d69f939f2dbf66842264a750e47d8cba455ff528b7879f115f3510e45690f8ff7a804e1b23428a5313c44c147049b3bcc269db31b4887f7c03b34e
6
+ metadata.gz: 9c7d7c9f0abe1465489936961e9cc297c1e9961b2e6afe6b2612fea975b5b0cc0c24a95748fd6fe14e5b45e480d15a27237ad5e5b01d4f84a8617258a659fcc1
7
+ data.tar.gz: d4d9f966dd440a3cd3031c3ba64b029d0f94b427945d64b77cd23aea3f1a900de2fea85a2f24ddd49fca8c1c821c7e3c6b7c63a9e120fc38e481ed1452ec388b
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeepStack
4
+ # Custom Model
5
+ module CustomModel
6
+ #
7
+ # Return predictions using a custom model
8
+ #
9
+ # @param [String] model custom model name
10
+ # @param [Object] image binary data or a File object
11
+ # @param [Hash] options additional fields for DeepStack, e.g. min_confidence: 0.5
12
+ #
13
+ # @return [Array] if successful, an array of DeepStack predictions
14
+ #
15
+ # @return [nil] if error
16
+ #
17
+ def custom_model(model, image, **options)
18
+ target = "vision/custom/#{model}"
19
+ api_post(target, image, **options)&.dig('predictions')
20
+ end
21
+ # @deprecated Use {custom_model} instead
22
+ alias custom_inference custom_model
23
+ end
24
+ end
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require_relative 'face'
6
+ require_relative 'detection'
7
+ require_relative 'scene'
8
+ require_relative 'custom_model'
9
+ require_relative 'version'
10
+
11
+ # DeepStack API
12
+ class DeepStack
13
+ include DeepStack::Face
14
+ include DeepStack::Detection
15
+ include DeepStack::Scene
16
+ include DeepStack::CustomModel
17
+
18
+ #
19
+ # Create a deepstack object connected to the given URL
20
+ #
21
+ # @param [String] base_url the url to DeepStack's server:port
22
+ # @param [String] api_key an optional API-KEY to use when connecting to DeepStack
23
+ # @param [String] admin_key an optional ADMIN-KEY to use when connecting to DeepStack
24
+ #
25
+ # @example
26
+ # DeepStack.new('http://127.0.0.1:5000')
27
+ #
28
+ def initialize(base_url, api_key: nil, admin_key: nil)
29
+ @base_url = base_url
30
+ @auth = { api_key: api_key, admin_key: admin_key }.select { |_k, v| v } # remove nil values
31
+ @http_mutex = Mutex.new
32
+ end
33
+
34
+ #
35
+ # Make a POST request to DeepStack path target
36
+ #
37
+ # @param [String] path to the DeepStack API URL
38
+ # @param [Array] images zero or more images to post
39
+ # @param [Hash] args additional named fields to post
40
+ #
41
+ # @return [Hash] if successful, the json data returned by DeepStack, nil otherwise
42
+ #
43
+ def api_post(path, *images, **args)
44
+ uri = build_uri(path)
45
+ args = @auth.merge(args)
46
+
47
+ result = nil
48
+ 10.times do
49
+ result = images ? post_files(uri, images.flatten, **args) : post(uri, args)
50
+ break unless result.is_a?(Net::HTTPRedirection)
51
+
52
+ uri.path = result['location']
53
+ end
54
+ raise Net::HTTPClientException, 'Too many redirections' if result.is_a?(Net::HTTPRedirection)
55
+
56
+ process_result(result)
57
+ end
58
+
59
+ #
60
+ # Close the HTTP connection to DeepStack server
61
+ #
62
+ def close
63
+ @http_mutex.synchronize do
64
+ @http.finish if @http&.started?
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def build_uri(path)
71
+ URI.join(@base_url, '/v1/', path)
72
+ end
73
+
74
+ def post(uri, **args)
75
+ Net::HTTP.post_form(uri, args)
76
+ end
77
+
78
+ def post_files(uri, *images, **args)
79
+ form_data = combine_images_and_args(images.flatten, **args)
80
+ req = Net::HTTP::Post.new(uri)
81
+ req.set_form(form_data, 'multipart/form-data')
82
+ @http_mutex.synchronize do
83
+ @http ||= Net::HTTP.start(uri.hostname, uri.port)
84
+ @http.start unless @http.started?
85
+ @http.request(req)
86
+ end
87
+ end
88
+
89
+ def combine_images_and_args(*images, **args)
90
+ stringify_keys(args).concat(image_form_data(images.flatten))
91
+ end
92
+
93
+ def stringify_keys(hash)
94
+ hash.map { |k, v| [k.to_s, v] }
95
+ end
96
+
97
+ #
98
+ # Return an array of image entries for form data.
99
+ # The field name is 'image' for a single image
100
+ # For multiple images, the field names will be 'image1', 'image2', ...
101
+ #
102
+ # @param [Array<Object>] images an array of raw image data or a File object
103
+ #
104
+ # @return [Array] the image entries for set_form
105
+ #
106
+ def image_form_data(*images)
107
+ images = images.flatten
108
+ return [image_entry('image', images.first)] if images.length == 1
109
+
110
+ images.map.with_index(1) { |image, i| image_entry("image#{i}", image) }
111
+ end
112
+
113
+ def image_entry(name, image)
114
+ [name, image].tap { |result| result << { filename: "#{name}.jpg" } unless image.instance_of? File }
115
+ end
116
+
117
+ def process_result(result)
118
+ result.is_a?(Net::HTTPSuccess) ? JSON.parse(result.body) : nil
119
+ end
120
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DeepStackModule
3
+ class DeepStack
4
4
  # APIs related to object detection
5
5
  module Detection
6
6
  #
@@ -13,8 +13,7 @@ module DeepStackModule
13
13
  #
14
14
  def detect_objects(image, **options)
15
15
  target = 'vision/detection'
16
- api_post(target, image, **options)
17
- predictions
16
+ api_post(target, image, **options)&.dig('predictions')
18
17
  end
19
18
  end
20
19
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DeepStackModule
3
+ class DeepStack
4
4
  # APIs related to face recognition
5
5
  module Face
6
6
  #
@@ -15,8 +15,7 @@ module DeepStackModule
15
15
  #
16
16
  def recognize_faces(image, **options)
17
17
  target = 'vision/face/recognize'
18
- api_post(target, image, **options)
19
- predictions
18
+ api_post(target, image, **options)&.dig('predictions')
20
19
  end
21
20
 
22
21
  #
@@ -29,8 +28,7 @@ module DeepStackModule
29
28
  #
30
29
  def detect_faces(image, **options)
31
30
  target = 'vision/face/' # the URL ends with a slash
32
- api_post(target, image, **options)
33
- predictions
31
+ api_post(target, image, **options)&.dig('predictions')
34
32
  end
35
33
 
36
34
  #
@@ -64,8 +62,7 @@ module DeepStackModule
64
62
  #
65
63
  def delete_face(userid)
66
64
  target = 'vision/face/delete'
67
- api_post(target, userid: userid)
68
- success?
65
+ api_post(target, userid: userid)&.dig('success') == true
69
66
  end
70
67
 
71
68
  #
@@ -78,8 +75,7 @@ module DeepStackModule
78
75
  #
79
76
  def register_face(userid, *images)
80
77
  target = 'vision/face/register'
81
- api_post(target, images, userid: userid)
82
- success?
78
+ api_post(target, images, userid: userid)&.dig('success') == true
83
79
  end
84
80
  end
85
81
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module DeepStackModule
3
+ class DeepStack
4
4
  # Scene Recognition
5
5
  module Scene
6
6
  #
@@ -5,5 +5,5 @@
5
5
  #
6
6
  class DeepStack
7
7
  # @return [String] Version of DeepStack helper libraries
8
- VERSION = '1.0.0'
8
+ VERSION = '1.3.0'
9
9
  end
data/lib/deepstack.rb CHANGED
@@ -1,109 +1,3 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pp'
4
- require 'net/http'
5
- require 'json'
6
- require_relative 'deepstack/face'
7
- require_relative 'deepstack/detection'
8
- require_relative 'deepstack/scene'
9
- require_relative 'deepstack/custom'
10
- require_relative 'deepstack/version'
11
-
12
- # DeepStack API
13
- class DeepStack
14
- include DeepStackModule::Face
15
- include DeepStackModule::Detection
16
- include DeepStackModule::Scene
17
- include DeepStackModule::Custom
18
-
19
- attr_reader :duration, :predictions, :success
20
-
21
- # Create a deepstack object connected to the given URL
22
- def initialize(base_url)
23
- @base_url = base_url
24
- end
25
-
26
- #
27
- # The result of the last call
28
- #
29
- # @return [Boolean] true if the last call was successful
30
- #
31
- def success?
32
- @success == true
33
- end
34
-
35
- #
36
- # Make a POST request to DeepStack path target
37
- #
38
- # @param [String] path to the DeepStack API URL
39
- # @param [Array] images zero or more images to post
40
- # @param [Hash] args additional named fields to post
41
- #
42
- # @return [Hash] if successful, the json data returned by DeepStack, nil otherwise
43
- #
44
- def api_post(path, *images, **args)
45
- uri = build_uri(path)
46
-
47
- result = nil
48
- 10.times do
49
- result = images ? post_files(uri, images.flatten, **args) : post(uri, args)
50
- break unless result.is_a?(Net::HTTPRedirection)
51
-
52
- uri.path = result['location']
53
- end
54
- raise Net::HTTPClientException, 'Too many redirections' if result.is_a?(Net::HTTPRedirection)
55
-
56
- process_result(result)
57
- end
58
-
59
- private
60
-
61
- def build_uri(path)
62
- URI.join(@base_url, '/v1/', path)
63
- end
64
-
65
- def post(uri, **args)
66
- Net::HTTP.post_form(uri, args)
67
- end
68
-
69
- def post_files(uri, *images, **args)
70
- form_data = combine_images_and_args(images.flatten, **args)
71
- req = Net::HTTP::Post.new(uri)
72
- req.set_form(form_data, 'multipart/form-data')
73
- Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }
74
- end
75
-
76
- def combine_images_and_args(*images, **args)
77
- stringify_keys(args).concat(image_form_data(images.flatten))
78
- end
79
-
80
- def stringify_keys(hash)
81
- hash.map { |k, v| [k.to_s, v] }
82
- end
83
-
84
- #
85
- # Return an array of image entries for form data.
86
- # The field name is 'image' for a single image
87
- # For multiple images, the field names will be 'image1', 'image2', ...
88
- #
89
- # @param [Array<Object>] images an array of raw image data or a File object
90
- #
91
- # @return [Array] the image entries for set_form
92
- #
93
- def image_form_data(*images)
94
- images = images.flatten
95
- return [image_entry('image', images.first)] if images.length == 1
96
-
97
- images.map.with_index(1) { |image, i| image_entry("image#{i}", image) }
98
- end
99
-
100
- def image_entry(name, image)
101
- [name, image].tap { |result| result << { filename: "#{name}.jpg" } unless image.instance_of? File }
102
- end
103
-
104
- def process_result(result)
105
- @result = result.is_a?(Net::HTTPSuccess) ? JSON.parse(result.body) : nil
106
- %w[success duration predictions].each { |attrib| instance_variable_set("@#{attrib}", @result&.dig(attrib)) }
107
- @result
108
- end
109
- end
3
+ require_relative 'deep_stack/deep_stack'
metadata CHANGED
@@ -1,36 +1,38 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deepstack
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jimmy Tanagra
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-09 00:00:00.000000000 Z
11
+ date: 2022-05-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description:
14
14
  email:
15
15
  - jcode@tanagra.id.au
16
16
  executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - lib/deep_stack/custom_model.rb
21
+ - lib/deep_stack/deep_stack.rb
22
+ - lib/deep_stack/detection.rb
23
+ - lib/deep_stack/face.rb
24
+ - lib/deep_stack/scene.rb
25
+ - lib/deep_stack/version.rb
20
26
  - lib/deepstack.rb
21
- - lib/deepstack/custom.rb
22
- - lib/deepstack/detection.rb
23
- - lib/deepstack/face.rb
24
- - lib/deepstack/scene.rb
25
- - lib/deepstack/version.rb
26
27
  homepage: https://github.com/jimtng/deepstack-ruby
27
28
  licenses:
28
29
  - EPL-2.0
29
30
  metadata:
30
31
  homepage_uri: https://github.com/jimtng/deepstack-ruby
31
32
  source_code_uri: https://github.com/jimtng/deepstack-ruby
33
+ documentation_uri: https://rubydoc.info/gems/deepstack
32
34
  changelog_uri: https://github.com/jimtng/deepstack-ruby/blob/main/CHANGELOG.md
33
- post_install_message:
35
+ post_install_message:
34
36
  rdoc_options: []
35
37
  require_paths:
36
38
  - lib
@@ -45,8 +47,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
47
  - !ruby/object:Gem::Version
46
48
  version: '0'
47
49
  requirements: []
48
- rubygems_version: 3.3.7
49
- signing_key:
50
+ rubygems_version: 3.0.3.1
51
+ signing_key:
50
52
  specification_version: 4
51
53
  summary: A Ruby wrapper for DeepStack API
52
54
  test_files: []
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepStackModule
4
- # Custom Model
5
- module Custom
6
- #
7
- # Register a custom inference method for convenience.
8
- # The custom method is called "identify_<modelname>"
9
- # Example:
10
- # deepstack.register_custom_model('license_plate')
11
- # deepstack.identify_license_plate(image)
12
- #
13
- # @param [Array] models a list of one or more custom model names to register
14
- #
15
- # @return [<Type>] <description>
16
- #
17
- def self.register_model(*models)
18
- models.flatten.each do |model|
19
- method_name = 'identify_'.concat model.gsub(/-+/, '_') # convert - to _
20
- define_method(method_name) do |image, **options|
21
- custom_inference(model, image, options)
22
- end
23
- end
24
- end
25
-
26
- #
27
- # Return predictions using a custom model
28
- #
29
- # @param [String] model custom model name
30
- # @param [Object] image binary data or a File object
31
- # @param [Hash] options additional fields for DeepStack, e.g. min_confidence: 0.5
32
- #
33
- # @return [Array] if successful, an array of DeepStack predictions
34
- #
35
- # @return [nil] if error
36
- #
37
- def custom_inference(model, image, **options)
38
- target = "vision/custom/#{model}"
39
- api_post(target, image, options)
40
- predictions
41
- end
42
- end
43
- end