radames HF staff commited on
Commit
838b286
1 Parent(s): 8bd7ccc

svelte frontend

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +11 -0
  2. Makefile +11 -0
  3. README.md +3 -1
  4. app.py +18 -0
  5. frontend/.eslintignore +13 -0
  6. frontend/.eslintrc.cjs +20 -0
  7. frontend/.gitignore +11 -0
  8. frontend/.npmrc +1 -0
  9. frontend/.prettierignore +13 -0
  10. frontend/.prettierrc +6 -0
  11. frontend/README.md +38 -0
  12. frontend/package-lock.json +0 -0
  13. frontend/package.json +43 -0
  14. frontend/postcss.config.cjs +6 -0
  15. frontend/src/app.css +10 -0
  16. frontend/src/app.d.ts +16 -0
  17. frontend/src/app.html +13 -0
  18. frontend/src/data.ts +69 -0
  19. frontend/src/lib/BrushSelector.svelte +85 -0
  20. frontend/src/lib/DrawingCanvas.svelte +136 -0
  21. frontend/src/lib/ParamsSelector.svelte +73 -0
  22. frontend/src/lib/ResultCanvas.svelte +126 -0
  23. frontend/src/lib/TemplateGallery.svelte +63 -0
  24. frontend/src/lib/store.ts +16 -0
  25. frontend/src/lib/utils.ts +3 -0
  26. frontend/src/routes/__layout.svelte +5 -0
  27. frontend/src/routes/index.svelte +82 -0
  28. frontend/src/types.ts +24 -0
  29. frontend/static/favicon.png +0 -0
  30. frontend/static/robots.txt +3 -0
  31. frontend/static/samples/MEN-Pants-id_00002565-02_1_front_segm.png +0 -0
  32. frontend/static/samples/MEN-Pants-id_00005213-02_4_full_segm.png +0 -0
  33. frontend/static/samples/WOMEN-Blouses_Shirts-id_00002356-02_4_full_segm.png +0 -0
  34. frontend/static/samples/WOMEN-Blouses_Shirts-id_00004090-03_7_additional_segm.png +0 -0
  35. frontend/static/samples/WOMEN-Cardigans-id_00000853-01_2_side_segm.png +0 -0
  36. frontend/static/samples/WOMEN-Cardigans-id_00000899-02_1_front_segm.png +0 -0
  37. frontend/static/samples/WOMEN-Cardigans-id_00006462-02_7_additional_segm.png +0 -0
  38. frontend/static/samples/WOMEN-Dresses-id_00000021-05_1_front_segm.png +0 -0
  39. frontend/static/samples/WOMEN-Dresses-id_00002430-04_1_front_segm.png +0 -0
  40. frontend/static/samples/WOMEN-Dresses-id_00002966-01_7_additional_segm.png +0 -0
  41. frontend/static/samples/WOMEN-Dresses-id_00007332-01_3_back_segm.png +0 -0
  42. frontend/static/samples/WOMEN-Graphic_Tees-id_00007242-01_4_full_segm.png +0 -0
  43. frontend/static/samples/WOMEN-Jackets_Coats-id_00005263-06_1_front_segm.png +0 -0
  44. frontend/static/samples/WOMEN-Jackets_Coats-id_00006296-05_7_additional_segm.png +0 -0
  45. frontend/static/samples/WOMEN-Rompers_Jumpsuits-id_00004575-02_1_front_segm.png +0 -0
  46. frontend/static/samples/WOMEN-Skirts-id_00006761-01_1_front_segm.png +0 -0
  47. frontend/static/samples/WOMEN-Sweaters-id_00004667-01_4_full_segm.png +0 -0
  48. frontend/static/samples/WOMEN-Tees_Tanks-id_00001620-02_4_full_segm.png +0 -0
  49. frontend/static/samples/WOMEN-Tees_Tanks-id_00005288-01_2_side_segm.png +0 -0
  50. 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: static
 
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