mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-01-18 00:47:56 -05:00
Compare commits
25 Commits
release/3.
...
3.6.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6539ef7c9f | ||
|
|
14c9a1e4f3 | ||
|
|
64b0feca31 | ||
|
|
0be9a2d906 | ||
|
|
d925f721b9 | ||
|
|
4e5be1891a | ||
|
|
156d4ec3b2 | ||
|
|
c45a43519a | ||
|
|
763816ca0c | ||
|
|
83a7c9059f | ||
|
|
c5f069a255 | ||
|
|
cd169ee082 | ||
|
|
66b106f107 | ||
|
|
b10d745dae | ||
|
|
d20f98fb4f | ||
|
|
c9c150f850 | ||
|
|
a60e2b7c77 | ||
|
|
da6e5b2ba1 | ||
|
|
c65d497cbc | ||
|
|
a68d8fe203 | ||
|
|
5de2288cfa | ||
|
|
2ce70b4457 | ||
|
|
6c5f743e2b | ||
|
|
bb242c4e1e | ||
|
|
c9e246ed1b |
@@ -28,7 +28,7 @@ This is done via Docker Desktop preferences
|
||||
|
||||
### Configure Invoke environment
|
||||
|
||||
1. Make a copy of `env.sample` and name it `.env` (`cp env.sample .env` (Mac/Linux) or `copy example.env .env` (Windows)). Make changes as necessary. Set `INVOKEAI_ROOT` to an absolute path to:
|
||||
1. Make a copy of `.env.sample` and name it `.env` (`cp .env.sample .env` (Mac/Linux) or `copy example.env .env` (Windows)). Make changes as necessary. Set `INVOKEAI_ROOT` to an absolute path to:
|
||||
a. the desired location of the InvokeAI runtime directory, or
|
||||
b. an existing, v3.0.0 compatible runtime directory.
|
||||
1. Execute `run.sh`
|
||||
|
||||
@@ -69,7 +69,7 @@ a token and copy it, since you will need in for the next step.
|
||||
|
||||
### Setup
|
||||
|
||||
Set up your environmnent variables. In the `docker` directory, make a copy of `env.sample` and name it `.env`. Make changes as necessary.
|
||||
Set up your environmnent variables. In the `docker` directory, make a copy of `.env.sample` and name it `.env`. Make changes as necessary.
|
||||
|
||||
Any environment variables supported by InvokeAI can be set here - please see the [CONFIGURATION](../features/CONFIGURATION.md) for further detail.
|
||||
|
||||
|
||||
@@ -91,8 +91,8 @@ def choose_version(available_releases: tuple | None = None) -> str:
|
||||
complete_while_typing=True,
|
||||
completer=FuzzyWordCompleter(choices),
|
||||
)
|
||||
console.print(f" Version {choices[0] if response == '' else response} will be installed.")
|
||||
|
||||
console.print(f" Version {choices[0]} will be installed.")
|
||||
console.line()
|
||||
|
||||
return "stable" if response == "" else response
|
||||
|
||||
@@ -14,7 +14,7 @@ class SocketIO:
|
||||
|
||||
def __init__(self, app: FastAPI):
|
||||
self.__sio = AsyncServer(async_mode="asgi", cors_allowed_origins="*")
|
||||
self.__app = ASGIApp(socketio_server=self.__sio, socketio_path="socket.io")
|
||||
self.__app = ASGIApp(socketio_server=self.__sio, socketio_path="/ws/socket.io")
|
||||
app.mount("/ws", self.__app)
|
||||
|
||||
self.__sio.on("subscribe_queue", handler=self._handle_sub_queue)
|
||||
|
||||
@@ -5,12 +5,12 @@ from typing import Literal
|
||||
import cv2
|
||||
import numpy as np
|
||||
import torch
|
||||
from basicsr.archs.rrdbnet_arch import RRDBNet
|
||||
from PIL import Image
|
||||
from pydantic import ConfigDict
|
||||
|
||||
from invokeai.app.invocations.primitives import ImageField, ImageOutput
|
||||
from invokeai.app.services.image_records.image_records_common import ImageCategory, ResourceOrigin
|
||||
from invokeai.backend.image_util.basicsr.rrdbnet_arch import RRDBNet
|
||||
from invokeai.backend.image_util.realesrgan.realesrgan import RealESRGAN
|
||||
from invokeai.backend.util.devices import choose_torch_device
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import copy
|
||||
import itertools
|
||||
from typing import Annotated, Any, Optional, Union, get_args, get_origin, get_type_hints
|
||||
from typing import Annotated, Any, Optional, TypeVar, Union, get_args, get_origin, get_type_hints
|
||||
|
||||
import networkx as nx
|
||||
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
||||
@@ -141,6 +141,16 @@ def are_connections_compatible(
|
||||
return are_connection_types_compatible(from_node_field, to_node_field)
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def copydeep(obj: T) -> T:
|
||||
"""Deep-copies an object. If it is a pydantic model, use the model's copy method."""
|
||||
if isinstance(obj, BaseModel):
|
||||
return obj.model_copy(deep=True)
|
||||
return copy.deepcopy(obj)
|
||||
|
||||
|
||||
class NodeAlreadyInGraphError(ValueError):
|
||||
pass
|
||||
|
||||
@@ -1118,17 +1128,22 @@ class GraphExecutionState(BaseModel):
|
||||
|
||||
def _prepare_inputs(self, node: BaseInvocation):
|
||||
input_edges = [e for e in self.execution_graph.edges if e.destination.node_id == node.id]
|
||||
# Inputs must be deep-copied, else if a node mutates the object, other nodes that get the same input
|
||||
# will see the mutation.
|
||||
if isinstance(node, CollectInvocation):
|
||||
output_collection = [
|
||||
getattr(self.results[edge.source.node_id], edge.source.field)
|
||||
copydeep(getattr(self.results[edge.source.node_id], edge.source.field))
|
||||
for edge in input_edges
|
||||
if edge.destination.field == "item"
|
||||
]
|
||||
node.collection = output_collection
|
||||
else:
|
||||
for edge in input_edges:
|
||||
output_value = getattr(self.results[edge.source.node_id], edge.source.field)
|
||||
setattr(node, edge.destination.field, output_value)
|
||||
setattr(
|
||||
node,
|
||||
edge.destination.field,
|
||||
copydeep(getattr(self.results[edge.source.node_id], edge.source.field)),
|
||||
)
|
||||
|
||||
# TODO: Add API for modifying underlying graph that checks if the change will be valid given the current execution state
|
||||
def _is_edge_valid(self, edge: Edge) -> bool:
|
||||
|
||||
201
invokeai/backend/image_util/basicsr/LICENSE
Normal file
201
invokeai/backend/image_util/basicsr/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2018-2022 BasicSR Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
18
invokeai/backend/image_util/basicsr/__init__.py
Normal file
18
invokeai/backend/image_util/basicsr/__init__.py
Normal file
@@ -0,0 +1,18 @@
|
||||
"""
|
||||
Adapted from https://github.com/XPixelGroup/BasicSR
|
||||
License: Apache-2.0
|
||||
|
||||
As of Feb 2024, `basicsr` appears to be unmaintained. It imports a function from `torchvision` that is removed in
|
||||
`torchvision` 0.17. Here is the deprecation warning:
|
||||
|
||||
UserWarning: The torchvision.transforms.functional_tensor module is deprecated in 0.15 and will be **removed in
|
||||
0.17**. Please don't rely on it. You probably just need to use APIs in torchvision.transforms.functional or in
|
||||
torchvision.transforms.v2.functional.
|
||||
|
||||
As a result, a dependency on `basicsr` means we cannot keep our `torchvision` dependency up to date.
|
||||
|
||||
Because we only rely on a single class `RRDBNet` from `basicsr`, we've copied the relevant code here and removed the
|
||||
dependency on `basicsr`.
|
||||
|
||||
The code is almost unchanged, only a few type annotations have been added. The license is also copied.
|
||||
"""
|
||||
75
invokeai/backend/image_util/basicsr/arch_util.py
Normal file
75
invokeai/backend/image_util/basicsr/arch_util.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from typing import Type
|
||||
|
||||
import torch
|
||||
from torch import nn as nn
|
||||
from torch.nn import init as init
|
||||
from torch.nn.modules.batchnorm import _BatchNorm
|
||||
|
||||
|
||||
@torch.no_grad()
|
||||
def default_init_weights(
|
||||
module_list: list[nn.Module] | nn.Module, scale: float = 1, bias_fill: float = 0, **kwargs
|
||||
) -> None:
|
||||
"""Initialize network weights.
|
||||
|
||||
Args:
|
||||
module_list (list[nn.Module] | nn.Module): Modules to be initialized.
|
||||
scale (float): Scale initialized weights, especially for residual
|
||||
blocks. Default: 1.
|
||||
bias_fill (float): The value to fill bias. Default: 0
|
||||
kwargs (dict): Other arguments for initialization function.
|
||||
"""
|
||||
if not isinstance(module_list, list):
|
||||
module_list = [module_list]
|
||||
for module in module_list:
|
||||
for m in module.modules():
|
||||
if isinstance(m, nn.Conv2d):
|
||||
init.kaiming_normal_(m.weight, **kwargs)
|
||||
m.weight.data *= scale
|
||||
if m.bias is not None:
|
||||
m.bias.data.fill_(bias_fill)
|
||||
elif isinstance(m, nn.Linear):
|
||||
init.kaiming_normal_(m.weight, **kwargs)
|
||||
m.weight.data *= scale
|
||||
if m.bias is not None:
|
||||
m.bias.data.fill_(bias_fill)
|
||||
elif isinstance(m, _BatchNorm):
|
||||
init.constant_(m.weight, 1)
|
||||
if m.bias is not None:
|
||||
m.bias.data.fill_(bias_fill)
|
||||
|
||||
|
||||
def make_layer(basic_block: Type[nn.Module], num_basic_block: int, **kwarg) -> nn.Sequential:
|
||||
"""Make layers by stacking the same blocks.
|
||||
|
||||
Args:
|
||||
basic_block (Type[nn.Module]): nn.Module class for basic block.
|
||||
num_basic_block (int): number of blocks.
|
||||
|
||||
Returns:
|
||||
nn.Sequential: Stacked blocks in nn.Sequential.
|
||||
"""
|
||||
layers = []
|
||||
for _ in range(num_basic_block):
|
||||
layers.append(basic_block(**kwarg))
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
|
||||
# TODO: may write a cpp file
|
||||
def pixel_unshuffle(x: torch.Tensor, scale: int) -> torch.Tensor:
|
||||
"""Pixel unshuffle.
|
||||
|
||||
Args:
|
||||
x (Tensor): Input feature with shape (b, c, hh, hw).
|
||||
scale (int): Downsample ratio.
|
||||
|
||||
Returns:
|
||||
Tensor: the pixel unshuffled feature.
|
||||
"""
|
||||
b, c, hh, hw = x.size()
|
||||
out_channel = c * (scale**2)
|
||||
assert hh % scale == 0 and hw % scale == 0
|
||||
h = hh // scale
|
||||
w = hw // scale
|
||||
x_view = x.view(b, c, h, scale, w, scale)
|
||||
return x_view.permute(0, 1, 3, 5, 2, 4).reshape(b, out_channel, h, w)
|
||||
125
invokeai/backend/image_util/basicsr/rrdbnet_arch.py
Normal file
125
invokeai/backend/image_util/basicsr/rrdbnet_arch.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import torch
|
||||
from torch import nn as nn
|
||||
from torch.nn import functional as F
|
||||
|
||||
from .arch_util import default_init_weights, make_layer, pixel_unshuffle
|
||||
|
||||
|
||||
class ResidualDenseBlock(nn.Module):
|
||||
"""Residual Dense Block.
|
||||
|
||||
Used in RRDB block in ESRGAN.
|
||||
|
||||
Args:
|
||||
num_feat (int): Channel number of intermediate features.
|
||||
num_grow_ch (int): Channels for each growth.
|
||||
"""
|
||||
|
||||
def __init__(self, num_feat: int = 64, num_grow_ch: int = 32) -> None:
|
||||
super(ResidualDenseBlock, self).__init__()
|
||||
self.conv1 = nn.Conv2d(num_feat, num_grow_ch, 3, 1, 1)
|
||||
self.conv2 = nn.Conv2d(num_feat + num_grow_ch, num_grow_ch, 3, 1, 1)
|
||||
self.conv3 = nn.Conv2d(num_feat + 2 * num_grow_ch, num_grow_ch, 3, 1, 1)
|
||||
self.conv4 = nn.Conv2d(num_feat + 3 * num_grow_ch, num_grow_ch, 3, 1, 1)
|
||||
self.conv5 = nn.Conv2d(num_feat + 4 * num_grow_ch, num_feat, 3, 1, 1)
|
||||
|
||||
self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True)
|
||||
|
||||
# initialization
|
||||
default_init_weights([self.conv1, self.conv2, self.conv3, self.conv4, self.conv5], 0.1)
|
||||
|
||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
x1 = self.lrelu(self.conv1(x))
|
||||
x2 = self.lrelu(self.conv2(torch.cat((x, x1), 1)))
|
||||
x3 = self.lrelu(self.conv3(torch.cat((x, x1, x2), 1)))
|
||||
x4 = self.lrelu(self.conv4(torch.cat((x, x1, x2, x3), 1)))
|
||||
x5 = self.conv5(torch.cat((x, x1, x2, x3, x4), 1))
|
||||
# Empirically, we use 0.2 to scale the residual for better performance
|
||||
return x5 * 0.2 + x
|
||||
|
||||
|
||||
class RRDB(nn.Module):
|
||||
"""Residual in Residual Dense Block.
|
||||
|
||||
Used in RRDB-Net in ESRGAN.
|
||||
|
||||
Args:
|
||||
num_feat (int): Channel number of intermediate features.
|
||||
num_grow_ch (int): Channels for each growth.
|
||||
"""
|
||||
|
||||
def __init__(self, num_feat: int, num_grow_ch: int = 32) -> None:
|
||||
super(RRDB, self).__init__()
|
||||
self.rdb1 = ResidualDenseBlock(num_feat, num_grow_ch)
|
||||
self.rdb2 = ResidualDenseBlock(num_feat, num_grow_ch)
|
||||
self.rdb3 = ResidualDenseBlock(num_feat, num_grow_ch)
|
||||
|
||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
out = self.rdb1(x)
|
||||
out = self.rdb2(out)
|
||||
out = self.rdb3(out)
|
||||
# Empirically, we use 0.2 to scale the residual for better performance
|
||||
return out * 0.2 + x
|
||||
|
||||
|
||||
class RRDBNet(nn.Module):
|
||||
"""Networks consisting of Residual in Residual Dense Block, which is used
|
||||
in ESRGAN.
|
||||
|
||||
ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks.
|
||||
|
||||
We extend ESRGAN for scale x2 and scale x1.
|
||||
Note: This is one option for scale 1, scale 2 in RRDBNet.
|
||||
We first employ the pixel-unshuffle (an inverse operation of pixelshuffle to reduce the spatial size
|
||||
and enlarge the channel size before feeding inputs into the main ESRGAN architecture.
|
||||
|
||||
Args:
|
||||
num_in_ch (int): Channel number of inputs.
|
||||
num_out_ch (int): Channel number of outputs.
|
||||
num_feat (int): Channel number of intermediate features.
|
||||
Default: 64
|
||||
num_block (int): Block number in the trunk network. Defaults: 23
|
||||
num_grow_ch (int): Channels for each growth. Default: 32.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_in_ch: int,
|
||||
num_out_ch: int,
|
||||
scale: int = 4,
|
||||
num_feat: int = 64,
|
||||
num_block: int = 23,
|
||||
num_grow_ch: int = 32,
|
||||
) -> None:
|
||||
super(RRDBNet, self).__init__()
|
||||
self.scale = scale
|
||||
if scale == 2:
|
||||
num_in_ch = num_in_ch * 4
|
||||
elif scale == 1:
|
||||
num_in_ch = num_in_ch * 16
|
||||
self.conv_first = nn.Conv2d(num_in_ch, num_feat, 3, 1, 1)
|
||||
self.body = make_layer(RRDB, num_block, num_feat=num_feat, num_grow_ch=num_grow_ch)
|
||||
self.conv_body = nn.Conv2d(num_feat, num_feat, 3, 1, 1)
|
||||
# upsample
|
||||
self.conv_up1 = nn.Conv2d(num_feat, num_feat, 3, 1, 1)
|
||||
self.conv_up2 = nn.Conv2d(num_feat, num_feat, 3, 1, 1)
|
||||
self.conv_hr = nn.Conv2d(num_feat, num_feat, 3, 1, 1)
|
||||
self.conv_last = nn.Conv2d(num_feat, num_out_ch, 3, 1, 1)
|
||||
|
||||
self.lrelu = nn.LeakyReLU(negative_slope=0.2, inplace=True)
|
||||
|
||||
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
||||
if self.scale == 2:
|
||||
feat = pixel_unshuffle(x, scale=2)
|
||||
elif self.scale == 1:
|
||||
feat = pixel_unshuffle(x, scale=4)
|
||||
else:
|
||||
feat = x
|
||||
feat = self.conv_first(feat)
|
||||
body_feat = self.conv_body(self.body(feat))
|
||||
feat = feat + body_feat
|
||||
# upsample
|
||||
feat = self.lrelu(self.conv_up1(F.interpolate(feat, scale_factor=2, mode="nearest")))
|
||||
feat = self.lrelu(self.conv_up2(F.interpolate(feat, scale_factor=2, mode="nearest")))
|
||||
out = self.conv_last(self.lrelu(self.conv_hr(feat)))
|
||||
return out
|
||||
@@ -7,10 +7,10 @@ import cv2
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
import torch
|
||||
from basicsr.archs.rrdbnet_arch import RRDBNet
|
||||
from cv2.typing import MatLike
|
||||
from tqdm import tqdm
|
||||
|
||||
from invokeai.backend.image_util.basicsr.rrdbnet_arch import RRDBNet
|
||||
from invokeai.backend.util.devices import choose_torch_device
|
||||
|
||||
"""
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
"nodeEditor": "Knoten Editor",
|
||||
"statusMergingModels": "Modelle zusammenführen",
|
||||
"ipAdapter": "IP Adapter",
|
||||
"controlAdapter": "Control Adapter",
|
||||
"controlAdapter": "Control-Adapter",
|
||||
"auto": "Automatisch",
|
||||
"controlNet": "ControlNet",
|
||||
"imageFailedToLoad": "Kann Bild nicht laden",
|
||||
@@ -75,12 +75,12 @@
|
||||
"linear": "Linear",
|
||||
"imagePrompt": "Bild Prompt",
|
||||
"checkpoint": "Checkpoint",
|
||||
"inpaint": "inpaint",
|
||||
"inpaint": "Inpaint",
|
||||
"simple": "Einfach",
|
||||
"template": "Vorlage",
|
||||
"outputs": "Ausgabe",
|
||||
"data": "Daten",
|
||||
"safetensors": "Safetensors",
|
||||
"safetensors": "Safe-Tensors",
|
||||
"outpaint": "Ausmalen",
|
||||
"details": "Details",
|
||||
"format": "Format",
|
||||
@@ -161,16 +161,16 @@
|
||||
"hotkeys": {
|
||||
"keyboardShortcuts": "Tastenkürzel",
|
||||
"appHotkeys": "App-Tastenkombinationen",
|
||||
"generalHotkeys": "Allgemeine Tastenkürzel",
|
||||
"galleryHotkeys": "Galerie Tastenkürzel",
|
||||
"unifiedCanvasHotkeys": "Unified Canvas Tastenkürzel",
|
||||
"generalHotkeys": "Allgemein",
|
||||
"galleryHotkeys": "Galerie",
|
||||
"unifiedCanvasHotkeys": "Leinwand",
|
||||
"invoke": {
|
||||
"desc": "Ein Bild erzeugen",
|
||||
"title": "Invoke"
|
||||
},
|
||||
"cancel": {
|
||||
"title": "Abbrechen",
|
||||
"desc": "Bilderzeugung abbrechen"
|
||||
"desc": "Aktuelle Bilderzeugung abbrechen"
|
||||
},
|
||||
"focusPrompt": {
|
||||
"title": "Fokussiere Prompt",
|
||||
@@ -356,7 +356,7 @@
|
||||
"title": "Staging-Bild akzeptieren",
|
||||
"desc": "Akzeptieren Sie das aktuelle Bild des Staging-Bereichs"
|
||||
},
|
||||
"nodesHotkeys": "Knoten Tastenkürzel",
|
||||
"nodesHotkeys": "Knoten",
|
||||
"addNodes": {
|
||||
"title": "Knotenpunkt hinzufügen",
|
||||
"desc": "Öffnet das Menü zum Hinzufügen von Knoten"
|
||||
@@ -399,7 +399,7 @@
|
||||
"vaeLocation": "VAE Ort",
|
||||
"vaeLocationValidationMsg": "Pfad zum Speicherort Ihres VAE.",
|
||||
"width": "Breite",
|
||||
"widthValidationMsg": "Standardbreite Ihres Models.",
|
||||
"widthValidationMsg": "Standardbreite Ihres Modells.",
|
||||
"height": "Höhe",
|
||||
"heightValidationMsg": "Standardbhöhe Ihres Models.",
|
||||
"addModel": "Modell hinzufügen",
|
||||
@@ -501,7 +501,7 @@
|
||||
"quickAdd": "Schnell hinzufügen",
|
||||
"simpleModelDesc": "Geben Sie einen Pfad zu einem lokalen Diffusers-Modell, einem lokalen Checkpoint-/Safetensors-Modell, einer HuggingFace-Repo-ID oder einer Checkpoint-/Diffusers-Modell-URL an.",
|
||||
"modelDeleted": "Modell gelöscht",
|
||||
"inpainting": "v1 Inpainting",
|
||||
"inpainting": "V1-Inpainting",
|
||||
"modelUpdateFailed": "Modellaktualisierung fehlgeschlagen",
|
||||
"useCustomConfig": "Benutzerdefinierte Konfiguration verwenden",
|
||||
"settings": "Einstellungen",
|
||||
@@ -518,7 +518,7 @@
|
||||
"interpolationType": "Interpolationstyp",
|
||||
"oliveModels": "Olives",
|
||||
"variant": "Variante",
|
||||
"loraModels": "LoRAs",
|
||||
"loraModels": "\"LoRAs\"",
|
||||
"modelDeleteFailed": "Modell konnte nicht gelöscht werden",
|
||||
"mergedModelName": "Zusammengeführter Modellname",
|
||||
"checkpointOrSafetensors": "$t(common.checkpoint) / $t(common.safetensors)",
|
||||
@@ -603,7 +603,8 @@
|
||||
"resetWebUIDesc2": "Wenn die Bilder nicht in der Galerie angezeigt werden oder etwas anderes nicht funktioniert, versuchen Sie bitte, die Einstellungen zurückzusetzen, bevor Sie einen Fehler auf GitHub melden.",
|
||||
"resetComplete": "Die Web-Oberfläche wurde zurückgesetzt.",
|
||||
"models": "Modelle",
|
||||
"useSlidersForAll": "Schieberegler für alle Optionen verwenden"
|
||||
"useSlidersForAll": "Schieberegler für alle Optionen verwenden",
|
||||
"showAdvancedOptions": "Erweiterte Optionen anzeigen"
|
||||
},
|
||||
"toast": {
|
||||
"tempFoldersEmptied": "Temp-Ordner geleert",
|
||||
@@ -646,7 +647,7 @@
|
||||
"upscale": "Verwenden Sie ESRGAN, um das Bild unmittelbar nach der Erzeugung zu vergrößern.",
|
||||
"faceCorrection": "Gesichtskorrektur mit GFPGAN oder Codeformer: Der Algorithmus erkennt Gesichter im Bild und korrigiert alle Fehler. Ein hoher Wert verändert das Bild stärker, was zu attraktiveren Gesichtern führt. Codeformer mit einer höheren Genauigkeit bewahrt das Originalbild auf Kosten einer stärkeren Gesichtskorrektur.",
|
||||
"imageToImage": "Bild zu Bild lädt ein beliebiges Bild als Ausgangsbild, aus dem dann zusammen mit dem Prompt ein neues Bild erzeugt wird. Je höher der Wert ist, desto stärker wird das Ergebnisbild verändert. Werte von 0,0 bis 1,0 sind möglich, der empfohlene Bereich ist .25-.75",
|
||||
"boundingBox": "Der Begrenzungsrahmen ist derselbe wie die Einstellungen für Breite und Höhe bei Text zu Bild oder Bild zu Bild. Es wird nur der Bereich innerhalb des Rahmens verarbeitet.",
|
||||
"boundingBox": "Der Begrenzungsrahmen ist derselbe wie die Einstellungen für Breite und Höhe bei Text-zu-Bild oder Bild-zu-Bild. Es wird nur der Bereich innerhalb des Rahmens verarbeitet.",
|
||||
"seamCorrection": "Steuert die Behandlung von sichtbaren Übergängen, die zwischen den erzeugten Bildern auf der Leinwand auftreten.",
|
||||
"infillAndScaling": "Verwalten Sie Infill-Methoden (für maskierte oder gelöschte Bereiche der Leinwand) und Skalierung (nützlich für kleine Begrenzungsrahmengrößen)."
|
||||
}
|
||||
@@ -714,7 +715,7 @@
|
||||
"showResultsOff": "Zeige Ergebnisse (Aus)"
|
||||
},
|
||||
"accessibility": {
|
||||
"modelSelect": "Model Auswahl",
|
||||
"modelSelect": "Modell-Auswahl",
|
||||
"uploadImage": "Bild hochladen",
|
||||
"previousImage": "Voriges Bild",
|
||||
"useThisParameter": "Benutze diesen Parameter",
|
||||
@@ -726,11 +727,11 @@
|
||||
"modifyConfig": "Optionen einstellen",
|
||||
"toggleAutoscroll": "Auroscroll ein/ausschalten",
|
||||
"toggleLogViewer": "Log Betrachter ein/ausschalten",
|
||||
"showOptionsPanel": "Zeige Optionen",
|
||||
"showOptionsPanel": "Seitenpanel anzeigen",
|
||||
"reset": "Zurücksetzten",
|
||||
"nextImage": "Nächstes Bild",
|
||||
"zoomOut": "Verkleinern",
|
||||
"rotateCounterClockwise": "Gegen den Uhrzeigersinn verdrehen",
|
||||
"rotateCounterClockwise": "Gegen den Uhrzeigersinn drehen",
|
||||
"showGalleryPanel": "Galeriefenster anzeigen",
|
||||
"exitViewer": "Betrachten beenden",
|
||||
"menu": "Menü",
|
||||
@@ -752,7 +753,7 @@
|
||||
"selectBoard": "Ordner aussuchen",
|
||||
"cancel": "Abbrechen",
|
||||
"addBoard": "Ordner hinzufügen",
|
||||
"uncategorized": "Nicht kategorisiert",
|
||||
"uncategorized": "Ohne Kategorie",
|
||||
"downloadBoard": "Ordner runterladen",
|
||||
"changeBoard": "Ordner wechseln",
|
||||
"loading": "Laden...",
|
||||
@@ -784,7 +785,7 @@
|
||||
"depthMidasDescription": "Tiefenmap erstellen mit Midas",
|
||||
"controlnet": "$t(controlnet.controlAdapter_one) #{{number}} ($t(common.controlNet))",
|
||||
"t2iEnabledControlNetDisabled": "$t(common.t2iAdapter) ist aktiv, $t(common.controlNet) ist deaktiviert",
|
||||
"weight": "Breite",
|
||||
"weight": "Einfluss",
|
||||
"selectModel": "Wähle ein Modell",
|
||||
"depthMidas": "Tiefe (Midas)",
|
||||
"w": "W",
|
||||
@@ -809,14 +810,14 @@
|
||||
"controlAdapter_other": "Control Adapter",
|
||||
"colorMapTileSize": "Kachelgröße",
|
||||
"depthZoeDescription": "Tiefenmap erstellen mit Zoe",
|
||||
"setControlImageDimensions": "Setze Control Bild Auflösung auf Breite/Höhe",
|
||||
"setControlImageDimensions": "Setze Control-Bild Auflösung auf Breite/Höhe",
|
||||
"handAndFace": "Hand und Gesicht",
|
||||
"enableIPAdapter": "Aktiviere IP Adapter",
|
||||
"resize": "Größe ändern",
|
||||
"resetControlImage": "Zurücksetzen vom Referenz Bild",
|
||||
"balanced": "Ausgewogen",
|
||||
"prompt": "Prompt",
|
||||
"resizeMode": "Größenänderungsmodus",
|
||||
"resizeMode": "Größe",
|
||||
"processor": "Prozessor",
|
||||
"saveControlImage": "Speichere Referenz Bild",
|
||||
"safe": "Speichern",
|
||||
@@ -841,10 +842,10 @@
|
||||
"autoConfigure": "Prozessor automatisch konfigurieren",
|
||||
"normalBaeDescription": "Normale BAE-Verarbeitung",
|
||||
"noneDescription": "Es wurde keine Verarbeitung angewendet",
|
||||
"openPose": "Openpose",
|
||||
"lineartAnime": "Lineart Anime",
|
||||
"openPose": "Openpose / \"Pose nutzen\"",
|
||||
"lineartAnime": "Lineart Anime / \"Strichzeichnung Anime\"",
|
||||
"mediapipeFaceDescription": "Gesichtserkennung mit Mediapipe",
|
||||
"canny": "Canny",
|
||||
"canny": "\"Canny\"",
|
||||
"hedDescription": "Ganzheitlich verschachtelte Kantenerkennung",
|
||||
"scribble": "Scribble",
|
||||
"maxFaces": "Maximale Anzahl Gesichter",
|
||||
@@ -853,7 +854,7 @@
|
||||
"modelSize": "Modell Größe",
|
||||
"small": "Klein",
|
||||
"base": "Basis",
|
||||
"depthAnything": "Depth Anything",
|
||||
"depthAnything": "Depth Anything / \"Tiefe irgendwas\"",
|
||||
"depthAnythingDescription": "Erstellung einer Tiefenkarte mit der Depth Anything-Technik"
|
||||
},
|
||||
"queue": {
|
||||
@@ -915,7 +916,9 @@
|
||||
"openQueue": "Warteschlange öffnen",
|
||||
"batchFailedToQueue": "Fehler beim Einreihen in die Stapelverarbeitung",
|
||||
"batchFieldValues": "Stapelverarbeitungswerte",
|
||||
"batchQueued": "Stapelverarbeitung eingereiht"
|
||||
"batchQueued": "Stapelverarbeitung eingereiht",
|
||||
"graphQueued": "Graph eingereiht",
|
||||
"graphFailedToQueue": "Fehler beim Einreihen des Graphen"
|
||||
},
|
||||
"metadata": {
|
||||
"negativePrompt": "Negativ Beschreibung",
|
||||
@@ -936,46 +939,130 @@
|
||||
"generationMode": "Generierungsmodus",
|
||||
"Threshold": "Rauschen-Schwelle",
|
||||
"seed": "Seed",
|
||||
"perlin": "Perlin Noise",
|
||||
"perlin": "Perlin-Rauschen",
|
||||
"hiresFix": "Optimierung für hohe Auflösungen",
|
||||
"initImage": "Erstes Bild",
|
||||
"variations": "Samengewichtspaare",
|
||||
"variations": "Seed-Gewichtungs-Paare",
|
||||
"vae": "VAE",
|
||||
"workflow": "Arbeitsablauf",
|
||||
"scheduler": "Planer",
|
||||
"noRecallParameters": "Es wurden keine Parameter zum Abrufen gefunden",
|
||||
"recallParameters": "Recall Parameters"
|
||||
"recallParameters": "Parameter wiederherstellen"
|
||||
},
|
||||
"popovers": {
|
||||
"noiseUseCPU": {
|
||||
"heading": "Nutze Prozessor rauschen"
|
||||
"heading": "Nutze Prozessor rauschen",
|
||||
"paragraphs": [
|
||||
"Entscheidet, ob auf der CPU oder GPU Rauschen erzeugt wird.",
|
||||
"Mit aktiviertem CPU-Rauschen wird ein bestimmter Seedwert das gleiche Bild auf jeder Maschine erzeugen.",
|
||||
"CPU-Rauschen einzuschalten beeinflusst nicht die Systemleistung."
|
||||
]
|
||||
},
|
||||
"paramModel": {
|
||||
"heading": "Modell"
|
||||
"heading": "Modell",
|
||||
"paragraphs": [
|
||||
"Modell für die Entrauschungsschritte.",
|
||||
"Verschiedene Modelle werden in der Regel so trainiert, dass sie sich auf die Erzeugung bestimmter Ästhetik und/oder Inhalte spezialisiert."
|
||||
]
|
||||
},
|
||||
"paramIterations": {
|
||||
"heading": "Iterationen"
|
||||
"heading": "Iterationen",
|
||||
"paragraphs": [
|
||||
"Die Anzahl der Bilder, die erzeugt werden sollen.",
|
||||
"Wenn \"Dynamische Prompts\" aktiviert ist, wird jeder einzelne Prompt so oft generiert."
|
||||
]
|
||||
},
|
||||
"paramCFGScale": {
|
||||
"heading": "CFG-Skala"
|
||||
"heading": "CFG-Skala",
|
||||
"paragraphs": [
|
||||
"Bestimmt, wie viel Ihr Prompt den Erzeugungsprozess beeinflusst."
|
||||
]
|
||||
},
|
||||
"paramSteps": {
|
||||
"heading": "Schritte"
|
||||
"heading": "Schritte",
|
||||
"paragraphs": [
|
||||
"Anzahl der Schritte, die bei jeder Generierung durchgeführt werden.",
|
||||
"Höhere Schrittzahlen werden in der Regel bessere Bilder ergeben, aber mehr Zeit benötigen."
|
||||
]
|
||||
},
|
||||
"lora": {
|
||||
"heading": "LoRA Gewichte"
|
||||
"heading": "LoRA Gewichte",
|
||||
"paragraphs": [
|
||||
"Höhere LoRA-Wichtungen führen zu größeren Auswirkungen auf das endgültige Bild."
|
||||
]
|
||||
},
|
||||
"infillMethod": {
|
||||
"heading": "Füllmethode"
|
||||
"heading": "Füllmethode",
|
||||
"paragraphs": [
|
||||
"Infill-Methode für den ausgewählten Bereich."
|
||||
]
|
||||
},
|
||||
"paramVAE": {
|
||||
"heading": "VAE"
|
||||
"heading": "VAE",
|
||||
"paragraphs": [
|
||||
"Verwendetes Modell, um den KI-Ausgang in das endgültige Bild zu übersetzen."
|
||||
]
|
||||
},
|
||||
"paramRatio": {
|
||||
"heading": "Seitenverhältnis",
|
||||
"paragraphs": [
|
||||
"Das Seitenverhältnis des erzeugten Bildes.",
|
||||
"Für SD1.5-Modelle wird eine Bildgröße von 512x512 Pixel empfohlen, für SDXL-Modelle sind es 1024x1024 Pixel."
|
||||
]
|
||||
},
|
||||
"paramDenoisingStrength": {
|
||||
"paragraphs": [
|
||||
"Wie viel Rauschen dem Eingabebild hinzugefügt wird.",
|
||||
"0 wird zu einem identischen Bild führen, während 1 zu einem völlig neuen Bild führt."
|
||||
],
|
||||
"heading": "Stärke der Entrauschung"
|
||||
},
|
||||
"paramVAEPrecision": {
|
||||
"heading": "VAE-Präzision",
|
||||
"paragraphs": [
|
||||
"Die bei der VAE-Kodierung und Dekodierung verwendete Präzision. FP16/Halbpräzision ist effizienter, aber auf Kosten kleiner Bildvariationen."
|
||||
]
|
||||
},
|
||||
"paramCFGRescaleMultiplier": {
|
||||
"heading": "CFG Rescale Multiplikator",
|
||||
"paragraphs": [
|
||||
"Rescale-Multiplikator für die CFG-Lenkung, der für Modelle verwendet wird, die mit dem zero-terminal SNR (ztsnr) trainiert wurden. Empfohlener Wert: 0,7."
|
||||
]
|
||||
},
|
||||
"scaleBeforeProcessing": {
|
||||
"paragraphs": [
|
||||
"Skaliert den ausgewählten Bereich auf die Größe, die für das Modell am besten geeignet ist."
|
||||
],
|
||||
"heading": "Skalieren vor der Verarbeitung"
|
||||
},
|
||||
"paramSeed": {
|
||||
"paragraphs": [
|
||||
"Kontrolliert das für die Erzeugung verwendete Startrauschen.",
|
||||
"Deaktivieren Sie “Random Seed”, um identische Ergebnisse mit den gleichen Generierungseinstellungen zu erzeugen."
|
||||
],
|
||||
"heading": "Seed"
|
||||
},
|
||||
"dynamicPromptsMaxPrompts": {
|
||||
"paragraphs": [
|
||||
"Beschränkt die Anzahl der Prompts, die von \"Dynamic Prompts\" generiert werden können."
|
||||
],
|
||||
"heading": "Maximale Prompts"
|
||||
},
|
||||
"dynamicPromptsSeedBehaviour": {
|
||||
"paragraphs": [
|
||||
"Bestimmt, wie der Seed-Wert beim Erzeugen von Prompts verwendet wird.",
|
||||
"Verwenden Sie dies, um schnelle Variationen eines einzigen Seeds zu erkunden.",
|
||||
"Wenn Sie z. B. 5 Prompts haben, wird jedes Bild den selben Seed-Wert verwenden.",
|
||||
"\"Per Bild\" wird einen einzigartigen Seed-Wert für jedes Bild verwenden. Dies bietet mehr Variationen."
|
||||
],
|
||||
"heading": "Seed-Verhalten"
|
||||
}
|
||||
},
|
||||
"ui": {
|
||||
"lockRatio": "Verhältnis sperren",
|
||||
"hideProgressImages": "Verstecke Prozess Bild",
|
||||
"showProgressImages": "Zeige Prozess Bild"
|
||||
"showProgressImages": "Zeige Prozess Bild",
|
||||
"swapSizes": "Tausche Größen"
|
||||
},
|
||||
"invocationCache": {
|
||||
"disable": "Deaktivieren",
|
||||
@@ -989,7 +1076,7 @@
|
||||
"enableFailed": "Problem beim Aktivieren des Zwischenspeichers",
|
||||
"disableFailed": "Problem bei Deaktivierung des Cache",
|
||||
"enableSucceeded": "Zwischenspeicher aktiviert",
|
||||
"disableSucceeded": "Aufrufcache deaktiviert",
|
||||
"disableSucceeded": "Invocation-Cache deaktiviert",
|
||||
"clearSucceeded": "Zwischenspeicher gelöscht",
|
||||
"invocationCache": "Zwischenspeicher",
|
||||
"clearFailed": "Problem beim Löschen des Zwischenspeichers"
|
||||
@@ -1035,15 +1122,15 @@
|
||||
"collectionFieldType": "{{name}} Sammlung",
|
||||
"controlCollectionDescription": "Kontrollinformationen zwischen Knotenpunkten weitergegeben.",
|
||||
"connectionWouldCreateCycle": "Verbindung würde einen Kreislauf/cycle schaffen",
|
||||
"ipAdapterDescription": "Ein Adapter für die Bildabfrage (IP-Adapter) / Bilderprompt-Adapter.",
|
||||
"ipAdapterDescription": "Ein Adapter für die Bildabfrage (IP-Adapter) / Bildprompt-Adapter.",
|
||||
"controlField": "Kontrolle",
|
||||
"inputFields": "Eingabefelder",
|
||||
"imageField": "Bild",
|
||||
"inputMayOnlyHaveOneConnection": "Eingang darf nur eine Verbindung haben",
|
||||
"integerCollectionDescription": "Eine Sammlung ganzer Zahlen.",
|
||||
"integerDescription": "Das sind ganze Zahlen ohne Dezimalpunkt.",
|
||||
"integerDescription": "\"Integer\" sind ganze Zahlen ohne Dezimalpunkt.",
|
||||
"conditioningPolymorphic": "Konditionierung polymorphisch",
|
||||
"conditioningPolymorphicDescription": "Die Konditionierung kann zwischen den Knotenpunkten weitergegeben werden.",
|
||||
"conditioningPolymorphicDescription": "Die Konditionierung kann zwischen den Knoten weitergegeben werden.",
|
||||
"invalidOutputSchema": "Ungültiges Ausgabeschema",
|
||||
"ipAdapterModel": "IP-Adapter Modell",
|
||||
"conditioningFieldDescription": "Die Konditionierung kann zwischen den Knotenpunkten weitergegeben werden.",
|
||||
@@ -1065,10 +1152,117 @@
|
||||
"imageCollection": "Bildersammlung",
|
||||
"imageCollectionDescription": "Eine Sammlung von Bildern.",
|
||||
"denoiseMaskField": "Entrauschen-Maske",
|
||||
"ipAdapterCollection": "IP-Adapter Sammlung"
|
||||
"ipAdapterCollection": "IP-Adapter Sammlung",
|
||||
"newWorkflowDesc2": "Ihr aktueller Arbeitsablauf hat ungespeicherte Änderungen.",
|
||||
"problemSettingTitle": "Problem beim Einstellen des Titels",
|
||||
"noConnectionData": "Keine Verbindungsdaten",
|
||||
"outputField": "Ausgabefeld",
|
||||
"outputFieldInInput": "Ausgabefeld im Eingang",
|
||||
"problemReadingWorkflow": "Problem beim Lesen des Arbeitsablaufs vom Bild",
|
||||
"reloadNodeTemplates": "Knoten-Vorlagen neu laden",
|
||||
"newWorkflow": "Neuer Arbeitsablauf",
|
||||
"newWorkflowDesc": "Einen neuen Arbeitsablauf erstellen?",
|
||||
"noFieldsLinearview": "Keine Felder zur linearen Ansicht hinzugefügt",
|
||||
"clearWorkflow": "Arbeitsablauf löschen",
|
||||
"clearWorkflowDesc": "Diesen Arbeitsablauf löschen und neu starten?",
|
||||
"noConnectionInProgress": "Es besteht keine Verbindung",
|
||||
"notes": "Anmerkungen",
|
||||
"nodeVersion": "Knoten Version",
|
||||
"noOutputSchemaName": "Kein Name des Ausgabeschemas im ref-Objekt gefunden",
|
||||
"node": "Knoten",
|
||||
"nodeSearch": "Knoten suchen",
|
||||
"removeLinearView": "Entfernen aus Linear View",
|
||||
"nodeOutputs": "Knoten-Ausgänge",
|
||||
"nodeTemplate": "Knoten-Vorlage",
|
||||
"nodeType": "Knotentyp",
|
||||
"noFieldType": "Kein Feldtyp",
|
||||
"oNNXModelField": "ONNX-Modell",
|
||||
"noMatchingNodes": "Keine passenden Knoten",
|
||||
"noNodeSelected": "Kein Knoten gewählt",
|
||||
"noImageFoundState": "Kein Anfangsbild im Status gefunden",
|
||||
"nodeOpacity": "Knoten-Deckkraft",
|
||||
"noOutputRecorded": "Keine Ausgänge aufgezeichnet",
|
||||
"outputSchemaNotFound": "Ausgabeschema nicht gefunden",
|
||||
"oNNXModelFieldDescription": "ONNX-Modellfeld.",
|
||||
"outputNode": "Ausgabeknoten",
|
||||
"pickOne": "Eins auswählen",
|
||||
"problemReadingMetadata": "Problem beim Lesen von Metadaten aus dem Bild",
|
||||
"notesDescription": "Anmerkungen zum Arbeitsablauf hinzufügen",
|
||||
"outputFields": "Ausgabefelder",
|
||||
"sDXLRefinerModelField": "Refiner-Modell",
|
||||
"sDXLMainModelFieldDescription": "SDXL Modellfeld.",
|
||||
"clearWorkflowDesc2": "Ihr aktueller Arbeitsablauf hat ungespeicherte Änderungen.",
|
||||
"skipped": "Übersprungen",
|
||||
"schedulerDescription": "Zu erledigen",
|
||||
"scheduler": "Planer",
|
||||
"showGraphNodes": "Graph Overlay anzeigen",
|
||||
"showMinimapnodes": "MiniMap anzeigen",
|
||||
"sDXLMainModelField": "SDXL Modell",
|
||||
"skippedReservedInput": "Reserviertes Eingabefeld übersprungen",
|
||||
"sDXLRefinerModelFieldDescription": "Zu erledigen",
|
||||
"showLegendNodes": "Feldtyp-Legende anzeigen",
|
||||
"skippedReservedOutput": "Reserviertes Ausgangsfeld übersprungen",
|
||||
"skippingInputNoTemplate": "Überspringe Eingabefeld ohne Vorlage",
|
||||
"executionStateCompleted": "Erledigt",
|
||||
"denoiseMaskFieldDescription": "Denoise Maske kann zwischen Knoten weitergegeben werden",
|
||||
"downloadWorkflow": "Workflow JSON herunterladen",
|
||||
"executionStateInProgress": "In Bearbeitung",
|
||||
"snapToGridHelp": "Knoten am Gitternetz einrasten bei Bewegung",
|
||||
"controlCollection": "Control-Sammlung",
|
||||
"controlFieldDescription": "Control-Informationen zwischen Knotenpunkten weitergegeben.",
|
||||
"latentsField": "Latents",
|
||||
"mainModelFieldDescription": "Zu erledigen",
|
||||
"missingTemplate": "Ungültiger Knoten: Knoten {{node}} vom Typ {{type}} fehlt Vorlage (nicht installiert?)",
|
||||
"skippingUnknownInputType": "Überspringe unbekannten Eingabe-Feldtyp",
|
||||
"stringCollectionDescription": "Eine Sammlung von Zeichenfolgen.",
|
||||
"string": "Zeichenfolge",
|
||||
"stringCollection": "Sammlung von Zeichenfolgen",
|
||||
"stringDescription": "Zeichenfolgen (Strings) sind Text.",
|
||||
"fieldTypesMustMatch": "Feldtypen müssen übereinstimmen",
|
||||
"fitViewportNodes": "An Ansichtsgröße anpassen",
|
||||
"missingCanvaInitMaskImages": "Fehlende Startbilder und Masken auf der Arbeitsfläche",
|
||||
"missingCanvaInitImage": "Fehlendes Startbild auf der Arbeitsfläche",
|
||||
"ipAdapterModelDescription": "IP-Adapter-Modellfeld",
|
||||
"latentsPolymorphicDescription": "Zwischen Nodes können Latents weitergegeben werden.",
|
||||
"loadingNodes": "Lade Nodes...",
|
||||
"latentsCollectionDescription": "Zwischen Knoten können Latents weitergegeben werden.",
|
||||
"mismatchedVersion": "Ungültiger Knoten: Knoten {{node}} vom Typ {{type}} hat keine passende Version (Update versuchen?)",
|
||||
"colorCollectionDescription": "Zu erledigen",
|
||||
"ipAdapterPolymorphicDescription": "Eine Sammlung von IP-Adaptern.",
|
||||
"fullyContainNodesHelp": "Nodes müssen vollständig innerhalb der Auswahlbox sein, um ausgewählt werden zu können",
|
||||
"latentsFieldDescription": "Zwischen Nodes können Latents weitergegeben werden.",
|
||||
"noWorkflow": "Kein Workflow",
|
||||
"hideGraphNodes": "Graph Overlay verbergen",
|
||||
"sourceNode": "Quellknoten",
|
||||
"executionStateError": "Fehler",
|
||||
"latentsCollection": "Latents Sammlung",
|
||||
"maybeIncompatible": "Möglicherweise inkompatibel mit installierten",
|
||||
"nodePack": "Knoten-Pack",
|
||||
"skippingUnknownOutputType": "Überspringe unbekannten Ausgabe-Feldtyp",
|
||||
"loadWorkflow": "Lade Workflow",
|
||||
"snapToGrid": "Am Gitternetz einrasten",
|
||||
"skippingReservedFieldType": "Überspringe reservierten Feldtyp",
|
||||
"loRAModelField": "LoRA",
|
||||
"loRAModelFieldDescription": "Zu erledigen",
|
||||
"mainModelField": "Modell",
|
||||
"doesNotExist": "existiert nicht",
|
||||
"vaeField": "VAE",
|
||||
"unknownOutput": "Unbekannte Ausgabe: {{name}}",
|
||||
"updateNode": "Knoten updaten",
|
||||
"edge": "Rand / Kante",
|
||||
"sourceNodeDoesNotExist": "Ungültiger Rand: Quell- / Ausgabe-Knoten {{node}} existiert nicht",
|
||||
"updateAllNodes": "Update Knoten",
|
||||
"allNodesUpdated": "Alle Knoten aktualisiert",
|
||||
"unknownTemplate": "Unbekannte Vorlage",
|
||||
"floatDescription": "Floats sind Zahlen mit einem Dezimalpunkt.",
|
||||
"updateApp": "Update App",
|
||||
"vaeFieldDescription": "VAE Submodell.",
|
||||
"unknownInput": "Unbekannte Eingabe: {{name}}",
|
||||
"unknownNodeType": "Unbekannter Knotentyp",
|
||||
"float": "Kommazahlen"
|
||||
},
|
||||
"hrf": {
|
||||
"enableHrf": "Aktivieren Sie die Korrektur für hohe Auflösungen",
|
||||
"enableHrf": "Korrektur für hohe Auflösungen",
|
||||
"upscaleMethod": "Vergrößerungsmethoden",
|
||||
"enableHrfTooltip": "Generieren Sie mit einer niedrigeren Anfangsauflösung, skalieren Sie auf die Basisauflösung hoch und führen Sie dann Image-to-Image aus.",
|
||||
"metadata": {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import IAIDndImage from 'common/components/IAIDndImage';
|
||||
import IAIDndImageIcon from 'common/components/IAIDndImageIcon';
|
||||
import { roundToMultiple } from 'common/util/roundDownToMultiple';
|
||||
import { setBoundingBoxDimensions } from 'features/canvas/store/canvasSlice';
|
||||
import { useControlAdapterControlImage } from 'features/controlAdapters/hooks/useControlAdapterControlImage';
|
||||
import { useControlAdapterProcessedControlImage } from 'features/controlAdapters/hooks/useControlAdapterProcessedControlImage';
|
||||
@@ -91,19 +92,14 @@ const ControlAdapterImagePreview = ({ isSmall, id }: Props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const width = roundToMultiple(controlImage.width, 8);
|
||||
const height = roundToMultiple(controlImage.height, 8);
|
||||
|
||||
if (activeTabName === 'unifiedCanvas') {
|
||||
dispatch(
|
||||
setBoundingBoxDimensions(
|
||||
{
|
||||
width: controlImage.width,
|
||||
height: controlImage.height,
|
||||
},
|
||||
optimalDimension
|
||||
)
|
||||
);
|
||||
dispatch(setBoundingBoxDimensions({ width, height }, optimalDimension));
|
||||
} else {
|
||||
dispatch(widthChanged(controlImage.width));
|
||||
dispatch(heightChanged(controlImage.height));
|
||||
dispatch(widthChanged(width));
|
||||
dispatch(heightChanged(height));
|
||||
}
|
||||
}, [controlImage, activeTabName, dispatch, optimalDimension]);
|
||||
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
import type { ContextMenuProps } from '@invoke-ai/ui-library';
|
||||
import { ContextMenu, MenuGroup, MenuItem, MenuList } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useFieldInputKind } from 'features/nodes/hooks/useFieldInputKind';
|
||||
import { useFieldLabel } from 'features/nodes/hooks/useFieldLabel';
|
||||
import { useFieldTemplateTitle } from 'features/nodes/hooks/useFieldTemplateTitle';
|
||||
import {
|
||||
selectWorkflowSlice,
|
||||
workflowExposedFieldAdded,
|
||||
workflowExposedFieldRemoved,
|
||||
} from 'features/nodes/store/workflowSlice';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiMinusBold, PiPlusBold } from 'react-icons/pi';
|
||||
|
||||
type Props = {
|
||||
nodeId: string;
|
||||
fieldName: string;
|
||||
kind: 'input' | 'output';
|
||||
children: ContextMenuProps<HTMLDivElement>['children'];
|
||||
};
|
||||
|
||||
const FieldContextMenu = ({ nodeId, fieldName, kind, children }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const label = useFieldLabel(nodeId, fieldName);
|
||||
const fieldTemplateTitle = useFieldTemplateTitle(nodeId, fieldName, kind);
|
||||
const input = useFieldInputKind(nodeId, fieldName);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const selectIsExposed = useMemo(
|
||||
() =>
|
||||
createSelector(selectWorkflowSlice, (workflow) => {
|
||||
return Boolean(workflow.exposedFields.find((f) => f.nodeId === nodeId && f.fieldName === fieldName));
|
||||
}),
|
||||
[fieldName, nodeId]
|
||||
);
|
||||
|
||||
const mayExpose = useMemo(() => input && ['any', 'direct'].includes(input), [input]);
|
||||
|
||||
const isExposed = useAppSelector(selectIsExposed);
|
||||
|
||||
const handleExposeField = useCallback(() => {
|
||||
dispatch(workflowExposedFieldAdded({ nodeId, fieldName }));
|
||||
}, [dispatch, fieldName, nodeId]);
|
||||
|
||||
const handleUnexposeField = useCallback(() => {
|
||||
dispatch(workflowExposedFieldRemoved({ nodeId, fieldName }));
|
||||
}, [dispatch, fieldName, nodeId]);
|
||||
|
||||
const menuItems = useMemo(() => {
|
||||
const menuItems: ReactNode[] = [];
|
||||
if (mayExpose && !isExposed) {
|
||||
menuItems.push(
|
||||
<MenuItem key={`${nodeId}.${fieldName}.expose-field`} icon={<PiPlusBold />} onClick={handleExposeField}>
|
||||
{t('nodes.addLinearView')}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
if (mayExpose && isExposed) {
|
||||
menuItems.push(
|
||||
<MenuItem key={`${nodeId}.${fieldName}.unexpose-field`} icon={<PiMinusBold />} onClick={handleUnexposeField}>
|
||||
{t('nodes.removeLinearView')}
|
||||
</MenuItem>
|
||||
);
|
||||
}
|
||||
return menuItems;
|
||||
}, [fieldName, handleExposeField, handleUnexposeField, isExposed, mayExpose, nodeId, t]);
|
||||
|
||||
const renderMenuFunc = useCallback(
|
||||
() =>
|
||||
!menuItems.length ? null : (
|
||||
<MenuList visibility="visible">
|
||||
<MenuGroup title={label || fieldTemplateTitle || t('nodes.unknownField')}>{menuItems}</MenuGroup>
|
||||
</MenuList>
|
||||
),
|
||||
[fieldTemplateTitle, label, menuItems, t]
|
||||
);
|
||||
|
||||
return <ContextMenu renderMenu={renderMenuFunc}>{children}</ContextMenu>;
|
||||
};
|
||||
|
||||
export default memo(FieldContextMenu);
|
||||
@@ -0,0 +1,67 @@
|
||||
import { IconButton } from '@invoke-ai/ui-library';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import {
|
||||
selectWorkflowSlice,
|
||||
workflowExposedFieldAdded,
|
||||
workflowExposedFieldRemoved,
|
||||
} from 'features/nodes/store/workflowSlice';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PiMinusBold, PiPlusBold } from 'react-icons/pi';
|
||||
|
||||
type Props = {
|
||||
nodeId: string;
|
||||
fieldName: string;
|
||||
};
|
||||
|
||||
const FieldLinearViewToggle = ({ nodeId, fieldName }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { t } = useTranslation();
|
||||
|
||||
const selectIsExposed = useMemo(
|
||||
() =>
|
||||
createSelector(selectWorkflowSlice, (workflow) => {
|
||||
return Boolean(workflow.exposedFields.find((f) => f.nodeId === nodeId && f.fieldName === fieldName));
|
||||
}),
|
||||
[fieldName, nodeId]
|
||||
);
|
||||
|
||||
const isExposed = useAppSelector(selectIsExposed);
|
||||
|
||||
const handleExposeField = useCallback(() => {
|
||||
dispatch(workflowExposedFieldAdded({ nodeId, fieldName }));
|
||||
}, [dispatch, fieldName, nodeId]);
|
||||
|
||||
const handleUnexposeField = useCallback(() => {
|
||||
dispatch(workflowExposedFieldRemoved({ nodeId, fieldName }));
|
||||
}, [dispatch, fieldName, nodeId]);
|
||||
|
||||
if (!isExposed) {
|
||||
return (
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
tooltip={t('nodes.addLinearView')}
|
||||
aria-label={t('nodes.addLinearView')}
|
||||
icon={<PiPlusBold />}
|
||||
onClick={handleExposeField}
|
||||
pointerEvents="auto"
|
||||
size="xs"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
tooltip={t('nodes.removeLinearView')}
|
||||
aria-label={t('nodes.removeLinearView')}
|
||||
icon={<PiMinusBold />}
|
||||
onClick={handleUnexposeField}
|
||||
pointerEvents="auto"
|
||||
size="xs"
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default memo(FieldLinearViewToggle);
|
||||
@@ -4,12 +4,12 @@ import { useDoesInputHaveValue } from 'features/nodes/hooks/useDoesInputHaveValu
|
||||
import { useFieldInputInstance } from 'features/nodes/hooks/useFieldInputInstance';
|
||||
import { useFieldInputTemplate } from 'features/nodes/hooks/useFieldInputTemplate';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { memo, useMemo } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import EditableFieldTitle from './EditableFieldTitle';
|
||||
import FieldContextMenu from './FieldContextMenu';
|
||||
import FieldHandle from './FieldHandle';
|
||||
import FieldLinearViewToggle from './FieldLinearViewToggle';
|
||||
import InputFieldRenderer from './InputFieldRenderer';
|
||||
|
||||
interface Props {
|
||||
@@ -22,6 +22,7 @@ const InputField = ({ nodeId, fieldName }: Props) => {
|
||||
const fieldTemplate = useFieldInputTemplate(nodeId, fieldName);
|
||||
const fieldInstance = useFieldInputInstance(nodeId, fieldName);
|
||||
const doesFieldHaveValue = useDoesInputHaveValue(nodeId, fieldName);
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const { isConnected, isConnectionInProgress, isConnectionStartField, connectionError, shouldDim } =
|
||||
useConnectionState({ nodeId, fieldName, kind: 'input' });
|
||||
@@ -46,6 +47,14 @@ const InputField = ({ nodeId, fieldName }: Props) => {
|
||||
return false;
|
||||
}, [fieldTemplate, isConnected, doesFieldHaveValue]);
|
||||
|
||||
const onMouseEnter = useCallback(() => {
|
||||
setIsHovered(true);
|
||||
}, []);
|
||||
|
||||
const onMouseLeave = useCallback(() => {
|
||||
setIsHovered(false);
|
||||
}, []);
|
||||
|
||||
if (!fieldTemplate || !fieldInstance) {
|
||||
return (
|
||||
<InputFieldWrapper shouldDim={shouldDim}>
|
||||
@@ -87,19 +96,17 @@ const InputField = ({ nodeId, fieldName }: Props) => {
|
||||
return (
|
||||
<InputFieldWrapper shouldDim={shouldDim}>
|
||||
<FormControl isInvalid={isMissingInput} isDisabled={isConnected} orientation="vertical" px={2}>
|
||||
<Flex flexDir="column" w="full" gap={1}>
|
||||
<FieldContextMenu nodeId={nodeId} fieldName={fieldName} kind="input">
|
||||
{(ref) => (
|
||||
<EditableFieldTitle
|
||||
ref={ref}
|
||||
nodeId={nodeId}
|
||||
fieldName={fieldName}
|
||||
kind="input"
|
||||
isMissingInput={isMissingInput}
|
||||
withTooltip
|
||||
/>
|
||||
)}
|
||||
</FieldContextMenu>
|
||||
<Flex flexDir="column" w="full" gap={1} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave}>
|
||||
<Flex>
|
||||
<EditableFieldTitle
|
||||
nodeId={nodeId}
|
||||
fieldName={fieldName}
|
||||
kind="input"
|
||||
isMissingInput={isMissingInput}
|
||||
withTooltip
|
||||
/>
|
||||
{isHovered && <FieldLinearViewToggle nodeId={nodeId} fieldName={fieldName} />}
|
||||
</Flex>
|
||||
<InputFieldRenderer nodeId={nodeId} fieldName={fieldName} />
|
||||
</Flex>
|
||||
</FormControl>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { RootState } from 'app/store/store';
|
||||
import type { LoRAMetadataItem } from 'features/nodes/types/metadata';
|
||||
import { zLoRAMetadataItem } from 'features/nodes/types/metadata';
|
||||
import { forEach, size } from 'lodash-es';
|
||||
import { filter, size } from 'lodash-es';
|
||||
import type { NonNullableGraph, SDXLLoraLoaderInvocation } from 'services/api/types';
|
||||
|
||||
import {
|
||||
@@ -31,8 +31,8 @@ export const addSDXLLoRAsToGraph = (
|
||||
* So we need to inject a LoRA chain into the graph.
|
||||
*/
|
||||
|
||||
const { loras } = state.lora;
|
||||
const loraCount = size(loras);
|
||||
const enabledLoRAs = filter(state.lora.loras, (l) => l.isEnabled ?? false);
|
||||
const loraCount = size(enabledLoRAs);
|
||||
|
||||
if (loraCount === 0) {
|
||||
return;
|
||||
@@ -59,7 +59,7 @@ export const addSDXLLoRAsToGraph = (
|
||||
let lastLoraNodeId = '';
|
||||
let currentLoraIndex = 0;
|
||||
|
||||
forEach(loras, (lora) => {
|
||||
enabledLoRAs.forEach((lora) => {
|
||||
const { model_name, base_model, weight } = lora;
|
||||
const currentLoraNodeId = `${LORA_LOADER}_${model_name.replace('.', '_')}`;
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ export const buildCanvasImageToImageGraph = (state: RootState, initialImage: Ima
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 1 - strength,
|
||||
|
||||
@@ -58,6 +58,7 @@ export const buildCanvasInpaintGraph = (
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
@@ -152,6 +153,7 @@ export const buildCanvasInpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - strength,
|
||||
denoising_end: 1,
|
||||
@@ -175,6 +177,7 @@ export const buildCanvasInpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: canvasCoherenceSteps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - canvasCoherenceStrength,
|
||||
denoising_end: 1,
|
||||
|
||||
@@ -60,6 +60,7 @@ export const buildCanvasOutpaintGraph = (
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
img2imgStrength: strength,
|
||||
@@ -161,6 +162,7 @@ export const buildCanvasOutpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - strength,
|
||||
denoising_end: 1,
|
||||
@@ -184,6 +186,7 @@ export const buildCanvasOutpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: canvasCoherenceSteps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - canvasCoherenceStrength,
|
||||
denoising_end: 1,
|
||||
|
||||
@@ -124,6 +124,7 @@ export const buildCanvasSDXLImageToImageGraph = (state: RootState, initialImage:
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
|
||||
@@ -60,6 +60,7 @@ export const buildCanvasSDXLInpaintGraph = (
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
seed,
|
||||
@@ -151,6 +152,7 @@ export const buildCanvasSDXLInpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
@@ -174,6 +176,7 @@ export const buildCanvasSDXLInpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: canvasCoherenceSteps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - canvasCoherenceStrength,
|
||||
denoising_end: 1,
|
||||
|
||||
@@ -62,6 +62,7 @@ export const buildCanvasSDXLOutpaintGraph = (
|
||||
negativePrompt,
|
||||
model,
|
||||
cfgScale: cfg_scale,
|
||||
cfgRescaleMultiplier: cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
seed,
|
||||
@@ -160,6 +161,7 @@ export const buildCanvasSDXLOutpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: steps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
denoising_end: refinerModel ? refinerStart : 1,
|
||||
@@ -183,6 +185,7 @@ export const buildCanvasSDXLOutpaintGraph = (
|
||||
is_intermediate,
|
||||
steps: canvasCoherenceSteps,
|
||||
cfg_scale: cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler: scheduler,
|
||||
denoising_start: 1 - canvasCoherenceStrength,
|
||||
denoising_end: 1,
|
||||
|
||||
@@ -117,6 +117,7 @@ export const buildCanvasSDXLTextToImageGraph = (state: RootState): NonNullableGr
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 0,
|
||||
|
||||
@@ -115,6 +115,7 @@ export const buildCanvasTextToImageGraph = (state: RootState): NonNullableGraph
|
||||
id: DENOISE_LATENTS,
|
||||
is_intermediate,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 0,
|
||||
|
||||
@@ -123,6 +123,7 @@ export const buildLinearImageToImageGraph = (state: RootState): NonNullableGraph
|
||||
type: 'denoise_latents',
|
||||
id: DENOISE_LATENTS,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 1 - strength,
|
||||
|
||||
@@ -126,6 +126,7 @@ export const buildLinearSDXLImageToImageGraph = (state: RootState): NonNullableG
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: refinerModel ? Math.min(refinerStart, 1 - strength) : 1 - strength,
|
||||
|
||||
@@ -109,6 +109,7 @@ export const buildLinearSDXLTextToImageGraph = (state: RootState): NonNullableGr
|
||||
type: 'denoise_latents',
|
||||
id: SDXL_DENOISE_LATENTS,
|
||||
cfg_scale,
|
||||
cfg_rescale_multiplier,
|
||||
scheduler,
|
||||
steps,
|
||||
denoising_start: 0,
|
||||
|
||||
@@ -23,7 +23,7 @@ import ParamMainModelSelect from 'features/parameters/components/MainModel/Param
|
||||
import { selectGenerationSlice } from 'features/parameters/store/generationSlice';
|
||||
import { useExpanderToggle } from 'features/settingsAccordions/hooks/useExpanderToggle';
|
||||
import { useStandaloneAccordionToggle } from 'features/settingsAccordions/hooks/useStandaloneAccordionToggle';
|
||||
import { filter, size } from 'lodash-es';
|
||||
import { filter } from 'lodash-es';
|
||||
import { memo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -33,7 +33,7 @@ const formLabelProps: FormLabelProps = {
|
||||
|
||||
const badgesSelector = createMemoizedSelector(selectLoraSlice, selectGenerationSlice, (lora, generation) => {
|
||||
const enabledLoRAsCount = filter(lora.loras, (l) => !!l.isEnabled).length;
|
||||
const loraTabBadges = size(lora.loras) ? [enabledLoRAsCount] : [];
|
||||
const loraTabBadges = enabledLoRAsCount ? [enabledLoRAsCount] : [];
|
||||
const accordionBadges: (string | number)[] = [];
|
||||
if (generation.model) {
|
||||
accordionBadges.push(generation.model.model_name);
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "3.6.2"
|
||||
__version__ = "3.6.3"
|
||||
|
||||
@@ -34,7 +34,6 @@ classifiers = [
|
||||
dependencies = [
|
||||
# Core generation dependencies, pinned for reproducible builds.
|
||||
"accelerate==0.26.1",
|
||||
"basicsr==1.4.2",
|
||||
"clip_anytorch==2.5.2", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip",
|
||||
"compel==2.0.2",
|
||||
"controlnet-aux==0.0.7",
|
||||
@@ -55,13 +54,13 @@ dependencies = [
|
||||
"transformers==4.37.2",
|
||||
|
||||
# Core application dependencies, pinned for reproducible builds.
|
||||
"fastapi-events==0.10.0",
|
||||
"fastapi==0.108.0",
|
||||
"fastapi-events==0.10.1",
|
||||
"fastapi==0.109.2",
|
||||
"huggingface-hub==0.20.3",
|
||||
"pydantic-settings==2.1.0",
|
||||
"pydantic==2.5.3",
|
||||
"python-socketio==5.11.0",
|
||||
"uvicorn[standard]==0.25.0",
|
||||
"pydantic==2.6.1",
|
||||
"python-socketio==5.11.1",
|
||||
"uvicorn[standard]==0.27.1",
|
||||
|
||||
# Auxiliary dependencies, pinned only if necessary.
|
||||
"albumentations",
|
||||
@@ -111,7 +110,7 @@ dependencies = [
|
||||
]
|
||||
"dev" = ["jurigged", "pudb", "snakeviz", "gprof2dot"]
|
||||
"test" = [
|
||||
"ruff==0.1.11",
|
||||
"ruff==0.2.1",
|
||||
"ruff-lsp",
|
||||
"mypy",
|
||||
"pre-commit",
|
||||
@@ -140,7 +139,7 @@ dependencies = [
|
||||
"invokeai-merge2" = "invokeai.frontend.merge.merge_diffusers2:main"
|
||||
"invokeai-ti" = "invokeai.frontend.training:invokeai_textual_inversion"
|
||||
"invokeai-model-install" = "invokeai.frontend.install.model_install:main"
|
||||
"invokeai-model-install2" = "invokeai.frontend.install.model_install2:main" # will eventually be renamed to invokeai-model-install
|
||||
"invokeai-model-install2" = "invokeai.frontend.install.model_install2:main" # will eventually be renamed to invokeai-model-install
|
||||
"invokeai-migrate3" = "invokeai.backend.install.migrate_to_3:main"
|
||||
"invokeai-update" = "invokeai.frontend.install.invokeai_update:main"
|
||||
"invokeai-metadata" = "invokeai.backend.image_util.invoke_metadata:main"
|
||||
@@ -207,13 +206,6 @@ output = "coverage/index.xml"
|
||||
#=== Begin: Ruff
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
ignore = [
|
||||
"E501", # https://docs.astral.sh/ruff/rules/line-too-long/
|
||||
"C901", # https://docs.astral.sh/ruff/rules/complex-structure/
|
||||
"B008", # https://docs.astral.sh/ruff/rules/function-call-in-default-argument/
|
||||
"B904", # https://docs.astral.sh/ruff/rules/raise-without-from-inside-except/
|
||||
]
|
||||
select = ["B", "C", "E", "F", "W", "I"]
|
||||
exclude = [
|
||||
".git",
|
||||
"__pycache__",
|
||||
@@ -222,6 +214,15 @@ exclude = [
|
||||
"invokeai/frontend/web/node_modules/",
|
||||
".venv*",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
ignore = [
|
||||
"E501", # https://docs.astral.sh/ruff/rules/line-too-long/
|
||||
"C901", # https://docs.astral.sh/ruff/rules/complex-structure/
|
||||
"B008", # https://docs.astral.sh/ruff/rules/function-call-in-default-argument/
|
||||
"B904", # https://docs.astral.sh/ruff/rules/raise-without-from-inside-except/
|
||||
]
|
||||
select = ["B", "C", "E", "F", "W", "I"]
|
||||
#=== End: Ruff
|
||||
|
||||
#=== Begin: MyPy
|
||||
|
||||
Reference in New Issue
Block a user