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 +4 -4
- data/lib/deep_stack/custom_model.rb +24 -0
- data/lib/deep_stack/deep_stack.rb +120 -0
- data/lib/{deepstack → deep_stack}/detection.rb +2 -3
- data/lib/{deepstack → deep_stack}/face.rb +5 -9
- data/lib/{deepstack → deep_stack}/scene.rb +1 -1
- data/lib/{deepstack → deep_stack}/version.rb +1 -1
- data/lib/deepstack.rb +1 -107
- metadata +14 -12
- data/lib/deepstack/custom.rb +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be874725c205c1b4c704649e99506f362fc711eeff315d9616deb112f7caecd5
|
4
|
+
data.tar.gz: f3a54bde1b2d8a533e9af9c58154a9004c7133f3e383d8e612a91d6052187fe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
data/lib/deepstack.rb
CHANGED
@@ -1,109 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
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.
|
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-
|
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.
|
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: []
|
data/lib/deepstack/custom.rb
DELETED
@@ -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
|