Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add metadata to images #1940

Merged
merged 71 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
c5a15c7
feat: add metadata logging for images
mashb1t Jan 15, 2024
8d56318
feat: add config and checkbox for save_metadata_to_images
mashb1t Jan 15, 2024
493e484
feat: add argument disable_metadata
mashb1t Jan 15, 2024
191f814
feat: add support for A1111 metadata schema
mashb1t Jan 15, 2024
f7489cc
feat: add model hash support for a1111
mashb1t Jan 15, 2024
1a52367
feat: use resolved prompts with included expansion and styles for a11…
mashb1t Jan 15, 2024
6662381
fix: code cleanup and resolved prompt fixes
mashb1t Jan 15, 2024
7b9deb1
feat: add config metadata_created_by
mashb1t Jan 15, 2024
cd65f21
fix: use stting isntead of quote wrap for A1111 created_by
mashb1t Jan 15, 2024
a2153db
fix: correctlyy hide/show metadata schema on app start
mashb1t Jan 15, 2024
80ad0d0
fix: do not generate hashes when arg --disable-metadata is used
mashb1t Jan 15, 2024
ba5d0b6
refactor: rename metadata_schema to metadata_scheme
mashb1t Jan 15, 2024
3812228
Merge branch 'main_upstream' into feature/add-metadata-to-files
mashb1t Jan 25, 2024
510b587
fix: use pnginfo "parameters" insteadf of "Comments"
mashb1t Jan 25, 2024
20b7978
feat: add resolved prompts to metadata
mashb1t Jan 25, 2024
051faf7
fix: use correct default value in metadata check for created_by
mashb1t Jan 25, 2024
f301031
wip: add metadata mapping, reading and writing
mashb1t Jan 27, 2024
ee21c2b
feat: rename metadata tab and import button label
mashb1t Jan 28, 2024
e19596c
feat: map basic information for scheme A1111
mashb1t Jan 28, 2024
7ddd4e5
wip: optimize handling for metadata in Gradio calls
mashb1t Jan 28, 2024
cbc63eb
feat: add enums for Performance, Steps and StepsUOV
mashb1t Jan 28, 2024
5dcb2bc
fix: correctly map resolution, use empty styles for A1111
mashb1t Jan 28, 2024
2362789
chore: code cleanup
mashb1t Jan 28, 2024
5e84a45
feat: add A1111 prompt style detection
mashb1t Jan 28, 2024
f94b96f
wip: add prompt style extraction for A1111 scheme
mashb1t Jan 29, 2024
13d0341
feat: sort styles after metadata import
mashb1t Jan 29, 2024
c3ab9f1
refactor: use central flag for LoRA count
mashb1t Jan 29, 2024
20e5302
refactor: use central flag for ControlNet image count
mashb1t Jan 29, 2024
c80011b
fix: use correct LoRA mapping, add fallback for backwards compatibility
mashb1t Jan 29, 2024
7fefe3a
feat: add created_by again
mashb1t Jan 29, 2024
33d644f
feat: add prefix "Fooocus" to version
mashb1t Jan 29, 2024
e388f6f
wip: code cleanup, update todos
mashb1t Jan 29, 2024
2656356
fix: use correct order to read LoRA in meta parser
mashb1t Jan 29, 2024
e541097
wip: code cleanup, update todos
mashb1t Jan 29, 2024
89c8e3a
feat: make sha256 with length 10 default
mashb1t Jan 29, 2024
78d1ad3
feat: add lora handling to A1111 scheme
mashb1t Jan 29, 2024
dcc4874
feat: override existing LoRA values when importing, would cause image…
mashb1t Jan 29, 2024
6939f79
fix: correctly extract prompt style when only prompt expansion is sel…
mashb1t Jan 29, 2024
5811234
feat: allow model / LoRA loading from subfolders
mashb1t Jan 29, 2024
e93a345
feat: code cleanup, do not queue metadata preview on image upload
mashb1t Jan 29, 2024
7772eb7
refactor: add flag for refiner_swap_method
mashb1t Jan 31, 2024
9bdb65e
feat: add metadata handling for all non-img2img parameters
mashb1t Jan 31, 2024
6b9c0bd
refactor: code cleanup
mashb1t Jan 31, 2024
23ba050
chore: use str as return type in calculate_sha256
mashb1t Feb 2, 2024
bc9b625
feat: add hash cache to metadata
mashb1t Feb 2, 2024
ea6839b
chore: code cleanup
mashb1t Feb 2, 2024
f4afc4a
feat: add method get_scheme to Metadata
mashb1t Feb 2, 2024
796cf3c
fix: align handling for scheme Fooocus by removing lcm lora from json…
mashb1t Feb 2, 2024
e558701
refactor: add step before parsing to set data in parser
mashb1t Feb 2, 2024
f7e24bd
feat: sort metadata attributes before writing to image
mashb1t Feb 2, 2024
934bdb1
feat: add translations and hint for image prompt parameters
mashb1t Feb 2, 2024
b438f7b
chore: check and remove ToDo's
mashb1t Feb 2, 2024
f745d40
refactor: merge metadata.py into meta_parser.py
mashb1t Feb 2, 2024
9aa82aa
fix: add missing refiner in A1111 parse_json
mashb1t Feb 2, 2024
1c3431e
wip: add TODO for ultiline prompt style resolution
mashb1t Feb 2, 2024
349556b
fix: remove sorting for A1111, change performance key position
mashb1t Feb 2, 2024
ed4a958
fix: add workaround for multiline prompts
mashb1t Feb 2, 2024
63403d6
feat: add sampler mapping
mashb1t Feb 2, 2024
1419231
feat: prevent config reset by renaming metadata_scheme to match confi…
mashb1t Feb 3, 2024
8af73e6
chore: remove remaining todos after analysis
mashb1t Feb 3, 2024
c668228
chore: specify too broad exception types
mashb1t Feb 4, 2024
fe33cc7
feat: add mapping for _gpu samplers to cpu samplers
mashb1t Feb 4, 2024
59dd1c2
Merge remote-tracking branch 'upstream/main' into feature/add-metadat…
mashb1t Feb 4, 2024
dfb48fd
feat: add better handling for image import with empty metadata
mashb1t Feb 4, 2024
c104d58
fix: parse adaptive_cfg as float instead of string
mashb1t Feb 4, 2024
832441e
chore: loosen strict type for parse_json, fix indent
mashb1t Feb 4, 2024
36f6715
chore: make steps enums more strict
mashb1t Feb 18, 2024
f93dd6e
feat: only override steps if metadata value is not in steps enum or i…
mashb1t Feb 18, 2024
692a2e4
Merge branch 'feature/add-metadata-to-files' of github.com:mashb1t/Fo…
mashb1t Feb 18, 2024
0b77db9
Merge branch 'develop' into feature/add-metadata-to-files
mashb1t Feb 26, 2024
65dae45
fix: handle empty strings in metadata
mashb1t Feb 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion args_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
help="Prevent writing images and logs to hard drive.")

