Spaces:
Build error
Build error
svelte frontend
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitignore +11 -0
- Makefile +11 -0
- README.md +3 -1
- app.py +18 -0
- frontend/.eslintignore +13 -0
- frontend/.eslintrc.cjs +20 -0
- frontend/.gitignore +11 -0
- frontend/.npmrc +1 -0
- frontend/.prettierignore +13 -0
- frontend/.prettierrc +6 -0
- frontend/README.md +38 -0
- frontend/package-lock.json +0 -0
- frontend/package.json +43 -0
- frontend/postcss.config.cjs +6 -0
- frontend/src/app.css +10 -0
- frontend/src/app.d.ts +16 -0
- frontend/src/app.html +13 -0
- frontend/src/data.ts +69 -0
- frontend/src/lib/BrushSelector.svelte +85 -0
- frontend/src/lib/DrawingCanvas.svelte +136 -0
- frontend/src/lib/ParamsSelector.svelte +73 -0
- frontend/src/lib/ResultCanvas.svelte +126 -0
- frontend/src/lib/TemplateGallery.svelte +63 -0
- frontend/src/lib/store.ts +16 -0
- frontend/src/lib/utils.ts +3 -0
- frontend/src/routes/__layout.svelte +5 -0
- frontend/src/routes/index.svelte +82 -0
- frontend/src/types.ts +24 -0
- frontend/static/favicon.png +0 -0
- frontend/static/robots.txt +3 -0
- frontend/static/samples/MEN-Pants-id_00002565-02_1_front_segm.png +0 -0
- frontend/static/samples/MEN-Pants-id_00005213-02_4_full_segm.png +0 -0
- frontend/static/samples/WOMEN-Blouses_Shirts-id_00002356-02_4_full_segm.png +0 -0
- frontend/static/samples/WOMEN-Blouses_Shirts-id_00004090-03_7_additional_segm.png +0 -0
- frontend/static/samples/WOMEN-Cardigans-id_00000853-01_2_side_segm.png +0 -0
- frontend/static/samples/WOMEN-Cardigans-id_00000899-02_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Cardigans-id_00006462-02_7_additional_segm.png +0 -0
- frontend/static/samples/WOMEN-Dresses-id_00000021-05_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Dresses-id_00002430-04_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Dresses-id_00002966-01_7_additional_segm.png +0 -0
- frontend/static/samples/WOMEN-Dresses-id_00007332-01_3_back_segm.png +0 -0
- frontend/static/samples/WOMEN-Graphic_Tees-id_00007242-01_4_full_segm.png +0 -0
- frontend/static/samples/WOMEN-Jackets_Coats-id_00005263-06_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Jackets_Coats-id_00006296-05_7_additional_segm.png +0 -0
- frontend/static/samples/WOMEN-Rompers_Jumpsuits-id_00004575-02_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Skirts-id_00006761-01_1_front_segm.png +0 -0
- frontend/static/samples/WOMEN-Sweaters-id_00004667-01_4_full_segm.png +0 -0
- frontend/static/samples/WOMEN-Tees_Tanks-id_00001620-02_4_full_segm.png +0 -0
- frontend/static/samples/WOMEN-Tees_Tanks-id_00005288-01_2_side_segm.png +0 -0
- frontend/static/samples/WOMEN-Tees_Tanks-id_00006566-04_4_full_segm.png +0 -0
.gitignore
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
.vercel
|
10 |
+
.output
|
11 |
+
venv/
|
Makefile
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
install-node:
|
2 |
+
./install-node.sh
|
3 |
+
build-client:
|
4 |
+
cd frontend && npm install && npm run build && rm -rf ../static && cp -r build/ ../static/
|
5 |
+
build-dev:
|
6 |
+
cd frontend && npm install && NODE_ENV=development npm run build && rm -rf ../static 2>&1 && cp -rv build/ ../static/
|
7 |
+
run-dev:
|
8 |
+
FLASK_ENV=development python app.py
|
9 |
+
run-prod:
|
10 |
+
python app.py
|
11 |
+
build-all: run-prod
|
README.md
CHANGED
@@ -3,8 +3,10 @@ title: Drawings to Human
|
|
3 |
emoji: ✍️🧍🏽♀️🧍🏻
|
4 |
colorFrom: blue
|
5 |
colorTo: blue
|
6 |
-
sdk:
|
|
|
7 |
pinned: false
|
|
|
8 |
---
|
9 |
|
10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
3 |
emoji: ✍️🧍🏽♀️🧍🏻
|
4 |
colorFrom: blue
|
5 |
colorTo: blue
|
6 |
+
sdk: gradio
|
7 |
+
sdk_version: 3.0.24
|
8 |
pinned: false
|
9 |
+
app_file: main.py
|
10 |
---
|
11 |
|
12 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from flask import Flask
|
3 |
+
|
4 |
+
|
5 |
+
app = Flask(__name__, static_url_path='/static')
|
6 |
+
|
7 |
+
mode = os.environ.get('FLASK_ENV', 'production')
|
8 |
+
dev = mode == 'development'
|
9 |
+
|
10 |
+
|
11 |
+
@app.route('/')
|
12 |
+
def index():
|
13 |
+
return app.send_static_file('index.html')
|
14 |
+
|
15 |
+
|
16 |
+
if __name__ == '__main__':
|
17 |
+
app.run(host='0.0.0.0', port=int(
|
18 |
+
os.environ.get('PORT', 7860)), debug=True, use_reloader=dev)
|
frontend/.eslintignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
frontend/.eslintrc.cjs
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
root: true,
|
3 |
+
parser: '@typescript-eslint/parser',
|
4 |
+
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
|
5 |
+
plugins: ['svelte3', '@typescript-eslint'],
|
6 |
+
ignorePatterns: ['*.cjs'],
|
7 |
+
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
|
8 |
+
settings: {
|
9 |
+
'svelte3/typescript': () => require('typescript')
|
10 |
+
},
|
11 |
+
parserOptions: {
|
12 |
+
sourceType: 'module',
|
13 |
+
ecmaVersion: 2020
|
14 |
+
},
|
15 |
+
env: {
|
16 |
+
browser: true,
|
17 |
+
es2017: true,
|
18 |
+
node: true
|
19 |
+
}
|
20 |
+
};
|
frontend/.gitignore
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
.vercel
|
10 |
+
.output
|
11 |
+
venv/
|
frontend/.npmrc
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
engine-strict=true
|
frontend/.prettierignore
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.DS_Store
|
2 |
+
node_modules
|
3 |
+
/build
|
4 |
+
/.svelte-kit
|
5 |
+
/package
|
6 |
+
.env
|
7 |
+
.env.*
|
8 |
+
!.env.example
|
9 |
+
|
10 |
+
# Ignore files for PNPM, NPM and YARN
|
11 |
+
pnpm-lock.yaml
|
12 |
+
package-lock.json
|
13 |
+
yarn.lock
|
frontend/.prettierrc
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"useTabs": true,
|
3 |
+
"singleQuote": true,
|
4 |
+
"trailingComma": "none",
|
5 |
+
"printWidth": 100
|
6 |
+
}
|
frontend/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# create-svelte
|
2 |
+
|
3 |
+
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
|
4 |
+
|
5 |
+
## Creating a project
|
6 |
+
|
7 |
+
If you're seeing this, you've probably already done this step. Congrats!
|
8 |
+
|
9 |
+
```bash
|
10 |
+
# create a new project in the current directory
|
11 |
+
npm init svelte
|
12 |
+
|
13 |
+
# create a new project in my-app
|
14 |
+
npm init svelte my-app
|
15 |
+
```
|
16 |
+
|
17 |
+
## Developing
|
18 |
+
|
19 |
+
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
20 |
+
|
21 |
+
```bash
|
22 |
+
npm run dev
|
23 |
+
|
24 |
+
# or start the server and open the app in a new browser tab
|
25 |
+
npm run dev -- --open
|
26 |
+
```
|
27 |
+
|
28 |
+
## Building
|
29 |
+
|
30 |
+
To create a production version of your app:
|
31 |
+
|
32 |
+
```bash
|
33 |
+
npm run build
|
34 |
+
```
|
35 |
+
|
36 |
+
You can preview the production build with `npm run preview`.
|
37 |
+
|
38 |
+
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
|
frontend/package-lock.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
frontend/package.json
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"name": "drawings-to-human",
|
3 |
+
"version": "0.0.1",
|
4 |
+
"scripts": {
|
5 |
+
"dev": "svelte-kit dev",
|
6 |
+
"build": "svelte-kit build",
|
7 |
+
"package": "svelte-kit package",
|
8 |
+
"preview": "svelte-kit preview",
|
9 |
+
"prepare": "svelte-kit sync",
|
10 |
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
11 |
+
"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
|
12 |
+
"lint": "prettier --check --plugin-search-dir=. . && eslint .",
|
13 |
+
"format": "prettier --write --plugin-search-dir=. ."
|
14 |
+
},
|
15 |
+
"devDependencies": {
|
16 |
+
"@sveltejs/adapter-static": "^1.0.0-next.34",
|
17 |
+
"@sveltejs/kit": "next",
|
18 |
+
"@tailwindcss/typography": "^0.5.2",
|
19 |
+
"@types/cookie": "^0.5.1",
|
20 |
+
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
21 |
+
"@typescript-eslint/parser": "^5.27.0",
|
22 |
+
"autoprefixer": "^10.4.7",
|
23 |
+
"eslint": "^8.16.0",
|
24 |
+
"eslint-config-prettier": "^8.3.0",
|
25 |
+
"eslint-plugin-svelte3": "^4.0.0",
|
26 |
+
"postcss": "^8.4.14",
|
27 |
+
"prettier": "^2.6.2",
|
28 |
+
"prettier-plugin-svelte": "^2.7.0",
|
29 |
+
"svelte": "^3.46.0",
|
30 |
+
"svelte-check": "^2.7.1",
|
31 |
+
"svelte-preprocess": "^4.10.7",
|
32 |
+
"tailwindcss": "^3.1.4",
|
33 |
+
"tslib": "^2.3.1",
|
34 |
+
"typescript": "^4.7.2"
|
35 |
+
},
|
36 |
+
"type": "module",
|
37 |
+
"dependencies": {
|
38 |
+
"@fontsource/fira-mono": "^4.5.0",
|
39 |
+
"cookie": "^0.4.1",
|
40 |
+
"nanoid": "^4.0.0",
|
41 |
+
"px-brush": "^1.0.1"
|
42 |
+
}
|
43 |
+
}
|
frontend/postcss.config.cjs
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
module.exports = {
|
2 |
+
plugins: {
|
3 |
+
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
+
}
|
frontend/src/app.css
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@100;200;300;400;500;600;700;800&display=swap');
|
2 |
+
@tailwind base;
|
3 |
+
@tailwind components;
|
4 |
+
@tailwind utilities;
|
5 |
+
|
6 |
+
@layer base {
|
7 |
+
html {
|
8 |
+
font-family: 'Open Sans', sans-serif;
|
9 |
+
}
|
10 |
+
}
|
frontend/src/app.d.ts
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/// <reference types="@sveltejs/kit" />
|
2 |
+
|
3 |
+
// See https://kit.svelte.dev/docs/types#app
|
4 |
+
// for information about these interfaces
|
5 |
+
// and what to do when importing types
|
6 |
+
declare namespace App {
|
7 |
+
interface Locals {
|
8 |
+
userid: string;
|
9 |
+
}
|
10 |
+
|
11 |
+
// interface Platform {}
|
12 |
+
|
13 |
+
// interface Session {}
|
14 |
+
|
15 |
+
// interface Stuff {}
|
16 |
+
}
|
frontend/src/app.html
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="utf-8" />
|
5 |
+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
7 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.1/iframeResizer.contentWindow.min.js"></script>
|
8 |
+
%sveltekit.head%
|
9 |
+
</head>
|
10 |
+
<body class="dark:bg-[rgb(11,15,25)] bg-white dark:text-white text-black">
|
11 |
+
%sveltekit.body%
|
12 |
+
</body>
|
13 |
+
</html>
|
frontend/src/data.ts
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { Color } from './types';
|
2 |
+
|
3 |
+
export const COLOR_LIST: Color[] = [
|
4 |
+
{ color: [0, 0, 0], label: 'background' },
|
5 |
+
{ color: [255, 140, 0], label: 'bag' },
|
6 |
+
{ color: [255, 255, 0], label: 'belt' },
|
7 |
+
{ color: [255, 250, 205], label: 'dress' },
|
8 |
+
{ color: [130, 165, 180], label: 'earrings' },
|
9 |
+
{ color: [0, 100, 0], label: 'eyeglass' },
|
10 |
+
{ color: [16, 78, 139], label: 'face' },
|
11 |
+
{ color: [245, 222, 179], label: 'footwear' },
|
12 |
+
{ color: [213, 140, 88], label: 'gloves' },
|
13 |
+
{ color: [255, 0, 0], label: 'hair' },
|
14 |
+
{ color: [127, 255, 212], label: 'headwear' },
|
15 |
+
{ color: [70, 130, 180], label: 'leggings' },
|
16 |
+
{ color: [90, 140, 90], label: 'necklace' },
|
17 |
+
{ color: [50, 205, 50], label: 'neckwear' },
|
18 |
+
{ color: [220, 220, 220], label: 'outer' },
|
19 |
+
{ color: [211, 211, 211], label: 'pants' },
|
20 |
+
{ color: [50, 205, 174], label: 'ring' },
|
21 |
+
{ color: [185, 210, 205], label: 'rompers' },
|
22 |
+
{ color: [144, 238, 144], label: 'skin' },
|
23 |
+
{ color: [250, 235, 215], label: 'skirt' },
|
24 |
+
{ color: [160, 140, 88], label: 'socks' },
|
25 |
+
{ color: [225, 141, 151], label: 'tie' },
|
26 |
+
{ color: [255, 250, 250], label: 'top' },
|
27 |
+
{ color: [50, 155, 250], label: 'wrist wearing' }
|
28 |
+
];
|
29 |
+
|
30 |
+
// export const API = 'https://hf.space/embed/radames/Text2Human-API';
|
31 |
+
export const API = 'https://hf.space/embed/CVPR/Text2Human';
|
32 |
+
|
33 |
+
export const IMAGES_LIST = [
|
34 |
+
'/samples/MEN-Pants-id_00002565-02_1_front_segm.png',
|
35 |
+
'/samples/MEN-Pants-id_00005213-02_4_full_segm.png',
|
36 |
+
'/samples/WOMEN-Blouses_Shirts-id_00002356-02_4_full_segm.png',
|
37 |
+
'/samples/WOMEN-Blouses_Shirts-id_00004090-03_7_additional_segm.png',
|
38 |
+
'/samples/WOMEN-Cardigans-id_00000853-01_2_side_segm.png',
|
39 |
+
'/samples/WOMEN-Cardigans-id_00000899-02_1_front_segm.png',
|
40 |
+
'/samples/WOMEN-Cardigans-id_00006462-02_7_additional_segm.png',
|
41 |
+
'/samples/WOMEN-Dresses-id_00000021-05_1_front_segm.png',
|
42 |
+
'/samples/WOMEN-Dresses-id_00002430-04_1_front_segm.png',
|
43 |
+
'/samples/WOMEN-Dresses-id_00002966-01_7_additional_segm.png',
|
44 |
+
'/samples/WOMEN-Dresses-id_00007332-01_3_back_segm.png',
|
45 |
+
'/samples/WOMEN-Graphic_Tees-id_00007242-01_4_full_segm.png',
|
46 |
+
'/samples/WOMEN-Jackets_Coats-id_00005263-06_1_front_segm.png',
|
47 |
+
'/samples/WOMEN-Jackets_Coats-id_00006296-05_7_additional_segm.png',
|
48 |
+
'/samples/WOMEN-Rompers_Jumpsuits-id_00004575-02_1_front_segm.png',
|
49 |
+
'/samples/WOMEN-Skirts-id_00006761-01_1_front_segm.png',
|
50 |
+
'/samples/WOMEN-Sweaters-id_00004667-01_4_full_segm.png',
|
51 |
+
'/samples/WOMEN-Tees_Tanks-id_00001620-02_4_full_segm.png',
|
52 |
+
'/samples/WOMEN-Tees_Tanks-id_00005288-01_2_side_segm.png',
|
53 |
+
'/samples/WOMEN-Tees_Tanks-id_00006566-04_4_full_segm.png'
|
54 |
+
];
|
55 |
+
|
56 |
+
|
57 |
+
export const SECTIONS = [
|
58 |
+
"lower clothing texture",
|
59 |
+
"upper clothing texture",
|
60 |
+
"outer clothing texture"
|
61 |
+
];
|
62 |
+
|
63 |
+
export const TEXTURES = [
|
64 |
+
"pure color",
|
65 |
+
"stripe/spline",
|
66 |
+
"plaid/lattice",
|
67 |
+
"floral",
|
68 |
+
"denim"
|
69 |
+
];
|
frontend/src/lib/BrushSelector.svelte
ADDED
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { COLOR_LIST } from '../data';
|
3 |
+
import type { Brush, RGB } from '../types';
|
4 |
+
import { selectedBrush } from '$lib/store';
|
5 |
+
|
6 |
+
const STARTCOLORID = 6;
|
7 |
+
const { color, label } = COLOR_LIST[STARTCOLORID];
|
8 |
+
|
9 |
+
let brushColor: RGB = `rgb(${color.join(',')})` as RGB;
|
10 |
+
let brushSize: number = 40;
|
11 |
+
$selectedBrush = {
|
12 |
+
color: brushColor,
|
13 |
+
size: brushSize,
|
14 |
+
label
|
15 |
+
};
|
16 |
+
const submit = async (e: Event) => {
|
17 |
+
const target = e.target as HTMLInputElement;
|
18 |
+
if (target.name === 'color') {
|
19 |
+
const selectedId = parseInt(target.value);
|
20 |
+
const { color, label } = COLOR_LIST[selectedId];
|
21 |
+
brushColor = `rgb(${color.join(',')})` as RGB;
|
22 |
+
$selectedBrush = {
|
23 |
+
color: brushColor,
|
24 |
+
size: brushSize,
|
25 |
+
label
|
26 |
+
};
|
27 |
+
} else if (target.name === 'brush') {
|
28 |
+
brushSize = parseInt(target.value);
|
29 |
+
$selectedBrush = {
|
30 |
+
color: brushColor,
|
31 |
+
size: brushSize,
|
32 |
+
label
|
33 |
+
};
|
34 |
+
}
|
35 |
+
};
|
36 |
+
</script>
|
37 |
+
|
38 |
+
<form on:input={submit}>
|
39 |
+
<h4 class="font-bold mt-6 mb-2 leading-6 my-3">Set the Brush Type</h4>
|
40 |
+
<div class="colors" name="colors">
|
41 |
+
{#each COLOR_LIST as color, id}
|
42 |
+
<div class="snap-always snap-start">
|
43 |
+
<input name="color" checked={id == STARTCOLORID} type="radio" id="color-{id}" value={id} />
|
44 |
+
<label for="color-{id}">
|
45 |
+
<svg width="20" height="20" viewBox="0 0 20 20">
|
46 |
+
<rect x="0" y="0" width="20" height="20" fill="rgb({color.color.join(',')})" /></svg
|
47 |
+
>
|
48 |
+
<span>{color.label}</span>
|
49 |
+
</label>
|
50 |
+
</div>
|
51 |
+
{/each}
|
52 |
+
</div>
|
53 |
+
<h4 class="font-bold mt-6 mb-2 my-6 leading-6">Set the Brush Size</h4>
|
54 |
+
<div class="brush">
|
55 |
+
<input value="10" min="1" max="50" step="1" name="brush" type="range" />
|
56 |
+
<label class="pl-2" for="brush">{$selectedBrush.size}</label>
|
57 |
+
</div>
|
58 |
+
</form>
|
59 |
+
|
60 |
+
<style lang="postcss" scoped>
|
61 |
+
.colors {
|
62 |
+
@apply grid grid-cols-2 sm:grid-cols-3 gap-2 max-h-[9rem] sm:max-h-[none] overflow-scroll snap-y snap-mandatory;
|
63 |
+
}
|
64 |
+
.colors span {
|
65 |
+
@apply ml-2;
|
66 |
+
}
|
67 |
+
.colors svg {
|
68 |
+
@apply block;
|
69 |
+
}
|
70 |
+
input[type='radio'] {
|
71 |
+
@apply opacity-0 w-0 h-0 absolute hidden;
|
72 |
+
}
|
73 |
+
input[type='radio']:active ~ label {
|
74 |
+
}
|
75 |
+
input[type='radio']:checked ~ label {
|
76 |
+
@apply outline outline-2 outline-yellow-500;
|
77 |
+
}
|
78 |
+
label {
|
79 |
+
@apply cursor-pointer flex transition-all duration-200 ease-in-out whitespace-nowrap
|
80 |
+
hover:outline outline-offset-[-2px] outline-2 outline-yellow-500;
|
81 |
+
}
|
82 |
+
.brush {
|
83 |
+
@apply flex;
|
84 |
+
}
|
85 |
+
</style>
|
frontend/src/lib/DrawingCanvas.svelte
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import PxBrush from 'px-brush';
|
3 |
+
import { onMount } from 'svelte';
|
4 |
+
import type { Brush } from '../types';
|
5 |
+
import { selectedBrush, selectedImageBlob, currentCanvas } from '$lib/store';
|
6 |
+
|
7 |
+
let canvas: HTMLCanvasElement;
|
8 |
+
let brush: HTMLCanvasElement;
|
9 |
+
|
10 |
+
let brushCtx: CanvasRenderingContext2D;
|
11 |
+
let ctx: CanvasRenderingContext2D;
|
12 |
+
let pxBrush: PxBrush;
|
13 |
+
let startPosition: { x: number; y: number } = { x: 0, y: 0 };
|
14 |
+
|
15 |
+
$: {
|
16 |
+
if (brushCtx && $selectedBrush) {
|
17 |
+
setBrush($selectedBrush);
|
18 |
+
brush.style.top = `${10 + $selectedBrush.size / 2}px`;
|
19 |
+
brush.style.left = `${10 + $selectedBrush.size / 2}px`;
|
20 |
+
}
|
21 |
+
}
|
22 |
+
$: {
|
23 |
+
if ($selectedImageBlob) {
|
24 |
+
drawImage(ctx, $selectedImageBlob);
|
25 |
+
}
|
26 |
+
}
|
27 |
+
onMount(() => {
|
28 |
+
ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
|
29 |
+
brushCtx = brush.getContext('2d') as CanvasRenderingContext2D;
|
30 |
+
window.devicePixelRatio = 1;
|
31 |
+
pxBrush = new PxBrush(canvas);
|
32 |
+
$currentCanvas = canvas;
|
33 |
+
clearCanvas(ctx);
|
34 |
+
});
|
35 |
+
|
36 |
+
const drawImage = (ctx: CanvasRenderingContext2D, blob: Blob) => {
|
37 |
+
const img = new Image();
|
38 |
+
img.onload = function () {
|
39 |
+
ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height);
|
40 |
+
};
|
41 |
+
img.src = URL.createObjectURL(blob);
|
42 |
+
};
|
43 |
+
|
44 |
+
let mouseDown: boolean = false;
|
45 |
+
function pointerEnter() {
|
46 |
+
// brush.hidden = false;
|
47 |
+
}
|
48 |
+
function pointerOut() {
|
49 |
+
brush.style.top = `${10 + $selectedBrush.size / 2}px`;
|
50 |
+
brush.style.left = `${10 + $selectedBrush.size / 2}px`;
|
51 |
+
mouseDown = false;
|
52 |
+
}
|
53 |
+
function pointerDown(e: MouseEvent) {
|
54 |
+
mouseDown = true;
|
55 |
+
startPosition = getPosition(canvas, e);
|
56 |
+
pxBrush.draw({
|
57 |
+
from: startPosition,
|
58 |
+
to: startPosition,
|
59 |
+
size: $selectedBrush.size,
|
60 |
+
color: $selectedBrush.color
|
61 |
+
});
|
62 |
+
}
|
63 |
+
function pointerMove(e: MouseEvent) {
|
64 |
+
const position = getPosition(canvas, e);
|
65 |
+
brush.style.top = `${position.y}px`;
|
66 |
+
brush.style.left = `${position.x}px`;
|
67 |
+
if (!mouseDown) {
|
68 |
+
return;
|
69 |
+
}
|
70 |
+
pxBrush.draw({
|
71 |
+
from: startPosition,
|
72 |
+
to: position,
|
73 |
+
size: $selectedBrush.size,
|
74 |
+
color: $selectedBrush.color
|
75 |
+
});
|
76 |
+
startPosition = position;
|
77 |
+
}
|
78 |
+
function getPosition(canvas: HTMLCanvasElement, event: MouseEvent) {
|
79 |
+
const rect = canvas.getBoundingClientRect();
|
80 |
+
return {
|
81 |
+
x: event.clientX - rect.left,
|
82 |
+
y: event.clientY - rect.top
|
83 |
+
};
|
84 |
+
}
|
85 |
+
function setBrush(sBrush: Brush) {
|
86 |
+
const { size, color } = sBrush;
|
87 |
+
brush.width = size;
|
88 |
+
brush.height = size;
|
89 |
+
// brushCtx.clearRect(0, 0, brush.width, brush.height);
|
90 |
+
// brushCtx.beginPath();
|
91 |
+
brushCtx.fillStyle = color;
|
92 |
+
brushCtx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
93 |
+
brushCtx.fill();
|
94 |
+
}
|
95 |
+
|
96 |
+
function clearCanvas(ctx: CanvasRenderingContext2D) {
|
97 |
+
ctx.fillStyle = '000';
|
98 |
+
ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
99 |
+
ctx.fill();
|
100 |
+
}
|
101 |
+
</script>
|
102 |
+
|
103 |
+
<div class="inline-block relative overflow-clip">
|
104 |
+
<canvas
|
105 |
+
bind:this={canvas}
|
106 |
+
class="canvas"
|
107 |
+
width="256"
|
108 |
+
height="512"
|
109 |
+
on:touchmove={(e) => e.preventDefault()}
|
110 |
+
on:pointerenter={pointerEnter}
|
111 |
+
on:pointerup={pointerOut}
|
112 |
+
on:pointerleave={pointerOut}
|
113 |
+
on:pointercancel={pointerOut}
|
114 |
+
on:pointerout={pointerOut}
|
115 |
+
on:pointermove={pointerMove}
|
116 |
+
on:pointerdown={pointerDown}
|
117 |
+
/>
|
118 |
+
<canvas bind:this={brush} class="brush" width="10" height="10" />
|
119 |
+
<span class="label">{$selectedBrush?.label} </span>
|
120 |
+
</div>
|
121 |
+
|
122 |
+
<style lang="postcss" scoped>
|
123 |
+
.canvas {
|
124 |
+
@apply box-border z-0 border dark:border-gray-300 border-gray-500 aspect-[256/512];
|
125 |
+
}
|
126 |
+
.brush {
|
127 |
+
@apply z-10 absolute pointer-events-none -translate-x-1/2 -translate-y-1/2;
|
128 |
+
}
|
129 |
+
.label {
|
130 |
+
@apply px-2 text-base z-20 absolute top-0 left-0 pointer-events-none text-white select-none;
|
131 |
+
color: white;
|
132 |
+
font-weight: bolder;
|
133 |
+
-webkit-text-stroke: 1px black;
|
134 |
+
-webkit-text-fill-color: white;
|
135 |
+
}
|
136 |
+
</style>
|
frontend/src/lib/ParamsSelector.svelte
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { SECTIONS, TEXTURES } from '../data';
|
3 |
+
import { selectedParams, generateHuman } from '$lib/store';
|
4 |
+
import type { FormElements } from '../types';
|
5 |
+
import { randomSeed } from '$lib/utils';
|
6 |
+
const submit = () => {
|
7 |
+
const elements: FormElements = form.elements as FormElements;
|
8 |
+
$selectedParams = {
|
9 |
+
texture: `${elements.texture0.value},${elements.texture1.value},${elements.texture2.value}`,
|
10 |
+
seed: BigInt(elements.seed.value),
|
11 |
+
steps: parseInt(elements.steps.value)
|
12 |
+
};
|
13 |
+
};
|
14 |
+
|
15 |
+
let form: HTMLFormElement;
|
16 |
+
let seed: bigint = $selectedParams.seed;
|
17 |
+
let sampleSteps: number = $selectedParams.steps;
|
18 |
+
</script>
|
19 |
+
|
20 |
+
<form bind:this={form} on:input={submit}>
|
21 |
+
<h4 class="font-bold mt-6 mb-2 my-6 leading-6">Texture Description</h4>
|
22 |
+
<div class="sections">
|
23 |
+
{#each SECTIONS as section, i}
|
24 |
+
<select name="texture{i}" disabled={$generateHuman === true}>
|
25 |
+
<option disabled selected>{section}</option>
|
26 |
+
{#each TEXTURES as texture}
|
27 |
+
<option value={texture}>{texture}</option>`
|
28 |
+
{/each}
|
29 |
+
</select>
|
30 |
+
{/each}
|
31 |
+
</div>
|
32 |
+
<h4 class="font-bold mt-6 mb-2 my-6 leading-6">Random Seed</h4>
|
33 |
+
<input
|
34 |
+
bind:value={seed}
|
35 |
+
type="Number"
|
36 |
+
name="seed"
|
37 |
+
placeholder="Integer Seed"
|
38 |
+
disabled={$generateHuman === true}
|
39 |
+
/>
|
40 |
+
<button on:click|preventDefault={() => (seed = randomSeed())} disabled={$generateHuman === true}>
|
41 |
+
Random
|
42 |
+
</button>
|
43 |
+
<h4 class="font-bold mt-6 mb-2 my-6 leading-6">Sample Steps</h4>
|
44 |
+
<div class="flex">
|
45 |
+
<input
|
46 |
+
bind:value={sampleSteps}
|
47 |
+
type="range"
|
48 |
+
name="steps"
|
49 |
+
min="10"
|
50 |
+
max="300"
|
51 |
+
step="1"
|
52 |
+
disabled={$generateHuman === true}
|
53 |
+
/>
|
54 |
+
<label class="pl-2" for="steps">{sampleSteps}</label>
|
55 |
+
</div>
|
56 |
+
</form>
|
57 |
+
|
58 |
+
<style lang="postcss" scoped>
|
59 |
+
.sections {
|
60 |
+
@apply flex sm:flex-row flex-col gap-1 sm:gap-3;
|
61 |
+
}
|
62 |
+
select,
|
63 |
+
button,
|
64 |
+
input {
|
65 |
+
@apply p-1 disabled:opacity-50 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500;
|
66 |
+
}
|
67 |
+
input:disabled + label {
|
68 |
+
@apply opacity-50;
|
69 |
+
}
|
70 |
+
input {
|
71 |
+
@apply pl-3;
|
72 |
+
}
|
73 |
+
</style>
|
frontend/src/lib/ResultCanvas.svelte
ADDED
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
let canvas: HTMLCanvasElement;
|
3 |
+
import { API } from '../data';
|
4 |
+
import { nanoid } from 'nanoid';
|
5 |
+
import type { Params } from '../types';
|
6 |
+
|
7 |
+
import {
|
8 |
+
selectedParams,
|
9 |
+
generateHuman,
|
10 |
+
currentCanvas,
|
11 |
+
resultImage,
|
12 |
+
saveResult
|
13 |
+
} from '$lib/store';
|
14 |
+
|
15 |
+
$: (async () => {
|
16 |
+
if ($generateHuman) {
|
17 |
+
const results = await predict($currentCanvas.toDataURL(), $selectedParams);
|
18 |
+
if (results) {
|
19 |
+
$resultImage = results.data[0];
|
20 |
+
}
|
21 |
+
$generateHuman = false;
|
22 |
+
}
|
23 |
+
})();
|
24 |
+
|
25 |
+
$: (async () => {
|
26 |
+
if ($saveResult) {
|
27 |
+
const results = await saveImage($resultImage);
|
28 |
+
$saveResult = false;
|
29 |
+
}
|
30 |
+
})();
|
31 |
+
|
32 |
+
let predictStatus: string = '';
|
33 |
+
async function saveImage(base64Image: string) {
|
34 |
+
return new Promise((resolve, reject) => {
|
35 |
+
try {
|
36 |
+
const a = document.createElement('a');
|
37 |
+
a.download = `sucess-${Date.now()}.png`;
|
38 |
+
a.target = '_self';
|
39 |
+
a.onclick = async (e) => {
|
40 |
+
if (a.href) {
|
41 |
+
URL.revokeObjectURL(a.href);
|
42 |
+
}
|
43 |
+
a.href = base64Image;
|
44 |
+
};
|
45 |
+
a.click();
|
46 |
+
resolve(null);
|
47 |
+
console.log('Downloading image.');
|
48 |
+
} catch (err) {
|
49 |
+
reject();
|
50 |
+
}
|
51 |
+
});
|
52 |
+
}
|
53 |
+
async function predict(base64Image: string, { texture, steps, seed }: Params) {
|
54 |
+
const session = nanoid(11);
|
55 |
+
let hash, queue_position;
|
56 |
+
let b = 0;
|
57 |
+
predictStatus = 'Generating';
|
58 |
+
const controller = new AbortController();
|
59 |
+
// invalidation.then(() => {
|
60 |
+
// controller.abort();
|
61 |
+
// });
|
62 |
+
await fetch(API + '/api/queue/push/', {
|
63 |
+
signal: controller.signal,
|
64 |
+
headers: { 'Content-Type': 'application/json' },
|
65 |
+
method: 'POST',
|
66 |
+
body: JSON.stringify({
|
67 |
+
fn_index: 2,
|
68 |
+
data: [base64Image, texture, steps, Number(seed)],
|
69 |
+
action: 'predict',
|
70 |
+
session_hash: session
|
71 |
+
})
|
72 |
+
})
|
73 |
+
.then(async (res) => {
|
74 |
+
const data = await res.json();
|
75 |
+
({ hash, queue_position } = data);
|
76 |
+
})
|
77 |
+
.catch((err) => {
|
78 |
+
console.log(err);
|
79 |
+
});
|
80 |
+
|
81 |
+
let timeout = 0;
|
82 |
+
let status, data;
|
83 |
+
while (status !== 'QUEUED' || status !== 'PENDING') {
|
84 |
+
try {
|
85 |
+
const resp = await fetch(API + '/api/queue/status/', {
|
86 |
+
signal: controller.signal,
|
87 |
+
headers: { 'Content-Type': 'application/json' },
|
88 |
+
method: 'POST',
|
89 |
+
body: JSON.stringify({ hash: hash })
|
90 |
+
});
|
91 |
+
if (resp.status != 200) {
|
92 |
+
break;
|
93 |
+
}
|
94 |
+
({ status, data } = await resp.json());
|
95 |
+
if (status === 'QUEUED') {
|
96 |
+
predictStatus = `Queue ${data}/${queue_position}`;
|
97 |
+
} else if (status === 'PENDING') {
|
98 |
+
predictStatus = 'Pending';
|
99 |
+
} else if (status === 'FAILED') {
|
100 |
+
predictStatus = 'Failed';
|
101 |
+
break;
|
102 |
+
} else if (status === 'COMPLETE') {
|
103 |
+
predictStatus = 'Complete';
|
104 |
+
break;
|
105 |
+
}
|
106 |
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
107 |
+
} catch (error) {
|
108 |
+
console.log(error);
|
109 |
+
break;
|
110 |
+
}
|
111 |
+
}
|
112 |
+
return data;
|
113 |
+
}
|
114 |
+
</script>
|
115 |
+
|
116 |
+
{#if $resultImage}
|
117 |
+
<div class="inline-block relative overflow-clip">
|
118 |
+
<img class="image" alt="Generative Human Result" src={$resultImage} width="256" height="512" />
|
119 |
+
</div>
|
120 |
+
{/if}
|
121 |
+
|
122 |
+
<style lang="postcss" scoped>
|
123 |
+
.image {
|
124 |
+
@apply max-w-none box-border z-0 border dark:border-gray-300 border-gray-500 aspect-[256/512];
|
125 |
+
}
|
126 |
+
</style>
|
frontend/src/lib/TemplateGallery.svelte
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { IMAGES_LIST } from '../data';
|
3 |
+
import { selectedImageBlob, generateHuman } from '$lib/store';
|
4 |
+
import { base } from '$app/paths';
|
5 |
+
|
6 |
+
const submit = async (e: Event) => {
|
7 |
+
e.preventDefault();
|
8 |
+
const src = IMAGES_LIST[parseInt((e.target as HTMLInputElement).value)];
|
9 |
+
if (src) {
|
10 |
+
const blob = await fetch(base + src).then((res) => res.blob());
|
11 |
+
$selectedImageBlob = blob;
|
12 |
+
}
|
13 |
+
};
|
14 |
+
</script>
|
15 |
+
|
16 |
+
<div>
|
17 |
+
<h4 class="font-bold mt-6 mb-2 my-6 leading-6">Select a Template</h4>
|
18 |
+
<form on:input={submit}>
|
19 |
+
<div class="samples ">
|
20 |
+
{#each IMAGES_LIST as file_name, id}
|
21 |
+
<div class="snap-always snap-start">
|
22 |
+
<input
|
23 |
+
type="radio"
|
24 |
+
name="samples"
|
25 |
+
id="sample-{id}"
|
26 |
+
value={id}
|
27 |
+
disabled={$generateHuman === true}
|
28 |
+
/>
|
29 |
+
<label for="sample-{id}">
|
30 |
+
<img src={base + file_name} alt={file_name} />
|
31 |
+
</label>
|
32 |
+
</div>
|
33 |
+
{/each}
|
34 |
+
</div>
|
35 |
+
</form>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<style lang="postcss" scoped>
|
39 |
+
form {
|
40 |
+
@apply overflow-hidden w-full;
|
41 |
+
}
|
42 |
+
.samples {
|
43 |
+
@apply flex overflow-x-scroll flex-nowrap snap-x snap-mandatory gap-1 scrollbar-hide;
|
44 |
+
}
|
45 |
+
input[type='radio'] {
|
46 |
+
@apply disabled:opacity-50 opacity-0 w-0 h-0 absolute hidden;
|
47 |
+
}
|
48 |
+
input[type='radio']:active ~ label {
|
49 |
+
}
|
50 |
+
input[type='radio']:checked ~ label {
|
51 |
+
@apply outline outline-2 outline-yellow-500;
|
52 |
+
}
|
53 |
+
input[type='radio']:disabled + label {
|
54 |
+
@apply opacity-50;
|
55 |
+
}
|
56 |
+
label {
|
57 |
+
@apply cursor-pointer flex transition-all duration-200 ease-in-out
|
58 |
+
hover:outline outline-offset-[-2px] outline-2 outline-yellow-500;
|
59 |
+
}
|
60 |
+
img {
|
61 |
+
@apply max-h-24 max-w-none;
|
62 |
+
}
|
63 |
+
</style>
|
frontend/src/lib/store.ts
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { writable } from 'svelte/store';
|
2 |
+
import type { Brush, Params } from '../types';
|
3 |
+
import { randomSeed } from '$lib/utils';
|
4 |
+
|
5 |
+
export const resultImage = writable<string>();
|
6 |
+
export const currentCanvas = writable<HTMLCanvasElement>();
|
7 |
+
export const selectedImageBlob = writable<Blob>();
|
8 |
+
export const selectedBrush = writable<Brush>();
|
9 |
+
export const selectedParams = writable<Params>({
|
10 |
+
texture: '',
|
11 |
+
seed: randomSeed(),
|
12 |
+
steps: 10
|
13 |
+
});
|
14 |
+
|
15 |
+
export const generateHuman = writable<boolean>(false);
|
16 |
+
export const saveResult = writable<boolean>(false);
|
frontend/src/lib/utils.ts
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
export function randomSeed() {
|
2 |
+
return BigInt(13248873089935215612 & (((1 << 63) - 1) * Math.random()));
|
3 |
+
}
|
frontend/src/routes/__layout.svelte
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script>
|
2 |
+
import '../app.css';
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<slot />
|
frontend/src/routes/index.svelte
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import TemplateGallery from '$lib/TemplateGallery.svelte';
|
3 |
+
import BrushSelector from '$lib/BrushSelector.svelte';
|
4 |
+
import ParamSelector from '$lib/ParamsSelector.svelte';
|
5 |
+
import DrawingCanvas from '$lib/DrawingCanvas.svelte';
|
6 |
+
import ResultCanvas from '$lib/ResultCanvas.svelte';
|
7 |
+
import { generateHuman, saveResult, resultImage } from '$lib/store';
|
8 |
+
|
9 |
+
let results = false;
|
10 |
+
</script>
|
11 |
+
|
12 |
+
<div class="max-w-screen-md mx-auto px-3 py-5 relative z-0">
|
13 |
+
<article class="prose dark:prose-invert">
|
14 |
+
<h1>Drawings to Human</h1>
|
15 |
+
<p>
|
16 |
+
This is an unofficial drawing tool to explore the generative human generator <a
|
17 |
+
href="https://github.com/yumingj/Text2Human"
|
18 |
+
target="_blank"><span>Text2Human</span></a
|
19 |
+
>. Please check all the model features on this
|
20 |
+
<a href="https://huggingface.co/spaces/CVPR/Text2Human" target="_blank">Space</a>.
|
21 |
+
</p>
|
22 |
+
<small>
|
23 |
+
<h4 id="thanks-to">Thanks to</h4>
|
24 |
+
<p>
|
25 |
+
Authors: <a href="https://yumingj.github.io/" target="_blank">Yuming Jiang</a>,
|
26 |
+
<a href="https://williamyang1991.github.io/" target="_blank">Shuai Yang</a>,
|
27 |
+
<a href="http://haonanqiu.com/" target="_blank">Haonan Qiu</a>,
|
28 |
+
<a href="https://wywu.github.io/" target="_blank">Wayne Wu</a>,
|
29 |
+
<a href="https://www.mmlab-ntu.com/person/ccloy/" target="_blank">Chen Change Loy</a>
|
30 |
+
and <a href="https://liuziwei7.github.io/" target="_blank">Ziwei Liu</a><br />
|
31 |
+
</p>
|
32 |
+
<p>
|
33 |
+
<a href="https://huggingface.co/hysts" target="_blank">@hysts</a> for the original Space implementation
|
34 |
+
</p>
|
35 |
+
</small>
|
36 |
+
<details open>
|
37 |
+
<summary class="cursor-pointer"> <small>More</small> </summary>
|
38 |
+
<p>
|
39 |
+
The backend is powered by a <a href="https://gradio.app/" target="_blank">Gradio</a>
|
40 |
+
application running on
|
41 |
+
<a href="https://huggingface.co/spaces/CVPR/Text2Human" target="_blank">Spaces</a>. You can
|
42 |
+
also check the source code and clone it locally if you want:
|
43 |
+
</p>
|
44 |
+
|
45 |
+
<p>
|
46 |
+
<code class="block whitespace-pre overflow-x-scroll">
|
47 |
+
git clone https://huggingface.co/spaces/CVPR/Text2Human
|
48 |
+
</code>
|
49 |
+
</p>
|
50 |
+
</details>
|
51 |
+
</article>
|
52 |
+
<BrushSelector />
|
53 |
+
<TemplateGallery />
|
54 |
+
<div class="drawings py-3">
|
55 |
+
<DrawingCanvas />
|
56 |
+
<!-- {#if results} -->
|
57 |
+
<ResultCanvas />
|
58 |
+
<!-- {/if} -->
|
59 |
+
</div>
|
60 |
+
<button
|
61 |
+
on:click|preventDefault={() => ($generateHuman = true)}
|
62 |
+
disabled={$generateHuman === true}
|
63 |
+
>
|
64 |
+
Generate Human
|
65 |
+
</button>
|
66 |
+
<button
|
67 |
+
on:click|preventDefault={() => ($saveResult = true)}
|
68 |
+
disabled={$saveResult === true || !$resultImage}
|
69 |
+
>
|
70 |
+
Save Result
|
71 |
+
</button>
|
72 |
+
<ParamSelector />
|
73 |
+
</div>
|
74 |
+
|
75 |
+
<style lang="postcss" scoped>
|
76 |
+
.drawings {
|
77 |
+
@apply grid grid-cols-1 sm:grid-cols-2 place-items-center;
|
78 |
+
}
|
79 |
+
button {
|
80 |
+
@apply p-1 disabled:opacity-50 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500;
|
81 |
+
}
|
82 |
+
</style>
|
frontend/src/types.ts
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export interface Color {
|
2 |
+
color: number[];
|
3 |
+
label: string;
|
4 |
+
}
|
5 |
+
export type RGB = `rgb(${number},${number},${number})`;
|
6 |
+
export interface Brush {
|
7 |
+
color: RGB;
|
8 |
+
size: number;
|
9 |
+
label: string;
|
10 |
+
}
|
11 |
+
|
12 |
+
export interface Params {
|
13 |
+
texture: string;
|
14 |
+
seed: bigint;
|
15 |
+
steps: number;
|
16 |
+
}
|
17 |
+
|
18 |
+
export interface FormElements extends HTMLCollection {
|
19 |
+
seed: HTMLInputElement;
|
20 |
+
steps: HTMLInputElement;
|
21 |
+
texture0: HTMLInputElement;
|
22 |
+
texture1: HTMLInputElement;
|
23 |
+
texture2: HTMLInputElement;
|
24 |
+
}
|
frontend/static/favicon.png
ADDED
frontend/static/robots.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# https://www.robotstxt.org/robotstxt.html
|
2 |
+
User-agent: *
|
3 |
+
Disallow:
|
frontend/static/samples/MEN-Pants-id_00002565-02_1_front_segm.png
ADDED
frontend/static/samples/MEN-Pants-id_00005213-02_4_full_segm.png
ADDED
frontend/static/samples/WOMEN-Blouses_Shirts-id_00002356-02_4_full_segm.png
ADDED
frontend/static/samples/WOMEN-Blouses_Shirts-id_00004090-03_7_additional_segm.png
ADDED
frontend/static/samples/WOMEN-Cardigans-id_00000853-01_2_side_segm.png
ADDED
frontend/static/samples/WOMEN-Cardigans-id_00000899-02_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Cardigans-id_00006462-02_7_additional_segm.png
ADDED
frontend/static/samples/WOMEN-Dresses-id_00000021-05_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Dresses-id_00002430-04_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Dresses-id_00002966-01_7_additional_segm.png
ADDED
frontend/static/samples/WOMEN-Dresses-id_00007332-01_3_back_segm.png
ADDED
frontend/static/samples/WOMEN-Graphic_Tees-id_00007242-01_4_full_segm.png
ADDED
frontend/static/samples/WOMEN-Jackets_Coats-id_00005263-06_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Jackets_Coats-id_00006296-05_7_additional_segm.png
ADDED
frontend/static/samples/WOMEN-Rompers_Jumpsuits-id_00004575-02_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Skirts-id_00006761-01_1_front_segm.png
ADDED
frontend/static/samples/WOMEN-Sweaters-id_00004667-01_4_full_segm.png
ADDED
frontend/static/samples/WOMEN-Tees_Tanks-id_00001620-02_4_full_segm.png
ADDED
frontend/static/samples/WOMEN-Tees_Tanks-id_00005288-01_2_side_segm.png
ADDED
frontend/static/samples/WOMEN-Tees_Tanks-id_00006566-04_4_full_segm.png
ADDED