deepstack 1.0.0 → 1.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.
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