args_parser.parser.add_argument("--disable-analytics", action='store_true',
help="Disables analytics for Gradio", default=False)
help="Disables analytics for Gradio.")

args_parser.parser.add_argument("--disable-metadata", action='store_true',
help="Disables saving metadata to images.")

args_parser.parser.add_argument("--disable-preset-download", action='store_true',
help="Disables downloading models for presets", default=False)
Expand Down
9 changes: 8 additions & 1 deletion language/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -374,5 +374,12 @@
"* Powered by Fooocus Inpaint Engine (beta)": "* Powered by Fooocus Inpaint Engine (beta)",
"Fooocus Enhance": "Fooocus Enhance",
"Fooocus Cinematic": "Fooocus Cinematic",
"Fooocus Sharp": "Fooocus Sharp"
"Fooocus Sharp": "Fooocus Sharp",
"Drag any image generated by Fooocus here": "Drag any image generated by Fooocus here",
"Metadata": "Metadata",
"Apply Metadata": "Apply Metadata",
"Metadata Scheme": "Metadata Scheme",
"Image Prompt parameters are not included. Use a1111 for compatibility with Civitai.": "Image Prompt parameters are not included. Use a1111 for compatibility with Civitai.",
"fooocus (json)": "fooocus (json)",
"a1111 (plain text)": "a1111 (plain text)"
}
101 changes: 55 additions & 46 deletions modules/async_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self, args):
def worker():
global async_tasks

import os
import traceback
import math
import numpy as np
Expand All @@ -39,13 +40,16 @@ def worker():
import extras.ip_adapter as ip_adapter
import extras.face_crop
import fooocus_version
import args_manager

