Skip to content

Commit 5d9f24d

Browse files
committed
Merge branch 'main' into lstein/chore/v6.10.0
2 parents ace1234 + dbb4a07 commit 5d9f24d

61 files changed

Lines changed: 2498 additions & 182 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/CODEOWNERS

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
/mkdocs.yml @lstein @blessedcoolant
77

88
# nodes
9-
/invokeai/app/ @blessedcoolant @lstein
9+
/invokeai/app/ @blessedcoolant @lstein @dunkeroni @JPPhoto
1010

1111
# installation and configuration
1212
/pyproject.toml @lstein @blessedcoolant
@@ -18,15 +18,15 @@
1818
/invokeai/version @lstein @blessedcoolant
1919

2020
# web ui
21-
/invokeai/frontend @blessedcoolant @lstein
21+
/invokeai/frontend @blessedcoolant @lstein @dunkeroni
2222

2323
# generation, model management, postprocessing
24-
/invokeai/backend @lstein @blessedcoolant
24+
/invokeai/backend @lstein @blessedcoolant @dunkeroni @JPPhoto
2525

2626
# front ends
2727
/invokeai/frontend/CLI @lstein
2828
/invokeai/frontend/install @lstein
2929
/invokeai/frontend/merge @lstein @blessedcoolant
3030
/invokeai/frontend/training @lstein @blessedcoolant
31-
/invokeai/frontend/web @blessedcoolant @lstein
31+
/invokeai/frontend/web @blessedcoolant @lstein @dunkeroni @Pfannkuchensack
3232

.github/workflows/mkdocs-material.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ jobs:
2222

2323
steps:
2424
- name: checkout
25-
uses: actions/checkout@v4
25+
uses: actions/checkout@v5
2626

2727
- name: setup python
28-
uses: actions/setup-python@v5
28+
uses: actions/setup-python@v6
2929
with:
30-
python-version: '3.10'
30+
python-version: '3.12'
3131
cache: pip
3232
cache-dependency-path: pyproject.toml
3333

docs/installation/quick_start.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ Hardware requirements vary significantly depending on model and image output siz
3131
- Memory: At least 32GB RAM.
3232
- Disk: 10GB for base installation plus 200GB for models.
3333

34+
=== "Z-Image Turbo - 1024x1024"
35+
- GPU: Nvidia 20xx series or later, 8GB+ VRAM for the Q4_K quantized model. 16GB+ needed for the Q8 or BF16 models.
36+
- Memory: At least 16GB RAM.
37+
- Disk: 10GB for base installation plus 35GB for models.
38+
39+
3440
More detail on system requirements can be found [here](./requirements.md).
3541

3642
## Step 2: Download and Set Up the Launcher

docs/installation/requirements.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ The requirements below are rough guidelines for best performance. GPUs with less
3131
- Memory: At least 32GB RAM.
3232
- Disk: 10GB for base installation plus 200GB for models.
3333

34+
=== "Z-Image Turbo - 1024x1024"
35+
- GPU: Nvidia 20xx series or later, 8GB+ VRAM for the Q4_K quantized model. 16GB+ needed for the Q8 or BF16 models.
36+
- Memory: At least 16GB RAM.
37+
- Disk: 10GB for base installation plus 35GB for models.
38+
3439
!!! info "`tmpfs` on Linux"
3540

3641
If your temporary directory is mounted as a `tmpfs`, ensure it has sufficient space.

