Spaces:
Runtime error
Runtime error
txt2img and inpainting
Browse files- frontend/src/lib/App.svelte +86 -6
- frontend/src/lib/Canvas.svelte +8 -3
- frontend/src/lib/Menu.svelte +13 -2
- frontend/src/lib/store.ts +2 -1
- frontend/vite.config.dev.ts +6 -1
frontend/src/lib/App.svelte
CHANGED
@@ -17,7 +17,8 @@
|
|
17 |
isPrompting,
|
18 |
clickedPosition,
|
19 |
imagesList,
|
20 |
-
showFrames
|
|
|
21 |
} from '$lib/store';
|
22 |
import { base64ToBlob, uploadImage } from '$lib/utils';
|
23 |
/**
|
@@ -26,6 +27,10 @@
|
|
26 |
*/
|
27 |
|
28 |
export let room: Room;
|
|
|
|
|
|
|
|
|
29 |
onMount(() => {});
|
30 |
|
31 |
async function onClose(e: CustomEvent) {
|
@@ -37,22 +42,91 @@
|
|
37 |
$isPrompting = false;
|
38 |
console.log('prompt', prompt, imgURLs);
|
39 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
async function generateImage(_prompt: string) {
|
|
|
|
|
41 |
if (!_prompt || $isLoading == true) return;
|
42 |
$loadingState = 'Pending';
|
43 |
$isLoading = true;
|
44 |
const sessionHash = crypto.randomUUID();
|
45 |
-
|
46 |
const payload = {
|
47 |
fn_index: 0,
|
48 |
data: [
|
49 |
-
null,
|
50 |
-
//{ mask: null, image: null },
|
51 |
_prompt,
|
52 |
-
|
53 |
],
|
54 |
session_hash: sessionHash
|
55 |
};
|
|
|
|
|
56 |
const websocket = new WebSocket(PUBLIC_WS_INPAINTING);
|
57 |
// websocket.onopen = async function (event) {
|
58 |
// websocket.send(JSON.stringify({ hash: sessionHash }));
|
@@ -127,8 +201,13 @@
|
|
127 |
{#if $isPrompting}
|
128 |
<PromptModal on:prompt={onPrompt} on:close={onClose} />
|
129 |
{/if}
|
|
|
|
|
|
|
|
|
|
|
130 |
<div class="fixed top-0 left-0 z-0 w-screen h-screen">
|
131 |
-
<Canvas />
|
132 |
|
133 |
<main class="z-10 relative">
|
134 |
{#if $imagesList && $showFrames}
|
@@ -174,6 +253,7 @@
|
|
174 |
{/if}
|
175 |
</main>
|
176 |
</div>
|
|
|
177 |
<div class="fixed bottom-0 left-0 right-0 z-10 my-2">
|
178 |
<Menu />
|
179 |
</div>
|
|
|
17 |
isPrompting,
|
18 |
clickedPosition,
|
19 |
imagesList,
|
20 |
+
showFrames,
|
21 |
+
text2img
|
22 |
} from '$lib/store';
|
23 |
import { base64ToBlob, uploadImage } from '$lib/utils';
|
24 |
/**
|
|
|
27 |
*/
|
28 |
|
29 |
export let room: Room;
|
30 |
+
let CA: HTMLCanvasElement;
|
31 |
+
let CB: HTMLCanvasElement;
|
32 |
+
let canvasEl: HTMLCanvasElement;
|
33 |
+
|
34 |
onMount(() => {});
|
35 |
|
36 |
async function onClose(e: CustomEvent) {
|
|
|
42 |
$isPrompting = false;
|
43 |
console.log('prompt', prompt, imgURLs);
|
44 |
}
|
45 |
+
function getImageMask(cursor: { x: number; y: number }) {
|
46 |
+
const blackImage = document.createElement('canvas');
|
47 |
+
const canvasCrop = document.createElement('canvas');
|
48 |
+
const mask = document.createElement('canvas');
|
49 |
+
|
50 |
+
blackImage.width = 512;
|
51 |
+
blackImage.height = 512;
|
52 |
+
canvasCrop.width = 512;
|
53 |
+
canvasCrop.height = 512;
|
54 |
+
mask.width = 512;
|
55 |
+
mask.height = 512;
|
56 |
+
|
57 |
+
const blackImageCtx = blackImage.getContext('2d') as CanvasRenderingContext2D;
|
58 |
+
const ctxCrop = canvasCrop.getContext('2d') as CanvasRenderingContext2D;
|
59 |
+
const ctxMask = mask.getContext('2d') as CanvasRenderingContext2D;
|
60 |
+
|
61 |
+
// crop image from point canvas
|
62 |
+
ctxCrop.save();
|
63 |
+
ctxCrop.clearRect(0, 0, 512, 512);
|
64 |
+
|
65 |
+
const imageData = ctxCrop.getImageData(0, 0, 512, 512);
|
66 |
+
const pix = imageData.data;
|
67 |
+
for (let i = 0, n = pix.length; i < n; i += 4) {
|
68 |
+
pix[i] = Math.round(255 * Math.random());
|
69 |
+
pix[i + 1] = Math.round(255 * Math.random());
|
70 |
+
pix[i + 2] = Math.round(255 * Math.random());
|
71 |
+
pix[i + 3] = 255;
|
72 |
+
}
|
73 |
+
// ctxCrop.putImageData(imageData, 0, 0);
|
74 |
+
ctxCrop.globalCompositeOperation = 'source-over';
|
75 |
+
ctxCrop.drawImage(canvasEl, cursor.x, cursor.y, 512, 512, 0, 0, 512, 512);
|
76 |
+
ctxCrop.restore();
|
77 |
+
|
78 |
+
// create black image
|
79 |
+
blackImageCtx.fillStyle = 'black';
|
80 |
+
blackImageCtx.fillRect(0, 0, 512, 512);
|
81 |
+
|
82 |
+
// create Mask
|
83 |
+
ctxMask.save();
|
84 |
+
// ctxMask.clearRect(0, 0, 512, 512);
|
85 |
+
ctxMask.drawImage(canvasCrop, 0, 0, 512, 512);
|
86 |
+
ctxMask.globalCompositeOperation = 'source-in';
|
87 |
+
ctxMask.drawImage(blackImage, 0, 0);
|
88 |
+
ctxMask.restore();
|
89 |
+
|
90 |
+
const contextA = CA.getContext('2d') as CanvasRenderingContext2D;
|
91 |
+
const contextB = CB.getContext('2d') as CanvasRenderingContext2D;
|
92 |
+
|
93 |
+
// draw image to canvas A
|
94 |
+
contextA.save();
|
95 |
+
contextA.clearRect(0, 0, 512, 512);
|
96 |
+
contextA.drawImage(canvasCrop, 0, 0, 512, 512);
|
97 |
+
contextA.restore();
|
98 |
+
|
99 |
+
// draw mask to canvas B
|
100 |
+
contextB.save();
|
101 |
+
contextB.clearRect(0, 0, 512, 512);
|
102 |
+
contextB.drawImage(mask, 0, 0, 512, 512);
|
103 |
+
contextB.restore();
|
104 |
+
|
105 |
+
//convert canvas to base64
|
106 |
+
const base64Crop = canvasCrop.toDataURL('image/png');
|
107 |
+
const base64Mask = mask.toDataURL('image/png');
|
108 |
+
return { image: base64Crop, mask: base64Mask };
|
109 |
+
}
|
110 |
+
|
111 |
async function generateImage(_prompt: string) {
|
112 |
+
// getImageMask($clickedPosition);
|
113 |
+
// return;
|
114 |
if (!_prompt || $isLoading == true) return;
|
115 |
$loadingState = 'Pending';
|
116 |
$isLoading = true;
|
117 |
const sessionHash = crypto.randomUUID();
|
|
|
118 |
const payload = {
|
119 |
fn_index: 0,
|
120 |
data: [
|
121 |
+
$text2img ? null : getImageMask($clickedPosition),
|
122 |
+
// { mask: null, image: null },
|
123 |
_prompt,
|
124 |
+
$text2img
|
125 |
],
|
126 |
session_hash: sessionHash
|
127 |
};
|
128 |
+
console.log('payload', payload);
|
129 |
+
|
130 |
const websocket = new WebSocket(PUBLIC_WS_INPAINTING);
|
131 |
// websocket.onopen = async function (event) {
|
132 |
// websocket.send(JSON.stringify({ hash: sessionHash }));
|
|
|
201 |
{#if $isPrompting}
|
202 |
<PromptModal on:prompt={onPrompt} on:close={onClose} />
|
203 |
{/if}
|
204 |
+
|
205 |
+
<div class="flex">
|
206 |
+
<canvas bind:this={CA} width="512" height="512" />
|
207 |
+
<canvas bind:this={CB} width="512" height="512" />
|
208 |
+
</div>
|
209 |
<div class="fixed top-0 left-0 z-0 w-screen h-screen">
|
210 |
+
<Canvas bind:value={canvasEl} />
|
211 |
|
212 |
<main class="z-10 relative">
|
213 |
{#if $imagesList && $showFrames}
|
|
|
253 |
{/if}
|
254 |
</main>
|
255 |
</div>
|
256 |
+
|
257 |
<div class="fixed bottom-0 left-0 right-0 z-10 my-2">
|
258 |
<Menu />
|
259 |
</div>
|
frontend/src/lib/Canvas.svelte
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
import { select } from 'd3-selection';
|
4 |
import { scaleLinear } from 'd3-scale';
|
5 |
import { onMount } from 'svelte';
|
|
|
6 |
import {
|
7 |
currZoomTransform,
|
8 |
myPresence,
|
@@ -15,6 +16,9 @@
|
|
15 |
const width = 512 * 5;
|
16 |
|
17 |
let canvasEl: HTMLCanvasElement;
|
|
|
|
|
|
|
18 |
let containerEl: HTMLDivElement;
|
19 |
let canvasCtx: CanvasRenderingContext2D;
|
20 |
let xScale: (x: number) => number;
|
@@ -47,6 +51,7 @@
|
|
47 |
.on('dblclick.zoom', () => {
|
48 |
$isPrompting = true;
|
49 |
$clickedPosition = $myPresence.cursor;
|
|
|
50 |
return null;
|
51 |
})
|
52 |
// .call(zoomHandler.scaleTo as any, 1 / scale)
|
@@ -61,13 +66,13 @@
|
|
61 |
|
62 |
function renderImages(imagesList) {
|
63 |
imagesList.forEach(({ imgURL, position }) => {
|
64 |
-
// console.log(item);
|
65 |
const img = new Image();
|
66 |
img.onload = () => {
|
67 |
-
console.log(img);
|
68 |
canvasCtx.drawImage(img, position.x, position.y, img.width, img.height);
|
69 |
};
|
70 |
-
|
|
|
|
|
71 |
});
|
72 |
}
|
73 |
|
|
|
3 |
import { select } from 'd3-selection';
|
4 |
import { scaleLinear } from 'd3-scale';
|
5 |
import { onMount } from 'svelte';
|
6 |
+
import { dev } from '$app/environment';
|
7 |
import {
|
8 |
currZoomTransform,
|
9 |
myPresence,
|
|
|
16 |
const width = 512 * 5;
|
17 |
|
18 |
let canvasEl: HTMLCanvasElement;
|
19 |
+
export { canvasEl as value };
|
20 |
+
let value = canvasEl;
|
21 |
+
|
22 |
let containerEl: HTMLDivElement;
|
23 |
let canvasCtx: CanvasRenderingContext2D;
|
24 |
let xScale: (x: number) => number;
|
|
|
51 |
.on('dblclick.zoom', () => {
|
52 |
$isPrompting = true;
|
53 |
$clickedPosition = $myPresence.cursor;
|
54 |
+
console.log('clicked', $clickedPosition);
|
55 |
return null;
|
56 |
})
|
57 |
// .call(zoomHandler.scaleTo as any, 1 / scale)
|
|
|
66 |
|
67 |
function renderImages(imagesList) {
|
68 |
imagesList.forEach(({ imgURL, position }) => {
|
|
|
69 |
const img = new Image();
|
70 |
img.onload = () => {
|
|
|
71 |
canvasCtx.drawImage(img, position.x, position.y, img.width, img.height);
|
72 |
};
|
73 |
+
const base = dev ? 'uploads/' : '';
|
74 |
+
const url = imgURL.split('/');
|
75 |
+
img.src = dev ? `uploads/${url.slice(3).join('/')}` : imgURL;
|
76 |
});
|
77 |
}
|
78 |
|
frontend/src/lib/Menu.svelte
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { showFrames } from '$lib/store';
|
3 |
</script>
|
4 |
|
5 |
-
<div class="grid grid-cols-
|
6 |
<div class="flex items-center">
|
7 |
<input
|
8 |
id="showframes"
|
@@ -14,6 +14,17 @@
|
|
14 |
>Show Frames</label
|
15 |
>
|
16 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
<button class="button" title="Add Prompt"> Add Prompt </button>
|
18 |
<button class="button-paint bg-violet-100 text-violet-900" title="New Paint Frame">
|
19 |
<span
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { showFrames, text2img } from '$lib/store';
|
3 |
</script>
|
4 |
|
5 |
+
<div class="grid grid-cols-4 gap-3 text-sm w-max mx-auto">
|
6 |
<div class="flex items-center">
|
7 |
<input
|
8 |
id="showframes"
|
|
|
14 |
>Show Frames</label
|
15 |
>
|
16 |
</div>
|
17 |
+
<div class="flex items-center">
|
18 |
+
<input
|
19 |
+
id="txt2img"
|
20 |
+
type="checkbox"
|
21 |
+
bind:checked={$text2img}
|
22 |
+
class="w-4 h-4 text-blue-600 bg-gray-100 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600 cursor-pointer"
|
23 |
+
/>
|
24 |
+
<label for="txt2img" class="text-black dark:text-white cursor-pointer ml-2"
|
25 |
+
>Text2Image</label
|
26 |
+
>
|
27 |
+
</div>
|
28 |
<button class="button" title="Add Prompt"> Add Prompt </button>
|
29 |
<button class="button-paint bg-violet-100 text-violet-900" title="New Paint Frame">
|
30 |
<span
|
frontend/src/lib/store.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { writable
|
2 |
import type { Room } from '@liveblocks/client';
|
3 |
|
4 |
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
@@ -8,6 +8,7 @@ export const isLoading = writable<boolean>(false);
|
|
8 |
export const isPrompting = writable<boolean>(false);
|
9 |
export const clickedPosition = writable<{ x: number; y: number }>();
|
10 |
export const showFrames = writable<boolean>(false);
|
|
|
11 |
|
12 |
export const currZoomTransform = writable<ZoomTransform>(zoomIdentity);
|
13 |
|
|
|
1 |
+
import { writable } from 'svelte/store';
|
2 |
import type { Room } from '@liveblocks/client';
|
3 |
|
4 |
import { type ZoomTransform, zoomIdentity } from 'd3-zoom';
|
|
|
8 |
export const isPrompting = writable<boolean>(false);
|
9 |
export const clickedPosition = writable<{ x: number; y: number }>();
|
10 |
export const showFrames = writable<boolean>(false);
|
11 |
+
export const text2img = writable<boolean>(true);
|
12 |
|
13 |
export const currZoomTransform = writable<ZoomTransform>(zoomIdentity);
|
14 |
|
frontend/vite.config.dev.ts
CHANGED
@@ -11,9 +11,14 @@ const config: UserConfig = {
|
|
11 |
changeOrigin: true,
|
12 |
cookieDomainRewrite: 'localhost',
|
13 |
rewrite: (path) => path.replace(/^\/moon/, '')
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
}
|
15 |
}
|
16 |
}
|
17 |
};
|
18 |
-
|
19 |
export default config;
|
|
|
11 |
changeOrigin: true,
|
12 |
cookieDomainRewrite: 'localhost',
|
13 |
rewrite: (path) => path.replace(/^\/moon/, '')
|
14 |
+
},
|
15 |
+
'/uploads': {
|
16 |
+
target: 'https://s3.amazonaws.com',
|
17 |
+
changeOrigin: true,
|
18 |
+
cookieDomainRewrite: 'localhost',
|
19 |
+
rewrite: (path) => path.replace(/^\/uploads/, '')
|
20 |
}
|
21 |
}
|
22 |
}
|
23 |
};
|
|
|
24 |
export default config;
|