from modules.sdxl_styles import apply_style, apply_wildcards, fooocus_expansion, apply_arrays
from modules.private_logger import log
from extras.expansion import safe_str
from modules.util import remove_empty_str, HWC3, resize_image, \
get_image_shape_ceil, set_image_shape_ceil, get_shape_ceil, resample_image, erode_or_dilate, ordinal_suffix
from modules.upscaler import perform_upscale
from modules.flags import Performance
from modules.meta_parser import get_metadata_parser, MetadataScheme

pid = os.getpid()
print(f'Started worker with PID {pid}')
Expand Down Expand Up @@ -135,7 +139,7 @@ def handler(async_task):
prompt = args.pop()
negative_prompt = args.pop()
style_selections = args.pop()
performance_selection = args.pop()
performance_selection = Performance(args.pop())
aspect_ratios_selection = args.pop()
image_number = args.pop()
image_seed = args.pop()
Expand All @@ -153,6 +157,7 @@ def handler(async_task):
inpaint_input_image = args.pop()
inpaint_additional_prompt = args.pop()
inpaint_mask_image_upload = args.pop()

disable_preview = args.pop()
disable_intermediate_results = args.pop()
disable_seed_increment = args.pop()
Expand Down Expand Up @@ -190,8 +195,11 @@ def handler(async_task):
invert_mask_checkbox = args.pop()
inpaint_erode_or_dilate = args.pop()

save_metadata_to_images = args.pop() if not args_manager.args.disable_metadata else False
metadata_scheme = MetadataScheme(args.pop()) if not args_manager.args.disable_metadata else MetadataScheme.FOOOCUS

cn_tasks = {x: [] for x in flags.ip_list}
for _ in range(4):
for _ in range(flags.controlnet_image_count):
cn_img = args.pop()
cn_stop = args.pop()
cn_weight = args.pop()
Expand All @@ -216,17 +224,9 @@ def handler(async_task):
print(f'Refiner disabled because base model and refiner are same.')
refiner_model_name = 'None'

assert performance_selection in ['Speed', 'Quality', 'Extreme Speed']

steps = 30

if performance_selection == 'Speed':
steps = 30

if performance_selection == 'Quality':
steps = 60
steps = performance_selection.steps()

if performance_selection == 'Extreme Speed':
if performance_selection == Performance.EXTREME_SPEED:
print('Enter LCM mode.')
progressbar(async_task, 1, 'Downloading LCM components ...')
loras += [(modules.config.downloading_sdxl_lcm_lora(), 1.0)]
Expand All @@ -244,7 +244,6 @@ def handler(async_task):
adm_scaler_positive = 1.0
adm_scaler_negative = 1.0
adm_scaler_end = 0.0
steps = 8

print(f'[Parameters] Adaptive CFG = {adaptive_cfg}')
print(f'[Parameters] Sharpness = {sharpness}')
Expand Down Expand Up @@ -305,16 +304,7 @@ def handler(async_task):
if 'fast' in uov_method:
skip_prompt_processing = True
else:
steps = 18

if performance_selection == 'Speed':
steps = 18

if performance_selection == 'Quality':
steps = 36

if performance_selection == 'Extreme Speed':
steps = 8
steps = performance_selection.steps_uov()

progressbar(async_task, 1, 'Downloading upscale models ...')
modules.config.downloading_upscale_model()
Expand Down Expand Up @@ -830,31 +820,50 @@ def callback(step, x0, x, total_steps, y):

img_paths = []
for x in imgs:
d = [
('Prompt', task['log_positive_prompt']),
('Negative Prompt', task['log_negative_prompt']),
('Fooocus V2 Expansion', task['expansion']),
('Styles', str(raw_style_selections)),
('Performance', performance_selection),
('Resolution', str((width, height))),
('Sharpness', sharpness),
('Guidance Scale', guidance_scale),
('ADM Guidance', str((
modules.patch.patch_settings[pid].positive_adm_scale,
modules.patch.patch_settings[pid].negative_adm_scale,
modules.patch.patch_settings[pid].adm_scaler_end))),
('Base Model', base_model_name),
('Refiner Model', refiner_model_name),
('Refiner Switch', refiner_switch),
('Sampler', sampler_name),
('Scheduler', scheduler_name),
('Seed', task['task_seed']),
]
d = [('Prompt', 'prompt', task['log_positive_prompt']),
('Negative Prompt', 'negative_prompt', task['log_negative_prompt']),
('Fooocus V2 Expansion', 'prompt_expansion', task['expansion']),
('Styles', 'styles', str(raw_style_selections)),
('Performance', 'performance', performance_selection.value),
('Resolution', 'resolution', str((width, height))),
('Guidance Scale', 'guidance_scale', guidance_scale),
('Sharpness', 'sharpness', sharpness),
('ADM Guidance', 'adm_guidance', str((
modules.patch.patch_settings[pid].positive_adm_scale,
modules.patch.patch_settings[pid].negative_adm_scale,
modules.patch.patch_settings[pid].adm_scaler_end))),
('Base Model', 'base_model', base_model_name),
('Refiner Model', 'refiner_model', refiner_model_name),
('Refiner Switch', 'refiner_switch', refiner_switch)]

