IDKiro commited on
Commit
f146523
1 Parent(s): ce3ad83

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +324 -0
  2. requirements.txt +23 -0
  3. style.css +213 -0
app.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import numpy as np
3
+ from PIL import Image
4
+ import base64
5
+ from io import BytesIO
6
+
7
+ import torch
8
+ import torchvision.transforms.functional as F
9
+ from diffusers import ControlNetModel, StableDiffusionControlNetPipeline
10
+ import gradio as gr
11
+
12
+ device = "cpu" # Linux & Windows
13
+ weight_type = torch.float32 # torch.float16 works as well, but pictures seem to be a bit worse
14
+
15
+ controlnet = ControlNetModel.from_pretrained(
16
+ "IDKiro/sdxs-512-dreamshaper-sketch", torch_dtype=weight_type
17
+ ).to(device)
18
+ pipe = StableDiffusionControlNetPipeline.from_pretrained(
19
+ "IDKiro/sdxs-512-dreamshaper", controlnet=controlnet, torch_dtype=weight_type
20
+ )
21
+ pipe.to(device)
22
+
23
+ style_list = [
24
+ {
25
+ "name": "No Style",
26
+ "prompt": "{prompt}",
27
+ },
28
+ {
29
+ "name": "Cinematic",
30
+ "prompt": "cinematic still {prompt} . emotional, harmonious, vignette, highly detailed, high budget, bokeh, cinemascope, moody, epic, gorgeous, film grain, grainy",
31
+ },
32
+ {
33
+ "name": "3D Model",
34
+ "prompt": "professional 3d model {prompt} . octane render, highly detailed, volumetric, dramatic lighting",
35
+ },
36
+ {
37
+ "name": "Anime",
38
+ "prompt": "anime artwork {prompt} . anime style, key visual, vibrant, studio anime, highly detailed",
39
+ },
40
+ {
41
+ "name": "Digital Art",
42
+ "prompt": "concept art {prompt} . digital artwork, illustrative, painterly, matte painting, highly detailed",
43
+ },
44
+ {
45
+ "name": "Photographic",
46
+ "prompt": "cinematic photo {prompt} . 35mm photograph, film, bokeh, professional, 4k, highly detailed",
47
+ },
48
+ {
49
+ "name": "Pixel art",
50
+ "prompt": "pixel-art {prompt} . low-res, blocky, pixel art style, 8-bit graphics",
51
+ },
52
+ {
53
+ "name": "Fantasy art",
54
+ "prompt": "ethereal fantasy concept art of {prompt} . magnificent, celestial, ethereal, painterly, epic, majestic, magical, fantasy art, cover art, dreamy",
55
+ },
56
+ {
57
+ "name": "Neonpunk",
58
+ "prompt": "neonpunk style {prompt} . cyberpunk, vaporwave, neon, vibes, vibrant, stunningly beautiful, crisp, detailed, sleek, ultramodern, magenta highlights, dark purple shadows, high contrast, cinematic, ultra detailed, intricate, professional",
59
+ },
60
+ {
61
+ "name": "Manga",
62
+ "prompt": "manga style {prompt} . vibrant, high-energy, detailed, iconic, Japanese comic style",
63
+ },
64
+ ]
65
+
66
+ styles = {k["name"]: k["prompt"] for k in style_list}
67
+ STYLE_NAMES = list(styles.keys())
68
+ DEFAULT_STYLE_NAME = "No Style"
69
+ MAX_SEED = np.iinfo(np.int32).max
70
+
71
+
72
+ def pil_image_to_data_url(img, format="PNG"):
73
+ buffered = BytesIO()
74
+ img.save(buffered, format=format)
75
+ img_str = base64.b64encode(buffered.getvalue()).decode()
76
+ return f"data:image/{format.lower()};base64,{img_str}"
77
+
78
+
79
+ def randomize_seed_fn(seed: int, randomize_seed: bool) -> int:
80
+ if randomize_seed:
81
+ seed = random.randint(0, MAX_SEED)
82
+ return seed
83
+
84
+
85
+ def run(
86
+ image,
87
+ prompt,
88
+ prompt_template,
89
+ style_name,
90
+ controlnet_conditioning_scale,
91
+ device_type="CPU",
92
+ param_dtype='torch.float16',
93
+ ):
94
+ if device_type == "CPU":
95
+ device = "cpu"
96
+ param_dtype = 'torch.float32'
97
+ else:
98
+ device = "cuda"
99
+
100
+ pipe.to(torch_device=device, torch_dtype=torch.float16 if param_dtype == 'torch.float16' else torch.float32)
101
+
102
+ print(f"prompt: {prompt}")
103
+ print("sketch updated")
104
+ if image is None:
105
+ ones = Image.new("L", (512, 512), 255)
106
+ temp_url = pil_image_to_data_url(ones)
107
+ return ones, gr.update(link=temp_url), gr.update(link=temp_url)
108
+ prompt = prompt_template.replace("{prompt}", prompt)
109
+ control_image = image.convert("RGB")
110
+ control_image = Image.fromarray(255 - np.array(control_image))
111
+
112
+ output_pil = pipe(
113
+ prompt=prompt,
114
+ image=control_image,
115
+ width=512,
116
+ height=512,
117
+ guidance_scale=0.0,
118
+ num_inference_steps=1,
119
+ num_images_per_prompt=1,
120
+ output_type="pil",
121
+ controlnet_conditioning_scale=controlnet_conditioning_scale,
122
+ ).images[0]
123
+
124
+ input_sketch_url = pil_image_to_data_url(control_image)
125
+ output_image_url = pil_image_to_data_url(output_pil)
126
+ return (
127
+ output_pil,
128
+ gr.update(link=input_sketch_url),
129
+ gr.update(link=output_image_url),
130
+ )
131
+
132
+
133
+ def update_canvas(use_line, use_eraser):
134
+ if use_eraser:
135
+ _color = "#ffffff"
136
+ brush_size = 20
137
+ if use_line:
138
+ _color = "#000000"
139
+ brush_size = 8
140
+ return gr.update(brush_radius=brush_size, brush_color=_color, interactive=True)
141
+
142
+
143
+ def upload_sketch(file):
144
+ _img = Image.open(file.name)
145
+ _img = _img.convert("L")
146
+ return gr.update(value=_img, source="upload", interactive=True)
147
+
148
+
149
+ scripts = """
150
+ async () => {
151
+ globalThis.theSketchDownloadFunction = () => {
152
+ console.log("test")
153
+ var link = document.createElement("a");
154
+ dataUrl = document.getElementById('download_sketch').href
155
+ link.setAttribute("href", dataUrl)
156
+ link.setAttribute("download", "sketch.png")
157
+ document.body.appendChild(link); // Required for Firefox
158
+ link.click();
159
+ document.body.removeChild(link); // Clean up
160
+
161
+ // also call the output download function
162
+ theOutputDownloadFunction();
163
+ return false
164
+ }
165
+
166
+ globalThis.theOutputDownloadFunction = () => {
167
+ console.log("test output download function")
168
+ var link = document.createElement("a");
169
+ dataUrl = document.getElementById('download_output').href
170
+ link.setAttribute("href", dataUrl);
171
+ link.setAttribute("download", "output.png");
172
+ document.body.appendChild(link); // Required for Firefox
173
+ link.click();
174
+ document.body.removeChild(link); // Clean up
175
+ return false
176
+ }
177
+
178
+ globalThis.UNDO_SKETCH_FUNCTION = () => {
179
+ console.log("undo sketch function")
180
+ var button_undo = document.querySelector('#input_image > div.image-container.svelte-p3y7hu > div.svelte-s6ybro > button:nth-child(1)');
181
+ // Create a new 'click' event
182
+ var event = new MouseEvent('click', {
183
+ 'view': window,
184
+ 'bubbles': true,
185
+ 'cancelable': true
186
+ });
187
+ button_undo.dispatchEvent(event);
188
+ }
189
+
190
+ globalThis.DELETE_SKETCH_FUNCTION = () => {
191
+ console.log("delete sketch function")
192
+ var button_del = document.querySelector('#input_image > div.image-container.svelte-p3y7hu > div.svelte-s6ybro > button:nth-child(2)');
193
+ // Create a new 'click' event
194
+ var event = new MouseEvent('click', {
195
+ 'view': window,
196
+ 'bubbles': true,
197
+ 'cancelable': true
198
+ });
199
+ button_del.dispatchEvent(event);
200
+ }
201
+
202
+ globalThis.togglePencil = () => {
203
+ el_pencil = document.getElementById('my-toggle-pencil');
204
+ el_pencil.classList.toggle('clicked');
205
+ // simulate a click on the gradio button
206
+ btn_gradio = document.querySelector("#cb-line > label > input");
207
+ var event = new MouseEvent('click', {
208
+ 'view': window,
209
+ 'bubbles': true,
210
+ 'cancelable': true
211
+ });
212
+ btn_gradio.dispatchEvent(event);
213
+ if (el_pencil.classList.contains('clicked')) {
214
+ document.getElementById('my-toggle-eraser').classList.remove('clicked');
215
+ document.getElementById('my-div-pencil').style.backgroundColor = "gray";
216
+ document.getElementById('my-div-eraser').style.backgroundColor = "white";
217
+ }
218
+ else {
219
+ document.getElementById('my-toggle-eraser').classList.add('clicked');
220
+ document.getElementById('my-div-pencil').style.backgroundColor = "white";
221
+ document.getElementById('my-div-eraser').style.backgroundColor = "gray";
222
+ }
223
+
224
+ }
225
+
226
+ globalThis.toggleEraser = () => {
227
+ element = document.getElementById('my-toggle-eraser');
228
+ element.classList.toggle('clicked');
229
+ // simulate a click on the gradio button
230
+ btn_gradio = document.querySelector("#cb-eraser > label > input");
231
+ var event = new MouseEvent('click', {
232
+ 'view': window,
233
+ 'bubbles': true,
234
+ 'cancelable': true
235
+ });
236
+ btn_gradio.dispatchEvent(event);
237
+ if (element.classList.contains('clicked')) {
238
+ document.getElementById('my-toggle-pencil').classList.remove('clicked');
239
+ document.getElementById('my-div-pencil').style.backgroundColor = "white";
240
+ document.getElementById('my-div-eraser').style.backgroundColor = "gray";
241
+ }
242
+ else {
243
+ document.getElementById('my-toggle-pencil').classList.add('clicked');
244
+ document.getElementById('my-div-pencil').style.backgroundColor = "gray";
245
+ document.getElementById('my-div-eraser').style.backgroundColor = "white";
246
+ }
247
+ }
248
+ }
249
+ """
250
+
251
+ with gr.Blocks(css="style.css") as demo:
252
+ gr.Markdown("# SDXS-512-DreamShaper-Sketch")
253
+ # these are hidden buttons that are used to trigger the canvas changes
254
+ line = gr.Checkbox(label="line", value=False, elem_id="cb-line")
255
+ eraser = gr.Checkbox(label="eraser", value=False, elem_id="cb-eraser")
256
+ with gr.Row(elem_id="main_row"):
257
+ with gr.Column(elem_id="column_input"):
258
+ gr.Markdown("## INPUT", elem_id="input_header")
259
+ image = gr.Image(
260
+ source="canvas", tool="color-sketch", type="pil", image_mode="L",
261
+ invert_colors=True, shape=(512, 512), brush_radius=8, height=440, width=440,
262
+ brush_color="#000000", interactive=True, show_download_button=True, elem_id="input_image", show_label=False)
263
+ download_sketch = gr.Button("Download sketch", scale=1, elem_id="download_sketch")
264
+
265
+ gr.HTML("""
266
+ <div class="button-row">
267
+ <div id="my-div-pencil" class="pad2"> <button id="my-toggle-pencil" onclick="return togglePencil(this)"></button> </div>
268
+ <div id="my-div-eraser" class="pad2"> <button id="my-toggle-eraser" onclick="return toggleEraser(this)"></button> </div>
269
+ <div class="pad2"> <button id="my-button-undo" onclick="return UNDO_SKETCH_FUNCTION(this)"></button> </div>
270
+ <div class="pad2"> <button id="my-button-clear" onclick="return DELETE_SKETCH_FUNCTION(this)"></button> </div>
271
+ <div class="pad2"> <button href="TODO" download="image" id="my-button-down" onclick='return theSketchDownloadFunction()'></button> </div>
272
+ </div>
273
+ """)
274
+ # gr.Markdown("## Prompt", elem_id="tools_header")
275
+ prompt = gr.Textbox(label="Prompt", value="", show_label=True)
276
+ with gr.Row():
277
+ style = gr.Dropdown(label="Style", choices=STYLE_NAMES, value=DEFAULT_STYLE_NAME, scale=1)
278
+ prompt_temp = gr.Textbox(label="Prompt Style Template", value=styles[DEFAULT_STYLE_NAME], scale=2, max_lines=1)
279
+
280
+ controlnet_conditioning_scale = gr.Slider(label="Control Strength", minimum=0, maximum=1, step=0.01, value=0.8)
281
+
282
+
283
+ device_choices = ['GPU','CPU']
284
+ device_type = gr.Radio(device_choices, label='Device',
285
+ value=device_choices[1],
286
+ interactive=False,
287
+ info='No funding to provide GPU-based demos now. Please choose CPU!')
288
+
289
+ dtype_choices = ['torch.float16','torch.float32']
290
+ param_dtype = gr.Radio(dtype_choices,label='torch.weight_type',
291
+ value=dtype_choices[0],
292
+ interactive=True,
293
+ info='To save GPU memory, use torch.float16. For better quality, use torch.float32.')
294
+
295
+
296
+ with gr.Column(elem_id="column_process", min_width=50, scale=0.4):
297
+ gr.Markdown("## SDXS-Sketch", elem_id="description")
298
+ run_button = gr.Button("Run", min_width=50)
299
+
300
+ with gr.Column(elem_id="column_output"):
301
+ gr.Markdown("## OUTPUT", elem_id="output_header")
302
+ result = gr.Image(label="Result", height=440, width=440, elem_id="output_image", show_label=False, show_download_button=True)
303
+ download_output = gr.Button("Download output", elem_id="download_output")
304
+ gr.Markdown("### Instructions")
305
+ gr.Markdown("**1**. Enter a text prompt (e.g. cat)")
306
+ gr.Markdown("**2**. Start sketching")
307
+ gr.Markdown("**3**. Change the image style using a style template")
308
+ gr.Markdown("**4**. Adjust the effect of sketch guidance using the slider")
309
+
310
+
311
+ eraser.change(fn=lambda x: gr.update(value=not x), inputs=[eraser], outputs=[line]).then(update_canvas, [line, eraser], [image])
312
+ line.change(fn=lambda x: gr.update(value=not x), inputs=[line], outputs=[eraser]).then(update_canvas, [line, eraser], [image])
313
+
314
+ demo.load(None,None,None,_js=scripts)
315
+ inputs = [image, prompt, prompt_temp, style, controlnet_conditioning_scale, device_type, param_dtype]
316
+ outputs = [result, download_sketch, download_output]
317
+ prompt.submit(fn=run, inputs=inputs, outputs=outputs)
318
+ style.change(lambda x: styles[x], inputs=[style], outputs=[prompt_temp]).then(
319
+ fn=run, inputs=inputs, outputs=outputs,)
320
+ run_button.click(fn=run, inputs=inputs, outputs=outputs)
321
+ image.change(run, inputs=inputs, outputs=outputs,)
322
+
323
+ if __name__ == "__main__":
324
+ demo.queue().launch(debug=True)
requirements.txt ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ einops>=0.6.1
2
+ numpy>=1.24.4
3
+ opencv-python==4.6.0.66
4
+ pillow>=9.5.0
5
+ scipy==1.11.1
6
+ timm>=0.9.2
7
+ tokenizers
8
+ torch>=2.0.1
9
+ torchaudio>=2.0.2
10
+ torchdata==0.6.1
11
+ torchvision>=0.15.2
12
+ tqdm>=4.65.0
13
+ transformers
14
+ triton==2.0.0
15
+ urllib3<1.27,>=1.25.4
16
+ xformers>=0.0.20
17
+ accelerate
18
+ streamlit-keyup==0.2.0
19
+ peft
20
+ dominate
21
+ diffusers==0.25.1
22
+ gradio==3.43.1
23
+ hf_transfer
style.css ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css');
2
+
3
+ /* the outermost contrained of the app */
4
+ .main{
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ width: 1200px;
9
+ }
10
+
11
+ /* #main_row{
12
+
13
+ } */
14
+
15
+ /* hide this class */
16
+ .svelte-p4aq0j {
17
+ display: none;
18
+ }
19
+
20
+ .wrap.svelte-p4aq0j.svelte-p4aq0j {
21
+ display: none;
22
+ }
23
+
24
+ #download_sketch{
25
+ display: none;
26
+ }
27
+
28
+ #download_output{
29
+ display: none;
30
+ }
31
+
32
+ #column_input, #column_output{
33
+ width: 500px;
34
+ display: flex;
35
+ /* justify-content: center; */
36
+ align-items: center;
37
+ }
38
+
39
+ #tools_header, #input_header, #output_header, #process_header {
40
+ display: flex;
41
+ justify-content: center;
42
+ align-items: center;
43
+ width: 400px;
44
+ }
45
+
46
+
47
+ #nn{
48
+ width: 100px;
49
+ height: 100px;
50
+ }
51
+
52
+
53
+ #column_process{
54
+ display: flex;
55
+ justify-content: center; /* Center horizontally */
56
+ align-items: center; /* Center vertically */
57
+ height: 600px;
58
+ }
59
+
60
+ /* this is the "pix2pix-turbo" above the process button */
61
+ #description > span{
62
+ display: flex;
63
+ justify-content: center; /* Center horizontally */
64
+ align-items: center; /* Center vertically */
65
+ }
66
+
67
+ /* this is the "UNDO_BUTTON, X_BUTTON" */
68
+ div.svelte-1030q2h{
69
+ width: 30px;
70
+ height: 30px;
71
+ display: none;
72
+ }
73
+
74
+
75
+ #component-5 > div{
76
+ border: 0px;
77
+ box-shadow: none;
78
+ }
79
+
80
+ #cb-eraser, #cb-line{
81
+ display: none;
82
+ }
83
+
84
+ /* eraser text */
85
+ #cb-eraser > label > span{
86
+ display: none;
87
+ }
88
+ #cb-line > label > span{
89
+ display: none;
90
+ }
91
+
92
+
93
+ .button-row {
94
+ display: flex;
95
+ justify-content: center;
96
+ align-items: center;
97
+ height: 50px;
98
+ border: 0px;
99
+ }
100
+
101
+ #my-toggle-pencil{
102
+ background-image: url("https://icons.getbootstrap.com/assets/icons/pencil.svg");
103
+ background-color: white;
104
+ background-size: cover;
105
+ margin: 0px;
106
+ box-shadow: none;
107
+ width: 40px;
108
+ height: 40px;
109
+ }
110
+
111
+ #my-toggle-pencil.clicked{
112
+ background-image: url("https://icons.getbootstrap.com/assets/icons/pencil-fill.svg");
113
+ transform: scale(0.98);
114
+ background-color: gray;
115
+ background-size: cover;
116
+ /* background-size: 95%;
117
+ background-position: center; */
118
+ /* border: 2px solid #000; */
119
+ margin: 0px;
120
+ box-shadow: none;
121
+ width: 40px;
122
+ height: 40px;
123
+ }
124
+
125
+
126
+ #my-toggle-eraser{
127
+ background-image: url("https://icons.getbootstrap.com/assets/icons/eraser.svg");
128
+ background-color: white;
129
+ background-color: white;
130
+ background-size: cover;
131
+ margin: 0px;
132
+ box-shadow: none;
133
+ width: 40px;
134
+ height: 40px;
135
+ }
136
+
137
+ #my-toggle-eraser.clicked{
138
+ background-image: url("https://icons.getbootstrap.com/assets/icons/eraser-fill.svg");
139
+ transform: scale(0.98);
140
+ background-color: gray;
141
+ background-size: cover;
142
+ margin: 0px;
143
+ box-shadow: none;
144
+ width: 40px;
145
+ height: 40px;
146
+ }
147
+
148
+
149
+
150
+ #my-button-undo{
151
+ background-image: url("https://icons.getbootstrap.com/assets/icons/arrow-counterclockwise.svg");
152
+ background-color: white;
153
+ background-size: cover;
154
+ margin: 0px;
155
+ box-shadow: none;
156
+ width: 40px;
157
+ height: 40px;
158
+ }
159
+
160
+ #my-button-clear{
161
+ background-image: url("https://icons.getbootstrap.com/assets/icons/x-lg.svg");
162
+ background-color: white;
163
+ background-size: cover;
164
+ margin: 0px;
165
+ box-shadow: none;
166
+ width: 40px;
167
+ height: 40px;
168
+
169
+ }
170
+
171
+
172
+ #my-button-down{
173
+ background-image: url("https://icons.getbootstrap.com/assets/icons/arrow-down.svg");
174
+ background-color: white;
175
+ background-size: cover;
176
+ margin: 0px;
177
+ box-shadow: none;
178
+ width: 40px;
179
+ height: 40px;
180
+
181
+ }
182
+
183
+ .pad2{
184
+ padding: 2px;
185
+ background-color: white;
186
+ border: 2px solid #000;
187
+ margin: 10px;
188
+ display: flex;
189
+ justify-content: center; /* Center horizontally */
190
+ align-items: center; /* Center vertically */
191
+ }
192
+
193
+
194
+
195
+
196
+ #output_image, #input_image{
197
+ border-radius: 0px;
198
+ border: 5px solid #000;
199
+ border-width: none;
200
+ }
201
+
202
+
203
+ #output_image > img{
204
+ border: 5px solid #000;
205
+ border-radius: 0px;
206
+ border-width: none;
207
+ }
208
+
209
+ #input_image > div.image-container.svelte-p3y7hu > div.wrap.svelte-yigbas > canvas:nth-child(1){
210
+ border: 5px solid #000;
211
+ border-radius: 0px;
212
+ border-width: none;
213
+ }