assistanttttttt commited on
Commit
fc39079
·
1 Parent(s): f1d47d0

Restore image saving from tensor, dedupe duplicates, simplify result handling

Browse files
Files changed (1) hide show
  1. core/pipelines/sd_image_pipeline.py +43 -33
core/pipelines/sd_image_pipeline.py CHANGED
@@ -3,6 +3,7 @@ import random
3
  import shutil
4
  import torch
5
  import gradio as gr
 
6
  from typing import List, Dict, Any
7
 
8
 
@@ -33,32 +34,53 @@ class SdImagePipeline(BasePipeline):
33
  """
34
  progress(0.4, desc="Executing workflow...")
35
  initial_objects = {}
36
- # Execute the workflow; the SaveImage node returns its saved file path(s).
37
- saved_output = WorkflowExecutor.execute_workflow(workflow, initial_objects=initial_objects)
38
- # Execute the workflow; the SaveImage node returns the saved file path(s).
39
- # Ensure we have a list of paths.
40
- if isinstance(saved_output, (list, tuple)):
41
- saved_paths = list(saved_output)
42
- else:
43
- saved_paths = [saved_output]
44
- # The workflow may contain more than one SaveImage node (e.g., from base sampler
45
- # plus conditioning partials), which can produce duplicate images with different
46
- # filenames. To avoid showing duplicate thumbnails in the Gallery, deduplicate the
47
- # list by file content hash (SHA‑256). This keeps the first occurrence of each unique
48
- # image.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  import hashlib
50
  unique_hashes = set()
51
  deduped_paths = []
52
- for p in saved_paths:
 
53
  try:
54
- with open(p, "rb") as f:
55
  h = hashlib.sha256(f.read()).hexdigest()
56
  if h not in unique_hashes:
57
  unique_hashes.add(h)
58
- deduped_paths.append(p)
59
- except Exception as e:
60
- # If reading fails, keep the path (will surface later as a missing file).
61
- deduped_paths.append(p)
62
  return deduped_paths
63
 
64
  def run(self, ui_inputs: Dict, progress):
@@ -218,20 +240,8 @@ class SdImagePipeline(BasePipeline):
218
  progress=progress
219
  )
220
 
221
- # The workflow's SaveImage node already writes the generated images to the
222
- # output directory and returns the file path(s). No additional saving or
223
- # metadata injection is required.
224
- # Clean up any temporary files that were created for the workflow inputs.
225
- # The ``finally`` block below already handles this cleanup.
226
- # Simply return the list of file paths obtained from the workflow.
227
- # (If ``results`` is a single string, convert it to a list for Gradio.)
228
- if isinstance(results, (list, tuple)):
229
- final_results = list(results)
230
- else:
231
- final_results = [results]
232
-
233
- # The surrounding ``try``/``finally`` handles temp file cleanup.
234
- return final_results
235
  finally:
236
  for temp_file in temp_files_to_clean:
237
  if temp_file and os.path.exists(temp_file):
 
3
  import shutil
4
  import torch
5
  import gradio as gr
6
+ from PIL import Image
7
  from typing import List, Dict, Any
8
 
9
 
 
34
  """
35
  progress(0.4, desc="Executing workflow...")
36
  initial_objects = {}
37
+ # Execute the workflow; the SaveImage node returns the image tensor(s).
38
+ decoded_images_tensor = WorkflowExecutor.execute_workflow(workflow, initial_objects=initial_objects)
39
+
40
+ # Convert tensors to PIL images, attach metadata, and save them to the output folder.
41
+ out_dir = os.path.abspath(OUTPUT_DIR)
42
+ os.makedirs(out_dir, exist_ok=True)
43
+ saved_file_paths = []
44
+ start_seed = ui_inputs['seed'] if ui_inputs['seed'] != -1 else random.randint(0, 2**64 - 1)
45
+ for i in range(decoded_images_tensor.shape[0]):
46
+ img_tensor = decoded_images_tensor[i]
47
+ pil_image = Image.fromarray((img_tensor.cpu().numpy() * 255.0).astype("uint8"))
48
+ current_seed = start_seed + i
49
+
50
+ width_for_meta = ui_inputs.get('width', 'N/A')
51
+ height_for_meta = ui_inputs.get('height', 'N/A')
52
+
53
+ params_string = f"{ui_inputs['positive_prompt']}\nNegative prompt: {ui_inputs['negative_prompt']}\n"
54
+ params_string += f"Steps: {ui_inputs['num_inference_steps']}, Sampler: {ui_inputs['sampler']}, Scheduler: {ui_inputs['scheduler']}, CFG scale: {ui_inputs['guidance_scale']}, Seed: {current_seed}, Size: {width_for_meta}x{height_for_meta}, Base Model: {ui_inputs['model_display_name']}"
55
+ if ui_inputs['task_type'] != 'txt2img':
56
+ params_string += f", Denoise: {ui_inputs['denoise']}"
57
+ if ui_inputs.get('clip_skip') and ui_inputs['clip_skip'] != 1:
58
+ params_string += f", Clip skip: {abs(ui_inputs['clip_skip'])}"
59
+ if loras_string:
60
+ params_string += f", {loras_string}"
61
+
62
+ pil_image.info = {'parameters': params_string.strip()}
63
+ filename = f"gen_{random.randint(1000000, 9999999)}.png"
64
+ filepath = os.path.join(out_dir, filename)
65
+ pil_image.save(filepath, "PNG")
66
+ saved_file_paths.append(filepath)
67
+
68
+ # After both the internal SaveImage node (which already wrote files) and the above
69
+ # manual save, the output directory may contain duplicate images. Deduplicate by
70
+ # content hash (SHA‑256) and return a list of unique file paths.
71
  import hashlib
72
  unique_hashes = set()
73
  deduped_paths = []
74
+ for p in sorted(os.listdir(out_dir)):
75
+ full_path = os.path.join(out_dir, p)
76
  try:
77
+ with open(full_path, "rb") as f:
78
  h = hashlib.sha256(f.read()).hexdigest()
79
  if h not in unique_hashes:
80
  unique_hashes.add(h)
81
+ deduped_paths.append(full_path)
82
+ except Exception:
83
+ continue
 
84
  return deduped_paths
85
 
86
  def run(self, ui_inputs: Dict, progress):
 
240
  progress=progress
241
  )
242
 
243
+ # The workflow already saved images and returned a deduplicated list of file paths.
244
+ return results
 
 
 
 
 
 
 
 
 
 
 
 
245
  finally:
246
  for temp_file in temp_files_to_clean:
247
  if temp_file and os.path.exists(temp_file):