if refiner_model_name != 'None':
if overwrite_switch > 0:
d.append(('Overwrite Switch', 'overwrite_switch', overwrite_switch))
if refiner_swap_method != flags.refiner_swap_method:
d.append(('Refiner Swap Method', 'refiner_swap_method', refiner_swap_method))
if modules.patch.patch_settings[pid].adaptive_cfg != modules.config.default_cfg_tsnr:
d.append(('CFG Mimicking from TSNR', 'adaptive_cfg', modules.patch.patch_settings[pid].adaptive_cfg))

d.append(('Sampler', 'sampler', sampler_name))
d.append(('Scheduler', 'scheduler', scheduler_name))
d.append(('Seed', 'seed', task['task_seed']))

if freeu_enabled:
d.append(('FreeU', 'freeu', str((freeu_b1, freeu_b2, freeu_s1, freeu_s2))))

metadata_parser = None
if save_metadata_to_images:
metadata_parser = modules.meta_parser.get_metadata_parser(metadata_scheme)
metadata_parser.set_data(task['log_positive_prompt'], task['positive'],
task['log_negative_prompt'], task['negative'],
steps, base_model_name, refiner_model_name, loras)

for li, (n, w) in enumerate(loras):
if n != 'None':
d.append((f'LoRA {li + 1}', f'{n} : {w}'))
d.append(('Version', 'v' + fooocus_version.version))
img_paths.append(log(x, d))
d.append((f'LoRA {li + 1}', f'lora_combined_{li + 1}', f'{n} : {w}'))

d.append(('Version', 'version', 'Fooocus v' + fooocus_version.version))
img_paths.append(log(x, d, metadata_parser))

yield_result(async_task, img_paths, do_not_show_finished_images=len(tasks) == 1 or disable_intermediate_results)
except ldm_patched.modules.model_management.InterruptProcessingException as e:
Expand Down
22 changes: 19 additions & 3 deletions modules/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from modules.model_loader import load_file_from_url
from modules.util import get_files_from_folder, makedirs_with_log

from modules.flags import Performance, MetadataScheme

config_path = os.path.abspath("./config.txt")
config_example_path = os.path.abspath("config_modification_tutorial.txt")
Expand Down Expand Up @@ -293,8 +293,8 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
)
default_performance = get_config_item_or_set_default(
key='default_performance',
default_value='Speed',
validator=lambda x: x in modules.flags.performance_selections
default_value=Performance.SPEED.value,
validator=lambda x: x in Performance.list()
)
default_advanced_checkbox = get_config_item_or_set_default(
key='default_advanced_checkbox',
Expand Down Expand Up @@ -369,6 +369,21 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
],
validator=lambda x: isinstance(x, list) and all(isinstance(v, str) for v in x)
)
default_save_metadata_to_images = get_config_item_or_set_default(
key='default_save_metadata_to_images',
default_value=False,
validator=lambda x: isinstance(x, bool)
)
default_metadata_scheme = get_config_item_or_set_default(
key='default_metadata_scheme',
default_value=MetadataScheme.FOOOCUS.value,
validator=lambda x: x in [y[1] for y in modules.flags.metadata_scheme if y[1] == x]
)
metadata_created_by = get_config_item_or_set_default(
key='metadata_created_by',
default_value='',
validator=lambda x: isinstance(x, str)
)

example_inpaint_prompts = [[x] for x in example_inpaint_prompts]

Expand All @@ -391,6 +406,7 @@ def get_config_item_or_set_default(key, default_value, validator, disable_empty_
"default_prompt_negative",
"default_styles",
"default_aspect_ratio",
"default_save_metadata_to_images",
"checkpoint_downloads",
"embeddings_downloads",
"lora_downloads",
Expand Down
91 changes: 85 additions & 6 deletions modules/flags.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import IntEnum, Enum

disabled = 'Disabled'
enabled = 'Enabled'
subtle_variation = 'Vary (Subtle)'
Expand All @@ -10,16 +12,49 @@
disabled, subtle_variation, strong_variation, upscale_15, upscale_2, upscale_fast
]

