@eyepop.ai/eyepop 0.9.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.
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/package.json +53 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 EyePop.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# EyePop.ai Node SDK
|
|
2
|
+
The EyePop.ai Node SDK provides convenient access to the EyePop.ai's inference API from applications written in the
|
|
3
|
+
TypeScript or JavaScript language.
|
|
4
|
+
## Installation
|
|
5
|
+
```shell
|
|
6
|
+
npm install --save eyepop
|
|
7
|
+
```
|
|
8
|
+
## Configuration
|
|
9
|
+
The EyePop SDK needs to be configured with the __Pop Id__ and your __Secret Api Key__.
|
|
10
|
+
```typescript
|
|
11
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
12
|
+
(async() => {
|
|
13
|
+
const endpoint = EyePopSdk.endpoint(
|
|
14
|
+
// This is the default and can be omitted
|
|
15
|
+
pop_id=process.env['EYEPOP_POP_ID'],
|
|
16
|
+
// This is the default and can be omitted
|
|
17
|
+
secret_key=process.env['EYEPOP_SECRET_KEY'],
|
|
18
|
+
)
|
|
19
|
+
await endpoint.connect()
|
|
20
|
+
// do work ....
|
|
21
|
+
await endpoint.disconnect()
|
|
22
|
+
})
|
|
23
|
+
```
|
|
24
|
+
While you can provide a secret_key keyword argument, we recommend using dotenv to add EYEPOP_SECRET_KEY="My API Key"
|
|
25
|
+
to your .env file so that your API Key is not stored in source control. By default, the SDK will read the following environment variables:
|
|
26
|
+
* `EYEPOP_POP_ID`: The Pop Id to use as an endpoint. You can copy and paste this string from your EyePop Dashboard in the Pop -> Settings section.
|
|
27
|
+
* `EYEPOP_SECRET_KEY`: Your Secret Api Key. You can create Api Keys in the profile section of your EyePop dashboard.
|
|
28
|
+
* `EYEPOP_URL`: (Optional) URL of the EyePop API service, if you want to use any other endpoint than production `http://api.eyepop.ai`
|
|
29
|
+
## Usage Examples
|
|
30
|
+
### Uploading and processing one single image
|
|
31
|
+
```typescript
|
|
32
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
33
|
+
|
|
34
|
+
const example_image_path = 'examples/example.jpg';
|
|
35
|
+
|
|
36
|
+
(async() => {
|
|
37
|
+
const endpoint = await EyePopSdk.endpoint().connect()
|
|
38
|
+
try {
|
|
39
|
+
let results = await endpoint.upload({filePath: example_image_path})
|
|
40
|
+
for await (let result of results) {
|
|
41
|
+
console.log(result)
|
|
42
|
+
}
|
|
43
|
+
} finally {
|
|
44
|
+
await endpoint.disconnect()
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
1. `EyePopSdk.endpoint()` returns a local endpoint object, that will authenticate with the Api Key found in
|
|
49
|
+
EYEPOP_SECRET_KEY and load the worker configuration for the Pop identified by EYEPOP_POP_ID.
|
|
50
|
+
2. Call `endpoint.connect()` before any job is submitted and `endpoint.disconnect()` to release all resources.
|
|
51
|
+
3. `endpoint.upload({filePath:'examples/example.jpg'})` initiates the upload to the local file to the worker service.
|
|
52
|
+
The image will be queued and processed immediately when the worker becomes available.
|
|
53
|
+
The result of endpoint.upload() implements `AsyncIterable<Prediction>` which can be iterated fir 'for await' as
|
|
54
|
+
shown in the example above. Predictions will become available when the submitted file becomes processed by the worker
|
|
55
|
+
and results are efficiently streamed back to the calling client. If the uploaded file is a video
|
|
56
|
+
e.g. 'video/mp4' or image container format e.g. 'image/gif', the client will receive one prediction per image frame
|
|
57
|
+
until the entire file has been processed.
|
|
58
|
+
|
|
59
|
+
4. Alternatively to `filePath` upload() also accepts a readable stream with a mandatory mime-type:
|
|
60
|
+
```typescript
|
|
61
|
+
// ...
|
|
62
|
+
let stream = fs.createReadStream(example_image_path)
|
|
63
|
+
endpoint.upload({stream: readableStream, mimeType: 'image/jpeg'})
|
|
64
|
+
// ...
|
|
65
|
+
```
|
|
66
|
+
### Visualizing Results
|
|
67
|
+
The EyePop SDK includes helper classes to visualize the predictions for images using `canvas.CanvasRenderingContext2D`.
|
|
68
|
+
```typescript
|
|
69
|
+
import {createCanvas, loadImage} from "canvas"
|
|
70
|
+
import {open} from 'openurl'
|
|
71
|
+
import { mkdtempSync, writeFileSync } from 'node:fs'
|
|
72
|
+
import { join } from 'node:path'
|
|
73
|
+
import { tmpdir } from 'node:os'
|
|
74
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
75
|
+
|
|
76
|
+
const example_image_path = 'examples/example.jpg';
|
|
77
|
+
|
|
78
|
+
(async() => {
|
|
79
|
+
const image = await loadImage(example_image_path)
|
|
80
|
+
const canvas = createCanvas(image.width, image.height)
|
|
81
|
+
const context = canvas.getContext("2d")
|
|
82
|
+
context.drawImage(image, 0, 0)
|
|
83
|
+
const plot = EyePopSdk.plot(context)
|
|
84
|
+
|
|
85
|
+
const endpoint = await EyePopSdk.endpoint().connect()
|
|
86
|
+
try {
|
|
87
|
+
let results = await endpoint.upload({filePath: example_image_path})
|
|
88
|
+
for await (let result of results) {
|
|
89
|
+
plot.prediction(result)
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
await endpoint.disconnect()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const tmp_dir = mkdtempSync(join(tmpdir(), 'ep-demo-'))
|
|
96
|
+
const temp_file = join(tmp_dir, 'out.png')
|
|
97
|
+
console.log(`creating temp file: ${temp_file}`)
|
|
98
|
+
|
|
99
|
+
const buffer = canvas.toBuffer('image/png')
|
|
100
|
+
writeFileSync(temp_file, buffer)
|
|
101
|
+
|
|
102
|
+
open(`file://${temp_file}`)
|
|
103
|
+
})
|
|
104
|
+
```
|
|
105
|
+
### Asynchronous uploading and processing of images
|
|
106
|
+
The above _synchronous_ way, upload() then iterate all results, is great for individual images or reasonable
|
|
107
|
+
sized batches. For larger batch sizes, or continuous stream of images, don't `await` the results but instead
|
|
108
|
+
use `then()` on the returned promise.
|
|
109
|
+
```typescript
|
|
110
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
111
|
+
|
|
112
|
+
const example_image_path = 'examples/example.jpg';
|
|
113
|
+
|
|
114
|
+
(async() => {
|
|
115
|
+
const endpoint = await EyePopSdk.endpoint().connect()
|
|
116
|
+
try {
|
|
117
|
+
for (let i = 0; i < 100; i++) {
|
|
118
|
+
endpoint.upload({filePath: example_image_path}).then(async (results) => {
|
|
119
|
+
for await (let result of results) {
|
|
120
|
+
console.log(`result for #${i}`, result)
|
|
121
|
+
}
|
|
122
|
+
}).catch((reason) => {
|
|
123
|
+
throw reason
|
|
124
|
+
})
|
|
125
|
+
}
|
|
126
|
+
} finally {
|
|
127
|
+
await endpoint.disconnect()
|
|
128
|
+
}
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
This will result in a most efficient processing, i.e. uploads will be processed in parallel (up to five HTTP
|
|
132
|
+
connections per endpoint) and results will be processed by your code as soon as they are available.
|
|
133
|
+
### Loading images from URLs
|
|
134
|
+
Alternatively to uploading files, you can also submit a publicly accessible URL for processing. Supported protocols are:
|
|
135
|
+
* HTTP(s) URLs with response Content-Type image/* or video/*
|
|
136
|
+
* RTSP (live-streaming)
|
|
137
|
+
* RTMP (live-streaming)
|
|
138
|
+
```typescript
|
|
139
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
140
|
+
|
|
141
|
+
const example_image_url = 'https://farm2.staticflickr.com/1080/1301049949_532835a8b5_z.jpg';
|
|
142
|
+
|
|
143
|
+
(async() => {
|
|
144
|
+
const endpoint = await EyePopSdk.endpoint().connect()
|
|
145
|
+
try {
|
|
146
|
+
let results = await endpoint.loadFrom(example_image_url)
|
|
147
|
+
for await (let result of results) {
|
|
148
|
+
console.log(result)
|
|
149
|
+
}
|
|
150
|
+
} finally {
|
|
151
|
+
await endpoint.disconnect()
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
```
|
|
155
|
+
### Processing Videos
|
|
156
|
+
You can process videos via upload or public URLs. This example shows how to process all video frames of a file
|
|
157
|
+
retrieved from a public URL.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
161
|
+
|
|
162
|
+
const example_video_url = 'https://demo-eyepop-videos.s3.amazonaws.com/test1_vlog.mp4';
|
|
163
|
+
|
|
164
|
+
(async() => {
|
|
165
|
+
const endpoint = await EyePopSdk.endpoint().connect()
|
|
166
|
+
try {
|
|
167
|
+
let results = await endpoint.loadFrom(example_image_url)
|
|
168
|
+
for await (let result of results) {
|
|
169
|
+
console.log(result)
|
|
170
|
+
}
|
|
171
|
+
} finally {
|
|
172
|
+
await endpoint.disconnect()
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
### Canceling Jobs
|
|
177
|
+
Any job that has been queued or is in-progress can be cancelled. E.g. stop the video processing after
|
|
178
|
+
predictions have been processed for 10 seconds duration of the video.
|
|
179
|
+
```typescript
|
|
180
|
+
import { EyePopSdk } from '@eyepop.ai/eyepop'
|
|
181
|
+
|
|
182
|
+
const example_video_url = 'https://demo-eyepop-videos.s3.amazonaws.com/test1_vlog.mp4';
|
|
183
|
+
|
|
184
|
+
(async() => {
|
|
185
|
+
const endpoint = EyePopSdk.endpoint().connect()
|
|
186
|
+
try {
|
|
187
|
+
let results = await endpoint.loadFrom(example_image_url)
|
|
188
|
+
for await (let result of results) {
|
|
189
|
+
console.log(result)
|
|
190
|
+
if (result['seconds'] >= 10.0) {
|
|
191
|
+
results.cancel()
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} finally {
|
|
195
|
+
await endpoint.disconnect()
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
## Other Usage Options
|
|
200
|
+
#### Auto start workers
|
|
201
|
+
By default, `EyePopSdk.endpoint().connect()` will start a worker if none is running yet. To disable this behavior
|
|
202
|
+
create an endpoint with `EyePopSdk.endpoint({autoStart: false})`.
|
|
203
|
+
#### Stop pending jobs
|
|
204
|
+
By default, `EyePopSdk.endpoint().connect()` will cancel all currently running or queued jobs on the worker.
|
|
205
|
+
It is assumed that the caller _takes full control_ of that worker. To disable this behavior create an endpoint with
|
|
206
|
+
`EyePopSdk.endpoint({stopJobs: false})`.
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eyepop.ai/eyepop",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "The official Node.js / Typescript library for EyePop.ai's inference API",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsup",
|
|
13
|
+
"test": "jest"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/eyepop-ai/eyepop-sdk-node.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"AI",
|
|
21
|
+
"CV",
|
|
22
|
+
"ML",
|
|
23
|
+
"EyePop"
|
|
24
|
+
],
|
|
25
|
+
"author": "EyePop.ai",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/eyepop-ai/eyepop-sdk-node/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/eyepop-ai/eyepop-sdk-node#readme",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@babel/preset-typescript": "^7.23.3",
|
|
33
|
+
"@jest/globals": "^29.7.0",
|
|
34
|
+
"@types/jest": "^29.5.11",
|
|
35
|
+
"@types/openurl": "^1.0.3",
|
|
36
|
+
"@types/uuid": "^9.0.7",
|
|
37
|
+
"canvas": "^2.11.2",
|
|
38
|
+
"jest": "^29.7.0",
|
|
39
|
+
"jest-mock-server": "^0.1.0",
|
|
40
|
+
"openurl": "^1.1.1",
|
|
41
|
+
"ts-jest": "^29.1.1",
|
|
42
|
+
"ts-node": "^10.9.2",
|
|
43
|
+
"tsup": "^8.0.1",
|
|
44
|
+
"typescript": "^5.3.3",
|
|
45
|
+
"uuid": "^9.0.1"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@types/mime-types": "^2.1.4",
|
|
49
|
+
"await-semaphore": "^0.1.3",
|
|
50
|
+
"pino": "^8.17.2",
|
|
51
|
+
"undici": "^6.3.0"
|
|
52
|
+
}
|
|
53
|
+
}
|