* feature: added flag, config and ui update for image extension change #1789 * moved function to config module * moved image extension to webui via async worker. Passing as parameter to log and get_current_html_path functions per feedback * check flag before displaying image extension radio button * disabled if image log flag is passed in * fix: add missing image_extension parameter to log call * refactor: change label * feat: add webp to image_extensions supported image extemsions: see https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html * feat: use consistent file name in gradio returns and uses filepaths instead of numpy image by saving to temp dir uses double the temp dir file storage on disk as it saves to temp dir and gradio temp dir when displaying the image, but reuses logged output image * feat: delete temp images after yielding to gradio * feat: use args temp path if given * chore: code cleanup, remove redundant if statement * feat: always show image_extension element this is now possible due to image extension support in gradio via https://github.com/lllyasviel/Fooocus/pull/1932 * refactor: rename image_extension to image_file_extension * feat: use optimized jpg parameters when saving the image quality=95 optimize=True progressive=True * refactor: rename image_file_extension to output_format * feat: add exif handling * refactor: code cleanup, remove items from metadata output --------- Co-authored-by: Manuel Schmid <dev@mash1t.de> Co-authored-by: Manuel Schmid <9307310+mashb1t@users.noreply.github.com> Co-authored-by: Manuel Schmid <manuel.schmid@odt.net> Co-authored by: eddyizm <wtfisup@hotmail.com>
131 lines
5.8 KiB
Python
131 lines
5.8 KiB
Python
import os
|
|
import args_manager
|
|
import modules.config
|
|
import json
|
|
import urllib.parse
|
|
|
|
from PIL import Image
|
|
from PIL.PngImagePlugin import PngInfo
|
|
from modules.util import generate_temp_filename
|
|
from modules.meta_parser import MetadataParser, get_exif
|
|
|
|
log_cache = {}
|
|
|
|
|
|
def get_current_html_path(output_format=None):
|
|
output_format = output_format if output_format else modules.config.default_output_format
|
|
date_string, local_temp_filename, only_name = generate_temp_filename(folder=modules.config.path_outputs,
|
|
extension=output_format)
|
|
html_name = os.path.join(os.path.dirname(local_temp_filename), 'log.html')
|
|
return html_name
|
|
|
|
|
|
def log(img, metadata, metadata_parser: MetadataParser | None = None, output_format=None) -> str:
|
|
path_outputs = args_manager.args.temp_path if args_manager.args.disable_image_log else modules.config.path_outputs
|
|
output_format = output_format if output_format else modules.config.default_output_format
|
|
date_string, local_temp_filename, only_name = generate_temp_filename(folder=path_outputs, extension=output_format)
|
|
os.makedirs(os.path.dirname(local_temp_filename), exist_ok=True)
|
|
|
|
parsed_parameters = metadata_parser.parse_string(metadata) if metadata_parser is not None else ''
|
|
image = Image.fromarray(img)
|
|
|
|
if output_format == 'png':
|
|
if parsed_parameters != '':
|
|
pnginfo = PngInfo()
|
|
pnginfo.add_text('parameters', parsed_parameters)
|
|
pnginfo.add_text('fooocus_scheme', metadata_parser.get_scheme().value)
|
|
else:
|
|
pnginfo = None
|
|
image.save(local_temp_filename, pnginfo=pnginfo)
|
|
elif output_format == 'jpg':
|
|
image.save(local_temp_filename, quality=95, optimize=True, progressive=True, exif=get_exif(parsed_parameters, metadata_parser.get_scheme().value) if metadata_parser else Image.Exif())
|
|
elif output_format == 'webp':
|
|
image.save(local_temp_filename, quality=95, lossless=False, exif=get_exif(parsed_parameters, metadata_parser.get_scheme().value) if metadata_parser else Image.Exif())
|
|
else:
|
|
image.save(local_temp_filename)
|
|
|
|
if args_manager.args.disable_image_log:
|
|
return local_temp_filename
|
|
|
|
html_name = os.path.join(os.path.dirname(local_temp_filename), 'log.html')
|
|
|
|
css_styles = (
|
|
"<style>"
|
|
"body { background-color: #121212; color: #E0E0E0; } "
|
|
"a { color: #BB86FC; } "
|
|
".metadata { border-collapse: collapse; width: 100%; } "
|
|
".metadata .label { width: 15%; } "
|
|
".metadata .value { width: 85%; font-weight: bold; } "
|
|
".metadata th, .metadata td { border: 1px solid #4d4d4d; padding: 4px; } "
|
|
".image-container img { height: auto; max-width: 512px; display: block; padding-right:10px; } "
|
|
".image-container div { text-align: center; padding: 4px; } "
|
|
"hr { border-color: gray; } "
|
|
"button { background-color: black; color: white; border: 1px solid grey; border-radius: 5px; padding: 5px 10px; text-align: center; display: inline-block; font-size: 16px; cursor: pointer; }"
|
|
"button:hover {background-color: grey; color: black;}"
|
|
"</style>"
|
|
)
|
|
|
|
js = (
|
|
"""<script>
|
|
function to_clipboard(txt) {
|
|
txt = decodeURIComponent(txt);
|
|
if (navigator.clipboard && navigator.permissions) {
|
|
navigator.clipboard.writeText(txt)
|
|
} else {
|
|
const textArea = document.createElement('textArea')
|
|
textArea.value = txt
|
|
textArea.style.width = 0
|
|
textArea.style.position = 'fixed'
|
|
textArea.style.left = '-999px'
|
|
textArea.style.top = '10px'
|
|
textArea.setAttribute('readonly', 'readonly')
|
|
document.body.appendChild(textArea)
|
|
|
|
textArea.select()
|
|
document.execCommand('copy')
|
|
document.body.removeChild(textArea)
|
|
}
|
|
alert('Copied to Clipboard!\\nPaste to prompt area to load parameters.\\nCurrent clipboard content is:\\n\\n' + txt);
|
|
}
|
|
</script>"""
|
|
)
|
|
|
|
begin_part = f"<!DOCTYPE html><html><head><title>Fooocus Log {date_string}</title>{css_styles}</head><body>{js}<p>Fooocus Log {date_string} (private)</p>\n<p>All images are clean, without any hidden data/meta, and safe to share with others.</p><!--fooocus-log-split-->\n\n"
|
|
end_part = f'\n<!--fooocus-log-split--></body></html>'
|
|
|
|
middle_part = log_cache.get(html_name, "")
|
|
|
|
if middle_part == "":
|
|
if os.path.exists(html_name):
|
|
existing_split = open(html_name, 'r', encoding='utf-8').read().split('<!--fooocus-log-split-->')
|
|
if len(existing_split) == 3:
|
|
middle_part = existing_split[1]
|
|
else:
|
|
middle_part = existing_split[0]
|
|
|
|
div_name = only_name.replace('.', '_')
|
|
item = f"<div id=\"{div_name}\" class=\"image-container\"><hr><table><tr>\n"
|
|
item += f"<td><a href=\"{only_name}\" target=\"_blank\"><img src='{only_name}' onerror=\"this.closest('.image-container').style.display='none';\" loading='lazy'/></a><div>{only_name}</div></td>"
|
|
item += "<td><table class='metadata'>"
|
|
for label, key, value in metadata:
|
|
value_txt = str(value).replace('\n', ' </br> ')
|
|
item += f"<tr><td class='label'>{label}</td><td class='value'>{value_txt}</td></tr>\n"
|
|
item += "</table>"
|
|
|
|
js_txt = urllib.parse.quote(json.dumps({k: v for _, k, v in metadata}, indent=0), safe='')
|
|
item += f"</br><button onclick=\"to_clipboard('{js_txt}')\">Copy to Clipboard</button>"
|
|
|
|
item += "</td>"
|
|
item += "</tr></table></div>\n\n"
|
|
|
|
middle_part = item + middle_part
|
|
|
|
with open(html_name, 'w', encoding='utf-8') as f:
|
|
f.write(begin_part + middle_part + end_part)
|
|
|
|
print(f'Image generated with private log at: {html_name}')
|
|
|
|
log_cache[html_name] = middle_part
|
|
|
|
return local_temp_filename
|