Spaces:
Running
on
Zero
Running
on
Zero
Add Sketch to system
Browse files- app.py +96 -22
- style_20250128.css +15 -0
- utils/file_utils.py +22 -21
- utils/image_utils.py +3 -3
app.py
CHANGED
@@ -102,8 +102,6 @@ from utils.version_info import (
|
|
102 |
)
|
103 |
#from utils.depth_estimation import (get_depth_map_from_state)
|
104 |
|
105 |
-
|
106 |
-
|
107 |
input_image_palette = []
|
108 |
current_prerendered_image = gr.State("./images/images/Beeuty-1.png")
|
109 |
user_dir = constants.TMPDIR
|
@@ -619,7 +617,43 @@ def add_border(image, mask_width, mask_height, blank_color):
|
|
619 |
print(f"Adding border to image with width: {mask_width}, height: {mask_height}, color: {margin_color}")
|
620 |
return shrink_and_paste_on_blank(bordered_image_output, mask_width, mask_height, margin_color)
|
621 |
|
622 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
623 |
####################################### DEPTH ESTIMATION #######################################
|
624 |
|
625 |
|
@@ -634,7 +668,6 @@ def preprocess_image(image: Image.Image) -> Image.Image:
|
|
634 |
processed_image = TRELLIS_PIPELINE.preprocess_image(image)
|
635 |
return processed_image
|
636 |
|
637 |
-
|
638 |
def pack_state(gs: Gaussian, mesh: MeshExtractResult, name: str) -> dict:
|
639 |
return {
|
640 |
'gaussian': {
|
@@ -651,8 +684,7 @@ def pack_state(gs: Gaussian, mesh: MeshExtractResult, name: str) -> dict:
|
|
651 |
},
|
652 |
'name': name
|
653 |
}
|
654 |
-
|
655 |
-
|
656 |
def unpack_state(state: dict) -> Tuple[Gaussian, edict, str]:
|
657 |
gs = Gaussian(
|
658 |
aabb=state['gaussian']['aabb'],
|
@@ -954,22 +986,35 @@ with gr.Blocks(css_paths="style_20250128.css", title=title, theme='Surn/beeuty',
|
|
954 |
elem_classes="centered solid imgcontainer",
|
955 |
key="imgInput",
|
956 |
image_mode=None,
|
957 |
-
format="PNG"
|
|
|
958 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
959 |
|
960 |
-
# New code to convert input image to RGBA PNG
|
961 |
-
def on_input_image_change(image_path):
|
962 |
-
if image_path is None:
|
963 |
-
gr.Warning("Please upload an Input Image to get started.")
|
964 |
-
return None
|
965 |
-
img, img_path = convert_to_rgba_png(image_path)
|
966 |
-
return img_path
|
967 |
-
|
968 |
-
input_image.change(
|
969 |
-
fn=on_input_image_change,
|
970 |
-
inputs=[input_image],
|
971 |
-
outputs=[input_image], scroll_to_output=True,
|
972 |
-
)
|
973 |
with gr.Column():
|
974 |
with gr.Accordion("Hex Coloring and Exclusion", open = False):
|
975 |
with gr.Row():
|
@@ -1032,7 +1077,7 @@ with gr.Blocks(css_paths="style_20250128.css", title=title, theme='Surn/beeuty',
|
|
1032 |
outputs=[input_image],
|
1033 |
scroll_to_output=True
|
1034 |
)
|
1035 |
-
|
1036 |
with gr.Row():
|
1037 |
with gr.Accordion("Generate AI Image (click here for options)", open = False):
|
1038 |
with gr.Row():
|
@@ -1267,8 +1312,29 @@ with gr.Blocks(css_paths="style_20250128.css", title=title, theme='Surn/beeuty',
|
|
1267 |
fn=generate_input_image_click,
|
1268 |
inputs=[input_image,map_options, prompt_textbox, negative_prompt_textbox, model_textbox, randomize_seed, seed_slider, gr.State(False), gr.State(0.5), image_size_ratio],
|
1269 |
outputs=[input_image, seed_slider], scroll_to_output=True
|
|
|
|
|
|
|
|
|
1270 |
)
|
1271 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1272 |
model_textbox.change(
|
1273 |
fn=update_prompt_notes,
|
1274 |
inputs=model_textbox,
|
@@ -1295,6 +1361,10 @@ with gr.Blocks(css_paths="style_20250128.css", title=title, theme='Surn/beeuty',
|
|
1295 |
fn=generate_input_image_click,
|
1296 |
inputs=[input_image, map_options, prompt_textbox, negative_prompt_textbox, model_textbox,randomize_seed, seed_slider, gr.State(True), image_guidance_stength, image_size_ratio],
|
1297 |
outputs=[input_image, seed_slider], scroll_to_output=True
|
|
|
|
|
|
|
|
|
1298 |
)
|
1299 |
|
1300 |
# Update the state variable with the prerendered image filepath when an image is selected
|
@@ -1309,6 +1379,10 @@ with gr.Blocks(css_paths="style_20250128.css", title=title, theme='Surn/beeuty',
|
|
1309 |
lambda: current_prerendered_image.value,
|
1310 |
inputs=None,
|
1311 |
outputs=[input_image], scroll_to_output=True
|
|
|
|
|
|
|
|
|
1312 |
)
|
1313 |
output_overlay_composite.change(
|
1314 |
fn=combine_images_with_lerp,
|
|
|
102 |
)
|
103 |
#from utils.depth_estimation import (get_depth_map_from_state)
|
104 |
|
|
|
|
|
105 |
input_image_palette = []
|
106 |
current_prerendered_image = gr.State("./images/images/Beeuty-1.png")
|
107 |
user_dir = constants.TMPDIR
|
|
|
617 |
print(f"Adding border to image with width: {mask_width}, height: {mask_height}, color: {margin_color}")
|
618 |
return shrink_and_paste_on_blank(bordered_image_output, mask_width, mask_height, margin_color)
|
619 |
|
620 |
+
def on_input_image_change(image_path):
|
621 |
+
if image_path is None:
|
622 |
+
gr.Warning("Please upload an Input Image to get started.")
|
623 |
+
return None, gr.update()
|
624 |
+
img, img_path = convert_to_rgba_png(image_path)
|
625 |
+
with Image.open(img_path) as pil_img:
|
626 |
+
width, height = pil_img.size
|
627 |
+
return [img_path, gr.update(width=width, height=height)]
|
628 |
+
|
629 |
+
def update_sketch_dimensions(input_image, sketch_image):
|
630 |
+
# Load the images using open_image() if they are provided as file paths.
|
631 |
+
in_img = open_image(input_image) if isinstance(input_image, str) else input_image
|
632 |
+
sk_img_path, _ = get_image_from_dict(sketch_image)
|
633 |
+
sk_img = open_image(sk_img_path)
|
634 |
+
# Resize sketch image if dimensions don't match input image.
|
635 |
+
if in_img.size != sk_img.size:
|
636 |
+
sk_img = sk_img.resize(in_img.size, Image.LANCZOS)
|
637 |
+
return [sk_img, gr.update(width=in_img.width, height=in_img.height)]
|
638 |
+
|
639 |
+
def composite_with_control_sync(input_image, sketch_image, slider_value):
|
640 |
+
# Load the images using open_image() if they are provided as file paths.
|
641 |
+
in_img = open_image(input_image) if isinstance(input_image, str) else input_image
|
642 |
+
sk_img_path, _ = get_image_from_dict(sketch_image)
|
643 |
+
sk_img = open_image(sk_img_path)
|
644 |
+
|
645 |
+
# Resize sketch image if dimensions don't match input image.
|
646 |
+
if in_img.size != sk_img.size:
|
647 |
+
sk_img = sk_img.resize(in_img.size, Image.LANCZOS)
|
648 |
+
|
649 |
+
# Now composite using the original alpha_composite_with_control function.
|
650 |
+
result_img = alpha_composite_with_control(in_img, sk_img, slider_value)
|
651 |
+
return result_img
|
652 |
+
|
653 |
+
def replace_input_with_sketch_image(sketch_image):
|
654 |
+
print(f"Sketch Image: {sketch_image}\n")
|
655 |
+
sketch, is_dict = get_image_from_dict(sketch_image)
|
656 |
+
return sketch
|
657 |
####################################### DEPTH ESTIMATION #######################################
|
658 |
|
659 |
|
|
|
668 |
processed_image = TRELLIS_PIPELINE.preprocess_image(image)
|
669 |
return processed_image
|
670 |
|
|
|
671 |
def pack_state(gs: Gaussian, mesh: MeshExtractResult, name: str) -> dict:
|
672 |
return {
|
673 |
'gaussian': {
|
|
|
684 |
},
|
685 |
'name': name
|
686 |
}
|
687 |
+
|
|
|
688 |
def unpack_state(state: dict) -> Tuple[Gaussian, edict, str]:
|
689 |
gs = Gaussian(
|
690 |
aabb=state['gaussian']['aabb'],
|
|
|
986 |
elem_classes="centered solid imgcontainer",
|
987 |
key="imgInput",
|
988 |
image_mode=None,
|
989 |
+
format="PNG",
|
990 |
+
height=512
|
991 |
)
|
992 |
+
with gr.Accordion("Sketch Pad", open = False, elem_id="sketchpd"):
|
993 |
+
with gr.Row():
|
994 |
+
sketch_image = gr.Sketchpad(
|
995 |
+
label="Sketch Image",
|
996 |
+
type="filepath",
|
997 |
+
#invert_colors=True,
|
998 |
+
#sources=['upload','canvas'],
|
999 |
+
#tool=['editor','select','color-sketch'],
|
1000 |
+
placeholder="Draw a sketch or upload an image. Currently broken in gradio 5.17.1",
|
1001 |
+
interactive=True,
|
1002 |
+
elem_classes="centered solid imgcontainer",
|
1003 |
+
key="imgSketch",
|
1004 |
+
image_mode="RGBA",
|
1005 |
+
format="PNG",
|
1006 |
+
brush=gr.Brush()
|
1007 |
+
)
|
1008 |
+
with gr.Row():
|
1009 |
+
with gr.Column(scale=1):
|
1010 |
+
sketch_replace_input_image_button = gr.Button(
|
1011 |
+
"Replace Input Image with sketch",
|
1012 |
+
elem_id="sketch_replace_input_image_button",
|
1013 |
+
elem_classes="solid"
|
1014 |
+
)
|
1015 |
+
sketch_alpha_composite_slider = gr.Slider(0,100,50,0.5, label="Sketch Transparancy", elem_id="alpha_composite_slider")
|
1016 |
+
btn_sketch_alpha_composite = gr.Button("Overlay Sketch on Input Image", elem_id="btn_sketchninput", elem_classes="solid")
|
1017 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1018 |
with gr.Column():
|
1019 |
with gr.Accordion("Hex Coloring and Exclusion", open = False):
|
1020 |
with gr.Row():
|
|
|
1077 |
outputs=[input_image],
|
1078 |
scroll_to_output=True
|
1079 |
)
|
1080 |
+
|
1081 |
with gr.Row():
|
1082 |
with gr.Accordion("Generate AI Image (click here for options)", open = False):
|
1083 |
with gr.Row():
|
|
|
1312 |
fn=generate_input_image_click,
|
1313 |
inputs=[input_image,map_options, prompt_textbox, negative_prompt_textbox, model_textbox, randomize_seed, seed_slider, gr.State(False), gr.State(0.5), image_size_ratio],
|
1314 |
outputs=[input_image, seed_slider], scroll_to_output=True
|
1315 |
+
).then(
|
1316 |
+
fn=update_sketch_dimensions,
|
1317 |
+
inputs=[input_image, sketch_image],
|
1318 |
+
outputs=[sketch_image, sketch_image]
|
1319 |
)
|
1320 |
+
input_image.input(
|
1321 |
+
fn=on_input_image_change,
|
1322 |
+
inputs=[input_image],
|
1323 |
+
outputs=[input_image,sketch_image], scroll_to_output=True,
|
1324 |
+
)
|
1325 |
+
###################### sketchpad ############################
|
1326 |
+
btn_sketch_alpha_composite.click(
|
1327 |
+
fn=composite_with_control_sync,
|
1328 |
+
inputs=[input_image, sketch_image, sketch_alpha_composite_slider],
|
1329 |
+
outputs=[input_image],
|
1330 |
+
scroll_to_output=True
|
1331 |
+
)
|
1332 |
+
sketch_replace_input_image_button.click(
|
1333 |
+
lambda sketch_image: replace_input_with_sketch_image(sketch_image),
|
1334 |
+
inputs=[sketch_image],
|
1335 |
+
outputs=[input_image], scroll_to_output=True
|
1336 |
+
)
|
1337 |
+
##################### model #######################################
|
1338 |
model_textbox.change(
|
1339 |
fn=update_prompt_notes,
|
1340 |
inputs=model_textbox,
|
|
|
1361 |
fn=generate_input_image_click,
|
1362 |
inputs=[input_image, map_options, prompt_textbox, negative_prompt_textbox, model_textbox,randomize_seed, seed_slider, gr.State(True), image_guidance_stength, image_size_ratio],
|
1363 |
outputs=[input_image, seed_slider], scroll_to_output=True
|
1364 |
+
).then(
|
1365 |
+
fn=update_sketch_dimensions,
|
1366 |
+
inputs=[input_image, sketch_image],
|
1367 |
+
outputs=[sketch_image, sketch_image]
|
1368 |
)
|
1369 |
|
1370 |
# Update the state variable with the prerendered image filepath when an image is selected
|
|
|
1379 |
lambda: current_prerendered_image.value,
|
1380 |
inputs=None,
|
1381 |
outputs=[input_image], scroll_to_output=True
|
1382 |
+
).then(
|
1383 |
+
fn=update_sketch_dimensions,
|
1384 |
+
inputs=[input_image, sketch_image],
|
1385 |
+
outputs=[sketch_image, sketch_image]
|
1386 |
)
|
1387 |
output_overlay_composite.change(
|
1388 |
fn=combine_images_with_lerp,
|
style_20250128.css
CHANGED
@@ -125,4 +125,19 @@ a {
|
|
125 |
.gradio-container, .gradio-container::before {
|
126 |
max-width: 1920px !important;
|
127 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
}
|
|
|
125 |
.gradio-container, .gradio-container::before {
|
126 |
max-width: 1920px !important;
|
127 |
}
|
128 |
+
}
|
129 |
+
|
130 |
+
.sidebar .toggle-button::before {
|
131 |
+
content: 'Sketch Pad';
|
132 |
+
font-weight: bold;
|
133 |
+
transform: rotate(180deg);
|
134 |
+
margin-right: -120px;
|
135 |
+
width: 120px;
|
136 |
+
background-color: rgba(242, 218, 163, 0.62);
|
137 |
+
}
|
138 |
+
.dark .sidebar .toggle-button::before {
|
139 |
+
background-color: rgba(41, 18, 5, 0.38) !important;
|
140 |
+
}
|
141 |
+
.sidebar.open .toggle-button::before {
|
142 |
+
content: '';
|
143 |
}
|
utils/file_utils.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
# file_utils
|
2 |
import os
|
3 |
import utils.constants as constants
|
4 |
-
import shutil
|
5 |
from pathlib import Path
|
6 |
|
7 |
def cleanup_temp_files():
|
@@ -11,32 +11,33 @@ def cleanup_temp_files():
|
|
11 |
except Exception as e:
|
12 |
print(f"Failed to delete temp file {file_path}: {e}")
|
13 |
|
14 |
-
def rename_file_to_lowercase_extension(
|
15 |
"""
|
16 |
-
Renames
|
17 |
|
18 |
Parameters:
|
19 |
-
|
20 |
|
21 |
Returns:
|
22 |
-
str: The new file path
|
23 |
|
24 |
Raises:
|
25 |
-
|
26 |
"""
|
27 |
-
path
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
41 |
else:
|
42 |
-
return
|
|
|
1 |
# file_utils
|
2 |
import os
|
3 |
import utils.constants as constants
|
4 |
+
#import shutil
|
5 |
from pathlib import Path
|
6 |
|
7 |
def cleanup_temp_files():
|
|
|
11 |
except Exception as e:
|
12 |
print(f"Failed to delete temp file {file_path}: {e}")
|
13 |
|
14 |
+
def rename_file_to_lowercase_extension(file_path: str) -> str:
|
15 |
"""
|
16 |
+
Renames a file's extension to lowercase in place.
|
17 |
|
18 |
Parameters:
|
19 |
+
file_path (str): The original file path.
|
20 |
|
21 |
Returns:
|
22 |
+
str: The new file path with the lowercase extension.
|
23 |
|
24 |
Raises:
|
25 |
+
OSError: If there is an error renaming the file (e.g., file not found, permissions issue).
|
26 |
"""
|
27 |
+
# Split the path into directory and filename
|
28 |
+
directory, filename = os.path.split(file_path)
|
29 |
+
|
30 |
+
# Split the filename into name and extension
|
31 |
+
name, ext = os.path.splitext(filename)
|
32 |
+
|
33 |
+
# Convert the extension to lowercase
|
34 |
+
new_ext = ext.lower()
|
35 |
+
|
36 |
+
# If the extension changes, rename the file
|
37 |
+
if ext != new_ext:
|
38 |
+
new_filename = name + new_ext
|
39 |
+
new_file_path = os.path.join(directory, new_filename)
|
40 |
+
os.rename(file_path, new_file_path)
|
41 |
+
return new_file_path
|
42 |
else:
|
43 |
+
return file_path
|
utils/image_utils.py
CHANGED
@@ -19,10 +19,10 @@ from utils.file_utils import rename_file_to_lowercase_extension
|
|
19 |
|
20 |
def get_image_from_dict(image_path):
|
21 |
if isinstance(image_path, dict) :
|
22 |
-
if '
|
23 |
-
image_path = image_path.get('image')
|
24 |
-
elif 'composite' in image_path:
|
25 |
image_path = image_path.get('composite')
|
|
|
|
|
26 |
else:
|
27 |
print("\n Unknown image dictionary.\n")
|
28 |
raise UserWarning("Unknown image dictionary.")
|
|
|
19 |
|
20 |
def get_image_from_dict(image_path):
|
21 |
if isinstance(image_path, dict) :
|
22 |
+
if 'composite' in image_path:
|
|
|
|
|
23 |
image_path = image_path.get('composite')
|
24 |
+
elif 'image' in image_path:
|
25 |
+
image_path = image_path.get('image')
|
26 |
else:
|
27 |
print("\n Unknown image dictionary.\n")
|
28 |
raise UserWarning("Unknown image dictionary.")
|