From 62dda471a3c3eb83ccd09c5dca756453ea14128b Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Fri, 12 May 2023 14:21:26 -0400 Subject: [PATCH] process images in threads --- log.json | 451 +++++++++++++++++++++ modules/cmd_args.py | 1 - modules/devices.py | 4 + modules/generation_parameters_copypaste.py | 17 +- modules/images.py | 209 ++++------ modules/processing.py | 1 - modules/shared.py | 4 +- modules/ui_common.py | 62 ++- modules/ui_tempdir.py | 24 +- 9 files changed, 592 insertions(+), 181 deletions(-) create mode 100644 log.json diff --git a/log.json b/log.json new file mode 100644 index 000000000..3111c46ac --- /dev/null +++ b/log.json @@ -0,0 +1,451 @@ +[ + { + "filename": "outputs/text/00189-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T12:51:23.125657", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00009-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:02:56.680825", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00010-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:03:10.309811", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00011-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:04:47.198064", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00012-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:16:21.231228", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:10.190976", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:12.646301", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:14.541258", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:45.747797", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:47.702913", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00013-3905757009-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:19:49.710276", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 3905757009, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00190-1800237580-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:25:36.025059", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1800237580, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00014-1800237580-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:25:38.529309", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1800237580, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00015-1800237580-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:25:41.355289", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1800237580, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00016-1800237580-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:25:42.365440", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1800237580, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00017-1800237580-beautiful woman wearing a bikini in a city during night", + "time": "2023-05-12T13:25:43.280769", + "info": "beautiful woman wearing a bikini in a city during night\nNegative prompt: easynegative, badprompts, ugly, duplicate, morbid, mutilated, out of frame, extra fingers, mutated hands, poorly drawn hands, poorly drawn face, mutation, deformed, blurry, bad anatomy, bad proportions, extra limbs, cloned face, disfigured, missing arms, missing legs, extra arms, extra legs, fused fingers, too many fingers, long neck\nSteps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1800237580, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00191-2754842760-", + "time": "2023-05-12T13:45:50.125245", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00018-2754842760-", + "time": "2023-05-12T13:45:52.586131", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00019-2754842760-", + "time": "2023-05-12T13:48:17.093520", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00020-2754842760-", + "time": "2023-05-12T13:48:18.196932", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00021-2754842760-", + "time": "2023-05-12T13:48:19.164835", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00022-2754842760-", + "time": "2023-05-12T13:48:20.047468", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00023-2754842760-", + "time": "2023-05-12T13:48:20.889756", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00024-2754842760-", + "time": "2023-05-12T13:48:21.679708", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00025-2754842760-", + "time": "2023-05-12T13:48:22.487093", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00026-2754842760-", + "time": "2023-05-12T13:48:23.288043", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00027-2754842760-", + "time": "2023-05-12T13:48:24.012309", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00028-2754842760-", + "time": "2023-05-12T13:48:24.910025", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00029-2754842760-", + "time": "2023-05-12T13:48:25.676995", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00030-2754842760-", + "time": "2023-05-12T13:48:26.504592", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00031-2754842760-", + "time": "2023-05-12T13:48:27.437550", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00032-2754842760-", + "time": "2023-05-12T13:48:28.239230", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00033-2754842760-", + "time": "2023-05-12T13:48:29.001641", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00034-2754842760-", + "time": "2023-05-12T13:48:29.744071", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00035-2754842760-", + "time": "2023-05-12T13:48:30.643950", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00036-2754842760-", + "time": "2023-05-12T13:48:31.487216", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00037-2754842760-", + "time": "2023-05-12T13:48:32.332520", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00038-2754842760-", + "time": "2023-05-12T13:48:33.101266", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00039-2754842760-", + "time": "2023-05-12T13:48:33.926420", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00040-2754842760-", + "time": "2023-05-12T13:48:34.710441", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00041-2754842760-", + "time": "2023-05-12T13:48:35.478143", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00042-2754842760-", + "time": "2023-05-12T13:48:36.316470", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00043-2754842760-", + "time": "2023-05-12T13:48:37.138220", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00044-2754842760-", + "time": "2023-05-12T13:48:37.813014", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00045-2754842760-", + "time": "2023-05-12T13:48:38.499095", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00046-2754842760-", + "time": "2023-05-12T13:48:39.095014", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00047-2754842760-", + "time": "2023-05-12T13:48:39.822240", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00048-2754842760-", + "time": "2023-05-12T13:48:40.373347", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00049-2754842760-", + "time": "2023-05-12T13:48:41.024193", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00050-2754842760-", + "time": "2023-05-12T13:48:41.608742", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00051-2754842760-", + "time": "2023-05-12T13:48:42.278164", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00052-2754842760-", + "time": "2023-05-12T13:48:42.892458", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00053-2754842760-", + "time": "2023-05-12T13:48:44.806619", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00054-2754842760-", + "time": "2023-05-12T13:48:45.716492", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00055-2754842760-", + "time": "2023-05-12T13:48:46.466023", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00056-2754842760-", + "time": "2023-05-12T13:48:47.509243", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/save/00057-2754842760-", + "time": "2023-05-12T13:50:16.947892", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2754842760, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00192-2011235778-", + "time": "2023-05-12T13:52:22.241607", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 2011235778, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00193-4221894582-", + "time": "2023-05-12T13:57:20.270873", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 4221894582, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00194-1506239356-", + "time": "2023-05-12T13:57:29.718097", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239356, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00195-1506239357-", + "time": "2023-05-12T13:57:35.325309", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239357, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00196-1506239358-", + "time": "2023-05-12T13:57:40.926351", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239358, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00197-1506239359-", + "time": "2023-05-12T13:57:46.527684", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239359, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00198-1506239360-", + "time": "2023-05-12T13:57:52.123180", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239360, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00199-1506239361-", + "time": "2023-05-12T13:57:57.744069", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239361, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00200-1506239362-", + "time": "2023-05-12T13:58:03.368185", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239362, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00201-1506239363-", + "time": "2023-05-12T13:58:09.009217", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239363, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00202-1506239364-", + "time": "2023-05-12T13:58:14.702054", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239364, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00203-1506239365-", + "time": "2023-05-12T13:58:20.400389", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239365, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00204-1506239366-", + "time": "2023-05-12T13:58:26.207687", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239366, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00205-1506239367-", + "time": "2023-05-12T13:58:31.935958", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239367, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00206-1506239368-", + "time": "2023-05-12T13:58:32.533708", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239368, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/grids/grid-0007-1506239356-", + "time": "2023-05-12T13:58:32.643158", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1506239356, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00207-1135673010-", + "time": "2023-05-12T14:01:56.795209", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 1135673010, Size: 512x512, Model hash: 362adb1351, Model: 3moon-realdoll, Clip skip: 1" + } +][ + { + "filename": "outputs/text/00208-60785991-", + "time": "2023-05-12T14:09:26.861751", + "info": "Steps: 20, Sampler: UniPC, CFG scale: 6, Seed: 60785991, Size: 512x512, Model hash: 6ce0161689, Model: v1-5-pruned-emaonly, Clip skip: 1" + } +] \ No newline at end of file diff --git a/modules/cmd_args.py b/modules/cmd_args.py index 80b11402d..2316dce03 100644 --- a/modules/cmd_args.py +++ b/modules/cmd_args.py @@ -102,5 +102,4 @@ def compatibility_args(opts, args): opts.dimensions_and_batch_together = True args = parser.parse_args() - return args diff --git a/modules/devices.py b/modules/devices.py index 68d092a7b..0358bb8f4 100644 --- a/modules/devices.py +++ b/modules/devices.py @@ -64,6 +64,8 @@ def get_device_for(task): def torch_gc(): + if shared.opts.disable_gc: + return gc.collect() if shared.cmd_opts.use_ipex: try: @@ -86,6 +88,7 @@ def test_fp16(): x = torch.tensor([[1.5,.0,.0,.0]]).to(device).half() layerNorm = torch.nn.LayerNorm(4, eps=0.00001, elementwise_affine=True, dtype=torch.float16, device=device) _y = layerNorm(x) + shared.log.debug('Torch FP16 test passed') return True except: shared.log.warning('Torch FP16 test failed: Forcing FP32 operations') @@ -131,6 +134,7 @@ def set_cuda_params(): if shared.opts.no_half_vae: # set dtype again as no-half-vae options take priority dtype_vae = torch.float32 unet_needs_upcast = shared.opts.upcast_sampling + shared.log.debug(f'Desired CUDA parameters: dtype={shared.opts.cuda_dtype} no-half={shared.opts.no_half} no-half-vae={shared.opts.no_half_vae} upscast={shared.opts.upcast_sampling}') shared.log.debug(f'Setting CUDA parameters: dtype={dtype} vae={dtype_vae} unet={dtype_unet}') diff --git a/modules/generation_parameters_copypaste.py b/modules/generation_parameters_copypaste.py index 44d2fab9b..f9c141c82 100644 --- a/modules/generation_parameters_copypaste.py +++ b/modules/generation_parameters_copypaste.py @@ -36,7 +36,6 @@ def reset(): def quote(text): if ',' not in str(text): return text - text = str(text) text = text.replace('\\', '\\\\') text = text.replace('"', '\\"') @@ -46,27 +45,29 @@ def quote(text): def image_from_url_text(filedata): if filedata is None: return None - if type(filedata) == list and len(filedata) > 0 and type(filedata[0]) == dict and filedata[0].get("is_file", False): filedata = filedata[0] - if type(filedata) == dict and filedata.get("is_file", False): filename = filedata["name"] is_in_right_dir = ui_tempdir.check_tmp_file(shared.demo, filename) if is_in_right_dir: return Image.open(filename) else: - shared.log.warning(f'Attempted to open file outside of allowed directories: {filename}') - + shared.log.warning(f'File access denied: {filename}') + return None if type(filedata) == list: if len(filedata) == 0: return None - filedata = filedata[0] - + if type(filedata) == dict: + shared.log.warning('Incorrect filedata received') + return None if filedata.startswith("data:image/png;base64,"): filedata = filedata[len("data:image/png;base64,"):] - + if filedata.startswith("data:image/webp;base64,"): + filedata = filedata[len("data:image/webp;base64,"):] + if filedata.startswith("data:image/jpeg;base64,"): + filedata = filedata[len("data:image/jpeg;base64,"):] filedata = base64.decodebytes(filedata.encode('utf-8')) image = Image.open(io.BytesIO(filedata)) return image diff --git a/modules/images.py b/modules/images.py index cbde622c3..83563b2ae 100644 --- a/modules/images.py +++ b/modules/images.py @@ -6,26 +6,26 @@ import math import json import string import hashlib +import queue +import threading from collections import namedtuple import pytz import numpy as np import piexif import piexif.helper from PIL import Image, ImageFont, ImageDraw, PngImagePlugin, ExifTags - from modules import sd_samplers, shared, script_callbacks, errors -from modules.shared import opts, log LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) def image_grid(imgs, batch_size=1, rows=None): if rows is None: - if opts.n_rows > 0: - rows = opts.n_rows - elif opts.n_rows == 0: + if shared.opts.n_rows > 0: + rows = shared.opts.n_rows + elif shared.opts.n_rows == 0: rows = batch_size - elif opts.grid_prevent_empty_spots: + elif shared.opts.grid_prevent_empty_spots: rows = math.floor(math.sqrt(len(imgs))) while len(imgs) % rows != 0: rows -= 1 @@ -34,18 +34,13 @@ def image_grid(imgs, batch_size=1, rows=None): rows = round(rows) if rows > len(imgs): rows = len(imgs) - cols = math.ceil(len(imgs) / rows) - params = script_callbacks.ImageGridLoopParams(imgs, cols, rows) script_callbacks.image_grid_callback(params) - w, h = imgs[0].size grid = Image.new('RGB', size=(params.cols * w, params.rows * h), color='black') - for i, img in enumerate(params.imgs): grid.paste(img, box=(i % params.cols * w, i // params.cols * h)) - return grid @@ -139,7 +134,7 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0): def get_font(fontsize): try: - return ImageFont.truetype(opts.font or 'javascript/roboto.ttf', fontsize) + return ImageFont.truetype(shared.opts.font or 'javascript/roboto.ttf', fontsize) except Exception: return ImageFont.truetype('javascript/roboto.ttf', fontsize) @@ -151,90 +146,65 @@ def draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin=0): fontsize -= 1 fnt = get_font(fontsize) drawing.multiline_text((draw_x, draw_y + line.size[1] / 2), line.text, font=fnt, fill=color_active if line.is_active else color_inactive, anchor="mm", align="center") - if not line.is_active: drawing.line((draw_x - line.size[0] // 2, draw_y + line.size[1] // 2, draw_x + line.size[0] // 2, draw_y + line.size[1] // 2), fill=color_inactive, width=4) - draw_y += line.size[1] + line_spacing fontsize = (width + height) // 25 line_spacing = fontsize // 2 - fnt = get_font(fontsize) - color_active = (0, 0, 0) color_inactive = (153, 153, 153) - pad_left = 0 if sum([sum([len(line.text) for line in lines]) for lines in ver_texts]) == 0 else width * 3 // 4 - cols = im.width // width rows = im.height // height - assert cols == len(hor_texts), f'bad number of horizontal texts: {len(hor_texts)}; must be {cols}' assert rows == len(ver_texts), f'bad number of vertical texts: {len(ver_texts)}; must be {rows}' - calc_img = Image.new("RGB", (1, 1), "white") calc_d = ImageDraw.Draw(calc_img) - for texts, allowed_width in zip(hor_texts + ver_texts, [width] * len(hor_texts) + [pad_left] * len(ver_texts)): items = [] + texts texts.clear() - for line in items: wrapped = wrap(calc_d, line.text, fnt, allowed_width) texts += [GridAnnotation(x, line.is_active) for x in wrapped] - for line in texts: bbox = calc_d.multiline_textbbox((0, 0), line.text, font=fnt) line.size = (bbox[2] - bbox[0], bbox[3] - bbox[1]) line.allowed_width = allowed_width - hor_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing for lines in hor_texts] ver_text_heights = [sum([line.size[1] + line_spacing for line in lines]) - line_spacing * len(lines) for lines in ver_texts] - pad_top = 0 if sum(hor_text_heights) == 0 else max(hor_text_heights) + line_spacing * 2 - result = Image.new("RGB", (im.width + pad_left + margin * (cols-1), im.height + pad_top + margin * (rows-1)), "white") - for row in range(rows): for col in range(cols): cell = im.crop((width * col, height * row, width * (col+1), height * (row+1))) result.paste(cell, (pad_left + (width + margin) * col, pad_top + (height + margin) * row)) - d = ImageDraw.Draw(result) - for col in range(cols): x = pad_left + (width + margin) * col + width / 2 y = pad_top / 2 - hor_text_heights[col] / 2 - draw_texts(d, x, y, hor_texts[col], fnt, fontsize) - for row in range(rows): x = pad_left / 2 y = pad_top + (height + margin) * row + height / 2 - ver_text_heights[row] / 2 - draw_texts(d, x, y, ver_texts[row], fnt, fontsize) - return result def draw_prompt_matrix(im, width, height, all_prompts, margin=0): prompts = all_prompts[1:] boundary = math.ceil(len(prompts) / 2) - prompts_horiz = prompts[:boundary] prompts_vert = prompts[boundary:] - hor_texts = [[GridAnnotation(x, is_active=pos & (1 << i) != 0) for i, x in enumerate(prompts_horiz)] for pos in range(1 << len(prompts_horiz))] ver_texts = [[GridAnnotation(x, is_active=pos & (1 << i) != 0) for i, x in enumerate(prompts_vert)] for pos in range(1 << len(prompts_vert))] - return draw_grid_annotations(im, width, height, hor_texts, ver_texts, margin) def resize_image(resize_mode, im, width, height, upscaler_name=None): """ Resizes an image with the specified resize_mode, width, and height. - Args: resize_mode: The mode to use when resizing the image. 0: Resize the image to the specified width and height. @@ -245,55 +215,42 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None): height: The height to resize the image to. upscaler_name: The name of the upscaler to use. If not provided, defaults to opts.upscaler_for_img2img. """ - - upscaler_name = upscaler_name or opts.upscaler_for_img2img + upscaler_name = upscaler_name or shared.opts.upscaler_for_img2img def resize(im, w, h): if upscaler_name is None or upscaler_name == "None" or im.mode == 'L': return im.resize((w, h), resample=LANCZOS) - scale = max(w / im.width, h / im.height) - if scale > 1.0: upscalers = [x for x in shared.sd_upscalers if x.name == upscaler_name] if len(upscalers) == 0: upscaler = shared.sd_upscalers[0] - log.warning(f"could not find upscaler named {upscaler_name or ''}, using {upscaler.name} as a fallback") + shared.log.warning(f"could not find upscaler named {upscaler_name or ''}, using {upscaler.name} as a fallback") else: upscaler = upscalers[0] - im = upscaler.scaler.upscale(im, scale, upscaler.data_path) - if im.width != w or im.height != h: im = im.resize((w, h), resample=LANCZOS) - return im if resize_mode == 0: res = resize(im, width, height) - elif resize_mode == 1: ratio = width / height src_ratio = im.width / im.height - src_w = width if ratio > src_ratio else im.width * height // im.height src_h = height if ratio <= src_ratio else im.height * width // im.width - resized = resize(im, src_w, src_h) res = Image.new("RGB", (width, height)) res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2)) - else: ratio = width / height src_ratio = im.width / im.height - src_w = width if ratio < src_ratio else im.width * height // im.height src_h = height if ratio >= src_ratio else im.height * width // im.width - resized = resize(im, src_w, src_h) res = Image.new("RGB", (width, height)) res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2)) - if ratio < src_ratio: fill_height = height // 2 - src_h // 2 res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0)) @@ -302,7 +259,6 @@ def resize_image(resize_mode, im, width, height, upscaler_name=None): fill_width = width // 2 - src_w // 2 res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0)) res.paste(resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)), box=(fill_width + src_w, 0)) - return res @@ -351,7 +307,7 @@ class FilenameGenerator: 'batch_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.batch_size == 1 else self.p.batch_index + 1, 'generation_number': lambda self: NOTHING_AND_SKIP_PREVIOUS_TEXT if self.p.n_iter == 1 and self.p.batch_size == 1 else self.p.iteration * self.p.batch_size + self.p.batch_index + 1, 'hasprompt': lambda self, *args: self.hasprompt(*args), # accepts formats:[hasprompt..] - 'clip_skip': lambda self: opts.data["CLIP_stop_at_last_layers"], + 'clip_skip': lambda self: shared.opts.data["CLIP_stop_at_last_layers"], } default_time_format = '%Y%m%d%H%M%S' @@ -380,7 +336,6 @@ class FilenameGenerator: def prompt_no_style(self): if self.p is None or self.prompt is None: return None - prompt_no_style = self.prompt for style in shared.prompt_styles.get_style_prompts(self.p.styles): if len(style) > 0: @@ -388,50 +343,42 @@ class FilenameGenerator: prompt_no_style = prompt_no_style.replace(part, "").replace(", ,", ",").strip().strip(',') prompt_no_style = prompt_no_style.replace(style, "").strip().strip(',').strip() - return sanitize_filename_part(prompt_no_style, replace_spaces=False) def prompt_words(self): words = [x for x in re_nonletters.split(self.prompt or "") if len(x) > 0] if len(words) == 0: words = ["empty"] - return sanitize_filename_part(" ".join(words[0:opts.directories_max_prompt_words]), replace_spaces=False) + return sanitize_filename_part(" ".join(words[0:shared.opts.directories_max_prompt_words]), replace_spaces=False) def datetime(self, *args): time_datetime = datetime.datetime.now() - time_format = args[0] if len(args) > 0 and args[0] != "" else self.default_time_format try: time_zone = pytz.timezone(args[1]) if len(args) > 1 else None except pytz.exceptions.UnknownTimeZoneError as _: time_zone = None - time_zone_time = time_datetime.astimezone(time_zone) try: formatted_time = time_zone_time.strftime(time_format) except (ValueError, TypeError) as _: formatted_time = time_zone_time.strftime(self.default_time_format) - return sanitize_filename_part(formatted_time, replace_spaces=False) def apply(self, x): res = '' - for m in re_pattern.finditer(x): text, pattern = m.groups() if pattern is None: res += text continue - pattern_args = [] while True: m = re_pattern_arg.match(pattern) if m is None: break - pattern, arg = m.groups() pattern_args.insert(0, arg) - fun = self.replacements.get(pattern.lower()) if fun is not None: try: @@ -439,28 +386,22 @@ class FilenameGenerator: except Exception as e: replacement = None errors.display(e, 'filename pattern') - if replacement == NOTHING_AND_SKIP_PREVIOUS_TEXT: continue elif replacement is not None: res += text + str(replacement) continue - res += f'{text}[{pattern}]' - return res def get_next_sequence_number(path, basename): """ Determines and returns the next sequence number to use when saving an image in the specified directory. - - The sequence starts at 0. """ result = -1 if basename != '': basename = basename + "-" - prefix_length = len(basename) for p in os.listdir(path): if p.startswith(basename): @@ -469,13 +410,68 @@ def get_next_sequence_number(path, basename): result = max(int(l[0]), result) except ValueError: pass - return result + 1 +def atomically_save_image(): + Image.MAX_IMAGE_PIXELS = None # disable check in Pillow and rely on check below to allow large custom image sizes + while True: + image, filename, extension, params, exifinfo_data, txt_fullfn = save_queue.get() + mp = round(image.width * image.height / 1000000) + if mp > shared.opts.img_max_size_mp: + shared.log.warning(f'Image size: {image.size} excedes {shared.opts.img_max_size_mp} MPixels') + fn = filename + extension + image_format = Image.registered_extensions()[extension] + shared.log.debug(f'Saving image: {image_format} {fn} {image.size}') + # actual save + if image_format == 'PNG': + pnginfo_data = PngImagePlugin.PngInfo() + for k, v in params.pnginfo.items(): + pnginfo_data.add_text(k, str(v)) + image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, pnginfo=pnginfo_data) + elif image_format == 'JPEG': + if image.mode == 'RGBA': + shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost') + image = image.convert("RGB") + elif image.mode == 'I;16': + image = image.point(lambda p: p * 0.0038910505836576).convert("L") + exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) + image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, exif=exif_bytes) + elif image_format == 'WEBP': + if image.mode == 'I;16': + image = image.point(lambda p: p * 0.0038910505836576).convert("RGB") + exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) + image.save(fn, format=image_format, quality=shared.opts.jpeg_quality, lossless=shared.opts.webp_lossless, exif=exif_bytes) + else: + shared.log.warning(f'Unrecognized image format: {extension} attempting save as {image_format}') + image.save(fn, format=image_format, quality=shared.opts.jpeg_quality) + # additional metadata saved in files + if shared.opts.save_txt and len(exifinfo_data) > 0: + with open(txt_fullfn, "w", encoding="utf8") as file: + file.write(exifinfo_data + "\n") + if shared.opts.save_log_fn != '' and len(exifinfo_data) > 0: + try: + with open(shared.opts.save_log_fn, mode='a+', encoding='utf-8') as f: + try: + entries = json.load(f) + except: + entries = [] + f.seek(0) + entries.append({ 'filename': filename, 'time': datetime.datetime.now().isoformat(), 'info': exifinfo_data }) + json.dump(entries, f, indent=4) + del entries + except Exception as e: + shared.log.warning(f'Failed to save log file: {shared.opts.save_log_fn} {e}') + save_queue.task_done() + + +save_queue = queue.Queue() +save_thread = threading.Thread(target=atomically_save_image, daemon=True) +save_thread.start() + + def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', info=None, short_filename=False, no_prompt=False, grid=False, pnginfo_section_name='parameters', p=None, existing_info=None, forced_filename=None, suffix="", save_to_dirs=None): """Save an image. - Args: image (`PIL.Image`): The image to be saved. @@ -499,7 +495,6 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i If specified, `basename` and filename pattern will be ignored. save_to_dirs (bool): If true, the image will be saved into a subdirectory of `path`. - Returns: (fullfn, txt_fullfn) fullfn (`str`): The full path of the saved imaged. @@ -507,23 +502,25 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i If a text file is saved for this image, this will be its full path. Otherwise None. """ namegen = FilenameGenerator(p, seed, prompt, image) - + if image is None: + shared.log.warning('Image is none') + return None, None if path is None: # set default path to avoid errors when functions are triggered manually or via api and param is not set - path = opts.outdir_save + path = shared.opts.outdir_save if save_to_dirs is None: - save_to_dirs = (grid and opts.grid_save_to_dirs) or (not grid and opts.save_to_dirs and not no_prompt) + save_to_dirs = (grid and shared.opts.grid_save_to_dirs) or (not grid and shared.opts.save_to_dirs and not no_prompt) if save_to_dirs: - dirname = namegen.apply(opts.directories_filename_pattern or "[prompt_words]").lstrip(' ').rstrip('\\ /') + dirname = namegen.apply(shared.opts.directories_filename_pattern or "[prompt_words]").lstrip(' ').rstrip('\\ /') path = os.path.join(path, dirname) os.makedirs(path, exist_ok=True) if forced_filename is None: if short_filename or seed is None: file_decoration = "" - elif opts.save_to_dirs: - file_decoration = opts.samples_filename_pattern or "[seed]" + elif shared.opts.save_to_dirs: + file_decoration = shared.opts.samples_filename_pattern or "[seed]" else: - file_decoration = opts.samples_filename_pattern or "[seed]-[prompt_spaces]" - add_number = opts.save_images_add_number or file_decoration == '' + file_decoration = shared.opts.samples_filename_pattern or "[seed]-[prompt_spaces]" + add_number = shared.opts.save_images_add_number or file_decoration == '' if file_decoration != "" and add_number: file_decoration = "-" + file_decoration file_decoration = namegen.apply(file_decoration) + suffix @@ -544,63 +541,25 @@ def save_image(image, path, basename, seed=None, prompt=None, extension='jpg', i pnginfo[pnginfo_section_name] = info params = script_callbacks.ImageSaveParams(image, p, fullfn, pnginfo) script_callbacks.before_image_saved_callback(params) - image = params.image - fullfn = params.filename exifinfo_data = params.pnginfo.get('UserComment', '') if len(exifinfo_data) > 0: exifinfo_data = exifinfo_data + ', ' + params.pnginfo.get(pnginfo_section_name, '') else: exifinfo_data = params.pnginfo.get(pnginfo_section_name, '') - - def atomically_save_image(image: Image, basename: str, extension: str): - Image.MAX_IMAGE_PIXELS = None # disable check in Pillow and rely on check below to allow large custom image sizes - mp = round(image.width * image.height / 1000000) - if mp > shared.opts.img_max_size_mp: - shared.log.warning(f'Image size: {image.size} excedes {shared.opts.img_max_size_mp} MPixels') - fn = basename + extension - image_format = Image.registered_extensions()[extension] - log.debug(f'Saving image: {image_format} {fn} {image.size}') - - if image_format == 'PNG': - pnginfo_data = PngImagePlugin.PngInfo() - for k, v in params.pnginfo.items(): - pnginfo_data.add_text(k, str(v)) - image.save(fn, format=image_format, quality=opts.jpeg_quality, pnginfo=pnginfo_data) - elif image_format == 'JPEG': - if image.mode == 'RGBA': - shared.log.warning('Saving RGBA image as JPEG: Alpha channel will be lost') - image = image.convert("RGB") - elif image.mode == 'I;16': - image = image.point(lambda p: p * 0.0038910505836576).convert("L") - exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) - image.save(fn, format=image_format, quality=opts.jpeg_quality, exif=exif_bytes) - elif image_format == 'WEBP': - if image.mode == 'I;16': - image = image.point(lambda p: p * 0.0038910505836576).convert("RGB") - exif_bytes = piexif.dump({ "Exif": { piexif.ExifIFD.UserComment: piexif.helper.UserComment.dump(exifinfo_data or "", encoding="unicode") } }) - image.save(fn, format=image_format, quality=opts.jpeg_quality, lossless=opts.webp_lossless, exif=exif_bytes) - else: - shared.log.warning(f'Unrecognized image format: {extension} attempting save as {image_format}') - image.save(fn, format=image_format, quality=opts.jpeg_quality) - filename, extension = os.path.splitext(params.filename) if hasattr(os, 'statvfs'): max_name_len = os.statvfs(path).f_namemax filename = filename[:max_name_len - max(4, len(extension))] params.filename = filename + extension - fullfn = params.filename - atomically_save_image(image, filename, extension) + txt_fullfn = f"{filename}.txt" if shared.opts.save_txt and len(exifinfo_data) > 0 else None - image.already_saved_as = fullfn - if opts.save_txt and len(exifinfo_data) > 0: - txt_fullfn = f"{filename}.txt" - with open(txt_fullfn, "w", encoding="utf8") as file: - file.write(exifinfo_data + "\n") - else: - txt_fullfn = None + save_queue.put((params.image, filename, extension, params, exifinfo_data, txt_fullfn)) + save_queue.join() + # atomically_save_image(params.image, filename, extension, params, exifinfo_data, txt_fullfn) + params.image.already_saved_as = params.filename script_callbacks.image_saved_callback(params) - return fullfn, txt_fullfn + return params.filename, txt_fullfn def safe_decode_string(s: bytes): diff --git a/modules/processing.py b/modules/processing.py index 32a7f4bb3..bd46390a0 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -538,7 +538,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: assert len(p.prompt) > 0 else: assert p.prompt is not None - # devices.torch_gc() # TODO: gc seed = get_fixed_seed(p.seed) subseed = get_fixed_seed(p.subseed) modules.sd_hijack.model_hijack.apply_circular(p.tiling) diff --git a/modules/shared.py b/modules/shared.py index 8e627f19d..9f1c8cc1e 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -286,6 +286,7 @@ options_templates.update(options_section(('saving-images', "Image options"), { "grid_prevent_empty_spots": OptionInfo(True, "Prevent empty spots in grid (when set to autodetect)"), "n_rows": OptionInfo(-1, "Grid row count; use -1 for autodetect and 0 for it to be same as batch size", gr.Slider, {"minimum": -1, "maximum": 16, "step": 1}), "save_txt": OptionInfo(False, "Create a text file next to every image with generation parameters"), + "save_log_fn": OptionInfo("", "Create a log file with image information for each saved image", component_args=hide_dirs), "save_images_before_face_restoration": OptionInfo(False, "Save a copy of image before doing face restoration"), "save_images_before_highres_fix": OptionInfo(False, "Save a copy of image before applying highres fix"), "save_images_before_color_correction": OptionInfo(False, "Save a copy of image before applying color correction to img2img results"), @@ -334,6 +335,7 @@ options_templates.update(options_section(('cuda', "Compute Settings"), { "cuda_compile_mode": OptionInfo("none", "Model compile mode (experimental)", gr.Radio, lambda: {"choices": ['none', 'inductor', 'cudagraphs', 'aot_ts_nvfuser', 'hidet']}), "cuda_compile_verbose": OptionInfo(True, "Model compile verbose mode"), "cuda_compile_errors": OptionInfo(True, "Model compile suppress errors"), + "disable_gc": OptionInfo(False, "Disable Torch memory garbage collection (experimental)"), })) options_templates.update(options_section(('upscaling', "Upscaling"), { @@ -594,10 +596,10 @@ class Options: opts = Options() -cmd_opts = cmd_args.compatibility_args(opts, cmd_opts) config_filename = cmd_opts.config if os.path.exists(config_filename): opts.load(config_filename) +cmd_opts = cmd_args.compatibility_args(opts, cmd_opts) os.makedirs(opts.hypernetwork_dir, exist_ok=True) prompt_styles = modules.styles.StyleDatabase(opts.styles_dir) diff --git a/modules/ui_common.py b/modules/ui_common.py index c14f9be75..a7d4601e2 100644 --- a/modules/ui_common.py +++ b/modules/ui_common.py @@ -2,10 +2,8 @@ import json import html import os import platform -import subprocess as sp - +import subprocess import gradio as gr - from modules import call_queue, shared from modules.generation_parameters_copypaste import image_from_url_text import modules.images @@ -31,7 +29,8 @@ def plaintext_to_html(text): def save_files(js_data, images, do_make_zip, index): - import csv + if js_data is None or len(js_data) == 0: + return filenames = [] fullfns = [] @@ -43,45 +42,32 @@ def save_files(js_data, images, do_make_zip, index): setattr(self, key, value) data = json.loads(js_data) - p = MyObject(data) path = shared.opts.outdir_save save_to_dirs = shared.opts.use_save_to_dirs_for_ui extension: str = shared.opts.samples_format start_index = 0 - if index > -1 and shared.opts.save_selected_only and (index >= data["index_of_first_image"]): # ensures we are looking at a specific non-grid picture, and we have save_selected_only - images = [images[index]] start_index = index - os.makedirs(shared.opts.outdir_save, exist_ok=True) - - with open(os.path.join(shared.opts.outdir_save, "log.csv"), "a", encoding="utf8", newline='') as file: - at_start = file.tell() == 0 - writer = csv.writer(file) - if at_start: - writer.writerow(["prompt", "seed", "width", "height", "sampler", "cfgs", "steps", "filename", "negative_prompt"]) - for image_index, filedata in enumerate(images, start_index): - image = image_from_url_text(filedata) - is_grid = image_index < p.index_of_first_image # pylint: disable=no-member - i = 0 if is_grid else (image_index - p.index_of_first_image) # pylint: disable=no-member - if len(p.all_seeds) <= i: # pylint: disable=no-member - p.all_seeds.append(p.seed) # pylint: disable=no-member - if len(p.all_prompts) <= i: # pylint: disable=no-member - p.all_prompts.append(p.prompt) # pylint: disable=no-member - fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs) # pylint: disable=no-member - - filename = os.path.relpath(fullfn, path) - filenames.append(filename) - fullfns.append(fullfn) - if txt_fullfn: - filenames.append(os.path.basename(txt_fullfn)) - fullfns.append(txt_fullfn) - - writer.writerow([data["prompt"], data["seed"], data["width"], data["height"], data["sampler_name"], data["cfg_scale"], data["steps"], filenames[0], data["negative_prompt"]]) - - # Make Zip + for image_index, filedata in enumerate(images, start_index): + image = image_from_url_text(filedata) + is_grid = image_index < p.index_of_first_image # pylint: disable=no-member + i = 0 if is_grid else (image_index - p.index_of_first_image) # pylint: disable=no-member + if len(p.all_seeds) <= i: # pylint: disable=no-member + p.all_seeds.append(p.seed) # pylint: disable=no-member + if len(p.all_prompts) <= i: # pylint: disable=no-member + p.all_prompts.append(p.prompt) # pylint: disable=no-member + fullfn, txt_fullfn = modules.images.save_image(image, path, "", seed=p.all_seeds[i], prompt=p.all_prompts[i], extension=extension, info=p.infotexts[image_index], grid=is_grid, p=p, save_to_dirs=save_to_dirs) # pylint: disable=no-member + if fullfn is None: + continue + filename = os.path.relpath(fullfn, path) + filenames.append(filename) + fullfns.append(fullfn) + if txt_fullfn: + filenames.append(os.path.basename(txt_fullfn)) + fullfns.append(txt_fullfn) if do_make_zip: zip_filepath = os.path.join(path, "images.zip") from zipfile import ZipFile @@ -90,7 +76,7 @@ def save_files(js_data, images, do_make_zip, index): with open(fullfns[i], mode="rb") as f: zip_file.writestr(filenames[i], f.read()) fullfns.insert(0, zip_filepath) - return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0]}") + return gr.File.update(value=fullfns, visible=True), plaintext_to_html(f"Saved: {filenames[0] if len(filenames) > 0 else 'none'}") def create_output_panel(tabname, outdir): @@ -109,11 +95,11 @@ def create_output_panel(tabname, outdir): if platform.system() == "Windows": os.startfile(path) # pylint: disable=no-member elif platform.system() == "Darwin": - sp.Popen(["open", path]) + subprocess.Popen(["open", path]) elif "microsoft-standard-WSL2" in platform.uname().release: - sp.Popen(["wsl-open", path]) + subprocess.Popen(["wsl-open", path]) else: - sp.Popen(["xdg-open", path]) + subprocess.Popen(["xdg-open", path]) with gr.Column(variant='panel', elem_id=f"{tabname}_results"): with gr.Group(elem_id=f"{tabname}_gallery_container"): diff --git a/modules/ui_tempdir.py b/modules/ui_tempdir.py index e913e2429..7e5849ba5 100644 --- a/modules/ui_tempdir.py +++ b/modules/ui_tempdir.py @@ -11,18 +11,28 @@ Savedfile = namedtuple("Savedfile", ["name"]) def register_tmp_file(gradio, filename): - if hasattr(gradio, 'temp_file_sets'): # gradio 3.15 + if hasattr(gradio, 'temp_file_sets'): gradio.temp_file_sets[0] = gradio.temp_file_sets[0] | {os.path.abspath(filename)} - if hasattr(gradio, 'temp_dirs'): # gradio 3.9 - gradio.temp_dirs = gradio.temp_dirs | {os.path.abspath(os.path.dirname(filename))} def check_tmp_file(gradio, filename): + ok = False if hasattr(gradio, 'temp_file_sets'): - return any([filename in fileset for fileset in gradio.temp_file_sets]) - if hasattr(gradio, 'temp_dirs'): - return any(Path(temp_dir).resolve() in Path(filename).resolve().parents for temp_dir in gradio.temp_dirs) - return False + ok = ok or any([filename in fileset for fileset in gradio.temp_file_sets]) + if shared.opts.outdir_samples != '': + ok = ok or Path(shared.opts.outdir_samples).resolve() in Path(filename).resolve().parents + else: + ok = ok or Path(shared.opts.outdir_txt2img_samples).resolve() in Path(filename).resolve().parents + ok = ok or Path(shared.opts.outdir_img2img_samples).resolve() in Path(filename).resolve().parents + ok = ok or Path(shared.opts.outdir_extras_samples).resolve() in Path(filename).resolve().parents + if shared.opts.outdir_grids != '': + ok = ok or Path(shared.opts.outdir_grids).resolve() in Path(filename).resolve().parents + else: + ok = ok or Path(shared.opts.outdir_txt2img_grids).resolve() in Path(filename).resolve().parents + ok = ok or Path(shared.opts.outdir_img2img_grids).resolve() in Path(filename).resolve().parents + ok = ok or Path(shared.opts.outdir_save).resolve() in Path(filename).resolve().parents + ok = ok or Path(shared.opts.outdir_init_images).resolve() in Path(filename).resolve().parents + return ok def save_pil_to_file(pil_image, dir=None): # pylint: disable=redefined-builtin