Surn commited on
Commit
1ddd9ab
·
1 Parent(s): 06b2c5c
Files changed (4) hide show
  1. app.py +17 -12
  2. utils/file_utils.py +15 -1
  3. utils/hex_grid.py +3 -3
  4. utils/image_utils.py +81 -17
app.py CHANGED
@@ -43,7 +43,7 @@ from utils.misc import (
43
  convert_ratio_to_dimensions,
44
  update_dimensions_on_ratio,
45
  get_seed,
46
- get_output_name
47
  ) #install_cuda_toolkit,install_torch, _get_output, setup_runtime_env)
48
 
49
  from utils.image_utils import (
@@ -62,7 +62,8 @@ from utils.image_utils import (
62
  resize_image_with_aspect_ratio,
63
  build_prerendered_images_by_quality,
64
  get_image_from_dict,
65
- calculate_optimal_fill_dimensions
 
66
  )
67
 
68
  from utils.hex_grid import (
@@ -717,8 +718,7 @@ def on_input_image_change(image_path):
717
  gr.Warning("Please upload an Input Image to get started.")
718
  return None, gr.update()
719
  img, img_path = convert_to_rgba_png(image_path)
720
- with Image.open(img_path) as pil_img:
721
- width, height = pil_img.size
722
  return [img_path, gr.update(width=width, height=height)]
723
 
724
  def update_sketch_dimensions(input_image, sketch_image):
@@ -759,7 +759,7 @@ def load_3d_models(is_open: bool = True) -> bool:
759
  global image_processor, depth_model, TRELLIS_PIPELINE
760
  image_processor = DPTImageProcessor.from_pretrained("Intel/dpt-large")
761
  depth_model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large", ignore_mismatched_sizes=True)
762
- TRELLIS_PIPELINE = TrellisImageTo3DPipeline.from_pretrained("JeffreyXiang/TRELLIS-image-large")
763
  try:
764
  # Preload with a dummy image to finalize initialization
765
  TRELLIS_PIPELINE.preprocess_image(Image.fromarray(np.zeros((256, 256, 3), dtype=np.uint8)))
@@ -773,10 +773,12 @@ def unload_3d_models(is_open: bool = False) -> bool:
773
  if not is_open:
774
  gr.Info("Unloading 3D models...")
775
  global image_processor, depth_model, TRELLIS_PIPELINE
776
- TRELLIS_PIPELINE.to("cpu")
777
- del image_processor
778
- del depth_model
779
- del TRELLIS_PIPELINE
 
 
780
  #torch.cuda.empty_cache()
781
  #torch.cuda.ipc_collect()
782
  gc.collect()
@@ -890,7 +892,7 @@ def depth_process_image(image_path, resized_width=800, z_scale=208):
890
  torch.cuda.ipc_collect()
891
  return img
892
 
893
- def generate_3d_asset_part1(depth_image_source, randomize_seed, seed, input_image, output_image, overlay_image, bordered_image_output, progress=gr.Progress(track_tqdm=True)):
894
  # Choose the image based on source
895
  if depth_image_source == "Input Image":
896
  image_path = input_image
@@ -912,6 +914,9 @@ def generate_3d_asset_part1(depth_image_source, randomize_seed, seed, input_imag
912
  # Process the image for depth estimation
913
  depth_img = depth_process_image(image_path, resized_width=1536, z_scale=336)
914
  depth_img = resize_image_with_aspect_ratio(depth_img, 1536, 1536)
 
 
 
915
 
916
  return depth_img, image_path, output_name, final_seed
917
 
@@ -1200,7 +1205,7 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1200
  lut_file.change(get_filename, inputs=[lut_file], outputs=[lut_filename])
1201
  lut_filename.change(show_lut, inputs=[lut_filename, lut_example_image], outputs=[lut_example_image])
1202
  apply_lut_button.click(
1203
- lambda lut_filename, input_image: gr.Warning("Please upload an Input Image to get started.") if input_image is None else apply_lut_to_image_path(lut_filename, input_image)[0],
1204
  inputs=[lut_filename, input_image],
1205
  outputs=[input_image],
1206
  scroll_to_output=True
@@ -1321,7 +1326,7 @@ with gr.Blocks(css_paths="style_20250314.css", title=title, theme='Surn/beeuty',
1321
  y_spacing = gr.Number(label="Adjust Vertical spacing", value=3, minimum=-200, maximum=200, precision=1)
1322
  with gr.Row():
1323
  rotation = gr.Slider(-90, 180, 0.0, 0.1, label="Hexagon Rotation (degree)")
1324
- add_hex_text = gr.Dropdown(label="Add Text to Hexagons", choices=[None, "Row-Column Coordinates", "Column Letter, Row Number", "Column Number, Row Letter", "Sequential Numbers", "Playing Cards Sequential", "Playing Cards Alternate Red and Black", "Custom List"], value=None)
1325
  with gr.Row():
1326
  custom_text_list = gr.TextArea(label="Custom Text List", value=constants.cards_alternating, visible=False,)
1327
  custom_text_color_list = gr.TextArea(label="Custom Text Color List", value=constants.card_colors_alternating, visible=False)
 
43
  convert_ratio_to_dimensions,
44
  update_dimensions_on_ratio,
45
  get_seed,
46
+ get_output_name
47
  ) #install_cuda_toolkit,install_torch, _get_output, setup_runtime_env)
48
 
49
  from utils.image_utils import (
 
62
  resize_image_with_aspect_ratio,
63
  build_prerendered_images_by_quality,
64
  get_image_from_dict,
65
+ calculate_optimal_fill_dimensions,
66
+ save_image_to_temp_png
67
  )
68
 
69
  from utils.hex_grid import (
 
718
  gr.Warning("Please upload an Input Image to get started.")
719
  return None, gr.update()
720
  img, img_path = convert_to_rgba_png(image_path)
721
+ width, height = img.size
 
722
  return [img_path, gr.update(width=width, height=height)]
723
 
724
  def update_sketch_dimensions(input_image, sketch_image):
 
759
  global image_processor, depth_model, TRELLIS_PIPELINE
760
  image_processor = DPTImageProcessor.from_pretrained("Intel/dpt-large")
761
  depth_model = DPTForDepthEstimation.from_pretrained("Intel/dpt-large", ignore_mismatched_sizes=True)
762
+ TRELLIS_PIPELINE = TrellisImageTo3DPipeline.from_pretrained("JeffreyXiang/TRELLIS-image-large").to("cpu")
763
  try:
764
  # Preload with a dummy image to finalize initialization
765
  TRELLIS_PIPELINE.preprocess_image(Image.fromarray(np.zeros((256, 256, 3), dtype=np.uint8)))
 
773
  if not is_open:
774
  gr.Info("Unloading 3D models...")
775
  global image_processor, depth_model, TRELLIS_PIPELINE
776
+ if TRELLIS_PIPELINE:
777
+ TRELLIS_PIPELINE.to("cpu")
778
+ del TRELLIS_PIPELINE
779
+ if depth_model:
780
+ del image_processor
781
+ del depth_model
782
  #torch.cuda.empty_cache()
783
  #torch.cuda.ipc_collect()
784
  gc.collect()
 
892
  torch.cuda.ipc_collect()
893
  return img
894
 
895
+ def generate_3d_asset_part1(depth_image_source, randomize_seed, seed, input_image, output_image, overlay_image, bordered_image_output, req: gr.Request, progress=gr.Progress(track_tqdm=True)):
896
  # Choose the image based on source
897
  if depth_image_source == "Input Image":
898
  image_path = input_image
 
914
  # Process the image for depth estimation
915
  depth_img = depth_process_image(image_path, resized_width=1536, z_scale=336)
916
  depth_img = resize_image_with_aspect_ratio(depth_img, 1536, 1536)
917
+
918
+ user_dir = os.path.join(constants.TMPDIR, str(req.session_hash))
919
+ depth_img = save_image_to_temp_png(depth_img, user_dir, f"{output_name}_depth")
920
 
921
  return depth_img, image_path, output_name, final_seed
922
 
 
1205
  lut_file.change(get_filename, inputs=[lut_file], outputs=[lut_filename])
1206
  lut_filename.change(show_lut, inputs=[lut_filename, lut_example_image], outputs=[lut_example_image])
1207
  apply_lut_button.click(
1208
+ lambda lut_filename, input_image: gr.Warning("Please upload an Input Image to get started.") if input_image is None else apply_lut_to_image_path(lut_filename, input_image)[1],
1209
  inputs=[lut_filename, input_image],
1210
  outputs=[input_image],
1211
  scroll_to_output=True
 
1326
  y_spacing = gr.Number(label="Adjust Vertical spacing", value=3, minimum=-200, maximum=200, precision=1)
1327
  with gr.Row():
1328
  rotation = gr.Slider(-90, 180, 0.0, 0.1, label="Hexagon Rotation (degree)")
1329
+ add_hex_text = gr.Dropdown(label="Add Text to Hexagons", choices=[None, "Column-Row Coordinates", "Column(Letter)-Row Coordinates", "Column-Row(Letter) Coordinates", "Sequential Numbers", "Playing Cards Sequential", "Playing Cards Alternate Red and Black", "Custom List"], value=None)
1330
  with gr.Row():
1331
  custom_text_list = gr.TextArea(label="Custom Text List", value=constants.cards_alternating, visible=False,)
1332
  custom_text_color_list = gr.TextArea(label="Custom Text Color List", value=constants.card_colors_alternating, visible=False)
utils/file_utils.py CHANGED
@@ -38,7 +38,21 @@ def rename_file_to_lowercase_extension(file_path: str) -> str:
38
  if ext != new_ext:
39
  new_filename = name + new_ext
40
  new_file_path = os.path.join(directory, new_filename)
41
- os.rename(file_path, new_file_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  return new_file_path
43
  else:
44
  return file_path
 
38
  if ext != new_ext:
39
  new_filename = name + new_ext
40
  new_file_path = os.path.join(directory, new_filename)
41
+ try:
42
+ os.rename(file_path, new_file_path)
43
+ except Exception as e:
44
+ print(f"os.rename failed: {e}. Falling back to binary copy operation.")
45
+ try:
46
+ # Read the file in binary mode and write it to new_file_path
47
+ with open(file_path, 'rb') as f:
48
+ data = f.read()
49
+ with open(new_file_path, 'wb') as f:
50
+ f.write(data)
51
+ # Optionally, remove the original file after copying
52
+ #os.remove(file_path)
53
+ except Exception as inner_e:
54
+ print(f"Failed to copy file from {file_path} to {new_file_path}: {inner_e}")
55
+ raise inner_e
56
  return new_file_path
57
  else:
58
  return file_path
utils/hex_grid.py CHANGED
@@ -274,13 +274,13 @@ def generate_hexagon_grid_with_text(hex_size, border_size, input_image=None, ima
274
  if font_size:
275
  font = ImageFont.truetype(font_path, font_size)
276
  # Determine the text to draw
277
- if add_hex_text_option == "Row-Column Coordinates":
278
  text = f"{col},{row}"
279
  elif add_hex_text_option == "Sequential Numbers":
280
  text = f"{hex_index}"
281
- elif add_hex_text_option == "Column Letter Row Number":
282
  text = f"{number_to_letter(col)}{row}"
283
- elif add_hex_text_option == "Column Number Row Letter":
284
  text = f"{col}{number_to_letter(row)}"
285
  elif text_list:
286
  text = text_list[hex_index % len(text_list)]
 
274
  if font_size:
275
  font = ImageFont.truetype(font_path, font_size)
276
  # Determine the text to draw
277
+ if add_hex_text_option == "Column-Row Coordinates":
278
  text = f"{col},{row}"
279
  elif add_hex_text_option == "Sequential Numbers":
280
  text = f"{hex_index}"
281
+ elif add_hex_text_option == "Column(Letter)-Row Coordinates":
282
  text = f"{number_to_letter(col)}{row}"
283
+ elif add_hex_text_option == "Column-Row(Letter) Coordinates":
284
  text = f"{col}{number_to_letter(row)}"
285
  elif text_list:
286
  text = text_list[hex_index % len(text_list)]
utils/image_utils.py CHANGED
@@ -17,6 +17,44 @@ from utils.color_utils import (
17
  )
18
  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 'composite' in image_path:
@@ -44,14 +82,16 @@ def open_image(image_path):
44
  Raises:
45
  Exception: If there is an error opening the image.
46
  """
 
47
  if isinstance(image_path, Image.Image):
48
  return image_path
49
- else:
50
- image_path = rename_file_to_lowercase_extension(image_path)
 
 
51
 
52
  import requests
53
  try:
54
- image_path, is_dict = get_image_from_dict(image_path)
55
  # Strip leading and trailing double quotation marks, if present
56
  image_path = image_path.strip('"')
57
  if image_path.startswith('http'):
@@ -587,7 +627,7 @@ def convert_rgb_to_rgba_safe(image: Image) -> Image:
587
  rgba_image = image.copy()
588
  # Optionally, set a default alpha value (e.g., fully opaque)
589
  alpha = Image.new('L', rgba_image.size, 255) # 255 for full opacity
590
- rgba_image.putalpha(alpha)
591
  return rgba_image
592
 
593
  def apply_lut_to_image_path(lut_filename: str, image_path: str) -> tuple[Image, str]:
@@ -602,9 +642,24 @@ def apply_lut_to_image_path(lut_filename: str, image_path: str) -> tuple[Image,
602
  Returns:
603
  tuple: A tuple containing the PIL Image object with the LUT applied and the new image path as a string.
604
  """
 
 
 
605
  if image_path is None:
606
  raise UserWarning("No image provided.")
607
  return None, None
 
 
 
 
 
 
 
 
 
 
 
 
608
  path = Path(image_path)
609
  img = open_image(image_path)
610
  if not ((path.suffix.lower() == '.png' and img.mode == 'RGBA')):
@@ -619,14 +674,22 @@ def apply_lut_to_image_path(lut_filename: str, image_path: str) -> tuple[Image,
619
  if image_path != new_image_path:
620
  delete_image(image_path)
621
  else:
622
- new_image_path = image_path
623
- if lut_filename is not None:
 
 
 
624
  try:
625
- img = apply_lut(img, lut_filename)
626
  except Exception as e:
627
  print(f"BAD LUT: Error applying LUT {str(e)}.")
628
- img.save(new_image_path.lower(), format='PNG')
629
- return img, str(new_image_path)
 
 
 
 
 
630
 
631
  ############################################# RGBA ###########################################################
632
 
@@ -659,7 +722,8 @@ def convert_jpg_to_rgba(input_path) -> tuple[Image, str]:
659
  # Check file extension first to skip unnecessary processing
660
  if input_path.suffix.lower() not in ('.jpg', '.jpeg'):
661
  print(f"Skipping conversion: {input_path} is not a JPG or JPEG file.")
662
- return Image.open(input_path), str(output_path)
 
663
 
664
  print(f"Converting to PNG: {input_path} is a JPG or JPEG file.")
665
 
@@ -779,22 +843,22 @@ def resize_all_images_in_folder(target_width: int, output_folder: str = "resized
779
  with Image.open(file_path) as img:
780
  # Convert to RGB if needed (handles RGBA, CMYK, etc.)
781
  if img.mode != 'RGB':
782
- img = img.convert('RGB')
783
  # Calculate target height maintaining aspect ratio
784
  original_width, original_height = img.size
785
  aspect_ratio = original_height / original_width
786
- target_height = int(target_width * aspect_ratio)
787
  # Resize using the reference function
788
- resized_img = resize_image_with_aspect_ratio(img, target_width, target_height)
789
  # Create output filename
790
- output_filename = output_path / f"{file_prefix}{file_path.name}"
791
  # Save the resized image
792
- resized_img.save(output_filename, quality=95)
793
  successful += 1
794
- print(f"Successfully resized: {file_path.name}")
795
  except Exception as e:
796
  failed += 1
797
- print(f"Failed to resize {file_path.name}: {str(e)}")
798
 
799
  print(f"\nResizing complete. Successfully processed: {successful}, Failed: {failed}")
800
  return successful, failed
 
17
  )
18
  from utils.file_utils import rename_file_to_lowercase_extension
19
 
20
+
21
+
22
+
23
+ def save_image_to_temp_png(image_source, user_dir: str = None, file_name: str = None):
24
+ """
25
+ Opens an image from a file path, URL, or DataURL and saves it as a PNG in the user's temporary directory.
26
+
27
+ Parameters:
28
+ image_source (str, dict or PIL.Image.Image): The source of the image to open.
29
+
30
+ Returns:
31
+ str: The file path of the saved PNG image in the temporary directory.
32
+ """
33
+ import tempfile
34
+ import uuid
35
+
36
+ # Open the image using the existing utility function
37
+ img = open_image(image_source)
38
+
39
+ # Ensure the image is in a format that supports PNG (convert if necessary)
40
+ if img.mode not in ("RGB", "RGBA"):
41
+ img = img.convert("RGBA")
42
+
43
+ # Generate a unique filename in the system temporary directory
44
+ if user_dir is None:
45
+ user_dir = tempfile.gettempdir()
46
+
47
+ if file_name is None:
48
+ file_name = f"{uuid.uuid4()}.png"
49
+
50
+ temp_filepath = os.path.join(user_dir, file_name.lower())
51
+ os.makedirs(user_dir, exist_ok=True)
52
+
53
+ # Save the image as PNG
54
+ img.save(temp_filepath, format="PNG")
55
+
56
+ return temp_filepath
57
+
58
  def get_image_from_dict(image_path):
59
  if isinstance(image_path, dict) :
60
  if 'composite' in image_path:
 
82
  Raises:
83
  Exception: If there is an error opening the image.
84
  """
85
+
86
  if isinstance(image_path, Image.Image):
87
  return image_path
88
+ elif isinstance(image_path, dict):
89
+ image_path, is_dict = get_image_from_dict(image_path)
90
+
91
+ image_path = rename_file_to_lowercase_extension(image_path)
92
 
93
  import requests
94
  try:
 
95
  # Strip leading and trailing double quotation marks, if present
96
  image_path = image_path.strip('"')
97
  if image_path.startswith('http'):
 
627
  rgba_image = image.copy()
628
  # Optionally, set a default alpha value (e.g., fully opaque)
629
  alpha = Image.new('L', rgba_image.size, 255) # 255 for full opacity
630
+ rgba_image.putalpha(alpha)
631
  return rgba_image
632
 
633
  def apply_lut_to_image_path(lut_filename: str, image_path: str) -> tuple[Image, str]:
 
642
  Returns:
643
  tuple: A tuple containing the PIL Image object with the LUT applied and the new image path as a string.
644
  """
645
+ import gradio as gr
646
+
647
+ img_lut = None
648
  if image_path is None:
649
  raise UserWarning("No image provided.")
650
  return None, None
651
+
652
+ # Split the path into directory and filename
653
+ directory, file_name = os.path.split(image_path)
654
+ lut_directory, lut_file_name = os.path.split(lut_filename)
655
+
656
+ # Split the filename into name and extension
657
+ name, ext = os.path.splitext(file_name)
658
+ lut_name, lut_ext = os.path.splitext(lut_file_name)
659
+
660
+ # Convert the extension to lowercase
661
+ new_ext = ext.lower()
662
+
663
  path = Path(image_path)
664
  img = open_image(image_path)
665
  if not ((path.suffix.lower() == '.png' and img.mode == 'RGBA')):
 
674
  if image_path != new_image_path:
675
  delete_image(image_path)
676
  else:
677
+ # ensure the file extension is lower_case, otherwise leave as is
678
+ new_filename = name + new_ext
679
+ new_image_path = os.path.join(directory, new_filename)
680
+ # Apply the LUT to the image
681
+ if (lut_filename is not None and img is not None):
682
  try:
683
+ img_lut = apply_lut(img, lut_filename)
684
  except Exception as e:
685
  print(f"BAD LUT: Error applying LUT {str(e)}.")
686
+ if img_lut is not None:
687
+ new_filename = name + "_"+ lut_name + new_ext
688
+ new_image_path = os.path.join(directory, new_filename)
689
+ delete_image(image_path)
690
+ img = img_lut
691
+ img.save(new_image_path, format='PNG')
692
+ return img, gr.update(value=str(new_image_path))
693
 
694
  ############################################# RGBA ###########################################################
695
 
 
722
  # Check file extension first to skip unnecessary processing
723
  if input_path.suffix.lower() not in ('.jpg', '.jpeg'):
724
  print(f"Skipping conversion: {input_path} is not a JPG or JPEG file.")
725
+ output_path = rename_file_to_lowercase_extension(input_path)
726
+ return Image.open(output_path), str(output_path)
727
 
728
  print(f"Converting to PNG: {input_path} is a JPG or JPEG file.")
729
 
 
843
  with Image.open(file_path) as img:
844
  # Convert to RGB if needed (handles RGBA, CMYK, etc.)
845
  if img.mode != 'RGB':
846
+ img = img.convert('RGB')
847
  # Calculate target height maintaining aspect ratio
848
  original_width, original_height = img.size
849
  aspect_ratio = original_height / original_width
850
+ target_height = int(target_width * aspect_ratio)
851
  # Resize using the reference function
852
+ resized_img = resize_image_with_aspect_ratio(img, target_width, target_height)
853
  # Create output filename
854
+ output_filename = output_path / f"{file_prefix}{file_path.name.lower()}"
855
  # Save the resized image
856
+ resized_img.save(output_filename, quality=95)
857
  successful += 1
858
+ print(f"Successfully resized: {file_path.name.lower()}")
859
  except Exception as e:
860
  failed += 1
861
+ print(f"Failed to resize {file_path.name.lower()}: {str(e)}")
862
 
863
  print(f"\nResizing complete. Successfully processed: {successful}, Failed: {failed}")
864
  return successful, failed