deepstack 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/deepstack/custom.rb +43 -0
- data/lib/deepstack/detection.rb +20 -0
- data/lib/deepstack/face.rb +85 -0
- data/lib/deepstack/scene.rb +20 -0
- data/lib/deepstack/version.rb +9 -0
- data/lib/deepstack.rb +111 -0
- metadata +52 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6bd245c6bb369aed6c65f88277a186cd283d396b5eeafa28bc10e854dfbd027c
|
4
|
+
data.tar.gz: b428c594d92837534124004c0ff0ef4f0b0c2aad0a2cf40ef4faa69f38d904d3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f4d4c306447142dd071a6a4a28ce8f36f2170cf23160401e7cbf1786d9b8124e9c72e1a144c13096b62ad03a361fe4684f0aa8d9fab4aee0b921017ed0fff8c
|
7
|
+
data.tar.gz: 2a7c706fbfe815f7faf5ce06294f1eff65c2c9aeec3cdddb865e97348475cf1ee3478b9152444e4a92ae3697eeae3ad4e97a1fd47edff3247578eae52b340953
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepstack
|
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
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepstack
|
4
|
+
# APIs related to object detection
|
5
|
+
module Detection
|
6
|
+
#
|
7
|
+
# Perform object detection
|
8
|
+
#
|
9
|
+
# @param [Object] image raw image data or a File object of an image file
|
10
|
+
# @param [Hash] options additional fields for Deepstack, e.g. min_confidence: 0.5
|
11
|
+
#
|
12
|
+
# @return [Array] a list of predictions, or nil on error
|
13
|
+
#
|
14
|
+
def detect_objects(image, **options)
|
15
|
+
target = 'vision/detection'
|
16
|
+
api_post(target, image, **options)
|
17
|
+
predictions
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepstack
|
4
|
+
# APIs related to face recognition
|
5
|
+
module Face
|
6
|
+
#
|
7
|
+
# Perform face recognition
|
8
|
+
#
|
9
|
+
# @param [Object] image binary data or a File object
|
10
|
+
# @param [Hash] options additional fields for Deepstack, e.g. min_confidence: 0.5
|
11
|
+
#
|
12
|
+
# @return [Array] if successful, an array of Deepstack predictions
|
13
|
+
#
|
14
|
+
# @return [nil] if error
|
15
|
+
#
|
16
|
+
def recognize_faces(image, **options)
|
17
|
+
target = 'vision/face/recognize'
|
18
|
+
api_post(target, image, **options)
|
19
|
+
predictions
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Detect faces in an image
|
24
|
+
#
|
25
|
+
# @param [Object] image binary data or a File object
|
26
|
+
# @param [Hash] options additional fields for Deepstack, e.g. min_confidence: 0.5
|
27
|
+
#
|
28
|
+
# @return [Array] if successful, an array of Deepstack predictions
|
29
|
+
#
|
30
|
+
def detect_faces(image, **options)
|
31
|
+
target = 'vision/face/' # the URL ends with a slash
|
32
|
+
api_post(target, image, **options)
|
33
|
+
predictions
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Get a list of registered faces
|
38
|
+
#
|
39
|
+
# @return [Array] a list of userids
|
40
|
+
#
|
41
|
+
def face_list
|
42
|
+
target = 'vision/face/list'
|
43
|
+
api_post(target)&.dig('faces')
|
44
|
+
# {"success"=>true, "faces"=>["face_1", "face_2"], "duration"=>0}
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Delete the given list of faces / userids
|
49
|
+
#
|
50
|
+
# @param [Array] userids a list of userids to delete
|
51
|
+
#
|
52
|
+
# @return [Hash] A hash of `userid => boolean` that indicates success/failure
|
53
|
+
#
|
54
|
+
def delete_faces(*userids)
|
55
|
+
userids.flatten.compact.to_h { |userid| [userid, delete_face(userid)] }
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Delete the given face / userid
|
60
|
+
#
|
61
|
+
# @param [String] userid to delete
|
62
|
+
#
|
63
|
+
# @return [Boolean] true when successful
|
64
|
+
#
|
65
|
+
def delete_face(userid)
|
66
|
+
target = 'vision/face/delete'
|
67
|
+
api_post(target, userid: userid)
|
68
|
+
success?
|
69
|
+
end
|
70
|
+
|
71
|
+
#
|
72
|
+
# Register a face
|
73
|
+
#
|
74
|
+
# @param [String] userid to register
|
75
|
+
# @param [Array] images facial image data in binary form or File object
|
76
|
+
#
|
77
|
+
# @return [Boolean] true when successful
|
78
|
+
#
|
79
|
+
def register_face(userid, *images)
|
80
|
+
target = 'vision/face/register'
|
81
|
+
api_post(target, images, userid: userid)
|
82
|
+
success?
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepstack
|
4
|
+
# Scene Recognition
|
5
|
+
module Scene
|
6
|
+
#
|
7
|
+
# Return
|
8
|
+
#
|
9
|
+
# @param [Object] image binary data or a File object
|
10
|
+
# @param [Hash] options additional fields for Deepstack, e.g. min_confidence: 0.5
|
11
|
+
#
|
12
|
+
# @return [Hash] if successful, Deepstack result hash {'label' => 'scene', 'confidence' => 2.2}
|
13
|
+
#
|
14
|
+
# @return [nil] if error
|
15
|
+
def identify_scene(image, **options)
|
16
|
+
target = 'vision/scene'
|
17
|
+
api_post(target, image, **options)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/deepstack.rb
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
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
|
+
module Deepstack
|
13
|
+
# Deepstack API
|
14
|
+
class API
|
15
|
+
include Deepstack::Face
|
16
|
+
include Deepstack::Detection
|
17
|
+
include Deepstack::Scene
|
18
|
+
include Deepstack::Custom
|
19
|
+
|
20
|
+
attr_reader :duration, :predictions, :success
|
21
|
+
|
22
|
+
# Create a deepstack object connected to the given URL
|
23
|
+
def initialize(base_url)
|
24
|
+
@base_url = base_url
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# The result of the last call
|
29
|
+
#
|
30
|
+
# @return [Boolean] true if the last call was successful
|
31
|
+
#
|
32
|
+
def success?
|
33
|
+
@success == true
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Make a POST request to Deepstack path target
|
38
|
+
#
|
39
|
+
# @param [String] path to the Deepstack API URL
|
40
|
+
# @param [Array] images zero or more images to post
|
41
|
+
# @param [Hash] args additional named fields to post
|
42
|
+
#
|
43
|
+
# @return [Hash] if successful, the json data returned by Deepstack, nil otherwise
|
44
|
+
#
|
45
|
+
def api_post(path, *images, **args)
|
46
|
+
uri = build_uri(path)
|
47
|
+
|
48
|
+
result = nil
|
49
|
+
10.times do
|
50
|
+
result = images ? post_files(uri, images.flatten, **args) : post(uri, args)
|
51
|
+
break unless result.is_a?(Net::HTTPRedirection)
|
52
|
+
|
53
|
+
uri.path = result['location']
|
54
|
+
end
|
55
|
+
raise Net::HTTPClientException, 'Too many redirections' if result.is_a?(Net::HTTPRedirection)
|
56
|
+
|
57
|
+
process_result(result)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def build_uri(path)
|
63
|
+
URI.join(@base_url, '/v1/', path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def post(uri, **args)
|
67
|
+
Net::HTTP.post_form(uri, args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def post_files(uri, *images, **args)
|
71
|
+
form_data = combine_images_and_args(images.flatten, **args)
|
72
|
+
req = Net::HTTP::Post.new(uri)
|
73
|
+
req.set_form(form_data, 'multipart/form-data')
|
74
|
+
Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def combine_images_and_args(*images, **args)
|
78
|
+
stringify_keys(args).concat(image_form_data(images.flatten))
|
79
|
+
end
|
80
|
+
|
81
|
+
def stringify_keys(hash)
|
82
|
+
hash.map { |k, v| [k.to_s, v] }
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Return an array of image entries for form data.
|
87
|
+
# The field name is 'image' for a single image
|
88
|
+
# For multiple images, the field names will be 'image1', 'image2', ...
|
89
|
+
#
|
90
|
+
# @param [Array<Object>] images an array of raw image data or a File object
|
91
|
+
#
|
92
|
+
# @return [Array] the image entries for set_form
|
93
|
+
#
|
94
|
+
def image_form_data(*images)
|
95
|
+
images = images.flatten
|
96
|
+
return [image_entry('image', images.first)] if images.length == 1
|
97
|
+
|
98
|
+
images.map.with_index(1) { |image, i| image_entry("image#{i}", image) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def image_entry(name, image)
|
102
|
+
[name, image].tap { |result| result << { filename: "#{name}.jpg" } unless image.instance_of? File }
|
103
|
+
end
|
104
|
+
|
105
|
+
def process_result(result)
|
106
|
+
@result = result.is_a?(Net::HTTPSuccess) ? JSON.parse(result.body) : nil
|
107
|
+
%w[success duration predictions].each { |attrib| instance_variable_set("@#{attrib}", @result&.dig(attrib)) }
|
108
|
+
@result
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deepstack
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jimmy Tanagra
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email:
|
15
|
+
- jcode@tanagra.id.au
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- 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
|
+
homepage: https://github.com/jimtng/deepstack-ruby
|
27
|
+
licenses:
|
28
|
+
- EPL-2.0
|
29
|
+
metadata:
|
30
|
+
homepage_uri: https://github.com/jimtng/deepstack-ruby
|
31
|
+
source_code_uri: https://github.com/jimtng/deepstack-ruby
|
32
|
+
changelog_uri: https://github.com/jimtng/deepstack-ruby/blob/main/CHANGELOG.md
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 2.6.0
|
42
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
requirements: []
|
48
|
+
rubygems_version: 3.3.7
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: A Ruby wrapper for Deepstack API
|
52
|
+
test_files: []
|