Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Commit
·
07d10ce
1
Parent(s):
5c099f3
various improvements
Browse files- package.json +1 -1
- src/index.mts +12 -6
- src/production/generateAudioLegacy.mts +3 -1
- src/production/generateVideo.mts +6 -3
- src/production/interpolateVideoLegacy.mts +3 -1
- src/production/renderScene.mts +68 -11
- src/types.mts +38 -0
- src/utils/downloadFileAsBase64.mts +17 -0
- src/utils/downloadFileToTmp.mts +2 -0
- src/utils/getFirstVideoFrame.mts +22 -0
- src/utils/getFirstVideoFrameAsBase64.mts +33 -0
- src/utils/segmentImage.mts +97 -15
- src/utils/segmentImageApi.mts +18 -0
package.json
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
"test:submitVideo": "node --loader ts-node/esm src/tests/submitVideo.mts",
|
9 |
"test:checkStatus": "node --loader ts-node/esm src/tests/checkStatus.mts",
|
10 |
"test:downloadFileToTmp": "node --loader ts-node/esm src/tests/downloadFileToTmp.mts",
|
11 |
-
"test:stuff": "node --loader ts-node/esm src/
|
12 |
"docker": "npm run docker:build && npm run docker:run",
|
13 |
"docker:build": "docker build -t videochain-api .",
|
14 |
"docker:run": "docker run -it -p 7860:7860 videochain-api"
|
|
|
8 |
"test:submitVideo": "node --loader ts-node/esm src/tests/submitVideo.mts",
|
9 |
"test:checkStatus": "node --loader ts-node/esm src/tests/checkStatus.mts",
|
10 |
"test:downloadFileToTmp": "node --loader ts-node/esm src/tests/downloadFileToTmp.mts",
|
11 |
+
"test:stuff": "node --loader ts-node/esm src/utils/segmentImage.mts",
|
12 |
"docker": "npm run docker:build && npm run docker:run",
|
13 |
"docker:build": "docker build -t videochain-api .",
|
14 |
"docker:run": "docker run -it -p 7860:7860 videochain-api"
|
src/index.mts
CHANGED
@@ -4,7 +4,7 @@ import path from "node:path"
|
|
4 |
import { validate as uuidValidate } from "uuid"
|
5 |
import express from "express"
|
6 |
|
7 |
-
import { Video, VideoStatus, VideoAPIRequest } from "./types.mts"
|
8 |
import { parseVideoRequest } from "./utils/parseVideoRequest.mts"
|
9 |
import { savePendingVideo } from "./scheduler/savePendingVideo.mts"
|
10 |
import { getVideo } from "./scheduler/getVideo.mts"
|
@@ -38,9 +38,9 @@ let isRendering = false
|
|
38 |
// a "fast track" pipeline
|
39 |
app.post("/render", async (req, res) => {
|
40 |
|
41 |
-
const
|
42 |
-
console.log(
|
43 |
-
if (!prompt) {
|
44 |
console.log("Invalid prompt")
|
45 |
res.status(400)
|
46 |
res.write(JSON.stringify({ url: "", error: "invalid prompt" }))
|
@@ -48,9 +48,15 @@ app.post("/render", async (req, res) => {
|
|
48 |
return
|
49 |
}
|
50 |
|
51 |
-
let result = {
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
try {
|
53 |
-
result = await renderScene(
|
54 |
} catch (err) {
|
55 |
// console.log("failed to render scene!")
|
56 |
result.error = `failed to render scene: ${err}`
|
|
|
4 |
import { validate as uuidValidate } from "uuid"
|
5 |
import express from "express"
|
6 |
|
7 |
+
import { Video, VideoStatus, VideoAPIRequest, RenderRequest, RenderAPIResponse } from "./types.mts"
|
8 |
import { parseVideoRequest } from "./utils/parseVideoRequest.mts"
|
9 |
import { savePendingVideo } from "./scheduler/savePendingVideo.mts"
|
10 |
import { getVideo } from "./scheduler/getVideo.mts"
|
|
|
38 |
// a "fast track" pipeline
|
39 |
app.post("/render", async (req, res) => {
|
40 |
|
41 |
+
const request = req.body as RenderRequest
|
42 |
+
console.log(req.body)
|
43 |
+
if (!request.prompt) {
|
44 |
console.log("Invalid prompt")
|
45 |
res.status(400)
|
46 |
res.write(JSON.stringify({ url: "", error: "invalid prompt" }))
|
|
|
48 |
return
|
49 |
}
|
50 |
|
51 |
+
let result: RenderAPIResponse = {
|
52 |
+
videoUrl: "",
|
53 |
+
maskBase64: "",
|
54 |
+
error: "",
|
55 |
+
segments: []
|
56 |
+
}
|
57 |
+
|
58 |
try {
|
59 |
+
result = await renderScene(request)
|
60 |
} catch (err) {
|
61 |
// console.log("failed to render scene!")
|
62 |
result.error = `failed to render scene: ${err}`
|
src/production/generateAudioLegacy.mts
CHANGED
@@ -18,7 +18,9 @@ export const generateAudio = async (prompt: string, options?: {
|
|
18 |
const instance = instances.shift()
|
19 |
instances.push(instance)
|
20 |
|
21 |
-
const api = await client(instance
|
|
|
|
|
22 |
|
23 |
const rawResponse = await api.predict('/run', [
|
24 |
prompt, // string in 'Prompt' Textbox component
|
|
|
18 |
const instance = instances.shift()
|
19 |
instances.push(instance)
|
20 |
|
21 |
+
const api = await client(instance, {
|
22 |
+
hf_token: `${process.env.VC_HF_API_TOKEN}` as any
|
23 |
+
})
|
24 |
|
25 |
const rawResponse = await api.predict('/run', [
|
26 |
prompt, // string in 'Prompt' Textbox component
|
src/production/generateVideo.mts
CHANGED
@@ -3,8 +3,9 @@ import { client } from "@gradio/client"
|
|
3 |
import { generateSeed } from "../utils/generateSeed.mts"
|
4 |
|
5 |
const instances: string[] = [
|
6 |
-
`${process.env.
|
7 |
-
`${process.env.
|
|
|
8 |
].filter(instance => instance?.length > 0)
|
9 |
|
10 |
export const generateVideo = async (prompt: string, options?: {
|
@@ -19,7 +20,9 @@ export const generateVideo = async (prompt: string, options?: {
|
|
19 |
const instance = instances.shift()
|
20 |
instances.push(instance)
|
21 |
|
22 |
-
const api = await client(instance
|
|
|
|
|
23 |
|
24 |
const rawResponse = await api.predict('/run', [
|
25 |
prompt, // string in 'Prompt' Textbox component
|
|
|
3 |
import { generateSeed } from "../utils/generateSeed.mts"
|
4 |
|
5 |
const instances: string[] = [
|
6 |
+
`${process.env.VC_ZEROSCOPE_SPACE_API_URL_1 || ""}`,
|
7 |
+
// `${process.env.VC_ZEROSCOPE_SPACE_API_URL_2 || ""}`,
|
8 |
+
// `${process.env.VC_ZEROSCOPE_SPACE_API_URL_3 || ""}`,
|
9 |
].filter(instance => instance?.length > 0)
|
10 |
|
11 |
export const generateVideo = async (prompt: string, options?: {
|
|
|
20 |
const instance = instances.shift()
|
21 |
instances.push(instance)
|
22 |
|
23 |
+
const api = await client(instance, {
|
24 |
+
hf_token: `${process.env.VC_HF_API_TOKEN}` as any
|
25 |
+
})
|
26 |
|
27 |
const rawResponse = await api.predict('/run', [
|
28 |
prompt, // string in 'Prompt' Textbox component
|
src/production/interpolateVideoLegacy.mts
CHANGED
@@ -18,7 +18,9 @@ export const interpolateVideo = async (fileName: string, steps: number, fps: num
|
|
18 |
const instance = instances.shift()
|
19 |
instances.push(instance)
|
20 |
|
21 |
-
const api = await client(instance
|
|
|
|
|
22 |
|
23 |
const video = await fs.readFile(inputFilePath)
|
24 |
|
|
|
18 |
const instance = instances.shift()
|
19 |
instances.push(instance)
|
20 |
|
21 |
+
const api = await client(instance, {
|
22 |
+
hf_token: `${process.env.VC_HF_API_TOKEN}` as any
|
23 |
+
})
|
24 |
|
25 |
const video = await fs.readFile(inputFilePath)
|
26 |
|
src/production/renderScene.mts
CHANGED
@@ -1,5 +1,12 @@
|
|
|
|
|
|
|
|
|
|
1 |
import { generateSeed } from "../utils/generateSeed.mts"
|
|
|
2 |
import { generateVideo } from "./generateVideo.mts"
|
|
|
|
|
3 |
|
4 |
const state = {
|
5 |
isRendering: false
|
@@ -7,15 +14,22 @@ const state = {
|
|
7 |
|
8 |
const seed = generateSeed()
|
9 |
|
10 |
-
export async function renderScene(
|
11 |
// console.log("renderScene")
|
|
|
|
|
|
|
|
|
12 |
if (state.isRendering) {
|
13 |
// console.log("renderScene: isRendering")
|
14 |
return {
|
15 |
-
|
16 |
-
error: "already rendering"
|
|
|
|
|
17 |
}
|
18 |
}
|
|
|
19 |
|
20 |
// onsole.log("marking as isRendering")
|
21 |
state.isRendering = true
|
@@ -24,11 +38,10 @@ export async function renderScene(prompt: string) {
|
|
24 |
let error = ""
|
25 |
|
26 |
try {
|
27 |
-
url = await generateVideo(prompt, {
|
28 |
-
seed: generateSeed(),
|
29 |
-
//
|
30 |
-
|
31 |
-
nbSteps: 10,
|
32 |
})
|
33 |
// console.log("successfull generation")
|
34 |
error = ""
|
@@ -36,12 +49,56 @@ export async function renderScene(prompt: string) {
|
|
36 |
error = `failed to render scene: ${err}`
|
37 |
}
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
// console.log("marking as not rendering anymore")
|
40 |
state.isRendering = false
|
41 |
error = ""
|
42 |
|
43 |
return {
|
44 |
-
url,
|
45 |
-
error
|
46 |
-
|
|
|
|
|
47 |
}
|
|
|
1 |
+
import { v4 as uuidv4 } from "uuid"
|
2 |
+
|
3 |
+
import { ImageSegment, RenderAPIResponse, RenderRequest } from "../types.mts"
|
4 |
+
import { downloadFileToTmp } from "../utils/downloadFileToTmp.mts"
|
5 |
import { generateSeed } from "../utils/generateSeed.mts"
|
6 |
+
import { getValidNumber } from "../utils/getValidNumber.mts"
|
7 |
import { generateVideo } from "./generateVideo.mts"
|
8 |
+
import { getFirstVideoFrame } from "../utils/getFirstVideoFrame.mts"
|
9 |
+
import { segmentImage } from "../utils/segmentImage.mts"
|
10 |
|
11 |
const state = {
|
12 |
isRendering: false
|
|
|
14 |
|
15 |
const seed = generateSeed()
|
16 |
|
17 |
+
export async function renderScene(scene: RenderRequest): Promise<RenderAPIResponse> {
|
18 |
// console.log("renderScene")
|
19 |
+
|
20 |
+
// let's disable this for now
|
21 |
+
// this is only reliable if nothing crashes anyway..
|
22 |
+
/*
|
23 |
if (state.isRendering) {
|
24 |
// console.log("renderScene: isRendering")
|
25 |
return {
|
26 |
+
videoUrl: "",
|
27 |
+
error: "already rendering",
|
28 |
+
maskBase64: "",
|
29 |
+
segments: [],
|
30 |
}
|
31 |
}
|
32 |
+
*/
|
33 |
|
34 |
// onsole.log("marking as isRendering")
|
35 |
state.isRendering = true
|
|
|
38 |
let error = ""
|
39 |
|
40 |
try {
|
41 |
+
url = await generateVideo(scene.prompt, {
|
42 |
+
seed: getValidNumber(scene.seed, 0, 4294967295, generateSeed()),
|
43 |
+
nbFrames: getValidNumber(scene.nbFrames, 8, 24, 16), // 2 seconds by default
|
44 |
+
nbSteps: getValidNumber(scene.nbSteps, 1, 50, 10), // use 10 by default to go fast, but not too sloppy
|
|
|
45 |
})
|
46 |
// console.log("successfull generation")
|
47 |
error = ""
|
|
|
49 |
error = `failed to render scene: ${err}`
|
50 |
}
|
51 |
|
52 |
+
|
53 |
+
|
54 |
+
// TODO add segmentation here
|
55 |
+
const actionnables = Array.isArray(scene.actionnables) ? scene.actionnables : []
|
56 |
+
|
57 |
+
let mask = ""
|
58 |
+
let segments: ImageSegment[] = []
|
59 |
+
|
60 |
+
if (actionnables.length > 0) {
|
61 |
+
console.log("we have some actionnables:", actionnables)
|
62 |
+
if (scene.segmentation === "firstframe") {
|
63 |
+
console.log("going to grab the first frame")
|
64 |
+
const tmpVideoFilePath = await downloadFileToTmp(url, `${uuidv4()}`)
|
65 |
+
console.log("downloaded the first frame to ", tmpVideoFilePath)
|
66 |
+
const firstFrameFilePath = await getFirstVideoFrame(tmpVideoFilePath)
|
67 |
+
console.log("downloaded the first frame to ", firstFrameFilePath)
|
68 |
+
|
69 |
+
if (!firstFrameFilePath) {
|
70 |
+
console.error("failed to get the image")
|
71 |
+
error = "failed to segment the image"
|
72 |
+
} else {
|
73 |
+
console.log("got the first frame! segmenting..")
|
74 |
+
const result = await segmentImage(firstFrameFilePath, actionnables)
|
75 |
+
mask = result.pngInBase64
|
76 |
+
segments = result.segments
|
77 |
+
// console.log("success!", { segments })
|
78 |
+
}
|
79 |
+
/*
|
80 |
+
const jpgBase64 = await getFirstVideoFrame(tmpVideoFileName)
|
81 |
+
if (!jpgBase64) {
|
82 |
+
console.error("failed to get the image")
|
83 |
+
error = "failed to segment the image"
|
84 |
+
} else {
|
85 |
+
console.log(`got the first frame (${jpgBase64.length})`)
|
86 |
+
|
87 |
+
console.log("TODO: call segmentImage with the base64 image")
|
88 |
+
await segmentImage()
|
89 |
+
}
|
90 |
+
*/
|
91 |
+
}
|
92 |
+
}
|
93 |
+
|
94 |
// console.log("marking as not rendering anymore")
|
95 |
state.isRendering = false
|
96 |
error = ""
|
97 |
|
98 |
return {
|
99 |
+
videoUrl: url,
|
100 |
+
error,
|
101 |
+
maskBase64: mask,
|
102 |
+
segments
|
103 |
+
} as RenderAPIResponse
|
104 |
}
|
src/types.mts
CHANGED
@@ -269,8 +269,46 @@ export type Video = VideoSequence & {
|
|
269 |
shots: VideoShot[]
|
270 |
}
|
271 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
272 |
|
273 |
export interface ImageSegmentationRequest {
|
274 |
image: string // in base64
|
275 |
keywords: string[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
276 |
}
|
|
|
269 |
shots: VideoShot[]
|
270 |
}
|
271 |
|
272 |
+
export interface RenderRequest {
|
273 |
+
prompt: string
|
274 |
+
|
275 |
+
// whether to use video segmentation
|
276 |
+
// disabled (default)
|
277 |
+
// firstframe: we only analyze the first frame
|
278 |
+
// allframes: we analyze all the frames
|
279 |
+
segmentation: 'disabled' | 'firstframe' | 'allframes'
|
280 |
+
|
281 |
+
// segmentation will only be executed if we have a non-empty list of actionnables
|
282 |
+
// actionnables are names of things like "chest", "key", "tree", "chair" etc
|
283 |
+
actionnables: string[]
|
284 |
+
|
285 |
+
// note: this is the number of frames for Zeroscope,
|
286 |
+
// which is currently configured to only output 3 seconds, so:
|
287 |
+
// nbFrames=8 -> 1 sec
|
288 |
+
// nbFrames=16 -> 2 sec
|
289 |
+
// nbFrames=24 -> 3 sec
|
290 |
+
nbFrames: number // min: 8, max: 24
|
291 |
+
|
292 |
+
nbSteps: number // min: 1, max: 50
|
293 |
+
|
294 |
+
seed: number
|
295 |
+
}
|
296 |
|
297 |
export interface ImageSegmentationRequest {
|
298 |
image: string // in base64
|
299 |
keywords: string[]
|
300 |
+
}
|
301 |
+
|
302 |
+
export interface ImageSegment {
|
303 |
+
id: number
|
304 |
+
box: number[]
|
305 |
+
label: string
|
306 |
+
score: number
|
307 |
+
}
|
308 |
+
|
309 |
+
export interface RenderAPIResponse {
|
310 |
+
videoUrl: string
|
311 |
+
error: string
|
312 |
+
maskBase64: string
|
313 |
+
segments: ImageSegment[]
|
314 |
}
|
src/utils/downloadFileAsBase64.mts
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const downloadImageAsBase64 = async (remoteUrl: string): Promise<string> => {
|
2 |
+
const controller = new AbortController()
|
3 |
+
|
4 |
+
// download the image
|
5 |
+
const response = await fetch(remoteUrl, {
|
6 |
+
signal: controller.signal
|
7 |
+
})
|
8 |
+
|
9 |
+
// get as Buffer
|
10 |
+
const arrayBuffer = await response.arrayBuffer()
|
11 |
+
const buffer = Buffer.from(arrayBuffer)
|
12 |
+
|
13 |
+
// convert it to base64
|
14 |
+
const base64 = buffer.toString('base64')
|
15 |
+
|
16 |
+
return base64
|
17 |
+
};
|
src/utils/downloadFileToTmp.mts
CHANGED
@@ -24,4 +24,6 @@ export const downloadFileToTmp = async (remoteUrl: string, fileName: string) =>
|
|
24 |
filePath,
|
25 |
Buffer.from(arrayBuffer)
|
26 |
)
|
|
|
|
|
27 |
}
|
|
|
24 |
filePath,
|
25 |
Buffer.from(arrayBuffer)
|
26 |
)
|
27 |
+
|
28 |
+
return filePath
|
29 |
}
|
src/utils/getFirstVideoFrame.mts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import path from "node:path"
|
2 |
+
|
3 |
+
import ffmpeg from "fluent-ffmpeg"
|
4 |
+
import { v4 as uuidv4 } from "uuid"
|
5 |
+
import tmpDir from "temp-dir"
|
6 |
+
|
7 |
+
export async function getFirstVideoFrame(videoFilePath: string): Promise<string | void> {
|
8 |
+
const tmpFileName = `${uuidv4()}.jpg`
|
9 |
+
|
10 |
+
const tmpFilePath = path.resolve(tmpDir, tmpFileName)
|
11 |
+
|
12 |
+
return new Promise((resolve, reject) => {
|
13 |
+
ffmpeg(videoFilePath)
|
14 |
+
.outputOptions("-vframes 1")
|
15 |
+
.output(tmpFilePath)
|
16 |
+
.on("end", async () => {
|
17 |
+
resolve(tmpFilePath)
|
18 |
+
})
|
19 |
+
.on("error", reject)
|
20 |
+
.run()
|
21 |
+
})
|
22 |
+
}
|
src/utils/getFirstVideoFrameAsBase64.mts
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import fs from "node:fs"
|
2 |
+
import util from "node:util"
|
3 |
+
import path from "node:path"
|
4 |
+
|
5 |
+
import ffmpeg from "fluent-ffmpeg"
|
6 |
+
import { v4 as uuidv4 } from "uuid"
|
7 |
+
import tmpDir from "temp-dir"
|
8 |
+
|
9 |
+
const unlinkAsync = util.promisify(fs.unlink)
|
10 |
+
|
11 |
+
export async function getFirstVideoFrameAsBase64(videoPath: string): Promise<string | void> {
|
12 |
+
const tmpFileName = `${uuidv4()}.jpg`
|
13 |
+
|
14 |
+
const tmpFilePath = path.resolve(tmpDir, tmpFileName)
|
15 |
+
|
16 |
+
return new Promise((resolve, reject) => {
|
17 |
+
ffmpeg(videoPath)
|
18 |
+
.outputOptions("-vframes 1")
|
19 |
+
.output(tmpFilePath)
|
20 |
+
.on("end", async () => {
|
21 |
+
let base64;
|
22 |
+
try {
|
23 |
+
base64 = await fs.promises.readFile(tmpFilePath, { encoding: "base64" });
|
24 |
+
await unlinkAsync(tmpFilePath)
|
25 |
+
} catch(err) {
|
26 |
+
return reject(err)
|
27 |
+
}
|
28 |
+
resolve(base64)
|
29 |
+
})
|
30 |
+
.on("error", reject)
|
31 |
+
.run()
|
32 |
+
})
|
33 |
+
}
|
src/utils/segmentImage.mts
CHANGED
@@ -1,15 +1,97 @@
|
|
1 |
-
import
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
const
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import puppeteer from "puppeteer"
|
2 |
+
|
3 |
+
import { sleep } from "./sleep.mts"
|
4 |
+
import { ImageSegment } from "../types.mts"
|
5 |
+
import { downloadImageAsBase64 } from "./downloadFileAsBase64.mts"
|
6 |
+
|
7 |
+
const instances: string[] = [
|
8 |
+
`${process.env.VC_SEGMENTATION_MODULE_SPACE_API_URL_1 || ""}`,
|
9 |
+
`${process.env.VC_SEGMENTATION_MODULE_SPACE_API_URL_2 || ""}`,
|
10 |
+
]
|
11 |
+
|
12 |
+
// TODO we should use an inference endpoint instead
|
13 |
+
|
14 |
+
// note: on a large T4 (8 vCPU)
|
15 |
+
// it takes about 30 seconds to compute
|
16 |
+
export async function segmentImage(
|
17 |
+
inputImageFilePath: string,
|
18 |
+
actionnables: string[]
|
19 |
+
): Promise<{
|
20 |
+
pngInBase64: string
|
21 |
+
segments: ImageSegment[]
|
22 |
+
}> {
|
23 |
+
|
24 |
+
console.log(`segmenting image..`)
|
25 |
+
|
26 |
+
const instance = instances.shift()
|
27 |
+
instances.push(instance)
|
28 |
+
|
29 |
+
const browser = await puppeteer.launch({
|
30 |
+
headless: true,
|
31 |
+
protocolTimeout: 70000,
|
32 |
+
})
|
33 |
+
|
34 |
+
const page = await browser.newPage()
|
35 |
+
await page.goto(instance, { waitUntil: 'networkidle2' })
|
36 |
+
|
37 |
+
await new Promise(r => setTimeout(r, 3000))
|
38 |
+
|
39 |
+
const fileField = await page.$('input[type="file"]')
|
40 |
+
|
41 |
+
// console.log(`uploading file..`)
|
42 |
+
await fileField.uploadFile(inputImageFilePath)
|
43 |
+
|
44 |
+
await sleep(500)
|
45 |
+
|
46 |
+
const firstTextarea = await page.$('textarea[data-testid="textbox"]')
|
47 |
+
|
48 |
+
const conceptsToDetect = actionnables.join(" . ")
|
49 |
+
await firstTextarea.type(conceptsToDetect)
|
50 |
+
|
51 |
+
// console.log('looking for the button to submit')
|
52 |
+
const submitButton = await page.$('button.lg')
|
53 |
+
|
54 |
+
await sleep(500)
|
55 |
+
|
56 |
+
// console.log('clicking on the button')
|
57 |
+
await submitButton.click()
|
58 |
+
|
59 |
+
await page.waitForSelector('img[data-testid="detailed-image"]', {
|
60 |
+
timeout: 70000, // need to be large enough in case someone else attemps to use our space
|
61 |
+
})
|
62 |
+
|
63 |
+
const maskUrl = await page.$$eval('img[data-testid="detailed-image"]', el => el.map(x => x.getAttribute("src"))[0])
|
64 |
+
|
65 |
+
let segments: ImageSegment[] = []
|
66 |
+
|
67 |
+
try {
|
68 |
+
segments = JSON.parse(await page.$$eval('textarea', el => el.map(x => x.value)[1]))
|
69 |
+
} catch (err) {
|
70 |
+
console.log(`failed to parse JSON: ${err}`)
|
71 |
+
segments = []
|
72 |
+
}
|
73 |
+
|
74 |
+
// const tmpMaskFileName = `${uuidv4()}.png`
|
75 |
+
// await downloadFileToTmp(maskUrl, tmpMaskFileName)
|
76 |
+
|
77 |
+
const pngInBase64 = await downloadImageAsBase64(maskUrl)
|
78 |
+
return {
|
79 |
+
pngInBase64,
|
80 |
+
segments,
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
/*
|
85 |
+
|
86 |
+
If you want to try:
|
87 |
+
|
88 |
+
/ note: must be a jpg and not jpeg it seems
|
89 |
+
// (probably a playwright bug)
|
90 |
+
const results = await segmentImage("./barn.jpg", [
|
91 |
+
"roof",
|
92 |
+
"door",
|
93 |
+
"window"
|
94 |
+
])
|
95 |
+
|
96 |
+
console.log("results:", results)
|
97 |
+
*/
|
src/utils/segmentImageApi.mts
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { client } from "@gradio/client"
|
2 |
+
|
3 |
+
|
4 |
+
const response_0 = await fetch("https://raw.githubusercontent.com/gradio-app/gradio/main/test/test_files/bus.png")
|
5 |
+
const exampleImage = await response_0.blob()
|
6 |
+
|
7 |
+
const app = await client("https://jbilcke-hf-image-segmentation.hf.space", {
|
8 |
+
hf_token: `${process.env.VC_HF_API_TOKEN}` as any
|
9 |
+
})
|
10 |
+
const result = await app.predict(0, [
|
11 |
+
exampleImage, // "data:image/jpeg;base64,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", // blob in 'Upload' Image component
|
12 |
+
"Howdy!", // string in 'Detection Prompt[To detect multiple objects, seperating each name with '.', like this: cat . dog . chair ]' Textbox component
|
13 |
+
0.3, // number (numeric value between 0.0 and 1.0) in 'Box Threshold' Slider component
|
14 |
+
0.25, // number (numeric value between 0.0 and 1.0) in 'Text Threshold' Slider component
|
15 |
+
0.8, // number (numeric value between 0.0 and 1.0) in 'IOU Threshold' Slider component
|
16 |
+
]) as any
|
17 |
+
|
18 |
+
console.log(result)
|