├── .gitignore ├── requirements.txt ├── ComfyUI_temp_nrhrt_00001_.png ├── ComfyUI_temp_pxnku_00001_.png ├── LICENSE ├── README.md ├── __init__.py ├── DZFaceDetailer.py └── workflow_example.json /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ultralytics 2 | mediapipe 3 | -------------------------------------------------------------------------------- /ComfyUI_temp_nrhrt_00001_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicofdga/DZ-FaceDetailer/HEAD/ComfyUI_temp_nrhrt_00001_.png -------------------------------------------------------------------------------- /ComfyUI_temp_pxnku_00001_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicofdga/DZ-FaceDetailer/HEAD/ComfyUI_temp_pxnku_00001_.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Daxthin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DZ FaceDetailer 2 | 3 | ## Custom Node for ComfyUI (Stable Diffusion) 4 | 5 | DZ FaceDetailer is a custom node for the "ComfyUI" framework inspired by !After Detailer extension from auto1111, it allows you to detect faces using Mediapipe and YOLOv8n to create masks for the detected faces. This custom node enables you to generate new faces, replace faces, and perform other face manipulation tasks using Stable Diffusion AI. 6 | 7 | ![image](https://github.com/daxthin/facedetailer/assets/78769008/22caf9e4-a29d-4e7c-b6d2-f02679b0dfff) 8 | ![image](https://github.com/daxthin/facedetailer/assets/78769008/b7bfa925-c127-427d-9ade-741ddf278648) 9 | 10 | 11 | ### Table of Contents 12 | 13 | - [Features](#features) 14 | - [Installation](#installation) 15 | 16 | ## Features 17 | 18 | - Face detection using Mediapipe. 19 | - Multiple face detection support on both models 20 | - Face mask generation for detected faces. 21 | - Latent/sample mapping to generated masks for face manipulation. 22 | - Generate new faces using Stable Diffusion. 23 | - Replace faces using LoRa or embeddings etc. 24 | - batch images support 25 | 26 | ## Installation 27 | clone the repo https://github.com/daxthin/DZ-FaceDetailer.git in custom_nodes folder 28 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import os, sys 3 | import subprocess 4 | from tqdm import tqdm 5 | from pip._internal import main as pip_main 6 | from pathlib import Path 7 | from folder_paths import models_dir 8 | 9 | try: 10 | import mediapipe 11 | except: 12 | print('FaceDetailer: Installing requirements') 13 | my_path = os.path.dirname(__file__) 14 | subprocess.check_call([sys.executable, "-s", "-m", "pip", "install", "-r", os.path.join(my_path, "requirements.txt")]) 15 | 16 | model_url = "https://huggingface.co/Bingsu/adetailer/resolve/main/face_yolov8n.pt" 17 | 18 | save_loc = os.path.join(models_dir, "dz_facedetailer", "yolo") 19 | 20 | def download_model(): 21 | if Path(os.path.join(save_loc, "face_yolov8n.pt")).is_file(): 22 | print('FaceDetailer: Model already exists') 23 | else: 24 | print('FaceDetailer: Model doesnt exist') 25 | print('FaceDetailer: Downloading model') 26 | response = requests.get(model_url, stream=True) 27 | 28 | try: 29 | if response.status_code == 200: 30 | total_size = int(response.headers.get('content-length', 0)) 31 | block_size = 1024 # 1 Kibibyte 32 | 33 | # tqdm will display a progress bar 34 | with open(os.path.join(save_loc, "face_yolov8n.pt"), 'wb') as file, tqdm( 35 | desc='Downloading', 36 | total=total_size, 37 | unit='iB', 38 | unit_scale=True, 39 | unit_divisor=1024, 40 | ) as bar: 41 | for data in response.iter_content(block_size): 42 | bar.update(len(data)) 43 | file.write(data) 44 | 45 | print('FaceDetailer: Model download finished') 46 | except requests.exceptions.RequestException as err: 47 | print('FaceDetailer: Model download failed: {err}') 48 | print(f'FaceDetailer: Download it manually from: {model_url}') 49 | print('FaceDetailer: And put it in /comfyui/models/dz_facedetailer/yolo/') 50 | except Exception as e: 51 | print(f'FaceDetailer: An unexpected error occurred: {e}') 52 | 53 | if not os.path.exists(save_loc): 54 | print('FaceDetailer: Creating models directory') 55 | os.makedirs(save_loc, exist_ok=True) 56 | download_model() 57 | else: 58 | print('FaceDetailer: Model directory already exists') 59 | download_model() 60 | 61 | from .DZFaceDetailer import FaceDetailer 62 | 63 | NODE_CLASS_MAPPINGS = { 64 | "DZ_Face_Detailer": FaceDetailer, 65 | } 66 | 67 | 68 | -------------------------------------------------------------------------------- /DZFaceDetailer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from mediapipe import solutions 3 | import cv2 4 | import numpy as np 5 | from PIL import Image, ImageFilter 6 | from ultralytics import YOLO 7 | import os 8 | import comfy 9 | import nodes 10 | from folder_paths import base_path 11 | 12 | face_model_path = os.path.join(base_path, "models/dz_facedetailer/yolo/face_yolov8n.pt") 13 | MASK_CONTROL = ["dilate", "erode", "disabled"] 14 | MASK_TYPE = ["face", "box"] 15 | 16 | class FaceDetailer: 17 | @classmethod 18 | def INPUT_TYPES(s): 19 | return {"required": 20 | { 21 | "model": ("MODEL",), 22 | "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), 23 | "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), 24 | "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01}), 25 | "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), 26 | "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), 27 | "positive": ("CONDITIONING", ), 28 | "negative": ("CONDITIONING", ), 29 | "latent_image": ("LATENT", ), 30 | "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), 31 | "latent_image": ("LATENT", ), 32 | "vae": ("VAE",), 33 | "mask_blur": ("INT", {"default": 0, "min": 0, "max": 100}), 34 | "mask_type": (MASK_TYPE, ), 35 | "mask_control": (MASK_CONTROL, ), 36 | "dilate_mask_value": ("INT", {"default": 3, "min": 0, "max": 100}), 37 | "erode_mask_value": ("INT", {"default": 3, "min": 0, "max": 100}), 38 | } 39 | } 40 | 41 | RETURN_TYPES = ("LATENT", "MASK",) 42 | 43 | FUNCTION = "detailer" 44 | 45 | CATEGORY = "face_detailer" 46 | 47 | def detailer(self, model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_image, denoise, vae, mask_blur, mask_type, mask_control, dilate_mask_value, erode_mask_value): 48 | 49 | # input latent decoded to tensor image for processing 50 | tensor_img = vae.decode(latent_image["samples"]) 51 | 52 | batch_size = tensor_img.shape[0] 53 | 54 | mask = Detection().detect_faces(tensor_img, batch_size, mask_type, mask_control, mask_blur, dilate_mask_value, erode_mask_value) 55 | 56 | latent_mask = set_mask(latent_image, mask) 57 | 58 | latent = nodes.common_ksampler(model, seed, steps, cfg, sampler_name, scheduler, positive, negative, latent_mask, denoise=denoise) 59 | 60 | return (latent[0], latent[0]["noise_mask"],) 61 | 62 | class Detection: 63 | def __init__(self): 64 | pass 65 | 66 | def detect_faces(self, tensor_img, batch_size, mask_type, mask_control, mask_blur, mask_dilate, mask_erode): 67 | mask_imgs = [] 68 | for i in range(0, batch_size): 69 | # print(input_tensor_img[i, :,:,:].shape) 70 | # convert input latent to numpy array for yolo model 71 | img = image2nparray(tensor_img[i], False) 72 | # Process the face mesh or make the face box for masking 73 | if mask_type == "box": 74 | final_mask = facebox_mask(img) 75 | else: 76 | final_mask = facemesh_mask(img) 77 | 78 | final_mask = self.mask_control(final_mask, mask_control, mask_blur, mask_dilate, mask_erode) 79 | 80 | final_mask = np.array(Image.fromarray(final_mask).getchannel('A')).astype(np.float32) / 255.0 81 | # Convert mask to tensor and assign the mask to the input tensor 82 | final_mask = torch.from_numpy(final_mask) 83 | 84 | mask_imgs.append(final_mask) 85 | 86 | final_mask = torch.stack(mask_imgs) 87 | 88 | return final_mask 89 | 90 | def mask_control(self, numpy_img, mask_control, mask_blur, mask_dilate, mask_erode): 91 | numpy_image = numpy_img.copy(); 92 | # Erode/Dilate mask 93 | if mask_control == "dilate": 94 | if mask_dilate > 0: 95 | numpy_image = self.dilate_mask(numpy_image, mask_dilate) 96 | elif mask_control == "erode": 97 | if mask_erode > 0: 98 | numpy_image = self.erode_mask(numpy_image, mask_erode) 99 | if mask_blur > 0: 100 | final_mask_image = Image.fromarray(numpy_image) 101 | blurred_mask_image = final_mask_image.filter( 102 | ImageFilter.GaussianBlur(radius=mask_blur)) 103 | numpy_image = np.array(blurred_mask_image) 104 | 105 | return numpy_image 106 | 107 | def erode_mask(self, mask, dilate): 108 | # I use erode function because the mask is inverted 109 | # later I will fix it 110 | kernel = np.ones((int(dilate), int(dilate)), np.uint8) 111 | dilated_mask = cv2.dilate(mask, kernel, iterations=1) 112 | return dilated_mask 113 | 114 | def dilate_mask(self, mask, erode): 115 | # I use dilate function because the mask is inverted like the other function 116 | # later I will fix it 117 | kernel = np.ones((int(erode), int(erode)), np.uint8) 118 | eroded_mask = cv2.erode(mask, kernel, iterations=1) 119 | return eroded_mask 120 | 121 | def facebox_mask(image): 122 | # Create an empty image with alpha 123 | mask = np.zeros((image.shape[0], image.shape[1], 4), dtype=np.uint8) 124 | 125 | # setup yolov8n face detection model 126 | face_model = YOLO(face_model_path) 127 | face_bbox = face_model(image) 128 | boxes = face_bbox[0].boxes 129 | # box = boxes[0].xyxy 130 | for box in boxes.xyxy: 131 | x_min, y_min, x_max, y_max = box.tolist() 132 | # Calculate the center of the bounding box 133 | center_x = (x_min + x_max) / 2 134 | center_y = (y_min + y_max) / 2 135 | 136 | # Calcule the maximum width and height 137 | width = x_max - x_min 138 | height = y_max - y_min 139 | max_size = max(width, height) 140 | 141 | # Get the new WxH for a ratio of 1:1 142 | new_width = max_size 143 | new_height = max_size 144 | 145 | # Calculate the new coordinates 146 | new_x_min = int(center_x - new_width / 2) 147 | new_y_min = int(center_y - new_height / 2) 148 | new_x_max = int(center_x + new_width / 2) 149 | new_y_max = int(center_y + new_height / 2) 150 | 151 | # print((new_x_min, new_y_min), (new_x_max, new_y_max)) 152 | # set the square in the face location 153 | cv2.rectangle(mask, (new_x_min, new_y_min), (new_x_max, new_y_max), (0, 0, 0, 255), -1) 154 | 155 | # mask[:, :, 3] = ~mask[:, :, 3] # invert the mask 156 | 157 | return mask 158 | 159 | 160 | def facemesh_mask(image): 161 | 162 | faces_mask = [] 163 | 164 | # Empty image with the same shape as input 165 | mask = np.zeros( 166 | (image.shape[0], image.shape[1], 4), dtype=np.uint8) 167 | 168 | # setup yolov8n face detection model 169 | face_model = YOLO(face_model_path) 170 | face_bbox = face_model(image) 171 | boxes = face_bbox[0].boxes 172 | # box = boxes[0].xyxy 173 | for box in boxes.xyxy: 174 | x_min, y_min, x_max, y_max = box.tolist() 175 | # Calculate the center of the bounding box 176 | center_x = (x_min + x_max) / 2 177 | center_y = (y_min + y_max) / 2 178 | 179 | # Calcule the maximum width and height 180 | width = x_max - x_min 181 | height = y_max - y_min 182 | max_size = max(width, height) 183 | 184 | # Get the new WxH for a ratio of 1:1 185 | new_width = max_size 186 | new_height = max_size 187 | 188 | # Calculate the new coordinates 189 | new_x_min = int(center_x - new_width / 2) 190 | new_y_min = int(center_y - new_height / 2) 191 | new_x_max = int(center_x + new_width / 2) 192 | new_y_max = int(center_y + new_height / 2) 193 | 194 | # print((new_x_min, new_y_min), (new_x_max, new_y_max)) 195 | # set the square in the face location 196 | face = image[new_y_min:new_y_max, new_x_min:new_x_max, :] 197 | 198 | mp_face_mesh = solutions.face_mesh 199 | face_mesh = mp_face_mesh.FaceMesh(static_image_mode=True, max_num_faces=1, min_detection_confidence=0.5) 200 | results = face_mesh.process(cv2.cvtColor(face, cv2.COLOR_BGR2RGB)) 201 | if results.multi_face_landmarks: 202 | for face_landmarks in results.multi_face_landmarks: 203 | # List of detected face points 204 | points = [] 205 | for landmark in face_landmarks.landmark: 206 | cx, cy = int( 207 | landmark.x * face.shape[1]), int(landmark.y * face.shape[0]) 208 | points.append([cx, cy]) 209 | 210 | face_mask = np.zeros((face.shape[0], face.shape[1], 4), dtype=np.uint8) 211 | 212 | # Obtain the countour of the face 213 | convex_hull = cv2.convexHull(np.array(points)) 214 | 215 | # Fill the contour and store it in alpha for the mask 216 | cv2.fillConvexPoly(face_mask, convex_hull, (0, 0, 0, 255)) 217 | 218 | faces_mask.append([face_mask, [new_x_min, new_x_max, new_y_min, new_y_max]]) 219 | 220 | for face_mask in faces_mask: 221 | paste_numpy_images(mask, face_mask[0], face_mask[1][0], face_mask[1][1], face_mask[1][2], face_mask[1][3]) 222 | 223 | # print(f"{len(faces_mask)} faces detected") 224 | # mask[:, :, 3] = ~mask[:, :, 3] 225 | return mask 226 | 227 | 228 | def paste_numpy_images(target_image, source_image, x_min, x_max, y_min, y_max): 229 | # Paste the source image into the target image at the specified coordinates 230 | target_image[y_min:y_max, x_min:x_max, :] = source_image 231 | 232 | return target_image 233 | 234 | 235 | 236 | def image2nparray(image, BGR): 237 | """ 238 | convert tensor image to numpy array 239 | 240 | Args: 241 | image (Tensor): Tensor image 242 | 243 | Returns: 244 | returns: Numpy array. 245 | 246 | """ 247 | narray = np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8) 248 | 249 | if BGR: 250 | return narray 251 | else: 252 | return narray[:, :, ::-1] 253 | 254 | 255 | def set_mask(samples, mask): 256 | s = samples.copy() 257 | s["noise_mask"] = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])) 258 | return s 259 | -------------------------------------------------------------------------------- /workflow_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "last_node_id": 40, 3 | "last_link_id": 91, 4 | "nodes": [ 5 | { 6 | "id": 21, 7 | "type": "Reroute", 8 | "pos": [ 9 | 187.97769512223223, 10 | 848.2900504152276 11 | ], 12 | "size": [ 13 | 75, 14 | 26 15 | ], 16 | "flags": {}, 17 | "order": 8, 18 | "mode": 0, 19 | "inputs": [ 20 | { 21 | "name": "", 22 | "type": "*", 23 | "link": 57 24 | } 25 | ], 26 | "outputs": [ 27 | { 28 | "name": "", 29 | "type": "CLIP", 30 | "links": [ 31 | 41 32 | ], 33 | "slot_index": 0 34 | } 35 | ], 36 | "properties": { 37 | "showOutputText": false, 38 | "horizontal": false 39 | } 40 | }, 41 | { 42 | "id": 22, 43 | "type": "Reroute", 44 | "pos": [ 45 | 752.977695122232, 46 | 852.2900504152276 47 | ], 48 | "size": [ 49 | 75, 50 | 26 51 | ], 52 | "flags": {}, 53 | "order": 12, 54 | "mode": 0, 55 | "inputs": [ 56 | { 57 | "name": "", 58 | "type": "*", 59 | "link": 41 60 | } 61 | ], 62 | "outputs": [ 63 | { 64 | "name": "", 65 | "type": "CLIP", 66 | "links": [ 67 | 42 68 | ], 69 | "slot_index": 0 70 | } 71 | ], 72 | "properties": { 73 | "showOutputText": false, 74 | "horizontal": false 75 | } 76 | }, 77 | { 78 | "id": 13, 79 | "type": "VAEDecode", 80 | "pos": [ 81 | 981, 82 | -179 83 | ], 84 | "size": { 85 | "0": 210, 86 | "1": 46 87 | }, 88 | "flags": {}, 89 | "order": 15, 90 | "mode": 0, 91 | "inputs": [ 92 | { 93 | "name": "samples", 94 | "type": "LATENT", 95 | "link": 15, 96 | "slot_index": 0 97 | }, 98 | { 99 | "name": "vae", 100 | "type": "VAE", 101 | "link": 72, 102 | "slot_index": 1 103 | } 104 | ], 105 | "outputs": [ 106 | { 107 | "name": "IMAGE", 108 | "type": "IMAGE", 109 | "links": [ 110 | 46 111 | ], 112 | "slot_index": 0 113 | } 114 | ], 115 | "properties": { 116 | "Node name for S&R": "VAEDecode" 117 | } 118 | }, 119 | { 120 | "id": 27, 121 | "type": "Reroute", 122 | "pos": [ 123 | 1917, 124 | 76 125 | ], 126 | "size": [ 127 | 75, 128 | 26 129 | ], 130 | "flags": {}, 131 | "order": 22, 132 | "mode": 0, 133 | "inputs": [ 134 | { 135 | "name": "", 136 | "type": "*", 137 | "link": 83, 138 | "slot_index": 0 139 | } 140 | ], 141 | "outputs": [ 142 | { 143 | "name": "", 144 | "type": "LATENT", 145 | "links": [ 146 | 51 147 | ], 148 | "slot_index": 0 149 | } 150 | ], 151 | "properties": { 152 | "showOutputText": false, 153 | "horizontal": false 154 | } 155 | }, 156 | { 157 | "id": 25, 158 | "type": "Reroute", 159 | "pos": [ 160 | 1780, 161 | 777 162 | ], 163 | "size": [ 164 | 75, 165 | 26 166 | ], 167 | "flags": {}, 168 | "order": 14, 169 | "mode": 0, 170 | "inputs": [ 171 | { 172 | "name": "", 173 | "type": "*", 174 | "link": 53, 175 | "slot_index": 0 176 | } 177 | ], 178 | "outputs": [ 179 | { 180 | "name": "", 181 | "type": "CONDITIONING", 182 | "links": [ 183 | 44 184 | ], 185 | "slot_index": 0 186 | } 187 | ], 188 | "properties": { 189 | "showOutputText": false, 190 | "horizontal": false 191 | } 192 | }, 193 | { 194 | "id": 30, 195 | "type": "Reroute", 196 | "pos": [ 197 | 93, 198 | 373 199 | ], 200 | "size": [ 201 | 75, 202 | 26 203 | ], 204 | "flags": {}, 205 | "order": 3, 206 | "mode": 0, 207 | "inputs": [ 208 | { 209 | "name": "", 210 | "type": "*", 211 | "link": 54, 212 | "slot_index": 0 213 | } 214 | ], 215 | "outputs": [ 216 | { 217 | "name": "", 218 | "type": "CLIP", 219 | "links": [ 220 | 55, 221 | 56, 222 | 57 223 | ], 224 | "slot_index": 0 225 | } 226 | ], 227 | "properties": { 228 | "showOutputText": false, 229 | "horizontal": false 230 | } 231 | }, 232 | { 233 | "id": 4, 234 | "type": "CheckpointLoaderSimple", 235 | "pos": [ 236 | -285, 237 | 186 238 | ], 239 | "size": { 240 | "0": 315, 241 | "1": 98 242 | }, 243 | "flags": {}, 244 | "order": 0, 245 | "mode": 0, 246 | "outputs": [ 247 | { 248 | "name": "MODEL", 249 | "type": "MODEL", 250 | "links": [ 251 | 58 252 | ], 253 | "slot_index": 0 254 | }, 255 | { 256 | "name": "CLIP", 257 | "type": "CLIP", 258 | "links": [ 259 | 54 260 | ], 261 | "slot_index": 1 262 | }, 263 | { 264 | "name": "VAE", 265 | "type": "VAE", 266 | "links": [ 267 | 61 268 | ], 269 | "slot_index": 2 270 | } 271 | ], 272 | "properties": { 273 | "Node name for S&R": "CheckpointLoaderSimple" 274 | }, 275 | "widgets_values": [ 276 | "majicmixSombre_v20.safetensors" 277 | ] 278 | }, 279 | { 280 | "id": 33, 281 | "type": "Reroute", 282 | "pos": [ 283 | 1208, 284 | 447 285 | ], 286 | "size": [ 287 | 75, 288 | 26 289 | ], 290 | "flags": {}, 291 | "order": 13, 292 | "mode": 0, 293 | "inputs": [ 294 | { 295 | "name": "", 296 | "type": "*", 297 | "link": 73, 298 | "slot_index": 0 299 | } 300 | ], 301 | "outputs": [ 302 | { 303 | "name": "", 304 | "type": "VAE", 305 | "links": [ 306 | 67, 307 | 68 308 | ], 309 | "slot_index": 0 310 | } 311 | ], 312 | "properties": { 313 | "showOutputText": false, 314 | "horizontal": false 315 | } 316 | }, 317 | { 318 | "id": 34, 319 | "type": "Reroute", 320 | "pos": [ 321 | 936, 322 | 146 323 | ], 324 | "size": [ 325 | 75, 326 | 26 327 | ], 328 | "flags": {}, 329 | "order": 9, 330 | "mode": 0, 331 | "inputs": [ 332 | { 333 | "name": "", 334 | "type": "*", 335 | "link": 71, 336 | "slot_index": 0 337 | } 338 | ], 339 | "outputs": [ 340 | { 341 | "name": "", 342 | "type": "VAE", 343 | "links": [ 344 | 72, 345 | 73 346 | ], 347 | "slot_index": 0 348 | } 349 | ], 350 | "properties": { 351 | "showOutputText": false, 352 | "horizontal": false 353 | } 354 | }, 355 | { 356 | "id": 32, 357 | "type": "Reroute", 358 | "pos": [ 359 | 185, 360 | 147 361 | ], 362 | "size": [ 363 | 75, 364 | 26 365 | ], 366 | "flags": {}, 367 | "order": 4, 368 | "mode": 0, 369 | "inputs": [ 370 | { 371 | "name": "", 372 | "type": "*", 373 | "link": 61, 374 | "slot_index": 0 375 | } 376 | ], 377 | "outputs": [ 378 | { 379 | "name": "", 380 | "type": "VAE", 381 | "links": [ 382 | 71 383 | ], 384 | "slot_index": 0 385 | } 386 | ], 387 | "properties": { 388 | "showOutputText": false, 389 | "horizontal": false 390 | } 391 | }, 392 | { 393 | "id": 31, 394 | "type": "Reroute", 395 | "pos": [ 396 | 199, 397 | 64 398 | ], 399 | "size": [ 400 | 75, 401 | 26 402 | ], 403 | "flags": {}, 404 | "order": 2, 405 | "mode": 0, 406 | "inputs": [ 407 | { 408 | "name": "", 409 | "type": "*", 410 | "link": 58, 411 | "slot_index": 0 412 | } 413 | ], 414 | "outputs": [ 415 | { 416 | "name": "", 417 | "type": "MODEL", 418 | "links": [ 419 | 59, 420 | 74 421 | ], 422 | "slot_index": 0 423 | } 424 | ], 425 | "properties": { 426 | "showOutputText": false, 427 | "horizontal": false 428 | } 429 | }, 430 | { 431 | "id": 35, 432 | "type": "Reroute", 433 | "pos": [ 434 | 1205, 435 | 198 436 | ], 437 | "size": [ 438 | 75, 439 | 26 440 | ], 441 | "flags": {}, 442 | "order": 5, 443 | "mode": 0, 444 | "inputs": [ 445 | { 446 | "name": "", 447 | "type": "*", 448 | "link": 74, 449 | "slot_index": 0 450 | } 451 | ], 452 | "outputs": [ 453 | { 454 | "name": "", 455 | "type": "MODEL", 456 | "links": [ 457 | 75, 458 | 84 459 | ], 460 | "slot_index": 0 461 | } 462 | ], 463 | "properties": { 464 | "showOutputText": false, 465 | "horizontal": false 466 | } 467 | }, 468 | { 469 | "id": 28, 470 | "type": "Reroute", 471 | "pos": [ 472 | 1202, 473 | 777 474 | ], 475 | "size": [ 476 | 75, 477 | 26 478 | ], 479 | "flags": {}, 480 | "order": 10, 481 | "mode": 0, 482 | "inputs": [ 483 | { 484 | "name": "", 485 | "type": "*", 486 | "link": 52, 487 | "slot_index": 0 488 | } 489 | ], 490 | "outputs": [ 491 | { 492 | "name": "", 493 | "type": "CONDITIONING", 494 | "links": [ 495 | 53, 496 | 86 497 | ], 498 | "slot_index": 0 499 | } 500 | ], 501 | "properties": { 502 | "showOutputText": false, 503 | "horizontal": false 504 | } 505 | }, 506 | { 507 | "id": 7, 508 | "type": "CLIPTextEncode", 509 | "pos": [ 510 | 240, 511 | 461 512 | ], 513 | "size": { 514 | "0": 425.27801513671875, 515 | "1": 180.6060791015625 516 | }, 517 | "flags": {}, 518 | "order": 6, 519 | "mode": 0, 520 | "inputs": [ 521 | { 522 | "name": "clip", 523 | "type": "CLIP", 524 | "link": 55 525 | } 526 | ], 527 | "outputs": [ 528 | { 529 | "name": "CONDITIONING", 530 | "type": "CONDITIONING", 531 | "links": [ 532 | 52, 533 | 91 534 | ], 535 | "slot_index": 0 536 | } 537 | ], 538 | "properties": { 539 | "Node name for S&R": "CLIPTextEncode" 540 | }, 541 | "widgets_values": [ 542 | "ng_deepnegative_v1_75t, (badhandv4:1.2),(holding:1.3),(worst quality:1.3),(low quality:1.2), (normal quality:1.2),lowres, bad anatomy, bad hands,\n" 543 | ], 544 | "color": "#322", 545 | "bgcolor": "#533" 546 | }, 547 | { 548 | "id": 24, 549 | "type": "Reroute", 550 | "pos": [ 551 | 2645, 552 | -217 553 | ], 554 | "size": [ 555 | 75, 556 | 26 557 | ], 558 | "flags": {}, 559 | "order": 18, 560 | "mode": 0, 561 | "inputs": [ 562 | { 563 | "name": "", 564 | "type": "*", 565 | "link": 46, 566 | "slot_index": 0 567 | } 568 | ], 569 | "outputs": [ 570 | { 571 | "name": "", 572 | "type": "IMAGE", 573 | "links": [ 574 | 47 575 | ], 576 | "slot_index": 0 577 | } 578 | ], 579 | "properties": { 580 | "showOutputText": false, 581 | "horizontal": false 582 | } 583 | }, 584 | { 585 | "id": 17, 586 | "type": "DZ_Face_Detailer", 587 | "pos": [ 588 | 2085, 589 | 327 590 | ], 591 | "size": { 592 | "0": 315, 593 | "1": 402 594 | }, 595 | "flags": {}, 596 | "order": 23, 597 | "mode": 0, 598 | "inputs": [ 599 | { 600 | "name": "model", 601 | "type": "MODEL", 602 | "link": 75, 603 | "slot_index": 0 604 | }, 605 | { 606 | "name": "positive", 607 | "type": "CONDITIONING", 608 | "link": 34, 609 | "slot_index": 1 610 | }, 611 | { 612 | "name": "negative", 613 | "type": "CONDITIONING", 614 | "link": 44, 615 | "slot_index": 2 616 | }, 617 | { 618 | "name": "latent_image", 619 | "type": "LATENT", 620 | "link": 51, 621 | "slot_index": 3 622 | }, 623 | { 624 | "name": "vae", 625 | "type": "VAE", 626 | "link": 67, 627 | "slot_index": 4 628 | } 629 | ], 630 | "outputs": [ 631 | { 632 | "name": "LATENT", 633 | "type": "LATENT", 634 | "links": [ 635 | 82 636 | ], 637 | "shape": 3, 638 | "slot_index": 0 639 | }, 640 | { 641 | "name": "MASK", 642 | "type": "MASK", 643 | "links": [ 644 | 32 645 | ], 646 | "shape": 3, 647 | "slot_index": 1 648 | } 649 | ], 650 | "properties": { 651 | "Node name for S&R": "DZ_Face_Detailer" 652 | }, 653 | "widgets_values": [ 654 | 465341715093210, 655 | "randomize", 656 | 20, 657 | 7, 658 | "ddim", 659 | "karras", 660 | 0.5, 661 | 0, 662 | "face", 663 | "disabled", 664 | 3, 665 | 3 666 | ], 667 | "color": "#232", 668 | "bgcolor": "#353" 669 | }, 670 | { 671 | "id": 19, 672 | "type": "PreviewImage", 673 | "pos": [ 674 | 2468, 675 | 496 676 | ], 677 | "size": { 678 | "0": 262.99151611328125, 679 | "1": 385.7229309082031 680 | }, 681 | "flags": {}, 682 | "order": 27, 683 | "mode": 0, 684 | "inputs": [ 685 | { 686 | "name": "images", 687 | "type": "IMAGE", 688 | "link": 33 689 | } 690 | ], 691 | "properties": { 692 | "Node name for S&R": "PreviewImage" 693 | } 694 | }, 695 | { 696 | "id": 18, 697 | "type": "MaskToImage", 698 | "pos": [ 699 | 2442, 700 | 423 701 | ], 702 | "size": { 703 | "0": 203.3360137939453, 704 | "1": 26 705 | }, 706 | "flags": { 707 | "collapsed": true 708 | }, 709 | "order": 25, 710 | "mode": 0, 711 | "inputs": [ 712 | { 713 | "name": "mask", 714 | "type": "MASK", 715 | "link": 32 716 | } 717 | ], 718 | "outputs": [ 719 | { 720 | "name": "IMAGE", 721 | "type": "IMAGE", 722 | "links": [ 723 | 33 724 | ], 725 | "shape": 3, 726 | "slot_index": 0 727 | } 728 | ], 729 | "properties": { 730 | "Node name for S&R": "MaskToImage" 731 | } 732 | }, 733 | { 734 | "id": 20, 735 | "type": "CLIPTextEncode", 736 | "pos": [ 737 | 1492, 738 | 343 739 | ], 740 | "size": { 741 | "0": 422.84503173828125, 742 | "1": 164.31304931640625 743 | }, 744 | "flags": {}, 745 | "order": 17, 746 | "mode": 0, 747 | "inputs": [ 748 | { 749 | "name": "clip", 750 | "type": "CLIP", 751 | "link": 42, 752 | "slot_index": 0 753 | } 754 | ], 755 | "outputs": [ 756 | { 757 | "name": "CONDITIONING", 758 | "type": "CONDITIONING", 759 | "links": [ 760 | 34 761 | ], 762 | "slot_index": 0 763 | } 764 | ], 765 | "properties": { 766 | "Node name for S&R": "CLIPTextEncode" 767 | }, 768 | "widgets_values": [ 769 | "beautiful detailed face, high details, fine details" 770 | ], 771 | "color": "#232", 772 | "bgcolor": "#353" 773 | }, 774 | { 775 | "id": 26, 776 | "type": "Reroute", 777 | "pos": [ 778 | 1024, 779 | 1 780 | ], 781 | "size": [ 782 | 75, 783 | 26 784 | ], 785 | "flags": {}, 786 | "order": 16, 787 | "mode": 0, 788 | "inputs": [ 789 | { 790 | "name": "", 791 | "type": "*", 792 | "link": 48, 793 | "slot_index": 0 794 | } 795 | ], 796 | "outputs": [ 797 | { 798 | "name": "", 799 | "type": "LATENT", 800 | "links": [ 801 | 89 802 | ], 803 | "slot_index": 0 804 | } 805 | ], 806 | "properties": { 807 | "showOutputText": false, 808 | "horizontal": false 809 | } 810 | }, 811 | { 812 | "id": 39, 813 | "type": "LatentUpscaleBy", 814 | "pos": [ 815 | 1176, 816 | 50 817 | ], 818 | "size": { 819 | "0": 315, 820 | "1": 82 821 | }, 822 | "flags": { 823 | "collapsed": true 824 | }, 825 | "order": 19, 826 | "mode": 0, 827 | "inputs": [ 828 | { 829 | "name": "samples", 830 | "type": "LATENT", 831 | "link": 89, 832 | "slot_index": 0 833 | } 834 | ], 835 | "outputs": [ 836 | { 837 | "name": "LATENT", 838 | "type": "LATENT", 839 | "links": [ 840 | 88 841 | ], 842 | "shape": 3, 843 | "slot_index": 0 844 | } 845 | ], 846 | "properties": { 847 | "Node name for S&R": "LatentUpscaleBy" 848 | }, 849 | "widgets_values": [ 850 | "nearest-exact", 851 | 1.2 852 | ] 853 | }, 854 | { 855 | "id": 38, 856 | "type": "KSampler", 857 | "pos": [ 858 | 1472, 859 | -45 860 | ], 861 | "size": { 862 | "0": 315, 863 | "1": 262 864 | }, 865 | "flags": {}, 866 | "order": 21, 867 | "mode": 0, 868 | "inputs": [ 869 | { 870 | "name": "model", 871 | "type": "MODEL", 872 | "link": 84, 873 | "slot_index": 0 874 | }, 875 | { 876 | "name": "positive", 877 | "type": "CONDITIONING", 878 | "link": 87, 879 | "slot_index": 1 880 | }, 881 | { 882 | "name": "negative", 883 | "type": "CONDITIONING", 884 | "link": 86, 885 | "slot_index": 2 886 | }, 887 | { 888 | "name": "latent_image", 889 | "type": "LATENT", 890 | "link": 88, 891 | "slot_index": 3 892 | } 893 | ], 894 | "outputs": [ 895 | { 896 | "name": "LATENT", 897 | "type": "LATENT", 898 | "links": [ 899 | 83 900 | ], 901 | "shape": 3, 902 | "slot_index": 0 903 | } 904 | ], 905 | "properties": { 906 | "Node name for S&R": "KSampler" 907 | }, 908 | "widgets_values": [ 909 | 888117471012813, 910 | "randomize", 911 | 15, 912 | 7, 913 | "euler_ancestral", 914 | "karras", 915 | 0.5 916 | ] 917 | }, 918 | { 919 | "id": 8, 920 | "type": "VAEDecode", 921 | "pos": [ 922 | 2534, 923 | 318 924 | ], 925 | "size": { 926 | "0": 210, 927 | "1": 46 928 | }, 929 | "flags": { 930 | "collapsed": true 931 | }, 932 | "order": 24, 933 | "mode": 0, 934 | "inputs": [ 935 | { 936 | "name": "samples", 937 | "type": "LATENT", 938 | "link": 82, 939 | "slot_index": 0 940 | }, 941 | { 942 | "name": "vae", 943 | "type": "VAE", 944 | "link": 68 945 | } 946 | ], 947 | "outputs": [ 948 | { 949 | "name": "IMAGE", 950 | "type": "IMAGE", 951 | "links": [ 952 | 43 953 | ], 954 | "slot_index": 0 955 | } 956 | ], 957 | "properties": { 958 | "Node name for S&R": "VAEDecode" 959 | } 960 | }, 961 | { 962 | "id": 6, 963 | "type": "CLIPTextEncode", 964 | "pos": [ 965 | 132, 966 | -178 967 | ], 968 | "size": { 969 | "0": 422.84503173828125, 970 | "1": 164.31304931640625 971 | }, 972 | "flags": {}, 973 | "order": 7, 974 | "mode": 0, 975 | "inputs": [ 976 | { 977 | "name": "clip", 978 | "type": "CLIP", 979 | "link": 56 980 | } 981 | ], 982 | "outputs": [ 983 | { 984 | "name": "CONDITIONING", 985 | "type": "CONDITIONING", 986 | "links": [ 987 | 4, 988 | 87 989 | ], 990 | "slot_index": 0 991 | } 992 | ], 993 | "properties": { 994 | "Node name for S&R": "CLIPTextEncode" 995 | }, 996 | "widgets_values": [ 997 | "1girl, wearing a dress, standing,(masterpiece, high quality, best quality), volumatic light, ray tracing, extremely detailed CG unity 8k wallpaper,solo, ((flying petal)), outdoors, mountains, paths, (flowers, flower field), sunlight\n" 998 | ], 999 | "color": "#223", 1000 | "bgcolor": "#335" 1001 | }, 1002 | { 1003 | "id": 5, 1004 | "type": "EmptyLatentImage", 1005 | "pos": [ 1006 | 282, 1007 | 252 1008 | ], 1009 | "size": { 1010 | "0": 315, 1011 | "1": 106 1012 | }, 1013 | "flags": {}, 1014 | "order": 1, 1015 | "mode": 0, 1016 | "outputs": [ 1017 | { 1018 | "name": "LATENT", 1019 | "type": "LATENT", 1020 | "links": [ 1021 | 2 1022 | ], 1023 | "slot_index": 0 1024 | } 1025 | ], 1026 | "properties": { 1027 | "Node name for S&R": "EmptyLatentImage" 1028 | }, 1029 | "widgets_values": [ 1030 | 512, 1031 | 512, 1032 | 1 1033 | ] 1034 | }, 1035 | { 1036 | "id": 3, 1037 | "type": "KSampler", 1038 | "pos": [ 1039 | 610, 1040 | -182 1041 | ], 1042 | "size": { 1043 | "0": 315, 1044 | "1": 262 1045 | }, 1046 | "flags": {}, 1047 | "order": 11, 1048 | "mode": 0, 1049 | "inputs": [ 1050 | { 1051 | "name": "model", 1052 | "type": "MODEL", 1053 | "link": 59 1054 | }, 1055 | { 1056 | "name": "positive", 1057 | "type": "CONDITIONING", 1058 | "link": 4 1059 | }, 1060 | { 1061 | "name": "negative", 1062 | "type": "CONDITIONING", 1063 | "link": 91, 1064 | "slot_index": 2 1065 | }, 1066 | { 1067 | "name": "latent_image", 1068 | "type": "LATENT", 1069 | "link": 2 1070 | } 1071 | ], 1072 | "outputs": [ 1073 | { 1074 | "name": "LATENT", 1075 | "type": "LATENT", 1076 | "links": [ 1077 | 15, 1078 | 48 1079 | ], 1080 | "slot_index": 0 1081 | } 1082 | ], 1083 | "properties": { 1084 | "Node name for S&R": "KSampler" 1085 | }, 1086 | "widgets_values": [ 1087 | 305757805868987, 1088 | "fixed", 1089 | 15, 1090 | 6, 1091 | "dpmpp_2m", 1092 | "karras", 1093 | 1 1094 | ], 1095 | "color": "#223", 1096 | "bgcolor": "#335" 1097 | }, 1098 | { 1099 | "id": 14, 1100 | "type": "PreviewImage", 1101 | "pos": [ 1102 | 3255, 1103 | 420 1104 | ], 1105 | "size": [ 1106 | 331.1569279423511, 1107 | 354.9488064968368 1108 | ], 1109 | "flags": {}, 1110 | "order": 20, 1111 | "mode": 0, 1112 | "inputs": [ 1113 | { 1114 | "name": "images", 1115 | "type": "IMAGE", 1116 | "link": 47 1117 | } 1118 | ], 1119 | "properties": { 1120 | "Node name for S&R": "PreviewImage" 1121 | } 1122 | }, 1123 | { 1124 | "id": 23, 1125 | "type": "PreviewImage", 1126 | "pos": [ 1127 | 2858.480640089445, 1128 | 425.4401372391366 1129 | ], 1130 | "size": [ 1131 | 325.9292706156648, 1132 | 359.02643171887235 1133 | ], 1134 | "flags": {}, 1135 | "order": 26, 1136 | "mode": 0, 1137 | "inputs": [ 1138 | { 1139 | "name": "images", 1140 | "type": "IMAGE", 1141 | "link": 43, 1142 | "slot_index": 0 1143 | } 1144 | ], 1145 | "properties": { 1146 | "Node name for S&R": "PreviewImage" 1147 | } 1148 | } 1149 | ], 1150 | "links": [ 1151 | [ 1152 | 2, 1153 | 5, 1154 | 0, 1155 | 3, 1156 | 3, 1157 | "LATENT" 1158 | ], 1159 | [ 1160 | 4, 1161 | 6, 1162 | 0, 1163 | 3, 1164 | 1, 1165 | "CONDITIONING" 1166 | ], 1167 | [ 1168 | 15, 1169 | 3, 1170 | 0, 1171 | 13, 1172 | 0, 1173 | "LATENT" 1174 | ], 1175 | [ 1176 | 32, 1177 | 17, 1178 | 1, 1179 | 18, 1180 | 0, 1181 | "MASK" 1182 | ], 1183 | [ 1184 | 33, 1185 | 18, 1186 | 0, 1187 | 19, 1188 | 0, 1189 | "IMAGE" 1190 | ], 1191 | [ 1192 | 34, 1193 | 20, 1194 | 0, 1195 | 17, 1196 | 1, 1197 | "CONDITIONING" 1198 | ], 1199 | [ 1200 | 41, 1201 | 21, 1202 | 0, 1203 | 22, 1204 | 0, 1205 | "*" 1206 | ], 1207 | [ 1208 | 42, 1209 | 22, 1210 | 0, 1211 | 20, 1212 | 0, 1213 | "CLIP" 1214 | ], 1215 | [ 1216 | 43, 1217 | 8, 1218 | 0, 1219 | 23, 1220 | 0, 1221 | "IMAGE" 1222 | ], 1223 | [ 1224 | 44, 1225 | 25, 1226 | 0, 1227 | 17, 1228 | 2, 1229 | "CONDITIONING" 1230 | ], 1231 | [ 1232 | 46, 1233 | 13, 1234 | 0, 1235 | 24, 1236 | 0, 1237 | "*" 1238 | ], 1239 | [ 1240 | 47, 1241 | 24, 1242 | 0, 1243 | 14, 1244 | 0, 1245 | "IMAGE" 1246 | ], 1247 | [ 1248 | 48, 1249 | 3, 1250 | 0, 1251 | 26, 1252 | 0, 1253 | "*" 1254 | ], 1255 | [ 1256 | 51, 1257 | 27, 1258 | 0, 1259 | 17, 1260 | 3, 1261 | "LATENT" 1262 | ], 1263 | [ 1264 | 52, 1265 | 7, 1266 | 0, 1267 | 28, 1268 | 0, 1269 | "*" 1270 | ], 1271 | [ 1272 | 53, 1273 | 28, 1274 | 0, 1275 | 25, 1276 | 0, 1277 | "*" 1278 | ], 1279 | [ 1280 | 54, 1281 | 4, 1282 | 1, 1283 | 30, 1284 | 0, 1285 | "*" 1286 | ], 1287 | [ 1288 | 55, 1289 | 30, 1290 | 0, 1291 | 7, 1292 | 0, 1293 | "CLIP" 1294 | ], 1295 | [ 1296 | 56, 1297 | 30, 1298 | 0, 1299 | 6, 1300 | 0, 1301 | "CLIP" 1302 | ], 1303 | [ 1304 | 57, 1305 | 30, 1306 | 0, 1307 | 21, 1308 | 0, 1309 | "*" 1310 | ], 1311 | [ 1312 | 58, 1313 | 4, 1314 | 0, 1315 | 31, 1316 | 0, 1317 | "*" 1318 | ], 1319 | [ 1320 | 59, 1321 | 31, 1322 | 0, 1323 | 3, 1324 | 0, 1325 | "MODEL" 1326 | ], 1327 | [ 1328 | 61, 1329 | 4, 1330 | 2, 1331 | 32, 1332 | 0, 1333 | "*" 1334 | ], 1335 | [ 1336 | 67, 1337 | 33, 1338 | 0, 1339 | 17, 1340 | 4, 1341 | "VAE" 1342 | ], 1343 | [ 1344 | 68, 1345 | 33, 1346 | 0, 1347 | 8, 1348 | 1, 1349 | "VAE" 1350 | ], 1351 | [ 1352 | 71, 1353 | 32, 1354 | 0, 1355 | 34, 1356 | 0, 1357 | "*" 1358 | ], 1359 | [ 1360 | 72, 1361 | 34, 1362 | 0, 1363 | 13, 1364 | 1, 1365 | "VAE" 1366 | ], 1367 | [ 1368 | 73, 1369 | 34, 1370 | 0, 1371 | 33, 1372 | 0, 1373 | "*" 1374 | ], 1375 | [ 1376 | 74, 1377 | 31, 1378 | 0, 1379 | 35, 1380 | 0, 1381 | "*" 1382 | ], 1383 | [ 1384 | 75, 1385 | 35, 1386 | 0, 1387 | 17, 1388 | 0, 1389 | "MODEL" 1390 | ], 1391 | [ 1392 | 82, 1393 | 17, 1394 | 0, 1395 | 8, 1396 | 0, 1397 | "LATENT" 1398 | ], 1399 | [ 1400 | 83, 1401 | 38, 1402 | 0, 1403 | 27, 1404 | 0, 1405 | "*" 1406 | ], 1407 | [ 1408 | 84, 1409 | 35, 1410 | 0, 1411 | 38, 1412 | 0, 1413 | "MODEL" 1414 | ], 1415 | [ 1416 | 86, 1417 | 28, 1418 | 0, 1419 | 38, 1420 | 2, 1421 | "CONDITIONING" 1422 | ], 1423 | [ 1424 | 87, 1425 | 6, 1426 | 0, 1427 | 38, 1428 | 1, 1429 | "CONDITIONING" 1430 | ], 1431 | [ 1432 | 88, 1433 | 39, 1434 | 0, 1435 | 38, 1436 | 3, 1437 | "LATENT" 1438 | ], 1439 | [ 1440 | 89, 1441 | 26, 1442 | 0, 1443 | 39, 1444 | 0, 1445 | "LATENT" 1446 | ], 1447 | [ 1448 | 91, 1449 | 7, 1450 | 0, 1451 | 3, 1452 | 2, 1453 | "CONDITIONING" 1454 | ] 1455 | ], 1456 | "groups": [ 1457 | { 1458 | "title": "Detailer", 1459 | "bounding": [ 1460 | 1351, 1461 | 253, 1462 | 1415, 1463 | 686 1464 | ], 1465 | "color": "#3f789e", 1466 | "font_size": 24 1467 | }, 1468 | { 1469 | "title": "Detailer", 1470 | "bounding": [ 1471 | 2816, 1472 | 278, 1473 | 411, 1474 | 596 1475 | ], 1476 | "color": "#8A8", 1477 | "font_size": 24 1478 | }, 1479 | { 1480 | "title": "Sampler", 1481 | "bounding": [ 1482 | 93, 1483 | -279, 1484 | 1158, 1485 | 1325 1486 | ], 1487 | "color": "#3f789e", 1488 | "font_size": 24 1489 | }, 1490 | { 1491 | "title": "Without detailer", 1492 | "bounding": [ 1493 | 3236, 1494 | 284, 1495 | 378, 1496 | 592 1497 | ], 1498 | "color": "#88A", 1499 | "font_size": 24 1500 | } 1501 | ], 1502 | "config": {}, 1503 | "extra": {}, 1504 | "version": 0.4 1505 | } --------------------------------------------------------------------------------