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

Remove SaveImage node, adjust workflow executor, restore tensor-to-image saving and deduplication

Browse files
core/pipelines/sd_image_pipeline.py CHANGED
@@ -34,10 +34,10 @@ class SdImagePipeline(BasePipeline):
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 = []
@@ -65,22 +65,19 @@ class SdImagePipeline(BasePipeline):
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):
 
34
  """
35
  progress(0.4, desc="Executing workflow...")
36
  initial_objects = {}
37
+ # Execute the workflow; it returns image tensor(s) from the VAE Decode node.
38
  decoded_images_tensor = WorkflowExecutor.execute_workflow(workflow, initial_objects=initial_objects)
39
 
40
+ # Convert tensors to PIL images, embed metadata and save them to the output directory.
41
  out_dir = os.path.abspath(OUTPUT_DIR)
42
  os.makedirs(out_dir, exist_ok=True)
43
  saved_file_paths = []
 
65
  pil_image.save(filepath, "PNG")
66
  saved_file_paths.append(filepath)
67
 
68
+ # Deduplicate by file content hash (SHA‑256) to avoid identical images.
 
 
69
  import hashlib
70
  unique_hashes = set()
71
  deduped_paths = []
72
+ for p in saved_file_paths:
 
73
  try:
74
+ with open(p, "rb") as f:
75
  h = hashlib.sha256(f.read()).hexdigest()
76
  if h not in unique_hashes:
77
  unique_hashes.add(h)
78
+ deduped_paths.append(p)
79
  except Exception:
80
+ deduped_paths.append(p)
81
  return deduped_paths
82
 
83
  def run(self, ui_inputs: Dict, progress):
core/pipelines/workflow_executor.py CHANGED
@@ -95,16 +95,17 @@ class WorkflowExecutor:
95
  result = execution_method(**kwargs)
96
  computed_outputs[node_id] = result
97
 
 
98
  final_node_id = None
99
  for node_id in reversed(sorted_node_ids):
100
- if workflow[node_id]['class_type'] == 'SaveImage':
101
- final_node_id = node_id
102
- break
103
-
104
- if not final_node_id:
105
- raise RuntimeError("Workflow does not contain a 'SaveImage' node as the output.")
106
-
107
- save_image_inputs = workflow[final_node_id]['inputs']
108
- image_source_node_id, image_source_index = save_image_inputs['images']
109
-
110
- return get_value_at_index(computed_outputs[image_source_node_id], image_source_index)
 
95
  result = execution_method(**kwargs)
96
  computed_outputs[node_id] = result
97
 
98
+ # Determine the final output. If a SaveImage node exists, use its input image.
99
  final_node_id = None
100
  for node_id in reversed(sorted_node_ids):
101
+ if workflow[node_id]['class_type'] == 'SaveImage':
102
+ final_node_id = node_id
103
+ break
104
+ if final_node_id:
105
+ save_image_inputs = workflow[final_node_id]['inputs']
106
+ image_source_node_id, image_source_index = save_image_inputs['images']
107
+ return get_value_at_index(computed_outputs[image_source_node_id], image_source_index)
108
+ else:
109
+ # No SaveImage node – return the output of the last node in execution order.
110
+ last_node_id = sorted_node_ids[-1]
111
+ return computed_outputs[last_node_id]
core/pipelines/workflow_recipes/_partials/_base_sampler_sd.yaml CHANGED
@@ -1,36 +1,29 @@
1
- nodes:
2
- pos_prompt:
3
- class_type: CLIPTextEncode
4
- title: "CLIP Text Encode (Positive)"
5
- neg_prompt:
6
- class_type: CLIPTextEncode
7
- title: "CLIP Text Encode (Negative)"
8
- ksampler:
9
- class_type: KSampler
10
- title: "KSampler"
11
- params:
12
- denoise: 1.0
13
- vae_decode:
14
- class_type: VAEDecode
15
- title: "VAE Decode"
16
- save_image:
17
- class_type: SaveImage
18
- title: "Save Image"
19
- params: {}
20
-
21
- connections:
22
- - from: "ksampler:0"
23
- to: "vae_decode:samples"
24
- - from: "vae_decode:0"
25
- to: "save_image:images"
26
-
27
- ui_map:
28
- positive_prompt: "pos_prompt:text"
29
- negative_prompt: "neg_prompt:text"
30
- seed: "ksampler:seed"
31
- steps: "ksampler:steps"
32
- cfg: "ksampler:cfg"
33
- sampler_name: "ksampler:sampler_name"
34
- scheduler: "ksampler:scheduler"
35
- denoise: "ksampler:denoise"
36
- filename_prefix: "save_image:filename_prefix"
 
1
+ nodes:
2
+ pos_prompt:
3
+ class_type: CLIPTextEncode
4
+ title: "CLIP Text Encode (Positive)"
5
+ neg_prompt:
6
+ class_type: CLIPTextEncode
7
+ title: "CLIP Text Encode (Negative)"
8
+ ksampler:
9
+ class_type: KSampler
10
+ title: "KSampler"
11
+ params:
12
+ denoise: 1.0
13
+ vae_decode:
14
+ class_type: VAEDecode
15
+ title: "VAE Decode"
16
+
17
+ connections:
18
+ - from: "ksampler:0"
19
+ to: "vae_decode:samples"
20
+
21
+ ui_map:
22
+ positive_prompt: "pos_prompt:text"
23
+ negative_prompt: "neg_prompt:text"
24
+ seed: "ksampler:seed"
25
+ steps: "ksampler:steps"
26
+ cfg: "ksampler:cfg"
27
+ sampler_name: "ksampler:sampler_name"
28
+ scheduler: "ksampler:scheduler"
29
+ denoise: "ksampler:denoise"