Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a25b5091c3 | ||
|
|
c4aeb168b6 | ||
|
|
266cef3800 | ||
|
|
18bc84204e |
@@ -1,7 +1,7 @@
|
||||
# 💾 Save Image Extended for ComfyUI
|
||||
Save as AVIF, WebP, JPEG, customize the folder, sub-folders, and filenames of your images!
|
||||
Save as JXL, AVIF, WebP, JPEG, JPEG2000, customize the folder, sub-folders, and filenames of your images!
|
||||
|
||||
Supports those extensions: **AVIF WebP JXL jpg jpeg png gif tiff bmp**
|
||||
Supports those extensions: **JXL AVIF WebP jpg jpeg j2k jp2 png gif tiff bmp**
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/save-image-extended-comfyui-example.png" />
|
||||
@@ -10,7 +10,7 @@ Supports those extensions: **AVIF WebP JXL jpg jpeg png gif tiff bmp**
|
||||
* Customize the folder, sub-folders, and filenames of your images!
|
||||
* Save data about the generated job (sampler, prompts, models) as entries in a `json` (text) file, in each folder.
|
||||
* Use the values of ANY node's widget, by simply adding its badge number in the form _id.widget_name_
|
||||
* Promt saved in jpeg and AVIF, but ComfyUI can only load PNG and WebP atm
|
||||
* Warning: ComfyUI can only load prompts from PNG and WebP atm
|
||||
|
||||
|
||||
<br>
|
||||
@@ -71,17 +71,19 @@ There is a requirements.txt that will take care of that, but just in case:
|
||||
|
||||
- python 10.6
|
||||
- piexif
|
||||
- imagecodecs
|
||||
- pillow
|
||||
- pillow-avif-plugin
|
||||
- pillow-jxl-plugin
|
||||
|
||||
```
|
||||
pip install piexif pillow pillow-avif-plugin
|
||||
```
|
||||
|
||||
For Jpeg XL / jxl it's more complicated. You need to install and compile the wheel jxlpy, and therefore, need a valid and functional MSVC installation.
|
||||
For Jpeg XL / jxl it's more complicated. You cannot compile the wheel jxlpy on Windows. Therefore, we use an alternative: [imagecodecs](https://pypi.org/project/imagecodecs/)
|
||||
|
||||
```
|
||||
pip install jxlpy
|
||||
pip install -U imagecodecs
|
||||
```
|
||||
|
||||
### Manual Download
|
||||
@@ -92,18 +94,35 @@ git clone https://github.com/audioscavenger/save-image-extended-comfyui
|
||||
```
|
||||
|
||||
## Miscelaneous
|
||||
#
|
||||
[JPEG XL is a heated debate on chromium forum](https://issues.chromium.org/issues/40168998#comment85) and if true indeed that Google is working on WebP2, jxl is unlikely to take off any day soon. Proponents arguably declare with no proof, that jxl is better and faster than the current best codec: AVIF. But again, without support from the industry, it's going nowhere.
|
||||
|
||||
I tested with compression 90 and it's good, with a caveat. The compression offered by pillow is 3x lower then Image Magick for the same level. No idea why.
|
||||
|
||||
#
|
||||
Pillow cannot save Exif data in JPEG2000, nor can it compress it in any way. Who the heck is using JPEG2000 in 2024 anyway?
|
||||
|
||||
#
|
||||
Disclaimer: Does not check for illegal characters entered in file or folder names. May not be compatible with every other custom node, depending on changes in the `prompt` object.
|
||||
Tested and working with default samplers, Efficiency nodes, UltimateSDUpscale, ComfyRoll, composer, NegiTools, and 45 other nodes.
|
||||
|
||||
#
|
||||
Quality and compression settings: default is 75, 100 will activate **lossless** for AVIF and WEBP only.
|
||||
Quality and compression settings: default is 90, 100 will activate **lossless** for AVIF and WEBP only.
|
||||
|
||||
Quick comparison of size per extension, for the same picture, AVIF quality=60, WebP quality=75, jpeg quality=91 (and yes, AVIF still looks better then WebP):
|
||||
Quick comparison of size per extension, for the same 512x512 picture, with similar visual quality:
|
||||
| Ext | Compression | Maker | Size |
|
||||
| --- | --- | --- | --- |
|
||||
| png | max 9 | PIL | 413111 |
|
||||
| j2k | n/a | PIL | 395028 |
|
||||
| jxl | lossless | PIL | 301310 |
|
||||
| jxl | 90 | PIL | 179210 |
|
||||
| jpeg | 90 | PIL | 88554 |
|
||||
| avif | 90 | Imagick | 67272 |
|
||||
| webp | 90 | Imagick | 64416 |
|
||||
| webp | 90 | PIL | 64356 |
|
||||
| avif | 60 | PIL | 47353 |
|
||||
| avif | 60 | Imagick | 33691 |
|
||||
|
||||
<p align="center">
|
||||
<img src="assets/save-image-extended-sizes-comparison.png" />
|
||||
</p>
|
||||
|
||||
#
|
||||
About extensions WebP AVIF JPEG JXL: ComfyUI can only load PNG and WebP atm... Feel free to ask ComfyUI team to add support for AVIF/jpeg/JXL!
|
||||
@@ -157,7 +176,12 @@ TODO:
|
||||
- [ ] keep same counter if extension changes?
|
||||
- [ ] files will be out of order if prefixes change... that is expected, but is this what we want?
|
||||
|
||||
### release 2.6.5 💾
|
||||
### release 2.70 💾
|
||||
- added JPEG-XL
|
||||
- added JPEG2000
|
||||
- fixed help popup text color in dark mode
|
||||
|
||||
### release 2.65 💾
|
||||
- published in comfy-registry
|
||||
|
||||
### release 2.64 💾
|
||||
@@ -238,3 +262,10 @@ TODO:
|
||||
|
||||
## :ribbon: Licence
|
||||
[GPL 3.0](https://choosealicense.com/licenses/gpl-3.0/)
|
||||
|
||||
## :beer: Buy me a beer
|
||||
Like my work? This tool helped you? Want to sponsor more awesomeness like this?
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.paypal.com/donate/?hosted_button_id=CD7P7PK3WP8WU"><img src="/assets/paypal-Donate-QR-Code.png" /></a>
|
||||
</p>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
+2
-2
@@ -1,9 +1,9 @@
|
||||
[project]
|
||||
name = "save-image-extended-comfyui"
|
||||
description = "Save as AVIF, WebP, JPEG, customize the folder, sub-folders, and filenames of your images!"
|
||||
version = "2.64"
|
||||
version = "2.70"
|
||||
license = "LICENCE"
|
||||
dependencies = ["piexif", "pillow", "pillow-avif-plugin"]
|
||||
dependencies = ["piexif", "imagecodecs", "pillow", "pillow-avif-plugin", "pillow-jxl-plugin"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/audioscavenger/save-image-extended-comfyui"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
piexif
|
||||
# jpegxl support: imagecodecs will bring libjxl 0.9.0
|
||||
imagecodecs
|
||||
|
||||
pillow
|
||||
pillow-avif-plugin
|
||||
pillow-jxl-plugin
|
||||
|
||||
+34
-26
@@ -11,7 +11,7 @@ import pprint
|
||||
import piexif
|
||||
import piexif.helper
|
||||
|
||||
version = 2.6.5
|
||||
version = 2.70
|
||||
|
||||
avif_supported = False
|
||||
jxl_supported = False
|
||||
@@ -23,20 +23,24 @@ except:
|
||||
print(f"\033[92m[save_image_extended]\033[0m AVIF is not supported. To add it: pip install pillow pillow-avif-plugin\033[0m")
|
||||
pass
|
||||
else:
|
||||
print(f"\033[92m[save_image_extended] AVIF is supported! Woohoo!\033[0m")
|
||||
print(f"\033[92m[save_image_extended] AVIF is supported! Woohoo!\033[0m")
|
||||
avif_supported = True
|
||||
|
||||
# Jxl requires jxlpy wheel to be compiled, and a valid MSVC environment, which is complex task
|
||||
try:
|
||||
from jxlpy import JXLImagePlugin
|
||||
# jxlpy is in early stages of development. None one has ever compiled it on Windows AFAIK
|
||||
# from jxlpy import JXLImagePlugin
|
||||
# from imagecodecs import (jpegxl_encode, jpegxl_decode, jpegxl_check, jpegxl_version, JPEGXL)
|
||||
import pillow_jxl
|
||||
except:
|
||||
print(f"\033[92m[save_image_extended]\033[0m JXL is not supported. To add it: pip install jxlpy\033[0m")
|
||||
print(f"\033[92m[save_image_extended]\033[0m You will need a valid MSVC env to build the wheel\033[0m")
|
||||
pass
|
||||
else:
|
||||
print(f"\033[92m[save_image_extended] JPEGXL is supported! YeePee!\033[0m")
|
||||
jxl_supported = True
|
||||
|
||||
# PIL must be loaded after pillow_avif
|
||||
# PIL must be loaded after pillow plugins
|
||||
from PIL import Image, ExifTags
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
|
||||
@@ -66,11 +70,12 @@ class SaveImageExtended:
|
||||
|
||||
png_compress_level = 9
|
||||
avif_quality = 60
|
||||
webp_quality = 75
|
||||
jpeg_quality = 91
|
||||
jxl_quality = 91
|
||||
webp_quality = 90
|
||||
jpeg_quality = 90
|
||||
jxl_quality = 90
|
||||
j2k_quality = 90
|
||||
# tiff_quality has no impact unless you start meddling with the compressions algorithms
|
||||
tiff_quality = 91
|
||||
tiff_quality = 90
|
||||
# optimize_image only works for jpeg, with like just 2% reduction in size
|
||||
optimize_image = True
|
||||
|
||||
@@ -90,14 +95,15 @@ class SaveImageExtended:
|
||||
image_preview = True
|
||||
extToRemove = ['.safetensors', '.ckpt', '.pt', '.bin', '.pth']
|
||||
output_ext = '.webp'
|
||||
output_exts = ['.webp', '.png', '.jpg', '.jpeg', '.gif', '.tiff', '.bmp']
|
||||
quality = 75
|
||||
output_exts = ['.webp', '.png', '.jpg', '.jpeg', '.j2k', '.jp2', '.gif', '.tiff', '.bmp']
|
||||
quality = 90
|
||||
|
||||
print(f"\033[92m[save_image_extended]\033[0m version: {version}\033[0m")
|
||||
if jxl_supported:
|
||||
output_exts.insert(0, '.jxl')
|
||||
# if pillow_avif not in sys.modules:
|
||||
if avif_supported:
|
||||
# no matter what people say, jxl is far away from being integrated in browsers. I bet on AVIF.
|
||||
output_exts.insert(0, '.avif')
|
||||
|
||||
def __init__(self):
|
||||
@@ -474,7 +480,7 @@ class SaveImageExtended:
|
||||
metadata.update(extra_pnginfo)
|
||||
|
||||
## For Comfy to load an image, it need a PNG tag at the root: Prompt={prompt}
|
||||
## For AVIF WebP Jpeg, it's only Exif that's available... and UserComment is the best choice.
|
||||
## For AVIF WebP Jpeg JXL, it's only Exif that's available... and UserComment is the best choice.
|
||||
|
||||
## This method gives good results, as long as you save the prompt/workflow in 2 separate Exif tags
|
||||
## Otherwise, [ExifTool] will issue Warning: Invalid EXIF text encoding for UserComment
|
||||
@@ -534,34 +540,36 @@ class SaveImageExtended:
|
||||
return exif_dat
|
||||
|
||||
|
||||
def save_image(self, image_path, img, prompt, save_metadata=save_metadata, extra_pnginfo=None, quality=75):
|
||||
def save_image(self, image_path, img, prompt, save_metadata=save_metadata, extra_pnginfo=None, quality=90):
|
||||
# print(f"debug save_images: image_path={image_path}")
|
||||
output_ext = os.path.splitext(os.path.basename(image_path))[1]
|
||||
metadata = None
|
||||
kwargs = dict()
|
||||
|
||||
# TODO: see if convert_hdr_to_8bit=False make a change
|
||||
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
||||
|
||||
# match is python 3.10+
|
||||
if output_ext in ['.avif']:
|
||||
if output_ext in ['.avif', '.webp', '.jxl']:
|
||||
if save_metadata: kwargs["exif"] = self.get_metadata_exif(img, prompt, extra_pnginfo)
|
||||
kwargs["quality"] = quality
|
||||
if quality == 100: kwargs["lossless"] = True
|
||||
elif output_ext in ['.webp']:
|
||||
if quality == 100:
|
||||
kwargs["lossless"] = True
|
||||
else:
|
||||
kwargs["quality"] = quality
|
||||
if output_ext in ['.j2k', '.jp2', '.jpc', '.jpf', '.jpx', '.j2c']:
|
||||
if save_metadata: kwargs["exif"] = self.get_metadata_exif(img, prompt, extra_pnginfo)
|
||||
kwargs["quality"] = quality
|
||||
if quality == 100: kwargs["lossless"] = True
|
||||
if quality < 100:
|
||||
kwargs["irreversible"] = True
|
||||
# there is no such thing as compression level in JPEG2000. Read https://comprimato.com/blog/2017/06/22/bitrate-control-quality-layers-jpeg2000/
|
||||
# kwargs["quality_mode"] = 'rates' or 'dB'
|
||||
# kwargs["quality_layers"] = [0,1,2] no refence online. i tried all values from 0 to 100 and no change in filesize
|
||||
else:
|
||||
kwargs["quality"] = quality
|
||||
elif output_ext in ['.jpg', '.jpeg']:
|
||||
if save_metadata: kwargs["exif"] = self.get_metadata_exif(img, prompt, extra_pnginfo)
|
||||
# https://stackoverflow.com/questions/19303621/why-is-the-quality-of-jpeg-images-produced-by-pil-so-poor
|
||||
kwargs["subsampling"] = 0
|
||||
kwargs["quality"] = quality
|
||||
kwargs["optimize"] = self.optimize_image
|
||||
elif output_ext in ['.jxl']:
|
||||
if save_metadata: kwargs["exif"] = self.get_metadata_exif(img, prompt, extra_pnginfo)
|
||||
kwargs["quality"] = quality
|
||||
if quality == 100: kwargs["lossless"] = True
|
||||
elif output_ext in ['.tiff']:
|
||||
# tiff: i suspect no quality either
|
||||
kwargs["quality"] = quality
|
||||
kwargs["optimize"] = self.optimize_image
|
||||
elif output_ext in ['.png', '.gif']:
|
||||
# png/gif: no quality
|
||||
|
||||
@@ -74,6 +74,7 @@ const create_documentation_stylesheet = () => {
|
||||
|
||||
let styleTag = document.head.querySelector(tag)
|
||||
|
||||
// color: var(--fg-color); will be black even in dark mode... bug but i can't tell where it comes from
|
||||
if (!styleTag) {
|
||||
styleTag = document.createElement('style')
|
||||
styleTag.type = 'text/css'
|
||||
@@ -82,7 +83,7 @@ const create_documentation_stylesheet = () => {
|
||||
.sie-documentation-popup {
|
||||
background: var(--comfy-menu-bg);
|
||||
position: absolute;
|
||||
color: var(--fg-color);
|
||||
color: white;
|
||||
font: 12px monospace;
|
||||
line-height: 1.5em;
|
||||
padding: 10px;
|
||||
|
||||
Reference in New Issue
Block a user