diff --git a/CHANGELOG.md b/CHANGELOG.md index 59573f410..ea76e3fbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - extra networks: add add/remove tags to prompt (e.g. lora activation keywords) - extensions: fix couple of compatibility items - firefox compatibility improvements + - minor image viewer improvements - original - fix hires secondary sampler this now fully obsoletes `fallback_sampler` and `force_latent_sampler` diff --git a/javascript/imageViewer.js b/javascript/imageViewer.js index 917e5a0c4..7ef6d140f 100644 --- a/javascript/imageViewer.js +++ b/javascript/imageViewer.js @@ -1,6 +1,7 @@ // A full size 'lightbox' preview modal shown when left clicking on gallery previews let previewDrag = false; let modalPreviewZone; +let previewInstance; function closeModal(evt, force = false) { if (force) gradioApp().getElementById('lightboxModal').style.display = 'none'; @@ -58,7 +59,11 @@ function showModal(event) { const source = event.target || event.srcElement; const modalImage = gradioApp().getElementById('modalImage'); const lb = gradioApp().getElementById('lightboxModal'); - modalImage.onload = () => modalPreviewZone.focus(); + lb.ownerSVGElement = modalImage; + modalImage.onload = () => { + previewInstance.moveTo(0, 0); + modalPreviewZone.focus(); + }; modalImage.src = source.src; if (modalImage.style.display === 'none') lb.style.setProperty('background-image', `url(${source.src})`); lb.style.display = 'flex'; @@ -111,6 +116,12 @@ function modalTileToggle(event) { event.stopPropagation(); } +function modalResetInstance(event) { + const modalImage = document.getElementById('modalImage'); + previewInstance.dispose(); + previewInstance = panzoom(modalImage, { zoomSpeed: 0.05, minZoom: 0.1, maxZoom: 5.0, filterKey: (/* e, dx, dy, dz */) => true }); +} + let imageViewerInitialized = false; function galleryClickEventHandler(event) { @@ -141,16 +152,14 @@ function initImageViewer() { // main elements const modal = document.createElement('div'); modal.id = 'lightboxModal'; - // modal.addEventListener('keydown', modalKeyHandler, true); modalPreviewZone = document.createElement('div'); modalPreviewZone.className = 'lightboxModalPreviewZone'; const modalImage = document.createElement('img'); modalImage.id = 'modalImage'; - // modalImage.addEventListener('keydown', modalKeyHandler, true); modalPreviewZone.appendChild(modalImage); - panzoom(modalImage, { zoomSpeed: 0.05, minZoom: 0.1, maxZoom: 5.0, filterKey: (/* e, dx, dy, dz */) => true }); + previewInstance = panzoom(modalImage, { zoomSpeed: 0.05, minZoom: 0.1, maxZoom: 5.0, filterKey: (/* e, dx, dy, dz */) => true }); // toolbar const modalZoom = document.createElement('span'); @@ -160,6 +169,13 @@ function initImageViewer() { modalZoom.title = 'Toggle zoomed view'; modalZoom.addEventListener('click', modalZoomToggle, true); + const modalReset = document.createElement('span'); + modalReset.id = 'modal_reset'; + modalReset.className = 'cursor'; + modalReset.innerHTML = '♻️'; + modalReset.title = 'Reset zoomed view'; + modalReset.addEventListener('click', modalResetInstance, true); + const modalTile = document.createElement('span'); modalTile.id = 'modal_tile'; modalTile.className = 'cursor'; @@ -218,6 +234,7 @@ function initImageViewer() { modal.appendChild(modalNext); modal.append(modalControls); modalControls.appendChild(modalZoom); + modalControls.appendChild(modalReset); modalControls.appendChild(modalTile); modalControls.appendChild(modalSave); modalControls.appendChild(modalDownload); diff --git a/modules/processing_diffusers.py b/modules/processing_diffusers.py index 726917808..a6d72b84f 100644 --- a/modules/processing_diffusers.py +++ b/modules/processing_diffusers.py @@ -18,7 +18,7 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro shared.state.current_latent = latents def vae_decode(latents, model, output_type='np'): - if hasattr(model, 'vae'): + if hasattr(model, 'vae' and isinstance(latents, torch.Tensor)): shared.log.debug(f'Diffusers VAE decode: name={model.vae.config.get("_name_or_path", "default")} upcast={model.vae.config.get("force_upcast", None)}') decoded = model.vae.decode(latents / model.vae.config.scaling_factor, return_dict=False)[0] imgs = model.image_processor.postprocess(decoded, output_type=output_type) @@ -95,7 +95,12 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro # from modules.prompt_parser import parse_prompt_attention # parsed_prompt = [parse_prompt_attention(prompt) for prompt in prompts] - shared.sd_model.to(devices.device) + if shared.state.interrupted or shared.state.skipped: + return results + + if shared.opts.diffusers_move_base: + shared.sd_model.to(devices.device) + pipe_args = set_pipeline_args( model=shared.sd_model, prompt=prompts, @@ -128,8 +133,9 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro images.save_image(decoded[i], path=p.outpath_samples, basename="", seed=seeds[i], prompt=prompts[i], extension=shared.opts.samples_format, info=info, p=p, suffix="-before-refiner") if shared.opts.diffusers_move_base: - shared.log.debug('Moving base model to CPU') + shared.log.debug('Diffusers: Moving base model to CPU') shared.sd_model.to('cpu') + devices.torch_gc() if (not hasattr(shared.sd_refiner.scheduler, 'name')) or (shared.sd_refiner.scheduler.name != p.latent_sampler) and (p.sampler_name != 'Default'): sampler = sd_samplers.all_samplers_map.get(p.latent_sampler, None) @@ -140,8 +146,8 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro if shared.state.interrupted or shared.state.skipped: return results - shared.sd_refiner.to(devices.device) - devices.torch_gc() + if shared.opts.diffusers_move_refiner: + shared.sd_refiner.to(devices.device) for i in range(len(output.images)): pipe_args = set_pipeline_args( @@ -157,17 +163,15 @@ def process_diffusers(p: StableDiffusionProcessing, seeds, prompts, negative_pro denoising_start=p.refiner_denoise_start, denoising_end=p.refiner_denoise_end, image=output.images[i], - output_type='latent' if hasattr(shared.sd_model, 'vae') else 'np', + output_type='latent' if hasattr(shared.sd_refiner, 'vae') else 'np', ) output = shared.sd_refiner(**pipe_args) # pylint: disable=not-callable - if shared.state.interrupted or shared.state.skipped: - return results - - output.images = vae_decode(output.images, shared.sd_model) - results.append(output.images[0]) + if not shared.state.interrupted and not shared.state.skipped: + output.images = vae_decode(output.images, shared.sd_refiner) + results.append(output.images[i]) if shared.opts.diffusers_move_refiner: - shared.log.debug('Moving refiner model to CPU') + shared.log.debug('Diffusers: Moving refiner model to CPU') shared.sd_refiner.to('cpu') else: results = output.images