From 87b7fd4fed1e29069e6e6adc808240060bac08e3 Mon Sep 17 00:00:00 2001 From: nomcycle Date: Mon, 22 Jul 2024 21:27:10 -0600 Subject: [PATCH 1/4] Implemented image batching, and modified index_of_face such that it works with a new input: selected_number_of_faces. --- __init__.py | 91 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 18 deletions(-) diff --git a/__init__.py b/__init__.py index 51f1b0b..b3cf287 100644 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,5 @@ import torch +import comfy.utils from .Pytorch_Retinaface.pytorch_retinaface import Pytorch_RetinaFace class AutoCropFaces: @@ -17,8 +18,14 @@ class AutoCropFaces: "step": 1, }), "index_of_face": ("INT", { - "default": 1, - "min": 1, + "default": 0, + "min": 0, + "step": 1, + "display": "number" + }), + "selected_number_of_faces": ("INT", { + "default": -1, + "min": -1, "step": 1, "display": "number" }), @@ -52,24 +59,72 @@ class AutoCropFaces: CATEGORY = "Faces" - def auto_crop_faces(self, image, max_number_of_faces, index_of_face, scale_factor, shift_factor, aspect_ratio): - original_size = (1, 1) - left, top, right, bottom = (0, 0, 1, 1) - #TODO: currently only support one single image. No batch. - image_without_batch = image[0] - right, bottom = image_without_batch.shape[:2] - original_size = (right, bottom) - image_255 = image_without_batch * 255 + def auto_crop_faces_in_image (self, image, max_number_of_faces, scale_factor, shift_factor, aspect_ratio, method='lanczos'): + image_255 = image * 255 rf = Pytorch_RetinaFace(top_k=50, keep_top_k=max_number_of_faces) dets = rf.detect_faces(image_255) - cropped_images, bbox_infos = rf.center_and_crop_rescale(image_without_batch, dets, scale_factor=scale_factor, shift_factor=shift_factor, aspect_ratio=aspect_ratio) - if len(cropped_images)>=1: - clamped_index = max(1, min(index_of_face, len(cropped_images))) - cropped_image = torch.unsqueeze(cropped_images[clamped_index-1], 0) - left, top, right, bottom = bbox_infos[clamped_index-1] - original_size = (right-left, bottom-top) - return (cropped_image,(original_size, (left, top, right, bottom))) - return (image,(original_size, (left, top, right, bottom))) + cropped_faces, bbox_info = rf.center_and_crop_rescale(image, dets, scale_factor=scale_factor, shift_factor=shift_factor, aspect_ratio=aspect_ratio) + + # Add a batch dimension to each cropped face + cropped_faces_with_batch = [face.unsqueeze(0) for face in cropped_faces] + return cropped_faces_with_batch, bbox_info + + def auto_crop_faces(self, image, max_number_of_faces, index_of_face, selected_number_of_faces, scale_factor, shift_factor, aspect_ratio, method='lanczos'): + + selected_faces, detected_cropped_faces = [], [] + selected_crop_data, detected_crop_data = [], [] + original_images = [] + + remaining_face_count = max_number_of_faces + for i in range(image.shape[0]): # Loop through each image in the batch + + original_images.append(image[i].unsqueeze(0)) + + cropped_images, infos = self.auto_crop_faces_in_image( + image[i], + max_number_of_faces, + scale_factor, + shift_factor, + aspect_ratio, + method) + + detected_cropped_faces.extend(cropped_images) + detected_crop_data.extend(infos) + + remaining_face_count = remaining_face_count - len(detected_cropped_faces) + if remaining_face_count <= 0: + break + + if not detected_cropped_faces or len(detected_cropped_faces) == 0: + selected_faces = original_images + selected_crop_data = [(0, 0, original_images.shape[3], original_images.shape[2])] * original_images.shape[0] + + index_of_face = 0 if index_of_face <= -1 else index_of_face + start = max(0, min(index_of_face, len(detected_cropped_faces) - 1)) + end = start + selected_number_of_faces if selected_number_of_faces > 0 else len(detected_cropped_faces) + selected_faces = detected_cropped_faces[start:end] + selected_crop_data = detected_crop_data[start:end] + + out = selected_faces[0] + if len(selected_faces) == 0: + return (image, ((1, 1), (0, 0, 1, 1))) + elif len(selected_faces) <= 1: + return (out, selected_crop_data[0]) + + shape = out.shape + for i in range(1, len(selected_faces)): + resized_image = selected_faces[i] + if shape != selected_faces[i].shape: + resized_image = comfy.utils.common_upscale( + selected_faces[i].movedim(-1, 1), + shape[2], + shape[1], + method, + "" # Only "center" is implemented right now. + ).movedim(1, -1) + out = torch.cat((out, resized_image), dim=0) + + return (out, selected_crop_data) NODE_CLASS_MAPPINGS = { "AutoCropFaces": AutoCropFaces From 565a8640d57834a41c0f6ec3c87459219e68626a Mon Sep 17 00:00:00 2001 From: nomcycle Date: Mon, 22 Jul 2024 22:05:13 -0600 Subject: [PATCH 2/4] Added comments, fixed some bugs. --- __init__.py | 59 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/__init__.py b/__init__.py index b3cf287..31f5396 100644 --- a/__init__.py +++ b/__init__.py @@ -14,7 +14,7 @@ class AutoCropFaces: "max_number_of_faces": ("INT", { "default": 5, "min": 1, - "max": 50, + "max": 100, "step": 1, }), "index_of_face": ("INT", { @@ -70,16 +70,30 @@ class AutoCropFaces: return cropped_faces_with_batch, bbox_info def auto_crop_faces(self, image, max_number_of_faces, index_of_face, selected_number_of_faces, scale_factor, shift_factor, aspect_ratio, method='lanczos'): + """ + "image" - Input can be one image or a batch of images with shape (batch, width, height, channel count) + "max_number_of_faces" - This is passed into PyTorch_RetinaFace which allows you to define a maximum number of faces to look for. + "index_of_face" - The starting index of which face you select out of the set of detected faces. + "selected_number_of_faces" - The number of faces you want to select from the set of detected faces starting from "index_of_face", if + this is -1, then it will be either "max_number_of_faces" or the number of detected faces, whichever is less. + "scale_factor" - How much crop factor or padding do you want around each detected face. + "shift_factor" - Pan up or down relative to the face, 0.5 should be right in the center. + "aspect_ratio" - When we crop, you can have it crop down at a particular aspect ratio. + "method" - Scaling pixel sampling interpolation method. + """ selected_faces, detected_cropped_faces = [], [] selected_crop_data, detected_crop_data = [], [] original_images = [] + # Foreach detected face, we substract that, counting down until 0, then stop detecting anymore faces. remaining_face_count = max_number_of_faces - for i in range(image.shape[0]): # Loop through each image in the batch - original_images.append(image[i].unsqueeze(0)) + # Loop through the input batches. Even if there is only one input image, it's still considered a batch. + for i in range(image.shape[0]): + original_images.append(image[i].unsqueeze(0)) # Temporarily the image, but insure it still has the batch dimension. + # Detect the faces in the image, this will return multiple images and crop data for it. cropped_images, infos = self.auto_crop_faces_in_image( image[i], max_number_of_faces, @@ -91,37 +105,50 @@ class AutoCropFaces: detected_cropped_faces.extend(cropped_images) detected_crop_data.extend(infos) + # Count down until we've reached our "max_number_of_faces" remaining_face_count = remaining_face_count - len(detected_cropped_faces) - if remaining_face_count <= 0: + if remaining_face_count <= 0: # We've reached the limit, break. break + # If we haven't detected anything, just return the original images, and default crop data. if not detected_cropped_faces or len(detected_cropped_faces) == 0: - selected_faces = original_images - selected_crop_data = [(0, 0, original_images.shape[3], original_images.shape[2])] * original_images.shape[0] + selected_crop_data = [(0, 0, img.shape[3], img.shape[2]) for img in original_images] + return (image, selected_crop_data) index_of_face = 0 if index_of_face <= -1 else index_of_face + + # Get the range at which we want to select the faces. start = max(0, min(index_of_face, len(detected_cropped_faces) - 1)) - end = start + selected_number_of_faces if selected_number_of_faces > 0 else len(detected_cropped_faces) + end = start + min(max_number_of_faces, selected_number_of_faces) if selected_number_of_faces > 0 else min(max_number_of_faces, len(detected_cropped_faces)) + selected_faces = detected_cropped_faces[start:end] selected_crop_data = detected_crop_data[start:end] out = selected_faces[0] + + # If we haven't selected anything, then return original images. if len(selected_faces) == 0: - return (image, ((1, 1), (0, 0, 1, 1))) + selected_crop_data = [(0, 0, img.shape[3], img.shape[2]) for img in original_images] + return (image, selected_crop_data) + + # If there is only one detected face in batch of images, just return that one. elif len(selected_faces) <= 1: - return (out, selected_crop_data[0]) + return (out, selected_crop_data) shape = out.shape + + # All images need to have the same width/height to fit into the tensor such that we can output as image batches. for i in range(1, len(selected_faces)): resized_image = selected_faces[i] - if shape != selected_faces[i].shape: - resized_image = comfy.utils.common_upscale( - selected_faces[i].movedim(-1, 1), - shape[2], - shape[1], - method, - "" # Only "center" is implemented right now. + if shape != selected_faces[i].shape: # Check all images against the first image and scale it to that size. + resized_image = comfy.utils.common_upscale( # This method expects (batch, channel, height, width) + selected_faces[i].movedim(-1, 1), # Move channel dimension to width dimension + shape[2], # Height + shape[1], # Width + method, # Pixel sampling method. + "" # Only "center" is implemented right now, and we don't want to use that. ).movedim(1, -1) + # Append the fitted image into the tensor. out = torch.cat((out, resized_image), dim=0) return (out, selected_crop_data) From 87feeea77b86adf22903cddecb68e38753b7d4e1 Mon Sep 17 00:00:00 2001 From: nomcycle Date: Mon, 22 Jul 2024 22:17:47 -0600 Subject: [PATCH 3/4] center_and_crop_rescale seems to be returning unexpected array structure, made minor adjustment. --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 2cb01f8..e82a89a 100644 --- a/test.py +++ b/test.py @@ -36,7 +36,7 @@ def main(): # Crop and save each detected face cropped_imgs = rf.center_and_crop_rescale(img_raw, dets) - for index, cropped_img in enumerate(cropped_imgs): + for index, cropped_img in enumerate(cropped_imgs[0]): # Save the final image os.makedirs(output_dir, exist_ok=True) cv2.imwrite(os.path.join(output_dir, f"cropped_face_{index}.jpg"), cropped_img) From 3101bf2ea171131f80dcc5c7b418c2d6960977a1 Mon Sep 17 00:00:00 2001 From: nomcycle Date: Mon, 22 Jul 2024 22:19:03 -0600 Subject: [PATCH 4/4] Added a test network. --- tests/AutoCropFaces-Testing.json | 1997 ++++++++++++++++++++++++++++++ 1 file changed, 1997 insertions(+) create mode 100644 tests/AutoCropFaces-Testing.json diff --git a/tests/AutoCropFaces-Testing.json b/tests/AutoCropFaces-Testing.json new file mode 100644 index 0000000..946a1c2 --- /dev/null +++ b/tests/AutoCropFaces-Testing.json @@ -0,0 +1,1997 @@ +{ + "last_node_id": 91, + "last_link_id": 141, + "nodes": [ + { + "id": 54, + "type": "VAEDecode", + "pos": [ + 2550, + 2240 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 18, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 78 + }, + { + "name": "vae", + "type": "VAE", + "link": 120 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 80 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 52, + "type": "KSampler", + "pos": [ + 2150, + 2370 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 12, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 73 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 74 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 75 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 76 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 78 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152418, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 51, + "type": "PreviewImage", + "pos": [ + 3120, + 2360 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 29, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 82 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 44, + "type": "KSampler", + "pos": [ + 2150, + 2060 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 11, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 62 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 63 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 64 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 65 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 68 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152274, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 49, + "type": "VAEDecode", + "pos": [ + 2540, + 2090 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 17, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 68 + }, + { + "name": "vae", + "type": "VAE", + "link": 121 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 79 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 61, + "type": "KSampler", + "pos": [ + 2210, + 1270 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 13, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 105 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 87 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 84 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 85 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 89 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152274, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 56, + "type": "ImageBatch", + "pos": [ + 2840, + 2170 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 23, + "mode": 0, + "inputs": [ + { + "name": "image1", + "type": "IMAGE", + "link": 79 + }, + { + "name": "image2", + "type": "IMAGE", + "link": 80 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 81, + 82 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageBatch" + } + }, + { + "id": 58, + "type": "AutoCropFaces", + "pos": [ + 2860, + 1340 + ], + "size": { + "0": 315, + "1": 198 + }, + "flags": {}, + "order": 24, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 90 + } + ], + "outputs": [ + { + "name": "face", + "type": "IMAGE", + "links": [ + 91 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CROP_DATA", + "type": "CROP_DATA", + "links": null, + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "AutoCropFaces" + }, + "widgets_values": [ + 5, + 0, + -1, + 1.5, + 0.5, + 1 + ] + }, + { + "id": 72, + "type": "AutoCropFaces", + "pos": [ + 3080, + 540 + ], + "size": { + "0": 315, + "1": 198 + }, + "flags": {}, + "order": 31, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 117 + } + ], + "outputs": [ + { + "name": "face", + "type": "IMAGE", + "links": [ + 102 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CROP_DATA", + "type": "CROP_DATA", + "links": null, + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "AutoCropFaces" + }, + "widgets_values": [ + 5, + 0, + -1, + 1.5, + 0.5, + 1 + ] + }, + { + "id": 67, + "type": "PreviewImage", + "pos": [ + 3230, + 1140 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 25, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 92 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 65, + "type": "VAEDecode", + "pos": [ + 2610, + 1290 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 89 + }, + { + "name": "vae", + "type": "VAE", + "link": 109 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 90, + 92 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 66, + "type": "PreviewImage", + "pos": [ + 3230, + 1430 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 30, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 91 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 73, + "type": "PreviewImage", + "pos": [ + 3460, + 540 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 35, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 102 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 68, + "type": "KSampler", + "pos": [ + 2190, + 260 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 14, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 93 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 94 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 95 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 96 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 99 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152274, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 70, + "type": "VAEDecode", + "pos": [ + 2530, + 280 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 20, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 99 + }, + { + "name": "vae", + "type": "VAE", + "link": 107 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 116 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 69, + "type": "CLIPTextEncode", + "pos": [ + 1630, + 270 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 97 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 94, + 111 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "Beautiful forest" + ] + }, + { + "id": 77, + "type": "Reroute", + "pos": [ + 1970, + 440 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 7, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 106 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 107, + 118 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 46, + "type": "EmptyLatentImage", + "pos": [ + 501, + 1441 + ], + "size": { + "0": 315, + "1": 106 + }, + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 65, + 76, + 85, + 96, + 113, + 126 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "EmptyLatentImage" + }, + "widgets_values": [ + 1024, + 1024, + 1 + ] + }, + { + "id": 78, + "type": "Reroute", + "pos": [ + 2009, + 1378 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 8, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 108 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 109 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 82, + "type": "Reroute", + "pos": [ + 2030, + 2490 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 9, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 119 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 120, + 121 + ], + "slot_index": 0 + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 48, + "type": "CLIPTextEncode", + "pos": [ + 1247, + 1461 + ], + "size": { + "0": 425.27801513671875, + "1": 180.6060791015625 + }, + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 67 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 64, + 75, + 84, + 95, + 112, + 125 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "text, watermark" + ] + }, + { + "id": 45, + "type": "CheckpointLoaderSimple", + "pos": [ + 507, + 1591 + ], + "size": { + "0": 315, + "1": 98 + }, + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [ + { + "name": "MODEL", + "type": "MODEL", + "links": [ + 62, + 73, + 93, + 105, + 110, + 123 + ], + "slot_index": 0 + }, + { + "name": "CLIP", + "type": "CLIP", + "links": [ + 66, + 67, + 86, + 97, + 127 + ], + "slot_index": 1 + }, + { + "name": "VAE", + "type": "VAE", + "links": [ + 106, + 108, + 119, + 131 + ], + "slot_index": 2 + } + ], + "properties": { + "Node name for S&R": "CheckpointLoaderSimple" + }, + "widgets_values": [ + "sd_xl_base_1.0.safetensors" + ] + }, + { + "id": 47, + "type": "CLIPTextEncode", + "pos": [ + 1630, + 2220 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 66 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 63, + 74 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "Large crowd of people all facing the camera posing for a company photo." + ] + }, + { + "id": 64, + "type": "CLIPTextEncode", + "pos": [ + 1737, + 1195 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 86 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 87 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "Female model posing looking at the camera" + ] + }, + { + "id": 79, + "type": "KSampler", + "pos": [ + 2190, + 560 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 110 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 111 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 112 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 113 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 114 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152415, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + }, + { + "id": 81, + "type": "VAEDecode", + "pos": [ + 2540, + 570 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 21, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 114 + }, + { + "name": "vae", + "type": "VAE", + "link": 118 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 115 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 80, + "type": "ImageBatch", + "pos": [ + 2810, + 580 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 26, + "mode": 0, + "inputs": [ + { + "name": "image1", + "type": "IMAGE", + "link": 116 + }, + { + "name": "image2", + "type": "IMAGE", + "link": 115 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 117, + 122 + ], + "shape": 3, + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "ImageBatch" + } + }, + { + "id": 40, + "type": "PreviewImage", + "pos": [ + 3530, + 2070 + ], + "size": [ + 140.9676944827229, + 232.36873662566404 + ], + "flags": {}, + "order": 34, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 53 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 87, + "type": "Reroute", + "pos": [ + 1990, + -170 + ], + "size": [ + 75, + 26 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "", + "type": "*", + "link": 131 + } + ], + "outputs": [ + { + "name": "", + "type": "VAE", + "links": [ + 129 + ] + } + ], + "properties": { + "showOutputText": false, + "horizontal": false + } + }, + { + "id": 84, + "type": "CLIPTextEncode", + "pos": [ + 1650, + -340 + ], + "size": { + "0": 422.84503173828125, + "1": 164.31304931640625 + }, + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "clip", + "type": "CLIP", + "link": 127 + } + ], + "outputs": [ + { + "name": "CONDITIONING", + "type": "CONDITIONING", + "links": [ + 124 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "CLIPTextEncode" + }, + "widgets_values": [ + "Beautiful forest" + ] + }, + { + "id": 86, + "type": "AutoCropFaces", + "pos": [ + 3050, + -320 + ], + "size": { + "0": 315, + "1": 198 + }, + "flags": {}, + "order": 27, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 141 + } + ], + "outputs": [ + { + "name": "face", + "type": "IMAGE", + "links": [ + 140 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CROP_DATA", + "type": "CROP_DATA", + "links": null, + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "AutoCropFaces" + }, + "widgets_values": [ + 5, + 0, + -1, + 1.5, + 0.5, + 1 + ] + }, + { + "id": 91, + "type": "PreviewImage", + "pos": [ + 3410, + -320 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 33, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 140 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 71, + "type": "PreviewImage", + "pos": [ + 3450, + 260 + ], + "size": { + "0": 210, + "1": 246 + }, + "flags": {}, + "order": 32, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 122 + } + ], + "properties": { + "Node name for S&R": "PreviewImage" + } + }, + { + "id": 36, + "type": "AutoCropFaces", + "pos": [ + 3120, + 2130 + ], + "size": { + "0": 315, + "1": 198 + }, + "flags": {}, + "order": 28, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 81 + } + ], + "outputs": [ + { + "name": "face", + "type": "IMAGE", + "links": [ + 53 + ], + "shape": 3, + "slot_index": 0 + }, + { + "name": "CROP_DATA", + "type": "CROP_DATA", + "links": null, + "shape": 3, + "slot_index": 1 + } + ], + "properties": { + "Node name for S&R": "AutoCropFaces" + }, + "widgets_values": [ + 50, + 5, + 15, + 1.5, + 0.5, + 1 + ] + }, + { + "id": 85, + "type": "VAEDecode", + "pos": [ + 2550, + -330 + ], + "size": { + "0": 210, + "1": 46 + }, + "flags": {}, + "order": 22, + "mode": 0, + "inputs": [ + { + "name": "samples", + "type": "LATENT", + "link": 128 + }, + { + "name": "vae", + "type": "VAE", + "link": 129 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 141 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "VAEDecode" + } + }, + { + "id": 83, + "type": "KSampler", + "pos": [ + 2200, + -350 + ], + "size": { + "0": 315, + "1": 262 + }, + "flags": {}, + "order": 16, + "mode": 0, + "inputs": [ + { + "name": "model", + "type": "MODEL", + "link": 123 + }, + { + "name": "positive", + "type": "CONDITIONING", + "link": 124 + }, + { + "name": "negative", + "type": "CONDITIONING", + "link": 125 + }, + { + "name": "latent_image", + "type": "LATENT", + "link": 126 + } + ], + "outputs": [ + { + "name": "LATENT", + "type": "LATENT", + "links": [ + 128 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "KSampler" + }, + "widgets_values": [ + 444793366152357, + "fixed", + 50, + 8, + "euler", + "normal", + 1 + ] + } + ], + "links": [ + [ + 53, + 36, + 0, + 40, + 0, + "IMAGE" + ], + [ + 62, + 45, + 0, + 44, + 0, + "MODEL" + ], + [ + 63, + 47, + 0, + 44, + 1, + "CONDITIONING" + ], + [ + 64, + 48, + 0, + 44, + 2, + "CONDITIONING" + ], + [ + 65, + 46, + 0, + 44, + 3, + "LATENT" + ], + [ + 66, + 45, + 1, + 47, + 0, + "CLIP" + ], + [ + 67, + 45, + 1, + 48, + 0, + "CLIP" + ], + [ + 68, + 44, + 0, + 49, + 0, + "LATENT" + ], + [ + 73, + 45, + 0, + 52, + 0, + "MODEL" + ], + [ + 74, + 47, + 0, + 52, + 1, + "CONDITIONING" + ], + [ + 75, + 48, + 0, + 52, + 2, + "CONDITIONING" + ], + [ + 76, + 46, + 0, + 52, + 3, + "LATENT" + ], + [ + 78, + 52, + 0, + 54, + 0, + "LATENT" + ], + [ + 79, + 49, + 0, + 56, + 0, + "IMAGE" + ], + [ + 80, + 54, + 0, + 56, + 1, + "IMAGE" + ], + [ + 81, + 56, + 0, + 36, + 0, + "IMAGE" + ], + [ + 82, + 56, + 0, + 51, + 0, + "IMAGE" + ], + [ + 84, + 48, + 0, + 61, + 2, + "CONDITIONING" + ], + [ + 85, + 46, + 0, + 61, + 3, + "LATENT" + ], + [ + 86, + 45, + 1, + 64, + 0, + "CLIP" + ], + [ + 87, + 64, + 0, + 61, + 1, + "CONDITIONING" + ], + [ + 89, + 61, + 0, + 65, + 0, + "LATENT" + ], + [ + 90, + 65, + 0, + 58, + 0, + "IMAGE" + ], + [ + 91, + 58, + 0, + 66, + 0, + "IMAGE" + ], + [ + 92, + 65, + 0, + 67, + 0, + "IMAGE" + ], + [ + 93, + 45, + 0, + 68, + 0, + "MODEL" + ], + [ + 94, + 69, + 0, + 68, + 1, + "CONDITIONING" + ], + [ + 95, + 48, + 0, + 68, + 2, + "CONDITIONING" + ], + [ + 96, + 46, + 0, + 68, + 3, + "LATENT" + ], + [ + 97, + 45, + 1, + 69, + 0, + "CLIP" + ], + [ + 99, + 68, + 0, + 70, + 0, + "LATENT" + ], + [ + 102, + 72, + 0, + 73, + 0, + "IMAGE" + ], + [ + 105, + 45, + 0, + 61, + 0, + "MODEL" + ], + [ + 106, + 45, + 2, + 77, + 0, + "*" + ], + [ + 107, + 77, + 0, + 70, + 1, + "VAE" + ], + [ + 108, + 45, + 2, + 78, + 0, + "*" + ], + [ + 109, + 78, + 0, + 65, + 1, + "VAE" + ], + [ + 110, + 45, + 0, + 79, + 0, + "MODEL" + ], + [ + 111, + 69, + 0, + 79, + 1, + "CONDITIONING" + ], + [ + 112, + 48, + 0, + 79, + 2, + "CONDITIONING" + ], + [ + 113, + 46, + 0, + 79, + 3, + "LATENT" + ], + [ + 114, + 79, + 0, + 81, + 0, + "LATENT" + ], + [ + 115, + 81, + 0, + 80, + 1, + "IMAGE" + ], + [ + 116, + 70, + 0, + 80, + 0, + "IMAGE" + ], + [ + 117, + 80, + 0, + 72, + 0, + "IMAGE" + ], + [ + 118, + 77, + 0, + 81, + 1, + "VAE" + ], + [ + 119, + 45, + 2, + 82, + 0, + "*" + ], + [ + 120, + 82, + 0, + 54, + 1, + "VAE" + ], + [ + 121, + 82, + 0, + 49, + 1, + "VAE" + ], + [ + 122, + 80, + 0, + 71, + 0, + "IMAGE" + ], + [ + 123, + 45, + 0, + 83, + 0, + "MODEL" + ], + [ + 124, + 84, + 0, + 83, + 1, + "CONDITIONING" + ], + [ + 125, + 48, + 0, + 83, + 2, + "CONDITIONING" + ], + [ + 126, + 46, + 0, + 83, + 3, + "LATENT" + ], + [ + 127, + 45, + 1, + 84, + 0, + "CLIP" + ], + [ + 128, + 83, + 0, + 85, + 0, + "LATENT" + ], + [ + 129, + 87, + 0, + 85, + 1, + "VAE" + ], + [ + 131, + 45, + 2, + 87, + 0, + "*" + ], + [ + 140, + 86, + 0, + 91, + 0, + "IMAGE" + ], + [ + 141, + 85, + 0, + 86, + 0, + "IMAGE" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 0.5730855330116886, + "offset": [ + -309.80067971195854, + -137.54286569310958 + ] + } + }, + "version": 0.4 +} \ No newline at end of file