From b8055d1527b5eeed871d20487bf8bfaef01ff112 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Thu, 26 Sep 2024 09:24:08 -0400 Subject: [PATCH] fix xyz grid --- cli/image-exif.py | 1 - modules/processing.py | 12 ++++-- modules/sd_hijack.py | 4 +- scripts/xyz_grid.py | 11 ++--- scripts/xyz_grid_draw.py | 29 ++++++------- scripts/xyz_grid_on.py | 90 +++++++++++++++++----------------------- 6 files changed, 69 insertions(+), 78 deletions(-) diff --git a/cli/image-exif.py b/cli/image-exif.py index 10fb4010f..2e3754241 100755 --- a/cli/image-exif.py +++ b/cli/image-exif.py @@ -100,7 +100,6 @@ def read_exif(filename: str): from pi_heif import register_heif_opener register_heif_opener() try: - print('HERE', filename) image = Image.open(filename) exif = Exif(image) print('image:', filename, 'format:', image) diff --git a/modules/processing.py b/modules/processing.py index fb7bc0752..6e85999d0 100644 --- a/modules/processing.py +++ b/modules/processing.py @@ -312,7 +312,10 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: samples = None timer.process.record('init') if p.scripts is not None and isinstance(p.scripts, scripts.ScriptRunner): - samples = p.scripts.process_images(p) + processed = p.scripts.process_images(p) + if processed is not None: + samples = processed.images + infotexts = processed.infotexts if samples is None: if not shared.native: from modules.processing_original import process_original @@ -342,7 +345,11 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: for i, sample in enumerate(samples): debug(f'Processing result: index={i+1}/{len(samples)} iteration={n+1}/{p.n_iter}') p.batch_index = i - info = create_infotext(p, p.prompts, p.seeds, p.subseeds, index=i, all_negative_prompts=p.negative_prompts) + if len(infotexts) > i: + info = infotexts[i] + else: + info = create_infotext(p, p.prompts, p.seeds, p.subseeds, index=i, all_negative_prompts=p.negative_prompts) + infotexts.append(info) if type(sample) == Image.Image: image = sample sample = np.array(sample) @@ -372,7 +379,6 @@ def process_images_inner(p: StableDiffusionProcessing) -> Processed: image = apply_color_correction(p.color_corrections[i], image) if shared.opts.mask_apply_overlay: image = apply_overlay(image, p.paste_to, i, p.overlay_images) - infotexts.append(info) image.info["parameters"] = info output_images.append(image) if shared.opts.samples_save and not p.do_not_save_samples and p.outpath_samples is not None: diff --git a/modules/sd_hijack.py b/modules/sd_hijack.py index 15e6dba7c..8ddd1f8ec 100644 --- a/modules/sd_hijack.py +++ b/modules/sd_hijack.py @@ -338,7 +338,7 @@ ldm.modules.distributions.distributions.DiagonalGaussianDistribution.sample = la # Upcast BF16 to FP32 original_fft_fftn = torch.fft.fftn @wraps(torch.fft.fftn) -def fft_fftn(input, s=None, dim=None, norm=None, *, out=None): +def fft_fftn(input, s=None, dim=None, norm=None, *, out=None): # pylint: disable=redefined-builtin return_dtype = input.dtype if input.dtype == torch.bfloat16: input = input.to(dtype=torch.float32) @@ -348,7 +348,7 @@ def fft_fftn(input, s=None, dim=None, norm=None, *, out=None): # Upcast BF16 to FP32 original_fft_ifftn = torch.fft.ifftn @wraps(torch.fft.ifftn) -def fft_ifftn(input, s=None, dim=None, norm=None, *, out=None): +def fft_ifftn(input, s=None, dim=None, norm=None, *, out=None): # pylint: disable=redefined-builtin return_dtype = input.dtype if input.dtype == torch.bfloat16: input = input.to(dtype=torch.float32) diff --git a/scripts/xyz_grid.py b/scripts/xyz_grid.py index 6350240bd..98c3fd8e5 100644 --- a/scripts/xyz_grid.py +++ b/scripts/xyz_grid.py @@ -265,11 +265,11 @@ class Script(scripts.Script): y_opt.apply(pc, y, ys) z_opt.apply(pc, z, zs) try: - res = processing.process_images(pc) + processed = processing.process_images(pc) except Exception as e: shared.log.error(f"XYZ grid: Failed to process image: {e}") errors.display(e, 'XYZ grid') - res = None + processed = None subgrid_index = 1 + iz # Sets subgrid infotexts if grid_infotext[subgrid_index] is None and ix == 0 and iy == 0: pc.extra_generation_params = copy(pc.extra_generation_params) @@ -284,7 +284,7 @@ class Script(scripts.Script): pc.extra_generation_params["Y Values"] = y_values if y_opt.label in ["[Param] Seed", "[Param] Variation seed"] and not no_fixed_seeds: pc.extra_generation_params["Fixed Y Values"] = ", ".join([str(y) for y in ys]) - grid_infotext[subgrid_index] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(x_values)}x{len(y_values)}') + grid_infotext[subgrid_index] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(xs)}x{len(ys)}') if grid_infotext[0] is None and ix == 0 and iy == 0 and iz == 0: # Sets main grid infotext pc.extra_generation_params = copy(pc.extra_generation_params) if z_opt.label != 'Nothing': @@ -292,8 +292,9 @@ class Script(scripts.Script): pc.extra_generation_params["Z Values"] = z_values if z_opt.label in ["[Param] Seed", "[Param] Variation seed"] and not no_fixed_seeds: pc.extra_generation_params["Fixed Z Values"] = ", ".join([str(z) for z in zs]) - grid_infotext[0] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(z_values)}x{len(x_values)}x{len(y_values)}') - return res + grid_text = f'{len(zs)}x{len(xs)}x{len(ys)}' if len(zs) > 0 else f'{len(xs)}x{len(ys)}' + grid_infotext[0] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=grid_text) + return processed with SharedSettingsStackHelper(): processed = draw_xyz_grid( diff --git a/scripts/xyz_grid_draw.py b/scripts/xyz_grid_draw.py index c56c50b4a..8f95e696b 100644 --- a/scripts/xyz_grid_draw.py +++ b/scripts/xyz_grid_draw.py @@ -85,29 +85,26 @@ def draw_xyz_grid(p, xs, ys, zs, x_labels, y_labels, z_labels, cell, draw_legend t1 = time.time() grid = None - for i in range(len(zs)): - start_index = (i * len(xs) * len(ys)) + i - end_index = start_index + len(xs) * len(ys) - to_process = processed_result.images[start_index:end_index] + for i in range(len(zs)): # create grid + idx0 = (i * len(xs) * len(ys)) + i # starting index of images in subgrid + idx1 = (len(xs) * len(ys)) + idx0 # ending index of images in subgrid + to_process = processed_result.images[idx0:idx1] w, h = max(i.width for i in to_process), max(i.height for i in to_process) if (not no_grid or include_sub_grids) and images.check_grid_size(to_process): grid = images.image_grid(to_process, rows=len(ys)) if draw_legend: grid = images.draw_grid_annotations(grid, w, h, hor_texts, ver_texts, margin_size, title=title_texts[i]) processed_result.images.insert(i, grid) - processed_result.all_prompts.insert(i, processed_result.all_prompts[start_index]) - processed_result.all_seeds.insert(i, processed_result.all_seeds[start_index]) - processed_result.infotexts.insert(i, processed_result.infotexts[start_index]) - t2 = time.time() - shared.log.info(f'XYZ grid complete: images={list_size} size={grid.size if grid is not None else None} time={t1-t0:.2f} save={t2-t1:.2f}') - """ - if not no_grid and images.check_grid_size(processed_result.images[:z_count]): - z_grid = images.image_grid(processed_result.images[:z_count], rows=1) - if draw_legend: - z_grid = images.draw_grid_annotations(z_grid, w, h, [[images.GridAnnotation()] for _ in z_labels], [[images.GridAnnotation()]]) - processed_result.images.insert(0, z_grid) + processed_result.all_prompts.insert(i, processed_result.all_prompts[idx0]) + processed_result.all_seeds.insert(i, processed_result.all_seeds[idx0]) + processed_result.infotexts.insert(i, processed_result.infotexts[idx0]) + if len(zs) > 1 and not no_grid and images.check_grid_size(processed_result.images[:len(zs)]): # create grid-of-grids + grid = images.image_grid(processed_result.images[:len(zs)], rows=1) + processed_result.images.insert(0, grid) processed_result.all_prompts.insert(0, processed_result.all_prompts[0]) processed_result.all_seeds.insert(0, processed_result.all_seeds[0]) processed_result.infotexts.insert(0, processed_result.infotexts[0]) - """ + + t2 = time.time() + shared.log.info(f'XYZ grid complete: images={list_size} size={grid.size if grid is not None else None} time={t1-t0:.2f} save={t2-t1:.2f}') return processed_result diff --git a/scripts/xyz_grid_on.py b/scripts/xyz_grid_on.py index 0ba29ae3f..cb13acf10 100644 --- a/scripts/xyz_grid_on.py +++ b/scripts/xyz_grid_on.py @@ -54,13 +54,13 @@ class Script(scripts.Script): fill_z_button = ToolButton(value=symbols.fill, elem_id="xyz_grid_fill_z_tool_button", visible=False) with gr.Row(): with gr.Column(): - csv_mode = gr.Checkbox(label='Text inputs', value=False, elem_id=self.elem_id("csv_mode"), container=False) - draw_legend = gr.Checkbox(label='Legend', value=True, elem_id=self.elem_id("draw_legend"), container=False) - no_fixed_seeds = gr.Checkbox(label='Random seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"), container=False) + draw_legend = gr.Checkbox(label='Draw legend', value=True, elem_id=self.elem_id("draw_legend"), container=False) + csv_mode = gr.Checkbox(label='Use text inputs', value=False, elem_id=self.elem_id("csv_mode"), container=False) + no_fixed_seeds = gr.Checkbox(label='Use random seeds', value=False, elem_id=self.elem_id("no_fixed_seeds"), container=False) with gr.Column(): - no_grid = gr.Checkbox(label='Skip grid', value=False, elem_id=self.elem_id("no_xyz_grid"), container=False) - include_lone_images = gr.Checkbox(label='Sub-images', value=False, elem_id=self.elem_id("include_lone_images"), container=False) - include_sub_grids = gr.Checkbox(label='Sub-grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False) + include_grid = gr.Checkbox(label='Create main grid', value=True, elem_id=self.elem_id("no_xyz_grid"), container=False) + include_subgrids = gr.Checkbox(label='Create partial grids', value=False, elem_id=self.elem_id("include_sub_grids"), container=False) + include_images = gr.Checkbox(label='Include images', value=False, elem_id=self.elem_id("include_lone_images"), container=False) with gr.Row(): margin_size = gr.Slider(label="Grid margins", minimum=0, maximum=500, value=0, step=2, elem_id=self.elem_id("margin_size")) with gr.Row(): @@ -139,14 +139,14 @@ class Script(scripts.Script): (z_values_dropdown, lambda params:get_dropdown_update_from_params("Z",params)), ) - return [enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, no_grid, include_lone_images, include_sub_grids, margin_size] + return [enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size] - def process(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, no_grid, include_lone_images, include_sub_grids, margin_size): # pylint: disable=W0221 + def process(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size): # pylint: disable=W0221 global active, cache # pylint: disable=W0603 if not enabled or active: return + cache = None active = True - shared.log.debug(f'XYZ grid: x_type={x_type}|x_values={x_values}|x_values_dropdown={x_values_dropdown}|y_type={y_type}|{y_values}={y_values}|{y_values_dropdown}={y_values_dropdown}|z_type={z_type}|z_values={z_values}|z_values_dropdown={z_values_dropdown}|draw_legend={draw_legend}|include_lone_images={include_lone_images}|include_sub_grids={include_sub_grids}|no_grid={no_grid}|margin_size={margin_size}') if not no_fixed_seeds: processing.fix_seed(p) if not shared.opts.return_grid: @@ -267,7 +267,7 @@ class Script(scripts.Script): second_axes_processed = 'x' else: second_axes_processed = 'y' - grid_infotext = [None] * (1 + len(zs)) + grid_infotext = [] def cell(x, y, z, ix, iy, iz): if shared.state.interrupted: @@ -279,13 +279,12 @@ class Script(scripts.Script): y_opt.apply(pc, y, ys) z_opt.apply(pc, z, zs) try: - res = processing.process_images(pc) + processed = processing.process_images(pc) except Exception as e: shared.log.error(f"XYZ grid: Failed to process image: {e}") errors.display(e, 'XYZ grid') - res = None - subgrid_index = 1 + iz # Sets subgrid infotexts - if grid_infotext[subgrid_index] is None and ix == 0 and iy == 0: + processed = None + if ix == 0 and iy == 0: # create subgrid info text pc.extra_generation_params = copy(pc.extra_generation_params) pc.extra_generation_params['Script'] = self.title() if x_opt.label != 'Nothing': @@ -298,17 +297,18 @@ class Script(scripts.Script): pc.extra_generation_params["Y Values"] = y_values if y_opt.label in ["[Param] Seed", "[Param] Variation seed"] and not no_fixed_seeds: pc.extra_generation_params["Fixed Y Values"] = ", ".join([str(y) for y in ys]) - grid_infotext[subgrid_index] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(x_values)}x{len(y_values)}') - if grid_infotext[0] is None and ix == 0 and iy == 0 and iz == 0: # Sets main grid infotext + info = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(xs)}x{len(ys)}') + grid_infotext.append(info) + if ix == 0 and iy == 0 and iz == 0 and len(zs) > 1: # create main grid info text pc.extra_generation_params = copy(pc.extra_generation_params) if z_opt.label != 'Nothing': pc.extra_generation_params["Z Type"] = z_opt.label pc.extra_generation_params["Z Values"] = z_values if z_opt.label in ["[Param] Seed", "[Param] Variation seed"] and not no_fixed_seeds: pc.extra_generation_params["Fixed Z Values"] = ", ".join([str(z) for z in zs]) - grid_text = f'{len(z_values)}x{len(x_values)}x{len(y_values)}' if len(z_values) > 0 else f'{len(x_values)}x{len(y_values)}' - grid_infotext[0] = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=grid_text) - return res + info = processing.create_infotext(pc, pc.all_prompts, pc.all_seeds, pc.all_subseeds, grid=f'{len(zs)}x{len(xs)}x{len(ys)}') + grid_infotext.insert(0, info) + return processed with SharedSettingsStackHelper(): processed = draw_xyz_grid( @@ -321,51 +321,39 @@ class Script(scripts.Script): z_labels=[z_opt.format_value(p, z_opt, z) for z in zs], cell=cell, draw_legend=draw_legend, - include_lone_images=include_lone_images, - include_sub_grids=include_sub_grids, + include_lone_images=include_images, + include_sub_grids=include_subgrids, first_axes_processed=first_axes_processed, second_axes_processed=second_axes_processed, margin_size=margin_size, - no_grid=no_grid, + no_grid=not include_grid, ) if not processed.images: active = False return processed # It broke, no further handling needed. - z_count = len(zs) - processed.infotexts[:1+z_count] = grid_infotext[:1+z_count] # Set the grid infotexts to the real ones with extra_generation_params (1 main grid + z_count sub-grids) - if not include_lone_images: - # TODO broken logic to delete sub-images - if no_grid and include_sub_grids: - processed.images = processed.images[:z_count] # we don't have the main grid image, and need zero additional sub-images - else: - processed.images = processed.images[:z_count+1] # we either have the main grid image, or need one sub-images - if shared.opts.grid_save: # Auto-save main and sub-grids: - grid_count = z_count + (1 if not no_grid and z_count > 1 else 0) - for g in range(grid_count): - adj_g = g-1 if g > 0 else g - info = processed.infotexts[g] - prompt = processed.all_prompts[adj_g] - seed = processed.all_seeds[adj_g] - _fn, _txt, _exif = images.save_image(processed.images[g], p.outpath_grids, "grid", info=info, extension=shared.opts.grid_format, prompt=prompt, seed=seed, grid=True, p=processed) - # TODO broken logic to delete sub-grids - if not include_sub_grids: # Done with sub-grids, drop all related information: - for _sg in range(z_count): + # images stucture: main-grid, sub-grid1, sub-grid2, ..., image-1, image-2, ... + z_count = len(processed.images) - (len(zs) * len(ys) * len(xs)) # how many grids are there: main grid + sub-grids + processed.infotexts[:z_count] = grid_infotext[:z_count] # replace grid info texts + if not include_images: + processed.images = processed.images[:z_count] + if shared.opts.grid_save: # auto-save main and sub-grids: + for i in range(z_count): + info = processed.infotexts[i] + prompt = processed.all_prompts[i] + seed = processed.all_seeds[i] + _fn, _txt, _exif = images.save_image(processed.images[i], p.outpath_grids, "grid", info=info, extension=shared.opts.grid_format, prompt=prompt, seed=seed, grid=True, p=processed) + if not include_subgrids and z_count > 1: # delete sub-grids + for _sg in range(z_count - 1): del processed.images[1] del processed.all_prompts[1] del processed.all_seeds[1] del processed.infotexts[1] - elif no_grid: - del processed.infotexts[0] + p.do_not_save_grid = True + p.do_not_save_samples = True active = False cache = processed - # TODO main processing loop auto-creates grid out of all returned images so we end up with grid of grids + images which we don't need, need to skip that return processed - def process_images(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, no_grid, include_lone_images, include_sub_grids, margin_size): # pylint: disable=W0221, W0613 - global cache # pylint: disable=W0603 - if cache is not None and hasattr(cache, 'images'): - samples = cache.images.copy() - cache = None - return samples - return None + def process_images(self, p, enabled, x_type, x_values, x_values_dropdown, y_type, y_values, y_values_dropdown, z_type, z_values, z_values_dropdown, csv_mode, draw_legend, no_fixed_seeds, include_grid, include_subgrids, include_images, margin_size): # pylint: disable=W0221, W0613 + return cache