invokeai/app/invocations/flux_denoise.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
pack,
4848
unpack,
4949
)
50+
from invokeai.backend.flux.schedulers import FLUX_SCHEDULER_LABELS, FLUX_SCHEDULER_MAP, FLUX_SCHEDULER_NAME_VALUES
5051
from invokeai.backend.flux.text_conditioning import FluxReduxConditioning, FluxTextConditioning
5152
from invokeai.backend.model_manager.taxonomy import BaseModelType, FluxVariantType, ModelFormat, ModelType
5253
from invokeai.backend.patches.layer_patcher import LayerPatcher
@@ -63,7 +64,7 @@
6364
title="FLUX Denoise",
6465
tags=["image", "flux"],
6566
category="image",
66-
version="4.1.0",
67+
version="4.2.0",
6768
)
6869
class FluxDenoiseInvocation(BaseInvocation):
6970
"""Run denoising process with a FLUX transformer model."""
@@ -132,6 +133,12 @@ class FluxDenoiseInvocation(BaseInvocation):
132133
num_steps: int = InputField(
133134
default=4, description="Number of diffusion steps. Recommended values are schnell: 4, dev: 50."
134135
)
136+
scheduler: FLUX_SCHEDULER_NAME_VALUES = InputField(
137+
default="euler",
138+
description="Scheduler (sampler) for the denoising process. 'euler' is fast and standard. "
139+
"'heun' is 2nd-order (better quality, 2x slower). 'lcm' is optimized for few steps.",
140+
ui_choice_labels=FLUX_SCHEDULER_LABELS,
141+
)
135142
guidance: float = InputField(
136143
default=4.0,
137144
description="The guidance strength. Higher values adhere more strictly to the prompt, and will produce less diverse images. FLUX dev only, ignored for schnell.",
@@ -242,6 +249,12 @@ def _run_diffusion(
242249
shift=not is_schnell,
243250
)
244251

252+
# Create scheduler if not using default euler
253+
scheduler = None
254+
if self.scheduler in FLUX_SCHEDULER_MAP:
255+
scheduler_class = FLUX_SCHEDULER_MAP[self.scheduler]
256+
scheduler = scheduler_class(num_train_timesteps=1000)
257+
245258
# Clip the timesteps schedule based on denoising_start and denoising_end.
246259
timesteps = clip_timestep_schedule_fractional(timesteps, self.denoising_start, self.denoising_end)
247260

@@ -426,6 +439,7 @@ def _run_diffusion(
426439
img_cond=img_cond,
427440
img_cond_seq=img_cond_seq,
428441
img_cond_seq_ids=img_cond_seq_ids,
442+
scheduler=scheduler,
429443
)
430444

431445
x = unpack(x.float(), self.height, self.width)

invokeai/app/invocations/metadata_linked.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
)
5353
from invokeai.app.invocations.scheduler import SchedulerOutput
5454
from invokeai.app.invocations.t2i_adapter import T2IAdapterField, T2IAdapterInvocation
55+
from invokeai.app.invocations.z_image_denoise import ZImageDenoiseInvocation
5556
from invokeai.app.services.shared.invocation_context import InvocationContext
5657
from invokeai.backend.model_manager.taxonomy import BaseModelType, ModelType, SubModelType
5758
from invokeai.backend.stable_diffusion.schedulers.schedulers import SCHEDULER_NAME_VALUES
@@ -729,6 +730,52 @@ def _loras_to_json(obj: Union[Any, list[Any]]):
729730
return LatentsMetaOutput(**params, metadata=MetadataField.model_validate(md))
730731

731732

