Compare commits

..

24 Commits

Author SHA1 Message Date
psychedelicious
83e33a4810 chore: bump version to v5.6.0 2025-01-21 17:58:47 +11:00
psychedelicious
e635028477 chore(ui): update whats new copy 2025-01-21 17:58:47 +11:00
psychedelicious
b7b8f8a9e5 fix(nodes): remove WithMetadata from non-image-outputting node 2025-01-21 17:58:47 +11:00
psychedelicious
e926d2f24b fix(nodes): add beta classification to new inpainting support nodes 2025-01-21 17:58:47 +11:00
psychedelicious
ad8885c456 chore(ui): typegen 2025-01-21 17:45:32 +11:00
psychedelicious
cf4c79fe2e feat(nodes): add PasteImageIntoBoundingBoxInvocation 2025-01-21 17:45:32 +11:00
psychedelicious
e0edfe6c40 feat(nodes): add CropImageToBoundingBoxInvocation 2025-01-21 17:45:32 +11:00
psychedelicious
8a0a37191a feat(nodes): add GetMaskBoundingBoxInvocation 2025-01-21 17:45:32 +11:00
psychedelicious
7dbd5f150a feat(nodes): add BoundingBoxField.tuple() to get bbox as PIL tuple 2025-01-21 17:45:32 +11:00
psychedelicious
1ad65ffd53 feat(nodes): re-title "Mask from ID" -> "Mask from Segmented Image" 2025-01-21 17:45:32 +11:00
psychedelicious
14b5c871dc feat(nodes): simplify MaskFromIDInvocation 2025-01-21 17:45:32 +11:00
psychedelicious
8d2b4e2bf5 feat(nodes): support FLUX, SD3 in ideal_size 2025-01-21 17:45:32 +11:00
psychedelicious
aba70eacab fix(ui): field handle positioning for non-batch fields
Accidentally overwrote some reactflow styles which caused field handles to be positioned differently for non-batch fields. Just a minor visual issue.
2025-01-21 11:49:49 +11:00
Riccardo Giovanetti
4b67175b1b translationBot(ui): update translation (Italian)
Currently translated at 99.1% (1690 of 1704 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Hosted Weblate
e3423d1ba8 translationBot(ui): update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Linos
87fb00ff5d translationBot(ui): update translation (Vietnamese)
Currently translated at 100.0% (1697 of 1697 strings)

translationBot(ui): update translation (Vietnamese)

Currently translated at 99.2% (1684 of 1697 strings)

translationBot(ui): update translation (Vietnamese)

Currently translated at 99.7% (1676 of 1681 strings)

translationBot(ui): update translation (Vietnamese)

Currently translated at 99.3% (1670 of 1681 strings)

translationBot(ui): update translation (Vietnamese)

Currently translated at 99.5% (1658 of 1666 strings)

translationBot(ui): update translation (Vietnamese)

Currently translated at 100.0% (1652 of 1652 strings)

Co-authored-by: Linos <linos.coding@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/vi/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Riccardo Giovanetti
d99a9ffb72 translationBot(ui): update translation (Italian)
Currently translated at 99.3% (1642 of 1652 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Hosted Weblate
7964f438dc translationBot(ui): update translation files
Updated by "Cleanup translation files" hook in Weblate.

Co-authored-by: Hosted Weblate <hosted@weblate.org>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Linos
b130a3a9ee translationBot(ui): update translation (Vietnamese)
Currently translated at 100.0% (1652 of 1652 strings)

Co-authored-by: Linos <linos.coding@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/vi/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
Riccardo Giovanetti
a6b32160b2 translationBot(ui): update translation (Italian)
Currently translated at 99.3% (1642 of 1652 strings)

translationBot(ui): update translation (Italian)

Currently translated at 99.3% (1641 of 1652 strings)

Co-authored-by: Riccardo Giovanetti <riccardo.giovanetti@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/invokeai/web-ui/it/
Translation: InvokeAI/Web UI
2025-01-21 09:12:45 +11:00
psychedelicious
7d110cc9d3 fix(ui): disable dynamic prompts generators pending resolution of infinite recursion issue
Dynamic prompts string generators can cause an infinite feedback loop when added to the linear view.

The root cause is how these generators handle "resolving" their collections. They hit the dynamic prompts HTTP API within the view component to get the prompts, then set the batch node's internal state with those values.

When the same generator is rendered in both the node editor view and linear view and the timing is just right, that state update causes an infinite feedback loop between the two components as they respond to the state updates from the other component.

The other generators never store the generated values in the batch node's internal state. The values are "resolved" just-in-time as they are needed.

To fix this, the batch value "resolver" utilities could be made async and hit the API. But there's a problem - the resolver utilities are used within the "are we ready to invoke? are there any problems with the current settings?" redux selectors, which are strictly synchronous. To fix that, we can refactor that "are we ready to invoke?" logic to not use redux selectors, so the whole thing could be async.

It's not a big change but I'm not going to spend time on it at the moment.

So, until I address this, the dynamic prompts generators are disabled.
2025-01-21 09:00:40 +11:00
psychedelicious
82122645e8 refactor(ui): organize special handling for batch field types 2025-01-21 07:17:29 +11:00
psychedelicious
f5c5b73383 fix(ui): string batch nodes' inputs get batch type 2025-01-21 07:17:29 +11:00
psychedelicious
2b2ec67cd6 fix(nodes): allow connection input on string batch nodes 2025-01-21 07:17:29 +11:00
16 changed files with 502 additions and 92 deletions

View File

@@ -83,7 +83,9 @@ class StringBatchInvocation(BaseBatchInvocation):
"""Create a batched generation, where the workflow is executed once for each string in the batch."""
strings: list[str] = InputField(
default=[], min_length=1, description="The strings to batch over", input=Input.Direct
default=[],
min_length=1,
description="The strings to batch over",
)
def invoke(self, context: InvocationContext) -> StringOutput:

View File

@@ -300,6 +300,13 @@ class BoundingBoxField(BaseModel):
raise ValueError(f"y_min ({self.y_min}) is greater than y_max ({self.y_max}).")
return self
def tuple(self) -> Tuple[int, int, int, int]:
"""
Returns the bounding box as a tuple suitable for use with PIL's `Image.crop()` method.
This method returns a tuple of the form (left, upper, right, lower) == (x_min, y_min, x_max, y_max).
"""
return (self.x_min, self.y_min, self.x_max, self.y_max)
class MetadataField(RootModel[dict[str, Any]]):
"""

View File

@@ -21,7 +21,7 @@ class IdealSizeOutput(BaseInvocationOutput):
"ideal_size",
title="Ideal Size",
tags=["latents", "math", "ideal_size"],
version="1.0.3",
version="1.0.4",
)
class IdealSizeInvocation(BaseInvocation):
"""Calculates the ideal size for generation to avoid duplication"""
@@ -41,11 +41,16 @@ class IdealSizeInvocation(BaseInvocation):
def invoke(self, context: InvocationContext) -> IdealSizeOutput:
unet_config = context.models.get_config(self.unet.unet.key)
aspect = self.width / self.height
dimension: float = 512
if unet_config.base == BaseModelType.StableDiffusion2:
if unet_config.base == BaseModelType.StableDiffusion1:
dimension = 512
elif unet_config.base == BaseModelType.StableDiffusion2:
dimension = 768
elif unet_config.base == BaseModelType.StableDiffusionXL:
elif unet_config.base in (BaseModelType.StableDiffusionXL, BaseModelType.Flux, BaseModelType.StableDiffusion3):
dimension = 1024
else:
raise ValueError(f"Unsupported model type: {unet_config.base}")
dimension = dimension * self.multiplier
min_dimension = math.floor(dimension * 0.5)
model_area = dimension * dimension # hardcoded for now since all models are trained on square images

View File

@@ -13,6 +13,7 @@ from invokeai.app.invocations.baseinvocation import (
)
from invokeai.app.invocations.constants import IMAGE_MODES
from invokeai.app.invocations.fields import (
BoundingBoxField,
ColorField,
FieldDescriptions,
ImageField,
@@ -997,10 +998,10 @@ class CanvasPasteBackInvocation(BaseInvocation, WithMetadata, WithBoard):
@invocation(
"mask_from_id",
title="Mask from ID",
title="Mask from Segmented Image",
tags=["image", "mask", "id"],
category="image",
version="1.0.0",
version="1.0.1",
)
class MaskFromIDInvocation(BaseInvocation, WithMetadata, WithBoard):
"""Generate a mask for a particular color in an ID Map"""
@@ -1010,40 +1011,24 @@ class MaskFromIDInvocation(BaseInvocation, WithMetadata, WithBoard):
threshold: int = InputField(default=100, description="Threshold for color detection")
invert: bool = InputField(default=False, description="Whether or not to invert the mask")
def rgba_to_hex(self, rgba_color: tuple[int, int, int, int]):
r, g, b, a = rgba_color
hex_code = "#{:02X}{:02X}{:02X}{:02X}".format(r, g, b, int(a * 255))
return hex_code
def invoke(self, context: InvocationContext) -> ImageOutput:
image = context.images.get_pil(self.image.image_name, mode="RGBA")
def id_to_mask(self, id_mask: Image.Image, color: tuple[int, int, int, int], threshold: int = 100):
if id_mask.mode != "RGB":
id_mask = id_mask.convert("RGB")
# Can directly just use the tuple but I'll leave this rgba_to_hex here
# incase anyone prefers using hex codes directly instead of the color picker
hex_color_str = self.rgba_to_hex(color)
rgb_color = numpy.array([int(hex_color_str[i : i + 2], 16) for i in (1, 3, 5)])
np_color = numpy.array(self.color.tuple())
# Maybe there's a faster way to calculate this distance but I can't think of any right now.
color_distance = numpy.linalg.norm(id_mask - rgb_color, axis=-1)
color_distance = numpy.linalg.norm(image - np_color, axis=-1)
# Create a mask based on the threshold and the distance calculated above
binary_mask = (color_distance < threshold).astype(numpy.uint8) * 255
binary_mask = (color_distance < self.threshold).astype(numpy.uint8) * 255
# Convert the mask back to PIL
binary_mask_pil = Image.fromarray(binary_mask)
return binary_mask_pil
def invoke(self, context: InvocationContext) -> ImageOutput:
image = context.images.get_pil(self.image.image_name)
mask = self.id_to_mask(image, self.color.tuple(), self.threshold)
if self.invert:
mask = ImageOps.invert(mask)
binary_mask_pil = ImageOps.invert(binary_mask_pil)
image_dto = context.images.save(image=mask, image_category=ImageCategory.MASK)
image_dto = context.images.save(image=binary_mask_pil, image_category=ImageCategory.MASK)
return ImageOutput.build(image_dto)
@@ -1154,3 +1139,59 @@ class ImageNoiseInvocation(BaseInvocation, WithMetadata, WithBoard):
image_dto = context.images.save(image=noisy_image)
return ImageOutput.build(image_dto)
@invocation(
"crop_image_to_bounding_box",
title="Crop Image to Bounding Box",
category="image",
version="1.0.0",
tags=["image", "crop"],
classification=Classification.Beta,
)
class CropImageToBoundingBoxInvocation(BaseInvocation, WithMetadata, WithBoard):
"""Crop an image to the given bounding box. If the bounding box is omitted, the image is cropped to the non-transparent pixels."""
image: ImageField = InputField(description="The image to crop")
bounding_box: BoundingBoxField | None = InputField(
default=None, description="The bounding box to crop the image to"
)
def invoke(self, context: InvocationContext) -> ImageOutput:
image = context.images.get_pil(self.image.image_name)
bounding_box = self.bounding_box.tuple() if self.bounding_box is not None else image.getbbox()
cropped_image = image.crop(bounding_box)
image_dto = context.images.save(image=cropped_image)
return ImageOutput.build(image_dto)
@invocation(
"paste_image_into_bounding_box",
title="Paste Image into Bounding Box",
category="image",
version="1.0.0",
tags=["image", "crop"],
classification=Classification.Beta,
)
class PasteImageIntoBoundingBoxInvocation(BaseInvocation, WithMetadata, WithBoard):
"""Paste the source image into the target image at the given bounding box.
The source image must be the same size as the bounding box, and the bounding box must fit within the target image."""
source_image: ImageField = InputField(description="The image to paste")
target_image: ImageField = InputField(description="The image to paste into")
bounding_box: BoundingBoxField = InputField(description="The bounding box to paste the image into")
def invoke(self, context: InvocationContext) -> ImageOutput:
source_image = context.images.get_pil(self.source_image.image_name, mode="RGBA")
target_image = context.images.get_pil(self.target_image.image_name, mode="RGBA")
bounding_box = self.bounding_box.tuple()
target_image.paste(source_image, bounding_box, source_image)
image_dto = context.images.save(image=target_image)
return ImageOutput.build(image_dto)

View File

@@ -2,9 +2,22 @@ import numpy as np
import torch
from PIL import Image
from invokeai.app.invocations.baseinvocation import BaseInvocation, Classification, InvocationContext, invocation
from invokeai.app.invocations.fields import ImageField, InputField, TensorField, WithBoard, WithMetadata
from invokeai.app.invocations.primitives import ImageOutput, MaskOutput
from invokeai.app.invocations.baseinvocation import (
BaseInvocation,
Classification,
InvocationContext,
invocation,
)
from invokeai.app.invocations.fields import (
BoundingBoxField,
ColorField,
ImageField,
InputField,
TensorField,
WithBoard,
WithMetadata,
)
from invokeai.app.invocations.primitives import BoundingBoxOutput, ImageOutput, MaskOutput
from invokeai.backend.image_util.util import pil_to_np
@@ -201,3 +214,48 @@ class ApplyMaskTensorToImageInvocation(BaseInvocation, WithMetadata, WithBoard):
image_dto = context.images.save(image=masked_image)
return ImageOutput.build(image_dto)
WHITE = ColorField(r=255, g=255, b=255, a=255)
@invocation(
"get_image_mask_bounding_box",
title="Get Image Mask Bounding Box",
tags=["mask"],
category="mask",
version="1.0.0",
classification=Classification.Beta,
)
class GetMaskBoundingBoxInvocation(BaseInvocation):
"""Gets the bounding box of the given mask image."""
mask: ImageField = InputField(description="The mask to crop.")
margin: int = InputField(default=0, description="Margin to add to the bounding box.")
mask_color: ColorField = InputField(default=WHITE, description="Color of the mask in the image.")
def invoke(self, context: InvocationContext) -> BoundingBoxOutput:
mask = context.images.get_pil(self.mask.image_name, mode="RGBA")
mask_np = np.array(mask)
# Convert mask_color to RGBA tuple
mask_color_rgb = self.mask_color.tuple()
# Find the bounding box of the mask color
y, x = np.where(np.all(mask_np == mask_color_rgb, axis=-1))
if len(x) == 0 or len(y) == 0:
# No pixels found with the given color
return BoundingBoxOutput(bounding_box=BoundingBoxField(x_min=0, y_min=0, x_max=0, y_max=0))
left, upper, right, lower = x.min(), y.min(), x.max(), y.max()
# Add the margin
left = max(0, left - self.margin)
upper = max(0, upper - self.margin)
right = min(mask_np.shape[1], right + self.margin)
lower = min(mask_np.shape[0], lower + self.margin)
bounding_box = BoundingBoxField(x_min=left, y_min=upper, x_max=right, y_max=lower)
return BoundingBoxOutput(bounding_box=bounding_box)

View File

@@ -219,6 +219,9 @@ class FluxCheckpointModel(ModelLoader):
assert isinstance(config, MainCheckpointConfig)
model_path = Path(config.path)
with accelerate.init_empty_weights():
model = Flux(params[config.config_path])
sd = load_file(model_path)
if "model.diffusion_model.double_blocks.0.img_attn.norm.key_norm.scale" in sd:
sd = convert_bundle_to_flux_transformer_checkpoint(sd)
@@ -227,11 +230,6 @@ class FluxCheckpointModel(ModelLoader):
for k in sd.keys():
# We need to cast to bfloat16 due to it being the only currently supported dtype for inference
sd[k] = sd[k].to(torch.bfloat16)
flux_params = infer_flux_params_from_state_dict(sd)
with accelerate.init_empty_weights():
model = Flux(flux_params)
model.load_state_dict(sd, assign=True)
return model

View File

@@ -2196,7 +2196,13 @@
},
"whatsNew": {
"whatsNewInInvoke": "What's New in Invoke",
"items": ["Low-VRAM mode", "Dynamic memory management", "Faster model loading times", "Fewer memory errors"],
"items": [
"Low-VRAM mode",
"Dynamic memory management",
"Faster model loading times",
"Fewer memory errors",
"Expanded workflow batch capabilities"
],
"readReleaseNotes": "Read Release Notes",
"watchRecentReleaseVideos": "Watch Recent Release Videos",
"watchUiUpdatesOverview": "Watch UI Updates Overview"

View File

@@ -901,9 +901,7 @@
}
},
"newUserExperience": {
"downloadStarterModels": "Descargar modelos de inicio",
"toGetStarted": "Para empezar, introduzca un mensaje en el cuadro y haga clic en <StrongComponent>Invocar</StrongComponent> para generar su primera imagen. Seleccione una plantilla para mejorar los resultados. Puede elegir guardar sus imágenes directamente en <StrongComponent>Galería</StrongComponent> o editarlas en <StrongComponent>Lienzo</StrongComponent>.",
"importModels": "Importar modelos",
"noModelsInstalled": "Parece que no tienes ningún modelo instalado",
"gettingStartedSeries": "¿Desea más orientación? Consulte nuestra <LinkComponent>Serie de introducción</LinkComponent> para obtener consejos sobre cómo aprovechar todo el potencial de Invoke Studio.",
"toGetStartedLocal": "Para empezar, asegúrate de descargar o importar los modelos necesarios para ejecutar Invoke. A continuación, introduzca un mensaje en el cuadro y haga clic en <StrongComponent>Invocar</StrongComponent> para generar su primera imagen. Seleccione una plantilla para mejorar los resultados. Puede elegir guardar sus imágenes directamente en <StrongComponent>Galería</StrongComponent> o editarlas en el <StrongComponent>Lienzo</StrongComponent>."

View File

@@ -352,7 +352,6 @@
"noT5EncoderModelSelected": "Aucun modèle T5 Encoder sélectionné pour la génération FLUX",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), la largeur de la bounding box mise à l'échelle est {{width}}",
"canvasIsCompositing": "La toile est en train de composer",
"collectionEmpty": "{{nodeLabel}} -> {{fieldLabel}} collection vide",
"collectionTooFewItems": "{{nodeLabel}} -> {{fieldLabel}} : trop peu d'éléments, minimum {{minItems}}",
"collectionTooManyItems": "{{nodeLabel}} -> {{fieldLabel}} : trop d'éléments, maximum {{maxItems}}",
"canvasIsSelectingObject": "La toile est occupée (sélection d'objet)"
@@ -2171,8 +2170,6 @@
"toGetStarted": "Pour commencer, saisissez un prompt dans la boîte et cliquez sur <StrongComponent>Invoke</StrongComponent> pour générer votre première image. Sélectionnez un template de prompt pour améliorer les résultats. Vous pouvez choisir de sauvegarder vos images directement dans la <StrongComponent>Galerie</StrongComponent> ou de les modifier sur la <StrongComponent>Toile</StrongComponent>.",
"gettingStartedSeries": "Vous souhaitez plus de conseils? Consultez notre <LinkComponent>Série de démarrage</LinkComponent> pour des astuces sur l'exploitation du plein potentiel de l'Invoke Studio.",
"noModelsInstalled": "Il semble qu'aucun modèle ne soit installé",
"downloadStarterModels": "Télécharger les modèles de démarrage",
"importModels": "Importer des Modèles",
"toGetStartedLocal": "Pour commencer, assurez-vous de télécharger ou d'importer des modèles nécessaires pour exécuter Invoke. Ensuite, saisissez le prompt dans la boîte et cliquez sur <StrongComponent>Invoke</StrongComponent> pour générer votre première image. Sélectionnez un template de prompt pour améliorer les résultats. Vous pouvez choisir de sauvegarder vos images directement sur <StrongComponent>Galerie</StrongComponent> ou les modifier sur la <StrongComponent>Toile</StrongComponent>."
},
"upsell": {

View File

@@ -97,7 +97,14 @@
"ok": "Ok",
"generating": "Generazione",
"loadingModel": "Caricamento del modello",
"warnings": "Avvisi"
"warnings": "Avvisi",
"step": "Passo",
"values": "Valori",
"start": "Inizio",
"end": "Fine",
"resetToDefaults": "Ripristina le impostazioni predefinite",
"seed": "Seme",
"combinatorial": "Combinatorio"
},
"gallery": {
"galleryImageSize": "Dimensione dell'immagine",
@@ -668,7 +675,7 @@
"addingImagesTo": "Aggiungi immagini a",
"systemDisconnected": "Sistema disconnesso",
"missingNodeTemplate": "Modello di nodo mancante",
"missingInputForField": "{{nodeLabel}} -> {{fieldLabel}}: ingresso mancante",
"missingInputForField": "ingresso mancante",
"missingFieldTemplate": "Modello di campo mancante",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), altezza riquadro è {{height}}",
"fluxModelIncompatibleBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), larghezza riquadro è {{width}}",
@@ -681,11 +688,22 @@
"canvasIsRasterizing": "La tela è occupata (sta rasterizzando)",
"canvasIsCompositing": "La tela è occupata (in composizione)",
"canvasIsFiltering": "La tela è occupata (sta filtrando)",
"collectionTooManyItems": "{{nodeLabel}} -> {{fieldLabel}}: troppi elementi, massimo {{maxItems}}",
"collectionTooManyItems": "troppi elementi, massimo {{maxItems}}",
"canvasIsSelectingObject": "La tela è occupata (selezione dell'oggetto)",
"collectionTooFewItems": "{{nodeLabel}} -> {{fieldLabel}}: troppi pochi elementi, minimo {{minItems}}",
"collectionEmpty": "{{nodeLabel}} -> {{fieldLabel}} raccolta vuota",
"fluxModelMultipleControlLoRAs": "È possibile utilizzare solo 1 Controllo LoRA alla volta"
"collectionTooFewItems": "troppi pochi elementi, minimo {{minItems}}",
"fluxModelMultipleControlLoRAs": "È possibile utilizzare solo 1 Controllo LoRA alla volta",
"collectionNumberGTMax": "{{value}} > {{maximum}} (incr max)",
"collectionStringTooLong": "troppo lungo, massimo {{maxLength}}",
"batchNodeNotConnected": "Nodo Lotto non connesso: {{label}}",
"batchNodeEmptyCollection": "Alcuni nodi lotto hanno raccolte vuote",
"emptyBatches": "lotti vuoti",
"batchNodeCollectionSizeMismatch": "Le dimensioni della raccolta nel Lotto {{batchGroupId}} non corrispondono",
"invalidBatchConfigurationCannotCalculate": "Configurazione lotto non valida; impossibile calcolare",
"collectionStringTooShort": "troppo corto, minimo {{minLength}}",
"collectionNumberNotMultipleOf": "{{value}} non è multiplo di {{multipleOf}}",
"collectionNumberLTMin": "{{value}} < {{minimum}} (incr min)",
"collectionNumberGTExclusiveMax": "{{value}} >= {{exclusiveMaximum}} (excl max)",
"collectionNumberLTExclusiveMin": "{{value}} <= {{exclusiveMinimum}} (excl min)"
},
"useCpuNoise": "Usa la CPU per generare rumore",
"iterations": "Iterazioni",
@@ -813,7 +831,8 @@
"imagesWillBeAddedTo": "Le immagini caricate verranno aggiunte alle risorse della bacheca {{boardName}}.",
"uploadFailedInvalidUploadDesc_withCount_one": "Devi caricare al massimo 1 immagine PNG o JPEG.",
"uploadFailedInvalidUploadDesc_withCount_many": "Devi caricare al massimo {{count}} immagini PNG o JPEG.",
"uploadFailedInvalidUploadDesc_withCount_other": "Devi caricare al massimo {{count}} immagini PNG o JPEG."
"uploadFailedInvalidUploadDesc_withCount_other": "Devi caricare al massimo {{count}} immagini PNG o JPEG.",
"outOfMemoryErrorDescLocal": "Segui la nostra <LinkComponent>guida per bassa VRAM</LinkComponent> per ridurre gli OOM."
},
"accessibility": {
"invokeProgressBar": "Barra di avanzamento generazione",
@@ -972,7 +991,25 @@
"noWorkflows": "Nessun flusso di lavoro",
"workflowHelpText": "Hai bisogno di aiuto? Consulta la nostra guida <LinkComponent>Introduzione ai flussi di lavoro</LinkComponent>.",
"specialDesc": "Questa invocazione comporta una gestione speciale nell'applicazione. Ad esempio, i nodi Lotto vengono utilizzati per mettere in coda più grafici da un singolo flusso di lavoro.",
"internalDesc": "Questa invocazione è utilizzata internamente da Invoke. Potrebbe subire modifiche significative durante gli aggiornamenti dell'app e potrebbe essere rimossa in qualsiasi momento."
"internalDesc": "Questa invocazione è utilizzata internamente da Invoke. Potrebbe subire modifiche significative durante gli aggiornamenti dell'app e potrebbe essere rimossa in qualsiasi momento.",
"addItem": "Aggiungi elemento",
"generateValues": "Genera valori",
"generatorNoValues": "vuoto",
"linearDistribution": "Distribuzione lineare",
"parseString": "Analizza stringa",
"splitOn": "Diviso su",
"noBatchGroup": "nessun gruppo",
"generatorLoading": "caricamento",
"generatorLoadFromFile": "Carica da file",
"dynamicPromptsRandom": "Prompt dinamici (casuali)",
"dynamicPromptsCombinatorial": "Prompt dinamici (combinatori)",
"floatRangeGenerator": "Generatore di intervalli di numeri in virgola mobile",
"integerRangeGenerator": "Generatore di intervalli di numeri interi",
"uniformRandomDistribution": "Distribuzione casuale uniforme",
"generatorNRandomValues_one": "{{count}} valore casuale",
"generatorNRandomValues_many": "{{count}} valori casuali",
"generatorNRandomValues_other": "{{count}} valori casuali",
"arithmeticSequence": "Sequenza aritmetica"
},
"boards": {
"autoAddBoard": "Aggiungi automaticamente bacheca",
@@ -1138,7 +1175,8 @@
"dynamicPrompts": "Prompt dinamici",
"promptsPreview": "Anteprima dei prompt",
"showDynamicPrompts": "Mostra prompt dinamici",
"loading": "Generazione prompt dinamici..."
"loading": "Generazione prompt dinamici...",
"promptsToGenerate": "Prompt da generare"
},
"popovers": {
"paramScheduler": {
@@ -1907,7 +1945,24 @@
},
"forMoreControl": "Per un maggiore controllo, fare clic su Avanzate qui sotto.",
"advanced": "Avanzate",
"processingLayerWith": "Elaborazione del livello con il filtro {{type}}."
"processingLayerWith": "Elaborazione del livello con il filtro {{type}}.",
"img_blur": {
"label": "Sfoca immagine",
"description": "Sfoca il livello selezionato.",
"blur_type": "Tipo di sfocatura",
"blur_radius": "Raggio",
"gaussian_type": "Gaussiana"
},
"img_noise": {
"size": "Dimensione del rumore",
"salt_and_pepper_type": "Sale e pepe",
"gaussian_type": "Gaussiano",
"noise_color": "Rumore colorato",
"description": "Aggiunge rumore al livello selezionato.",
"noise_type": "Tipo di rumore",
"label": "Aggiungi rumore",
"noise_amount": "Quantità"
}
},
"controlLayers_withCount_hidden": "Livelli di controllo ({{count}} nascosti)",
"regionalGuidance_withCount_hidden": "Guida regionale ({{count}} nascosti)",
@@ -2166,10 +2221,9 @@
"newUserExperience": {
"gettingStartedSeries": "Desideri maggiori informazioni? Consulta la nostra <LinkComponent>Getting Started Series</LinkComponent> per suggerimenti su come sfruttare appieno il potenziale di Invoke Studio.",
"toGetStarted": "Per iniziare, inserisci un prompt nella casella e fai clic su <StrongComponent>Invoke</StrongComponent> per generare la tua prima immagine. Seleziona un modello di prompt per migliorare i risultati. Puoi scegliere di salvare le tue immagini direttamente nella <StrongComponent>Galleria</StrongComponent> o modificarle nella <StrongComponent>Tela</StrongComponent>.",
"importModels": "Importa modelli",
"downloadStarterModels": "Scarica i modelli per iniziare",
"noModelsInstalled": "Sembra che tu non abbia installato alcun modello",
"toGetStartedLocal": "Per iniziare, assicurati di scaricare o importare i modelli necessari per eseguire Invoke. Quindi, inserisci un prompt nella casella e fai clic su <StrongComponent>Invoke</StrongComponent> per generare la tua prima immagine. Seleziona un modello di prompt per migliorare i risultati. Puoi scegliere di salvare le tue immagini direttamente nella <StrongComponent>Galleria</StrongComponent> o modificarle nella <StrongComponent>Tela</StrongComponent>."
"noModelsInstalled": "Sembra che non hai installato alcun modello! Puoi <DownloadStarterModelsButton>scaricare un pacchetto di modelli di avvio</DownloadStarterModelsButton> o <ImportModelsButton>importare modelli</ImportModelsButton>.",
"toGetStartedLocal": "Per iniziare, assicurati di scaricare o importare i modelli necessari per eseguire Invoke. Quindi, inserisci un prompt nella casella e fai clic su <StrongComponent>Invoke</StrongComponent> per generare la tua prima immagine. Seleziona un modello di prompt per migliorare i risultati. Puoi scegliere di salvare le tue immagini direttamente nella <StrongComponent>Galleria</StrongComponent> o modificarle nella <StrongComponent>Tela</StrongComponent>.",
"lowVRAMMode": "Per prestazioni ottimali, segui la nostra <LinkComponent>guida per bassa VRAM</LinkComponent>."
},
"whatsNew": {
"whatsNewInInvoke": "Novità in Invoke",
@@ -2177,7 +2231,10 @@
"watchRecentReleaseVideos": "Guarda i video su questa versione",
"watchUiUpdatesOverview": "Guarda le novità dell'interfaccia",
"items": [
"<StrongComponent>Livelli di controllo Flux</StrongComponent>: nuovi modelli di controllo per il rilevamento dei bordi e la mappatura della profondità sono ora supportati per i modelli di Flux dev."
"Modalità Bassa-VRAM",
"Gestione dinamica della memoria",
"Tempi di caricamento del modello più rapidi",
"Meno errori di memoria"
]
},
"system": {

View File

@@ -220,7 +220,15 @@
"tab": "Tab",
"loadingModel": "Đang Tải Model",
"generating": "Đang Tạo Sinh",
"warnings": "Cảnh Báo"
"warnings": "Cảnh Báo",
"count": "Đếm",
"step": "Bước",
"values": "Giá Trị",
"start": "Bắt Đầu",
"end": "Kết Thúc",
"min": "Tối Thiểu",
"max": "Tối Đa",
"resetToDefaults": "Đặt Lại Về Mặc Định"
},
"prompt": {
"addPromptTrigger": "Thêm Prompt Trigger",
@@ -965,7 +973,19 @@
"outputFieldTypeParseError": "Không thể phân tích loại dữ liệu đầu ra của {{node}}.{{field}} ({{message}})",
"modelAccessError": "Không thể tìm thấy model {{key}}, chuyển về mặc định",
"internalDesc": "Trình kích hoạt này được dùng bên trong bởi Invoke. Nó có thể phá hỏng thay đổi trong khi cập nhật ứng dụng và có thể bị xoá bất cứ lúc nào.",
"specialDesc": "Trình kích hoạt này có một số xử lý đặc biệt trong ứng dụng. Ví dụ, Node Hàng Loạt được dùng để xếp vào nhiều đồ thị từ một workflow."
"specialDesc": "Trình kích hoạt này có một số xử lý đặc biệt trong ứng dụng. Ví dụ, Node Hàng Loạt được dùng để xếp vào nhiều đồ thị từ một workflow.",
"addItem": "Thêm Mục",
"generateValues": "Cho Ra Giá Trị",
"floatRangeGenerator": "Phạm Vị Tạo Ra Số Thực",
"integerRangeGenerator": "Phạm Vị Tạo Ra Số Nguyên",
"linearDistribution": "Phân Bố Tuyến Tính",
"uniformRandomDistribution": "Phân Bố Ngẫu Nhiên Đồng Nhất",
"parseString": "Phân Tích Chuỗi",
"noBatchGroup": "không có nhóm",
"generatorNoValues": "trống",
"splitOn": "Tách Ở",
"arithmeticSequence": "Cấp Số Cộng",
"generatorNRandomValues_other": "{{count}} giá trị ngẫu nhiên"
},
"popovers": {
"paramCFGRescaleMultiplier": {
@@ -1433,13 +1453,24 @@
"missingNodeTemplate": "Thiếu mẫu trình bày node",
"fluxModelIncompatibleBboxHeight": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), chiều dài hộp giới hạn là {{height}}",
"fluxModelIncompatibleScaledBboxWidth": "$t(parameters.invoke.fluxRequiresDimensionsToBeMultipleOf16), tỉ lệ chiều rộng hộp giới hạn là {{width}}",
"missingInputForField": "{{nodeLabel}} -> {{fieldLabel}}: thiếu đầu vào",
"missingInputForField": "thiếu đầu vào",
"missingFieldTemplate": "Thiếu vùng mẫu trình bày",
"collectionEmpty": "{{nodeLabel}} -> {{fieldLabel}} tài nguyên trống",
"collectionTooFewItems": "{{nodeLabel}} -> {{fieldLabel}}: quá ít mục, tối thiểu {{minItems}}",
"collectionTooManyItems": "{{nodeLabel}} -> {{fieldLabel}}: quá nhiều mục, tối đa {{maxItems}}",
"collectionTooFewItems": "quá ít mục, tối thiểu là {{minItems}}",
"collectionTooManyItems": "quá nhiều mục, tối đa là {{maxItems}}",
"canvasIsSelectingObject": "Canvas đang bận (đang chọn đồ vật)",
"fluxModelMultipleControlLoRAs": "Chỉ có thể dùng 1 LoRA Điều Khiển Được"
"fluxModelMultipleControlLoRAs": "Chỉ có thể dùng 1 LoRA Điều Khiển Được",
"collectionStringTooLong": "quá dài, tối đa là {{maxLength}}",
"collectionStringTooShort": "quá ngắn, tối thiểu là {{minLength}}",
"collectionNumberGTMax": "{{value}} > {{maximum}} (giá trị tối đa)",
"collectionNumberLTMin": "{{value}} < {{minimum}} (giá trị tối thiểu)",
"collectionNumberNotMultipleOf": "{{value}} không phải bội của {{multipleOf}}",
"collectionNumberLTExclusiveMin": "{{value}} <= {{exclusiveMinimum}} (giá trị chọn lọc tối thiểu)",
"collectionNumberGTExclusiveMax": "{{value}} >= {{exclusiveMaximum}} (giá trị chọn lọc tối đa)",
"batchNodeCollectionSizeMismatch": "Kích cỡ tài nguyên không phù hợp với Lô {{batchGroupId}}",
"emptyBatches": "lô trống",
"batchNodeNotConnected": "Node Hàng Loạt chưa được kết nối: {{label}}",
"batchNodeEmptyCollection": "Một vài node hàng loạt có tài nguyên rỗng",
"invalidBatchConfigurationCannotCalculate": "Thiết lập lô không hợp lệ; không thể tính toán"
},
"cfgScale": "Thang CFG",
"useSeed": "Dùng Hạt Giống",
@@ -1458,8 +1489,8 @@
"recallMetadata": "Gợi Lại Metadata",
"clipSkip": "CLIP Skip",
"general": "Cài Đặt Chung",
"boxBlur": "Box Blur",
"gaussianBlur": "Gaussian Blur",
"boxBlur": "Làm Mờ Dạng Box",
"gaussianBlur": "Làm Mờ Dạng Gaussian",
"staged": "Staged (Tăng khử nhiễu có hệ thống)",
"scaledHeight": "Tỉ Lệ Dài",
"cancel": {
@@ -1859,7 +1890,25 @@
},
"advanced": "Nâng Cao",
"processingLayerWith": "Đang xử lý layer với bộ lọc {{type}}.",
"forMoreControl": "Để kiểm soát tốt hơn, bấm vào mục Nâng Cao bên dưới."
"forMoreControl": "Để kiểm soát tốt hơn, bấm vào mục Nâng Cao bên dưới.",
"img_blur": {
"description": "Làm mờ layer được chọn.",
"blur_type": "Dạng Làm Mờ",
"blur_radius": "Radius",
"gaussian_type": "Gaussian",
"label": "Làm Mờ Ảnh",
"box_type": "Box"
},
"img_noise": {
"salt_and_pepper_type": "Salt and Pepper",
"noise_amount": "Lượng Nhiễu",
"label": "Độ Nhiễu Ảnh",
"description": "Tăng độ nhiễu vào layer được chọn.",
"noise_type": "Dạng Nhiễu",
"gaussian_type": "Gaussian",
"noise_color": "Màu Nhiễu",
"size": "Cỡ Nhiễu"
}
},
"transform": {
"fitModeCover": "Che Phủ",
@@ -2067,7 +2116,8 @@
"problemCopyingImage": "Không Thể Sao Chép Ảnh",
"problemDownloadingImage": "Không Thể Tải Xuống Ảnh",
"problemCopyingLayer": "Không Thể Sao Chép Layer",
"problemSavingLayer": "Không Thể Lưu Layer"
"problemSavingLayer": "Không Thể Lưu Layer",
"outOfMemoryErrorDescLocal": "Làm theo <LinkComponent>hướng dẫn VRAM Thấp</LinkComponent> của chúng tôi để hạn chế OOM (Tràn bộ nhớ)."
},
"ui": {
"tabs": {
@@ -2153,9 +2203,8 @@
"toGetStartedLocal": "Để bắt đầu, hãy chắc chắn đã tải xuống hoặc thêm vào model cần để chạy Invoke. Sau đó, nhập lệnh vào hộp và nhấp chuột vào <StrongComponent>Kích Hoạt</StrongComponent> để tạo ra bức ảnh đầu tiên. Chọn một mẫu trình bày cho lệnh để cải thiện kết quả. Bạn có thể chọn để lưu ảnh trực tiếp vào <StrongComponent>Thư Viện</StrongComponent> hoặc chỉnh sửa chúng ở <StrongComponent>Canvas</StrongComponent>.",
"gettingStartedSeries": "Cần thêm hướng dẫn? Xem thử <LinkComponent>Bắt Đầu Làm Quen</LinkComponent> để biết thêm mẹo khai thác toàn bộ tiềm năng của Invoke Studio.",
"toGetStarted": "Để bắt đầu, hãy nhập lệnh vào hộp và nhấp chuột vào <StrongComponent>Kích Hoạt</StrongComponent> để tạo ra bức ảnh đầu tiên. Chọn một mẫu trình bày cho lệnh để cải thiện kết quả. Bạn có thể chọn để lưu ảnh trực tiếp vào <StrongComponent>Thư Viện</StrongComponent> hoặc chỉnh sửa chúng ở <StrongComponent>Canvas</StrongComponent>.",
"downloadStarterModels": "Tải Xuống Model Khởi Đầu",
"importModels": "Nhập Vào Model",
"noModelsInstalled": "Hình như bạn không có model nào được tải cả"
"noModelsInstalled": "Dường như bạn chưa tải model nào cả! Bạn có thể <DownloadStarterModelsButton>tải xuống các model khởi đầu</DownloadStarterModelsButton> hoặc <ImportModelsButton>nhập vào thêm model</ImportModelsButton>.",
"lowVRAMMode": "Cho hiệu suất tốt nhất, hãy làm theo <LinkComponent>hướng dẫn VRAM Thấp</LinkComponent> của chúng tôi."
},
"whatsNew": {
"whatsNewInInvoke": "Có Gì Mới Ở Invoke",
@@ -2163,7 +2212,10 @@
"watchRecentReleaseVideos": "Xem Video Phát Hành Mới Nhất",
"watchUiUpdatesOverview": "Xem Tổng Quan Về Những Cập Nhật Cho Giao Diện Người Dùng",
"items": [
"<StrongComponent>Hướng Dẫn Khu Vực FLUX (beta)</StrongComponent>: Bản beta của Hướng Dẫn Khu Vực FLUX của chúng ta đã có mắt tại bảng điều khiển lệnh khu vực."
"Chế độ VRAM thấp",
"Trình quản lý bộ nhớ động",
"Tải model nhanh hơn",
"Ít lỗi bộ nhớ hơn"
]
},
"upsell": {

View File

@@ -38,10 +38,13 @@ const FieldHandle = (props: FieldHandleProps) => {
borderColor: color,
borderRadius: isModelType || type.batch ? 4 : '100%',
zIndex: 1,
transform: type.batch ? 'rotate(45deg) translateX(-0.3rem) translateY(-0.3rem)' : 'none',
transformOrigin: 'center',
};
if (type.batch) {
s.transform = 'rotate(45deg) translateX(-0.3rem) translateY(-0.3rem)';
}
if (handleType === 'target') {
s.insetInlineStart = '-1rem';
} else {

View File

@@ -75,10 +75,10 @@ export const StringGeneratorFieldInputComponent = memo(
<Flex flexDir="column" gap={2}>
<Select className="nowheel nodrag" onChange={onChangeGeneratorType} value={field.value.type} size="sm">
<option value={StringGeneratorParseStringType}>{t('nodes.parseString')}</option>
<option value={StringGeneratorDynamicPromptsRandomType}>{t('nodes.dynamicPromptsRandom')}</option>
{/* <option value={StringGeneratorDynamicPromptsRandomType}>{t('nodes.dynamicPromptsRandom')}</option>
<option value={StringGeneratorDynamicPromptsCombinatorialType}>
{t('nodes.dynamicPromptsCombinatorial')}
</option>
</option> */}
</Select>
{field.value.type === StringGeneratorParseStringType && (
<StringGeneratorParseStringSettings state={field.value} onChange={onChange} />

View File

@@ -64,6 +64,32 @@ const isAllowedOutputField = (nodeType: string, fieldName: string) => {
return true;
};
const isBatchInputField = (nodeType: string, fieldName: string) => {
if (nodeType === 'float_batch' && fieldName === 'floats') {
return true;
}
if (nodeType === 'integer_batch' && fieldName === 'integers') {
return true;
}
if (nodeType === 'string_batch' && fieldName === 'strings') {
return true;
}
return false;
};
const isBatchOutputField = (nodeType: string, fieldName: string) => {
if (nodeType === 'float_generator' && fieldName === 'floats') {
return true;
}
if (nodeType === 'integer_generator' && fieldName === 'integers') {
return true;
}
if (nodeType === 'string_generator' && fieldName === 'strings') {
return true;
}
return false;
};
const isNotInDenylist = (schema: InvocationSchemaObject) =>
!invocationDenylist.includes(schema.properties.type.default);
@@ -128,9 +154,7 @@ export const parseSchema = (
fieldType.originalType = deepClone(originalFieldType);
}
if (type === 'float_batch' && propertyName === 'floats') {
fieldType.batch = true;
} else if (type === 'integer_batch' && propertyName === 'integers') {
if (isBatchInputField(type, propertyName)) {
fieldType.batch = true;
}
@@ -195,11 +219,7 @@ export const parseSchema = (
fieldType.originalType = deepClone(originalFieldType);
}
if (type === 'float_generator' && propertyName === 'floats') {
fieldType.batch = true;
} else if (type === 'integer_generator' && propertyName === 'integers') {
fieldType.batch = true;
} else if (type === 'string_generator' && propertyName === 'strings') {
if (isBatchOutputField(type, propertyName)) {
fieldType.batch = true;
}

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
__version__ = "5.6.0rc4"
__version__ = "5.6.0"