KSAMPLER_NAMES = ["euler", "euler_ancestral", "heun", "heunpp2","dpm_2", "dpm_2_ancestral",
"lms", "dpm_fast", "dpm_adaptive", "dpmpp_2s_ancestral", "dpmpp_sde", "dpmpp_sde_gpu",
"dpmpp_2m", "dpmpp_2m_sde", "dpmpp_2m_sde_gpu", "dpmpp_3m_sde", "dpmpp_3m_sde_gpu", "ddpm", "lcm"]
CIVITAI_NO_KARRAS = ["euler", "euler_ancestral", "heun", "dpm_fast", "dpm_adaptive", "ddim", "uni_pc"]

# fooocus: a1111 (Civitai)
KSAMPLER = {
"euler": "Euler",
"euler_ancestral": "Euler a",
"heun": "Heun",
"heunpp2": "",
"dpm_2": "DPM2",
"dpm_2_ancestral": "DPM2 a",
"lms": "LMS",
"dpm_fast": "DPM fast",
"dpm_adaptive": "DPM adaptive",
"dpmpp_2s_ancestral": "DPM++ 2S a",
"dpmpp_sde": "DPM++ SDE",
"dpmpp_sde_gpu": "DPM++ SDE",
"dpmpp_2m": "DPM++ 2M",
"dpmpp_2m_sde": "DPM++ 2M SDE",
"dpmpp_2m_sde_gpu": "DPM++ 2M SDE",
"dpmpp_3m_sde": "",
"dpmpp_3m_sde_gpu": "",
"ddpm": "",
"lcm": "LCM"
}

SAMPLER_EXTRA = {
"ddim": "DDIM",
"uni_pc": "UniPC",
"uni_pc_bh2": ""
}

SAMPLERS = KSAMPLER | SAMPLER_EXTRA

KSAMPLER_NAMES = list(KSAMPLER.keys())

SCHEDULER_NAMES = ["normal", "karras", "exponential", "sgm_uniform", "simple", "ddim_uniform", "lcm", "turbo"]
SAMPLER_NAMES = KSAMPLER_NAMES + ["ddim", "uni_pc", "uni_pc_bh2"]
SAMPLER_NAMES = KSAMPLER_NAMES + list(SAMPLER_EXTRA.keys())

sampler_list = SAMPLER_NAMES
scheduler_list = SCHEDULER_NAMES

refiner_swap_method = 'joint'

cn_ip = "ImagePrompt"
cn_ip_face = "FaceSwap"
cn_canny = "PyraCanny"
Expand All @@ -33,12 +68,56 @@
} # stop, weight

inpaint_engine_versions = ['None', 'v1', 'v2.5', 'v2.6']
performance_selections = ['Speed', 'Quality', 'Extreme Speed']

inpaint_option_default = 'Inpaint or Outpaint (default)'
inpaint_option_detail = 'Improve Detail (face, hand, eyes, etc.)'
inpaint_option_modify = 'Modify Content (add objects, change background, etc.)'
inpaint_options = [inpaint_option_default, inpaint_option_detail, inpaint_option_modify]

desc_type_photo = 'Photograph'
desc_type_anime = 'Art/Anime'


class MetadataScheme(Enum):
FOOOCUS = 'fooocus'
A1111 = 'a1111'


metadata_scheme = [
(f'{MetadataScheme.FOOOCUS.value} (json)', MetadataScheme.FOOOCUS.value),
(f'{MetadataScheme.A1111.value} (plain text)', MetadataScheme.A1111.value),
]

lora_count = 5

controlnet_image_count = 4


class Steps(IntEnum):
QUALITY = 60
SPEED = 30
EXTREME_SPEED = 8


class StepsUOV(IntEnum):
QUALITY = 36
SPEED = 18
EXTREME_SPEED = 8


class Performance(Enum):
QUALITY = 'Quality'
SPEED = 'Speed'
EXTREME_SPEED = 'Extreme Speed'

@classmethod
def list(cls) -> list:
return list(map(lambda c: c.value, cls))

def steps(self) -> int | None:
return Steps[self.name].value if Steps[self.name] else None

def steps_uov(self) -> int | None:
return StepsUOV[self.name].value if Steps[self.name] else None


performance_selections = Performance.list()
Loading