733+
@invocation(
734+
"z_image_denoise_meta",
735+
title=f"{ZImageDenoiseInvocation.UIConfig.title} + Metadata",
736+
tags=["z-image", "latents", "denoise", "txt2img", "t2i", "t2l", "img2img", "i2i", "l2l"],
737+
category="latents",
738+
version="1.0.0",
739+
)
740+
class ZImageDenoiseMetaInvocation(ZImageDenoiseInvocation, WithMetadata):
741+
"""Run denoising process with a Z-Image transformer model + metadata."""
742+
743+
def invoke(self, context: InvocationContext) -> LatentsMetaOutput:
744+
def _loras_to_json(obj: Union[Any, list[Any]]):
745+
if not isinstance(obj, list):
746+
obj = [obj]
747+
748+
output: list[dict[str, Any]] = []
749+
for item in obj:
750+
output.append(
751+
LoRAMetadataField(
752+
model=item.lora,
753+
weight=item.weight,
754+
).model_dump(exclude_none=True, exclude={"id", "type", "is_intermediate", "use_cache"})
755+
)
756+
return output
757+
758+
obj = super().invoke(context)
759+
760+
md: Dict[str, Any] = {} if self.metadata is None else self.metadata.root
761+
md.update({"width": obj.width})
762+
md.update({"height": obj.height})
763+
md.update({"steps": self.steps})
764+
md.update({"guidance": self.guidance_scale})
765+
md.update({"denoising_start": self.denoising_start})
766+
md.update({"denoising_end": self.denoising_end})
767+
md.update({"scheduler": self.scheduler})
768+
md.update({"model": self.transformer.transformer})
769+
md.update({"seed": self.seed})
770+
if len(self.transformer.loras) > 0:
771+
md.update({"loras": _loras_to_json(self.transformer.loras)})
772+
773+
params = obj.__dict__.copy()
774+
del params["type"]
775+
776+
return LatentsMetaOutput(**params, metadata=MetadataField.model_validate(md))
777+
778+
732779
@invocation(
733780
"metadata_to_vae",
734781
title="Metadata To VAE",
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pathlib
2+
from typing import Literal
3+
4+
from invokeai.app.invocations.baseinvocation import BaseInvocation, BaseInvocationOutput, invocation, invocation_output
5+
from invokeai.app.invocations.fields import ImageField, InputField, OutputField, WithBoard, WithMetadata
6+
from invokeai.app.services.shared.invocation_context import InvocationContext
7+
from invokeai.backend.image_util.pbr_maps.architecture.pbr_rrdb_net import PBR_RRDB_Net
8+
from invokeai.backend.image_util.pbr_maps.pbr_maps import NORMAL_MAP_MODEL, OTHER_MAP_MODEL, PBRMapsGenerator
9+
from invokeai.backend.util.devices import TorchDevice
10+
11+
12+
@invocation_output("pbr_maps-output")
13+
class PBRMapsOutput(BaseInvocationOutput):
14+
normal_map: ImageField = OutputField(default=None, description="The generated normal map")
15+
roughness_map: ImageField = OutputField(default=None, description="The generated roughness map")
16+
displacement_map: ImageField = OutputField(default=None, description="The generated displacement map")
17+
18+
19+
@invocation("pbr_maps", title="PBR Maps", tags=["image", "material"], category="image", version="1.0.0")
20+
class PBRMapsInvocation(BaseInvocation, WithMetadata, WithBoard):
21+
"""Generate Normal, Displacement and Roughness Map from a given image"""
22+
23+
image: ImageField = InputField(description="Input image")
24+
tile_size: int = InputField(default=512, description="Tile size")
25+
border_mode: Literal["none", "seamless", "mirror", "replicate"] = InputField(
26+
default="none", description="Border mode to apply to eliminate any artifacts or seams"
27+
)
28+
29+
def invoke(self, context: InvocationContext) -> PBRMapsOutput:
30+
image_pil = context.images.get_pil(self.image.image_name, mode="RGB")
31+
32+
def loader(model_path: pathlib.Path):
33+
return PBRMapsGenerator.load_model(model_path, TorchDevice.choose_torch_device())
34+
35+
torch_device = TorchDevice.choose_torch_device()
36+
37+
with (
38+
context.models.load_remote_model(NORMAL_MAP_MODEL, loader) as normal_map_model,
39+
context.models.load_remote_model(OTHER_MAP_MODEL, loader) as other_map_model,
40+
):
41+
assert isinstance(normal_map_model, PBR_RRDB_Net)
42+
assert isinstance(other_map_model, PBR_RRDB_Net)
43+
pbr_pipeline = PBRMapsGenerator(normal_map_model, other_map_model, torch_device)
44+
normal_map, roughness_map, displacement_map = pbr_pipeline.generate_maps(
45+
image_pil, self.tile_size, self.border_mode
46+
)
47+
48+
normal_map = context.images.save(normal_map)
49+
normal_map_field = ImageField(image_name=normal_map.image_name)
50+
51+
roughness_map = context.images.save(roughness_map)
52+
roughness_map_field = ImageField(image_name=roughness_map.image_name)
53+
54+
displacement_map = context.images.save(displacement_map)
55+
displacement_map_field = ImageField(image_name=displacement_map.image_name)
56+
57+
return PBRMapsOutput(
58+
normal_map=normal_map_field, roughness_map=roughness_map_field, displacement_map=displacement_map_field
59+
)

0 commit comments

Comments
 (0)