Spaces:
Runtime error
Runtime error
Commit
β’
f4af987
1
Parent(s):
4f64da5
work in progress
Browse files- package-lock.json +0 -0
- src/app/agents/{ant.ts β city.ts} +37 -15
- src/app/agents/{fox.ts β dungeon.ts} +41 -16
- src/app/agents/index.ts +5 -5
- src/app/agents/{fish.ts β pirates.ts} +34 -7
- src/app/agents/types.ts +3 -1
- src/app/layout.tsx +2 -2
- src/app/main.tsx +43 -16
- src/app/render.ts +23 -6
- src/app/types.ts +40 -0
- src/components/business/image-renderer.tsx +88 -0
- src/components/business/{video-player.tsx β video-renderer.tsx} +2 -2
package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
src/app/agents/{ant.ts β city.ts}
RENAMED
@@ -2,40 +2,62 @@ import { pick } from "./pick"
|
|
2 |
import { Agent, Scene } from "./types"
|
3 |
|
4 |
const actions = [
|
5 |
-
"
|
6 |
-
"
|
7 |
-
"
|
8 |
-
"
|
9 |
-
"
|
10 |
-
"collecting sugar",
|
11 |
-
"collecting aphids"
|
12 |
]
|
13 |
|
14 |
const positions = [
|
15 |
-
"
|
16 |
-
"
|
17 |
-
"
|
18 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
]
|
20 |
|
21 |
export const agent: Agent = {
|
22 |
-
title: "
|
23 |
-
type: "
|
24 |
simulate: (): Scene => {
|
25 |
const action = pick(actions)
|
26 |
const position = pick(positions)
|
|
|
27 |
|
28 |
const prompt = [
|
29 |
-
`
|
30 |
action,
|
31 |
position,
|
|
|
|
|
|
|
32 |
`high res`,
|
33 |
-
`documentary`,
|
34 |
].join(", ")
|
35 |
|
36 |
return {
|
37 |
action,
|
38 |
position,
|
|
|
|
|
39 |
prompt
|
40 |
}
|
41 |
}
|
|
|
2 |
import { Agent, Scene } from "./types"
|
3 |
|
4 |
const actions = [
|
5 |
+
"busy pedestrians",
|
6 |
+
"busy traffic",
|
7 |
+
"typical street life",
|
8 |
+
"skyscrapper being constructed",
|
9 |
+
"a building is on fire",
|
|
|
|
|
10 |
]
|
11 |
|
12 |
const positions = [
|
13 |
+
"city center with skyscrappers",
|
14 |
+
"city center with a hospital",
|
15 |
+
"market area",
|
16 |
+
"residential area with small houses",
|
17 |
+
"residential area and houses with pools",
|
18 |
+
"industrial area with a smoking factory",
|
19 |
+
"beachfront area with villas",
|
20 |
+
"theme park with one big rollercoaster"
|
21 |
+
]
|
22 |
+
|
23 |
+
const lights = [
|
24 |
+
"during the day",
|
25 |
+
// "during the night",
|
26 |
+
]
|
27 |
+
|
28 |
+
const actionnables = [
|
29 |
+
"building",
|
30 |
+
"road",
|
31 |
+
"car",
|
32 |
+
"tower",
|
33 |
+
"tree",
|
34 |
+
"river",
|
35 |
+
"sea"
|
36 |
]
|
37 |
|
38 |
export const agent: Agent = {
|
39 |
+
title: "City",
|
40 |
+
type: "city",
|
41 |
simulate: (): Scene => {
|
42 |
const action = pick(actions)
|
43 |
const position = pick(positions)
|
44 |
+
const light = pick(lights)
|
45 |
|
46 |
const prompt = [
|
47 |
+
`static isometrical view of 3D rendered city`,
|
48 |
action,
|
49 |
position,
|
50 |
+
light,
|
51 |
+
`isometric`,
|
52 |
+
`game`,
|
53 |
`high res`,
|
|
|
54 |
].join(", ")
|
55 |
|
56 |
return {
|
57 |
action,
|
58 |
position,
|
59 |
+
light,
|
60 |
+
actionnables,
|
61 |
prompt
|
62 |
}
|
63 |
}
|
src/app/agents/{fox.ts β dungeon.ts}
RENAMED
@@ -2,42 +2,67 @@ import { pick } from "./pick"
|
|
2 |
import { Agent, Scene } from "./types"
|
3 |
|
4 |
const actions = [
|
5 |
-
"
|
6 |
-
"
|
7 |
-
"
|
8 |
-
"looking
|
9 |
-
"
|
10 |
-
"
|
11 |
-
"
|
12 |
]
|
13 |
|
14 |
const positions = [
|
15 |
-
"
|
16 |
-
"
|
17 |
-
"
|
18 |
-
"
|
|
|
|
|
|
|
|
|
19 |
]
|
20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
export const agent: Agent = {
|
23 |
-
title: "
|
24 |
-
type: "
|
25 |
simulate: (): Scene => {
|
26 |
const action = pick(actions)
|
27 |
const position = pick(positions)
|
|
|
28 |
|
29 |
const prompt = [
|
30 |
-
`
|
31 |
action,
|
32 |
position,
|
33 |
-
|
|
|
|
|
34 |
`documentary`,
|
|
|
35 |
].join(", ")
|
36 |
|
37 |
return {
|
38 |
action,
|
39 |
position,
|
|
|
|
|
40 |
prompt
|
41 |
}
|
42 |
}
|
43 |
-
}
|
|
|
2 |
import { Agent, Scene } from "./types"
|
3 |
|
4 |
const actions = [
|
5 |
+
"not moving",
|
6 |
+
"walking in",
|
7 |
+
"looking up",
|
8 |
+
"looking down",
|
9 |
+
"looking left",
|
10 |
+
"looking right",
|
11 |
+
"looking around"
|
12 |
]
|
13 |
|
14 |
const positions = [
|
15 |
+
"corridor with a beautiful wooden door at the end, wooden floor and stone walls",
|
16 |
+
"a beautiful wooden door",
|
17 |
+
"beautiful room with stone walls and wooden floor",
|
18 |
+
"large ball room with stone pillars, stone floor and red carpet",
|
19 |
+
"a cosy room with a fireplace, stone walls and wooden floor",
|
20 |
+
"a fireplace with stone walls",
|
21 |
+
"a cold dungeon with stone walls",
|
22 |
+
"a damp medieval jail cell with stone walls and wooden floor"
|
23 |
]
|
24 |
|
25 |
+
const lights = [
|
26 |
+
"lit through windows",
|
27 |
+
"lit through wall-mounted torchs"
|
28 |
+
// "poorly lit"
|
29 |
+
]
|
30 |
+
|
31 |
+
const actionnables = [
|
32 |
+
"floor",
|
33 |
+
"fireplace",
|
34 |
+
"door",
|
35 |
+
"window",
|
36 |
+
"chair",
|
37 |
+
"table",
|
38 |
+
"torch"
|
39 |
+
]
|
40 |
|
41 |
export const agent: Agent = {
|
42 |
+
title: "Dungeon",
|
43 |
+
type: "dungeon",
|
44 |
simulate: (): Scene => {
|
45 |
const action = pick(actions)
|
46 |
const position = pick(positions)
|
47 |
+
const light = pick(lights)
|
48 |
|
49 |
const prompt = [
|
50 |
+
`first-person footage`,
|
51 |
action,
|
52 |
position,
|
53 |
+
light,
|
54 |
+
`medieval`,
|
55 |
+
`photography`,
|
56 |
`documentary`,
|
57 |
+
`high res`,
|
58 |
].join(", ")
|
59 |
|
60 |
return {
|
61 |
action,
|
62 |
position,
|
63 |
+
light,
|
64 |
+
actionnables,
|
65 |
prompt
|
66 |
}
|
67 |
}
|
68 |
+
}
|
src/app/agents/index.ts
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
import { Agent, AgentType } from "./types"
|
2 |
|
3 |
-
import { agent as
|
4 |
-
import { agent as
|
5 |
-
import { agent as
|
6 |
|
7 |
-
export const agents = {
|
8 |
|
9 |
-
export const defaultAgent: AgentType = "
|
10 |
|
11 |
export const getAgent = (type?: AgentType) => agents[type || defaultAgent] || agents[defaultAgent]
|
|
|
1 |
import { Agent, AgentType } from "./types"
|
2 |
|
3 |
+
import { agent as pirates } from "./pirates"
|
4 |
+
import { agent as city } from "./city"
|
5 |
+
import { agent as dungeon } from "./dungeon"
|
6 |
|
7 |
+
export const agents = { pirates, city, dungeon }
|
8 |
|
9 |
+
export const defaultAgent: AgentType = "pirates"
|
10 |
|
11 |
export const getAgent = (type?: AgentType) => agents[type || defaultAgent] || agents[defaultAgent]
|
src/app/agents/{fish.ts β pirates.ts}
RENAMED
@@ -21,24 +21,51 @@ const positions = [
|
|
21 |
"hiding in the coral"
|
22 |
]
|
23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
export const agent: Agent = {
|
25 |
-
title: "
|
26 |
-
type: "
|
27 |
simulate: (): Scene => {
|
28 |
const action = pick(actions)
|
29 |
const position = pick(positions)
|
|
|
30 |
|
|
|
|
|
31 |
const prompt = [
|
32 |
-
`
|
33 |
-
|
34 |
-
|
35 |
-
`
|
36 |
-
`
|
|
|
|
|
37 |
].join(", ")
|
38 |
|
39 |
return {
|
40 |
action,
|
41 |
position,
|
|
|
|
|
42 |
prompt
|
43 |
}
|
44 |
}
|
|
|
21 |
"hiding in the coral"
|
22 |
]
|
23 |
|
24 |
+
const lights = [
|
25 |
+
"during the day",
|
26 |
+
]
|
27 |
+
|
28 |
+
const actionnables = [
|
29 |
+
"chest",
|
30 |
+
// "door",
|
31 |
+
// "window",
|
32 |
+
// "sail",
|
33 |
+
// "capstan",
|
34 |
+
// "ship's wheel",
|
35 |
+
// "hat",
|
36 |
+
// "barrel",
|
37 |
+
// "cannon",
|
38 |
+
// "rope",
|
39 |
+
// "bucket",
|
40 |
+
"parrot",
|
41 |
+
// "wooden leg"
|
42 |
+
]
|
43 |
+
|
44 |
export const agent: Agent = {
|
45 |
+
title: "Pirates",
|
46 |
+
type: "pirates",
|
47 |
simulate: (): Scene => {
|
48 |
const action = pick(actions)
|
49 |
const position = pick(positions)
|
50 |
+
const light = pick(lights)
|
51 |
|
52 |
+
// this prompt is beautiful:
|
53 |
+
// screenshot from an adventure videogame, inside the hold of a pirate ship, with a pirate chest in the center, at sunset, beautiful, award winning, unreal engine, intricate details
|
54 |
const prompt = [
|
55 |
+
`screenshot from an adventure videogame`,
|
56 |
+
`inside the hold of a pirate ship`,
|
57 |
+
`with a pirate chest in the center`,
|
58 |
+
`a parrot`,
|
59 |
+
`and a wooden leg`,
|
60 |
+
`at sunset`,
|
61 |
+
`unreal engine`,
|
62 |
].join(", ")
|
63 |
|
64 |
return {
|
65 |
action,
|
66 |
position,
|
67 |
+
light,
|
68 |
+
actionnables,
|
69 |
prompt
|
70 |
}
|
71 |
}
|
src/app/agents/types.ts
CHANGED
@@ -1,8 +1,10 @@
|
|
1 |
-
export type AgentType = '
|
2 |
|
3 |
export interface Scene {
|
4 |
action: string
|
5 |
position: string
|
|
|
|
|
6 |
prompt: string
|
7 |
}
|
8 |
|
|
|
1 |
+
export type AgentType = 'pirates' | 'city' | 'dungeon'
|
2 |
|
3 |
export interface Scene {
|
4 |
action: string
|
5 |
position: string
|
6 |
+
light: string
|
7 |
+
actionnables: string[]
|
8 |
prompt: string
|
9 |
}
|
10 |
|
src/app/layout.tsx
CHANGED
@@ -5,8 +5,8 @@ import { Inter } from 'next/font/google'
|
|
5 |
const inter = Inter({ subsets: ['latin'] })
|
6 |
|
7 |
export const metadata: Metadata = {
|
8 |
-
title: '
|
9 |
-
description: '
|
10 |
}
|
11 |
|
12 |
export default function RootLayout({
|
|
|
5 |
const inter = Inter({ subsets: ['latin'] })
|
6 |
|
7 |
export const metadata: Metadata = {
|
8 |
+
title: 'Generative AI Adventure',
|
9 |
+
description: 'Generative AI Adventure',
|
10 |
}
|
11 |
|
12 |
export default function RootLayout({
|
src/app/main.tsx
CHANGED
@@ -2,7 +2,7 @@
|
|
2 |
|
3 |
import { useEffect, useRef, useState, useTransition } from "react"
|
4 |
|
5 |
-
import {
|
6 |
|
7 |
import {
|
8 |
Select,
|
@@ -15,20 +15,25 @@ import {
|
|
15 |
import { render } from "./render"
|
16 |
import { Agent, AgentType, Scene } from "./agents/types"
|
17 |
import { agents, defaultAgent, getAgent } from "./agents"
|
|
|
18 |
|
19 |
export default function Main() {
|
20 |
-
const [url, setUrl] = useState<string>()
|
21 |
const [isPending, startTransition] = useTransition()
|
22 |
-
const [agent, setAgent] = useState<Agent>()
|
23 |
const [scene, setScene] = useState<Scene>()
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
const ref = useRef<AgentType>(defaultAgent)
|
25 |
|
26 |
useEffect(() => {
|
27 |
|
28 |
const updateView = async () => {
|
29 |
-
console.log(`update view..`)
|
30 |
|
31 |
-
startTransition(async () => {
|
32 |
|
33 |
// console.log(`getting agent..`)
|
34 |
const type = ref?.current
|
@@ -38,19 +43,27 @@ export default function Main() {
|
|
38 |
const scene = agent.simulate()
|
39 |
|
40 |
// console.log(`rendering scene..`)
|
41 |
-
const
|
|
|
|
|
|
|
42 |
|
43 |
if (type !== ref?.current) {
|
44 |
-
console.log("agent type changed
|
45 |
setTimeout(() => { updateView() }, 0)
|
46 |
return
|
47 |
}
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
54 |
})
|
55 |
}
|
56 |
|
@@ -62,12 +75,17 @@ export default function Main() {
|
|
62 |
<div className="flex flex-col w-full pt-4">
|
63 |
<div className="flex flex-col space-y-3 px-2">
|
64 |
<div className="flex flex-row items-center space-x-3">
|
65 |
-
<label className="flex">
|
66 |
<Select
|
67 |
defaultValue={defaultAgent}
|
68 |
onValueChange={(value) => {
|
69 |
ref.current = value as AgentType
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
71 |
}}>
|
72 |
<SelectTrigger className="w-[180px]">
|
73 |
<SelectValue placeholder="Type" />
|
@@ -79,12 +97,21 @@ export default function Main() {
|
|
79 |
</SelectContent>
|
80 |
</Select>
|
81 |
</div>
|
82 |
-
|
|
|
|
|
83 |
<p>Action: {scene.action}</p>
|
84 |
<p>Position: {scene.position}</p>
|
|
|
85 |
</div> : null}
|
|
|
|
|
|
|
|
|
|
|
|
|
86 |
</div>
|
87 |
-
<
|
88 |
</div>
|
89 |
)
|
90 |
}
|
|
|
2 |
|
3 |
import { useEffect, useRef, useState, useTransition } from "react"
|
4 |
|
5 |
+
import { ImageRenderer } from "@/components/business/image-renderer"
|
6 |
|
7 |
import {
|
8 |
Select,
|
|
|
15 |
import { render } from "./render"
|
16 |
import { Agent, AgentType, Scene } from "./agents/types"
|
17 |
import { agents, defaultAgent, getAgent } from "./agents"
|
18 |
+
import { ImageSegment, RenderedScene } from "./types"
|
19 |
|
20 |
export default function Main() {
|
|
|
21 |
const [isPending, startTransition] = useTransition()
|
|
|
22 |
const [scene, setScene] = useState<Scene>()
|
23 |
+
const [rendered, setRendered] = useState<RenderedScene>({
|
24 |
+
assetUrl: "",
|
25 |
+
error: "",
|
26 |
+
maskBase64: "",
|
27 |
+
segments:[]
|
28 |
+
})
|
29 |
const ref = useRef<AgentType>(defaultAgent)
|
30 |
|
31 |
useEffect(() => {
|
32 |
|
33 |
const updateView = async () => {
|
34 |
+
// console.log(`update view..`)
|
35 |
|
36 |
+
await startTransition(async () => {
|
37 |
|
38 |
// console.log(`getting agent..`)
|
39 |
const type = ref?.current
|
|
|
43 |
const scene = agent.simulate()
|
44 |
|
45 |
// console.log(`rendering scene..`)
|
46 |
+
const newRendered = await render(
|
47 |
+
scene.prompt,
|
48 |
+
scene.actionnables.slice(0, 5) // too many can slow us down it seems
|
49 |
+
)
|
50 |
|
51 |
if (type !== ref?.current) {
|
52 |
+
console.log("agent type changed! reloading scene")
|
53 |
setTimeout(() => { updateView() }, 0)
|
54 |
return
|
55 |
}
|
56 |
|
57 |
+
|
58 |
+
if (newRendered.assetUrl) {
|
59 |
+
setRendered(newRendered)
|
60 |
+
// console.log(`got a new url: ${newUrl}`)
|
61 |
+
setScene(scene)
|
62 |
+
setTimeout(() => { updateView()}, 1000)
|
63 |
+
} else {
|
64 |
+
// console.log(`going to wait a bit more: ${newUrl}`)
|
65 |
+
setTimeout(() => { updateView()}, newRendered.error ? 6000 : 3000)
|
66 |
+
}
|
67 |
})
|
68 |
}
|
69 |
|
|
|
75 |
<div className="flex flex-col w-full pt-4">
|
76 |
<div className="flex flex-col space-y-3 px-2">
|
77 |
<div className="flex flex-row items-center space-x-3">
|
78 |
+
<label className="flex">Select a game:</label>
|
79 |
<Select
|
80 |
defaultValue={defaultAgent}
|
81 |
onValueChange={(value) => {
|
82 |
ref.current = value as AgentType
|
83 |
+
setRendered({
|
84 |
+
assetUrl: "",
|
85 |
+
error: "",
|
86 |
+
maskBase64: "",
|
87 |
+
segments:[]
|
88 |
+
})
|
89 |
}}>
|
90 |
<SelectTrigger className="w-[180px]">
|
91 |
<SelectValue placeholder="Type" />
|
|
|
97 |
</SelectContent>
|
98 |
</Select>
|
99 |
</div>
|
100 |
+
<p>Note: changing the model might take up to 1 minute</p>
|
101 |
+
|
102 |
+
{(scene) ? <div>
|
103 |
<p>Action: {scene.action}</p>
|
104 |
<p>Position: {scene.position}</p>
|
105 |
+
<p>Light: {scene.light}</p>
|
106 |
</div> : null}
|
107 |
+
<div className="flex flex-col">
|
108 |
+
{rendered.segments.map((segment, i) =>
|
109 |
+
<div key={i}>
|
110 |
+
{segment.label} ({segment.score})
|
111 |
+
</div>)}
|
112 |
+
</div>
|
113 |
</div>
|
114 |
+
<ImageRenderer rendered={rendered} />
|
115 |
</div>
|
116 |
)
|
117 |
}
|
src/app/render.ts
CHANGED
@@ -1,10 +1,19 @@
|
|
1 |
"use server"
|
2 |
|
|
|
|
|
3 |
// note: there is no / at the end in the variable
|
4 |
// so we have to add it ourselves if needed
|
5 |
const apiUrl = process.env.RENDERING_ENGINE_API
|
6 |
|
7 |
-
export async function render(prompt: string) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
try {
|
9 |
console.log(`calling ${apiUrl}/render with prompt: ${prompt}`)
|
10 |
const res = await fetch(`${apiUrl}/render`, {
|
@@ -14,12 +23,20 @@ export async function render(prompt: string) {
|
|
14 |
"Content-Type": "application/json",
|
15 |
// Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
|
16 |
},
|
17 |
-
body: JSON.stringify({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
cache: 'no-store',
|
19 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
20 |
// next: { revalidate: 1 }
|
21 |
})
|
22 |
|
|
|
23 |
// The return value is *not* serialized
|
24 |
// You can return Date, Map, Set, etc.
|
25 |
|
@@ -29,11 +46,11 @@ export async function render(prompt: string) {
|
|
29 |
throw new Error('Failed to fetch data')
|
30 |
}
|
31 |
|
32 |
-
const
|
33 |
-
|
34 |
-
return
|
35 |
} catch (err) {
|
36 |
console.error(err)
|
37 |
-
return
|
38 |
}
|
39 |
}
|
|
|
1 |
"use server"
|
2 |
|
3 |
+
import { RenderedScene } from "./types"
|
4 |
+
|
5 |
// note: there is no / at the end in the variable
|
6 |
// so we have to add it ourselves if needed
|
7 |
const apiUrl = process.env.RENDERING_ENGINE_API
|
8 |
|
9 |
+
export async function render(prompt: string, actionnables: string[] = []) {
|
10 |
+
let defaulResult: RenderedScene = {
|
11 |
+
assetUrl: "",
|
12 |
+
maskBase64: "",
|
13 |
+
error: "",
|
14 |
+
segments: []
|
15 |
+
}
|
16 |
+
|
17 |
try {
|
18 |
console.log(`calling ${apiUrl}/render with prompt: ${prompt}`)
|
19 |
const res = await fetch(`${apiUrl}/render`, {
|
|
|
23 |
"Content-Type": "application/json",
|
24 |
// Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
|
25 |
},
|
26 |
+
body: JSON.stringify({
|
27 |
+
prompt,
|
28 |
+
// nbFrames: 8 and nbSteps: 15 --> ~10 sec generation
|
29 |
+
nbFrames: 1, // when nbFrames is 1, we will only generate static images
|
30 |
+
nbSteps: 20,
|
31 |
+
actionnables,
|
32 |
+
segmentation: "firstframe", // one day we will remove this param, to make it automatic
|
33 |
+
}),
|
34 |
cache: 'no-store',
|
35 |
// we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
|
36 |
// next: { revalidate: 1 }
|
37 |
})
|
38 |
|
39 |
+
// console.log("res:", res)
|
40 |
// The return value is *not* serialized
|
41 |
// You can return Date, Map, Set, etc.
|
42 |
|
|
|
46 |
throw new Error('Failed to fetch data')
|
47 |
}
|
48 |
|
49 |
+
const response = (await res.json()) as RenderedScene
|
50 |
+
// console.log("response:", response)
|
51 |
+
return response
|
52 |
} catch (err) {
|
53 |
console.error(err)
|
54 |
+
return defaulResult
|
55 |
}
|
56 |
}
|
src/app/types.ts
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
export interface RenderRequest {
|
3 |
+
prompt: string
|
4 |
+
|
5 |
+
// whether to use video segmentation
|
6 |
+
// disabled (default)
|
7 |
+
// firstframe: we only analyze the first frame
|
8 |
+
// allframes: we analyze all the frames
|
9 |
+
segmentation: 'disabled' | 'firstframe' | 'allframes'
|
10 |
+
|
11 |
+
// segmentation will only be executed if we have a non-empty list of actionnables
|
12 |
+
// actionnables are names of things like "chest", "key", "tree", "chair" etc
|
13 |
+
actionnables: string[]
|
14 |
+
|
15 |
+
// note: this is the number of frames for Zeroscope,
|
16 |
+
// which is currently configured to only output 3 seconds, so:
|
17 |
+
// nbFrames=8 -> 1 sec
|
18 |
+
// nbFrames=16 -> 2 sec
|
19 |
+
// nbFrames=24 -> 3 sec
|
20 |
+
nbFrames: number // min: 1, max: 24
|
21 |
+
|
22 |
+
nbSteps: number // min: 1, max: 50
|
23 |
+
|
24 |
+
seed: number
|
25 |
+
}
|
26 |
+
|
27 |
+
export interface ImageSegment {
|
28 |
+
id: number
|
29 |
+
box: number[]
|
30 |
+
color: number[]
|
31 |
+
label: string
|
32 |
+
score: number
|
33 |
+
}
|
34 |
+
|
35 |
+
export interface RenderedScene {
|
36 |
+
assetUrl: string
|
37 |
+
error: string
|
38 |
+
maskBase64: string
|
39 |
+
segments: ImageSegment[]
|
40 |
+
}
|
src/components/business/image-renderer.tsx
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useRef } from "react"
|
2 |
+
|
3 |
+
import { ImageSegment, RenderedScene } from "@/app/types"
|
4 |
+
|
5 |
+
export const ImageRenderer = ({
|
6 |
+
assetUrl = "",
|
7 |
+
maskBase64 = "",
|
8 |
+
segments = []
|
9 |
+
}: RenderedScene) => {
|
10 |
+
const imgRef = useRef<HTMLImageElement | null>(null)
|
11 |
+
const canvas = document.createElement('canvas')
|
12 |
+
const context = canvas.getContext('2d')
|
13 |
+
|
14 |
+
const getPixelColor = (x: number, y: number) => {
|
15 |
+
console.log("getting pixel color")
|
16 |
+
if (!context) {
|
17 |
+
throw new Error("Unable to get context from canvas")
|
18 |
+
}
|
19 |
+
|
20 |
+
const imgData = context.getImageData(x, y, 1, 1).data
|
21 |
+
|
22 |
+
return `[${imgData[0]},${imgData[1]},${imgData[2]},${imgData[3]/255}]`
|
23 |
+
}
|
24 |
+
|
25 |
+
const handleMouseDown = (event: React.MouseEvent) => {
|
26 |
+
const boundingRect = imgRef.current!.getBoundingClientRect()
|
27 |
+
|
28 |
+
const x = event.clientX - boundingRect.left
|
29 |
+
const y = event.clientY - boundingRect.top
|
30 |
+
|
31 |
+
if (maskBase64) {
|
32 |
+
const image = new Image()
|
33 |
+
|
34 |
+
image.onload = function () {
|
35 |
+
if (context) {
|
36 |
+
context.drawImage(image, 0, 0)
|
37 |
+
|
38 |
+
const clickedColor = getPixelColor(x, y) as any
|
39 |
+
|
40 |
+
let closestSegment: ImageSegment | null = null
|
41 |
+
|
42 |
+
let minDistance = Infinity
|
43 |
+
|
44 |
+
segments.forEach(segment => {
|
45 |
+
const segmentColor = segment.color.slice(0,3) // get the RGB part only
|
46 |
+
|
47 |
+
const distance = Math.sqrt(
|
48 |
+
Math.pow(clickedColor[0] - segmentColor[0], 2) +
|
49 |
+
Math.pow(clickedColor[1] - segmentColor[1], 2) +
|
50 |
+
Math.pow(clickedColor[2] - segmentColor[2], 2)
|
51 |
+
)
|
52 |
+
|
53 |
+
if(distance < minDistance) {
|
54 |
+
minDistance = distance;
|
55 |
+
closestSegment = segment;
|
56 |
+
}
|
57 |
+
})
|
58 |
+
|
59 |
+
console.log("closestSegment:", closestSegment)
|
60 |
+
console.log(closestSegment) // Here is the closest matching segment
|
61 |
+
}
|
62 |
+
}
|
63 |
+
|
64 |
+
image.src = maskBase64
|
65 |
+
// image.src = "data:image/png;base64," + maskBase64;
|
66 |
+
} else {
|
67 |
+
console.log("No mask available, aborting..")
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
|
72 |
+
if (!assetUrl) {
|
73 |
+
return <div className="flex w-full h-screen items-center justify-center text-center">
|
74 |
+
<div>Rendering first frame.. (might take around 30s)</div>
|
75 |
+
</div>
|
76 |
+
}
|
77 |
+
|
78 |
+
return (
|
79 |
+
<div className="w-full py-8 px-2">
|
80 |
+
<img
|
81 |
+
src={assetUrl}
|
82 |
+
ref={imgRef}
|
83 |
+
className="w-full rounded-md overflow-hidden"
|
84 |
+
onMouseDown={handleMouseDown}
|
85 |
+
/>
|
86 |
+
</div>
|
87 |
+
)
|
88 |
+
}
|
src/components/business/{video-player.tsx β video-renderer.tsx}
RENAMED
@@ -1,10 +1,10 @@
|
|
1 |
"use client"
|
2 |
|
3 |
-
export const
|
4 |
|
5 |
if (!url) {
|
6 |
return <div className="flex w-full h-screen items-center justify-center text-center">
|
7 |
-
<div>
|
8 |
</div>
|
9 |
}
|
10 |
|
|
|
1 |
"use client"
|
2 |
|
3 |
+
export const VideoRenderer = ({ url }: { url?: string }) => {
|
4 |
|
5 |
if (!url) {
|
6 |
return <div className="flex w-full h-screen items-center justify-center text-center">
|
7 |
+
<div>Rendering first frames.. (might take around 30s)</div>
|
8 |
</div>
|
9 |
}
|
10 |
|