Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
937eb3101d | ||
|
|
ce9eec1735 | ||
|
|
ac23f89b9f | ||
|
|
d00e2073f6 | ||
|
|
79a23faea3 | ||
|
|
1dfe46dad0 | ||
|
|
ed18d9446c | ||
|
|
2573467b1a | ||
|
|
b498b11bd7 | ||
|
|
11a3da87bd |
@@ -1,23 +0,0 @@
|
||||
name: Publish to Comfy registry
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
# if this is a forked repository. Skipping the workflow.
|
||||
if: github.event.repository.fork == false
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@main
|
||||
with:
|
||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
@@ -0,0 +1,601 @@
|
||||
import os
|
||||
import json
|
||||
import random
|
||||
import folder_paths
|
||||
import numpy as np
|
||||
import torch
|
||||
from pathlib import Path
|
||||
from huggingface_hub import snapshot_download
|
||||
from folder_paths import get_filename_list, get_output_directory, models_dir
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
from tqdm import tqdm
|
||||
|
||||
import comfy.utils as utils
|
||||
import mlx.core as mx
|
||||
|
||||
from mflux.config.model_config import ModelConfig
|
||||
from mflux.config.config import Config, ConfigControlnet
|
||||
from mflux.flux.flux import Flux1
|
||||
from mflux.controlnet.controlnet_util import ControlnetUtil
|
||||
from mflux.controlnet.flux_controlnet import Flux1Controlnet
|
||||
from mflux.config.runtime_config import RuntimeConfig
|
||||
from mflux.post_processing.array_util import ArrayUtil
|
||||
from mflux.post_processing.image_util import ImageUtil
|
||||
from mflux.post_processing.generated_image import GeneratedImage
|
||||
from mflux.latent_creator.latent_creator import LatentCreator
|
||||
from mflux.post_processing.stepwise_handler import StepwiseHandler
|
||||
from mflux.controlnet.controlnet_util import ControlnetUtil
|
||||
from mflux.models.vae.vae import VAE
|
||||
|
||||
flux_cache = {}
|
||||
|
||||
def load_or_create_flux(model, quantize, path, lora_paths, lora_scales, is_controlnet=False):
|
||||
key = (model, quantize, path, tuple(lora_paths), tuple(lora_scales), is_controlnet)
|
||||
if key not in flux_cache:
|
||||
flux_cache.clear()
|
||||
FluxClass = Flux1Controlnet if is_controlnet else Flux1
|
||||
flux_cache[key] = FluxClass(
|
||||
model_config=ModelConfig.from_alias(model),
|
||||
quantize=quantize,
|
||||
local_path=path,
|
||||
lora_paths=lora_paths,
|
||||
lora_scales=lora_scales,
|
||||
)
|
||||
# This code is licensed under the Apache 2.0 License.
|
||||
# Portions of this code are derived from the work of CharafChnioune at https://github.com/CharafChnioune/MFLUX-WEBUI
|
||||
return flux_cache[key]
|
||||
|
||||
def get_lora_info(Loras):
|
||||
if Loras:
|
||||
return Loras.lora_paths, Loras.lora_scales
|
||||
return [], []
|
||||
|
||||
def generate_image(prompt, model, seed, width, height, steps, guidance, quantize="None", metadata=True, Local_model="", img2img=None, Loras=None, ControlNet=None):
|
||||
model = "dev" if "dev" in Local_model.lower() else "schnell" if "schnell" in Local_model.lower() else model
|
||||
print(f"Using model: {model}")
|
||||
img2img_image_path = img2img.image_path if img2img else None
|
||||
img2img_strength = img2img.strength if img2img else None
|
||||
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
if Loras:
|
||||
print(f"LoRA paths: {lora_paths}")
|
||||
print(f"LoRA scales: {lora_scales}")
|
||||
|
||||
ControlNet_model_selection, control_image_path, controlnet_strength, save_canny = None, None, None, None
|
||||
if ControlNet and isinstance(ControlNet, MfluxControlNetPipeline):
|
||||
ControlNet_model_selection = ControlNet.model_selection
|
||||
save_canny = ControlNet.save_canny
|
||||
control_strength = ControlNet.control_strength
|
||||
control_image_path = ControlNet.control_image_path
|
||||
|
||||
quantize = None if quantize == "None" else int(quantize)
|
||||
seed = random.randint(0, 0xffffffffffffffff) if seed == -1 else int(seed)
|
||||
print(f"Using seed: {seed}")
|
||||
steps = min(max(steps, 2), 4) if model == "schnell" else steps
|
||||
|
||||
flux = load_or_create_flux(model, quantize, Local_model if Local_model else None, lora_paths, lora_scales, ControlNet is not None)
|
||||
|
||||
config_class = ConfigControlnet if ControlNet else Config
|
||||
config = RuntimeConfig(
|
||||
config=config_class(
|
||||
num_inference_steps=steps,
|
||||
height=height,
|
||||
width=width,
|
||||
guidance=guidance,
|
||||
**({"controlnet_strength": control_strength} if ControlNet else {
|
||||
"init_image_path": img2img_image_path,
|
||||
"init_image_strength": img2img_strength
|
||||
})
|
||||
),
|
||||
model_config=flux.model_config
|
||||
)
|
||||
|
||||
time_steps = tqdm(range(config.init_time_step if not ControlNet else 0, config.num_inference_steps))
|
||||
|
||||
stepwise_handler = StepwiseHandler(
|
||||
flux=flux,
|
||||
config=config,
|
||||
seed=seed,
|
||||
prompt=prompt,
|
||||
time_steps=time_steps,
|
||||
output_dir=None
|
||||
)
|
||||
|
||||
t5_tokens = flux.t5_tokenizer.tokenize(prompt)
|
||||
clip_tokens = flux.clip_tokenizer.tokenize(prompt)
|
||||
prompt_embeds = flux.t5_text_encoder(t5_tokens)
|
||||
pooled_prompt_embeds = flux.clip_text_encoder(clip_tokens)
|
||||
|
||||
if ControlNet:
|
||||
control_image = ImageUtil.load_image(control_image_path)
|
||||
control_image = ControlnetUtil.scale_image(config.height, config.width, control_image)
|
||||
control_image = ControlnetUtil.preprocess_canny(control_image)
|
||||
controlnet_cond = ImageUtil.to_array(control_image)
|
||||
controlnet_cond = flux.vae.encode(controlnet_cond)
|
||||
controlnet_cond = (controlnet_cond / flux.vae.scaling_factor) + flux.vae.shift_factor
|
||||
controlnet_cond = ArrayUtil.pack_latents(latents=controlnet_cond, height=config.height, width=config.width)
|
||||
|
||||
latents = LatentCreator.create(seed=seed, height=config.height, width=config.width)
|
||||
else:
|
||||
latents = LatentCreator.create_for_txt2img_or_img2img(seed, config, flux.vae)
|
||||
|
||||
pbar = None
|
||||
for gen_step, t in enumerate(time_steps, 1):
|
||||
if gen_step == 2:
|
||||
pbar = utils.ProgressBar(total=steps)
|
||||
|
||||
predict_args = {
|
||||
't': t,
|
||||
'prompt_embeds': prompt_embeds,
|
||||
'pooled_prompt_embeds': pooled_prompt_embeds,
|
||||
'hidden_states': latents,
|
||||
'config': config,
|
||||
}
|
||||
|
||||
if ControlNet:
|
||||
controlnet_block_samples, controlnet_single_block_samples = flux.transformer_controlnet(
|
||||
t=t,
|
||||
prompt_embeds=prompt_embeds,
|
||||
pooled_prompt_embeds=pooled_prompt_embeds,
|
||||
hidden_states=latents,
|
||||
controlnet_cond=controlnet_cond,
|
||||
config=config,
|
||||
)
|
||||
|
||||
predict_args['controlnet_block_samples'] = controlnet_block_samples
|
||||
predict_args['controlnet_single_block_samples'] = controlnet_single_block_samples
|
||||
|
||||
noise = flux.transformer.predict(**predict_args)
|
||||
dt = config.sigmas[t + 1] - config.sigmas[t]
|
||||
latents += noise * dt
|
||||
|
||||
stepwise_handler.process_step(gen_step, latents)
|
||||
|
||||
if pbar:
|
||||
pbar.update(1)
|
||||
|
||||
mx.eval(latents)
|
||||
|
||||
if pbar:
|
||||
pbar.update(1)
|
||||
|
||||
latents = ArrayUtil.unpack_latents(latents=latents, height=config.height, width=config.width)
|
||||
decoded = flux.vae.decode(latents)
|
||||
normalized_latents = ImageUtil._denormalize(decoded)
|
||||
|
||||
numpy_image = ImageUtil._to_numpy(normalized_latents)
|
||||
tensor_image = torch.from_numpy(numpy_image)
|
||||
|
||||
if tensor_image.dim() == 3:
|
||||
tensor_image = tensor_image.unsqueeze(0)
|
||||
|
||||
return (tensor_image,)
|
||||
|
||||
def save_images_with_metadata(images, prompt, model, quantize, Local_model, seed, height, width, steps, guidance, lora_paths, lora_scales, img2img_image_path, img2img_strength, filename_prefix="Mflux", full_prompt=None, extra_pnginfo=None):
|
||||
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename_prefix, output_dir, images[0].shape[1], images[0].shape[0])
|
||||
mflux_output_folder = os.path.join(full_output_folder, "MFlux")
|
||||
os.makedirs(mflux_output_folder, exist_ok=True)
|
||||
existing_files = os.listdir(mflux_output_folder)
|
||||
existing_counters = [
|
||||
int(f.split("_")[-1].split(".")[0])
|
||||
for f in existing_files
|
||||
if f.startswith(filename_prefix) and f.endswith(".png")
|
||||
]
|
||||
counter = max(existing_counters, default=0) + 1
|
||||
|
||||
results = list()
|
||||
for image in images:
|
||||
i = 255. * image.cpu().numpy().squeeze()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
metadata = None
|
||||
if full_prompt is not None or extra_pnginfo is not None:
|
||||
metadata = PngInfo()
|
||||
if full_prompt is not None:
|
||||
metadata.add_text("full_prompt", json.dumps(full_prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
image_file = f"{filename_prefix}_{counter:05}.png"
|
||||
img.save(os.path.join(mflux_output_folder, image_file), pnginfo=metadata, compress_level=4)
|
||||
results.append({
|
||||
"filename": image_file,
|
||||
"subfolder": subfolder,
|
||||
"type": "output"
|
||||
})
|
||||
|
||||
metadata_jsonfile = os.path.join(mflux_output_folder, f"{filename_prefix}_{counter:05}.json")
|
||||
json_dict = {
|
||||
"prompt": prompt,
|
||||
"model": model,
|
||||
"quantize": quantize,
|
||||
"seed": seed,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"steps": steps,
|
||||
"guidance": guidance if model == "dev" else None,
|
||||
"Local_model": Local_model,
|
||||
"img2img_image_path": img2img_image_path,
|
||||
"img2img_strength": img2img_strength,
|
||||
"lora_paths": lora_paths,
|
||||
"lora_scales": lora_scales,
|
||||
}
|
||||
with open(metadata_jsonfile, 'w') as metadata_file:
|
||||
json.dump(json_dict, metadata_file, indent=4)
|
||||
counter += 1
|
||||
return {"ui": {"images": results}, "counter": counter}
|
||||
|
||||
def create_directory(directory):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
return directory
|
||||
|
||||
mflux_dir = os.path.join(models_dir, "Mflux")
|
||||
create_directory(mflux_dir)
|
||||
|
||||
def get_full_model_path(model_dir, model_name):
|
||||
return os.path.join(model_dir, model_name)
|
||||
|
||||
def download_hg_model(model_version):
|
||||
repo_id = f"madroid/{model_version}" if "4bit" in model_version else f"AITRADER/{model_version}"
|
||||
model_checkpoint = get_full_model_path(mflux_dir, model_version)
|
||||
if not os.path.exists(model_checkpoint):
|
||||
print(f"Downloading model {model_version} to {model_checkpoint}...")
|
||||
try:
|
||||
snapshot_download(repo_id=repo_id, local_dir=model_checkpoint)
|
||||
except Exception as e:
|
||||
print(f"Error downloading model {model_version}: {e}")
|
||||
return None
|
||||
else:
|
||||
print(f"Model {model_version} already exists at {model_checkpoint}. Skipping download.")
|
||||
return model_checkpoint
|
||||
|
||||
class MfluxModelsDownloader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_version": (["flux.1-schnell-mflux-4bit", "flux.1-dev-mflux-4bit", "MFLUX.1-schnell-8-bit", "MFLUX.1-dev-8-bit"], {"default": "flux.1-schnell-mflux-4bit"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Downloaded_model",)
|
||||
CATEGORY = "MFlux"
|
||||
FUNCTION = "download_model"
|
||||
|
||||
def download_model(self, model_version):
|
||||
model_path = download_hg_model(model_version)
|
||||
if model_path:
|
||||
return (model_path,)
|
||||
return (None,)
|
||||
|
||||
class MfluxCustomModels:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model": (["dev", "schnell"], {"default": "schnell"}),
|
||||
"quantize": (["4", "8"], {"default": "4"}),
|
||||
},
|
||||
"optional": {
|
||||
"Loras": ("MfluxLorasPipeline",),
|
||||
"custom_identifier": ("STRING", {"default": "", "tooltip": "Custom identifier for the model."}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Custom_model",)
|
||||
CATEGORY = "MFlux"
|
||||
FUNCTION = "save_model"
|
||||
|
||||
def save_model(self, model, quantize, Loras=None, custom_identifier=""):
|
||||
identifier = custom_identifier if custom_identifier else "default"
|
||||
save_dir = get_full_model_path(mflux_dir, f"Mflux-{model}-{quantize}bit-{identifier}")
|
||||
create_directory(save_dir)
|
||||
print(f"Saving model: {model}, quantize: {quantize}, save_dir: {save_dir}")
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
if lora_paths:
|
||||
print(f"LoRA paths: {lora_paths}")
|
||||
print(f"LoRA scales: {lora_scales}")
|
||||
model_config = ModelConfig.from_alias(model)
|
||||
flux = Flux1(model_config=model_config, quantize=int(quantize), lora_paths=lora_paths, lora_scales=lora_scales)
|
||||
flux.save_model(save_dir)
|
||||
print(f"Model saved to {save_dir}")
|
||||
return (save_dir,)
|
||||
|
||||
class MfluxModelsLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_name": (cls.get_sorted_model_paths() or ["None"], {"default": cls.get_sorted_model_paths()[0] if cls.get_sorted_model_paths() else "None"}),
|
||||
},
|
||||
"optional": {
|
||||
"free_path": ("STRING", {"default": "", "tooltip": "Free model path. If provided, this will override the model_name."}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Local_model",)
|
||||
CATEGORY = "MFlux"
|
||||
FUNCTION = "load"
|
||||
|
||||
@classmethod
|
||||
def get_sorted_model_paths(cls):
|
||||
model_paths = [p.name for p in Path(mflux_dir).iterdir() if p.is_dir()]
|
||||
return sorted(model_paths, key=lambda x: x.lower())
|
||||
|
||||
def load(self, model_name="", free_path=""):
|
||||
if free_path:
|
||||
full_model_path = free_path
|
||||
if not os.path.exists(full_model_path):
|
||||
raise ValueError(f"Provided custom path does not exist: {full_model_path}")
|
||||
elif model_name and model_name != "None":
|
||||
full_model_path = get_full_model_path(mflux_dir, model_name)
|
||||
else:
|
||||
raise ValueError("Either 'model_name' must be provided or 'free_path' must be used.")
|
||||
|
||||
return (full_model_path,)
|
||||
|
||||
class QuickMfluxNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True, "dynamicPrompts": True, "tooltip": "The text to be encoded.", "default": "Luxury food photograph"}),
|
||||
"model": (["dev", "schnell"], {"default": "schnell"}),
|
||||
"quantize": (["None", "4", "8"], {"default": "4"}),
|
||||
"seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}),
|
||||
"width": ("INT", {"default": 512}),
|
||||
"height": ("INT", {"default": 512}),
|
||||
"steps": ("INT", {"default": 2, "min": 1}),
|
||||
"guidance": ("FLOAT", {"default": 3.5, "min": 0.0}),
|
||||
"metadata": ("BOOLEAN", {"default": True, "label_on": "True", "label_off": "False"}),
|
||||
},
|
||||
"optional": {
|
||||
"Local_model": ("PATH",),
|
||||
"img2img": ("MfluxImg2ImgPipeline",),
|
||||
"Loras": ("MfluxLorasPipeline",),
|
||||
"ControlNet": ("MfluxControlNetPipeline",),
|
||||
},
|
||||
"hidden": {
|
||||
"full_prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO"
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
CATEGORY = "MFlux"
|
||||
FUNCTION = "generate"
|
||||
|
||||
def generate(self, prompt, model, seed, width, height, steps, guidance, quantize="None", metadata=True, Local_model="", img2img=None, Loras=None, ControlNet=None, full_prompt=None, extra_pnginfo=None):
|
||||
generated_images = generate_image(
|
||||
prompt, model, seed, width, height, steps, guidance, quantize, metadata, Local_model, img2img, Loras, ControlNet
|
||||
)
|
||||
|
||||
img2img_image_path = img2img.image_path if img2img else None
|
||||
img2img_strength = img2img.strength if img2img else None
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
|
||||
if metadata:
|
||||
save_images_params = {
|
||||
"images": generated_images,
|
||||
"prompt": prompt,
|
||||
"model": "dev" if "dev" in Local_model.lower() else "schnell" if "schnell" in Local_model.lower() else model,
|
||||
"quantize": quantize,
|
||||
"Local_model": Local_model,
|
||||
"seed": seed,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"steps": steps,
|
||||
"guidance": guidance,
|
||||
"img2img_image_path": img2img_image_path,
|
||||
"img2img_strength": img2img_strength,
|
||||
"lora_paths": lora_paths,
|
||||
"lora_scales": lora_scales,
|
||||
"filename_prefix": "Mflux",
|
||||
"full_prompt": full_prompt,
|
||||
"extra_pnginfo": extra_pnginfo,
|
||||
}
|
||||
|
||||
result = save_images_with_metadata(**save_images_params)
|
||||
counter = result["counter"]
|
||||
|
||||
return generated_images
|
||||
|
||||
class MfluxImg2ImgPipeline:
|
||||
def __init__(self, image_path, strength):
|
||||
self.image_path = image_path
|
||||
self.strength = strength
|
||||
|
||||
def clear_cache(self):
|
||||
self.image_path = None
|
||||
self.strength = None
|
||||
|
||||
class MfluxImg2Img:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"strength": ("FLOAT", {"default": 0.4, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = "MFlux"
|
||||
RETURN_TYPES = ("MfluxImg2ImgPipeline", "INT", "INT")
|
||||
RETURN_NAMES = ("img2img", "width", "height")
|
||||
FUNCTION = "load_and_process"
|
||||
|
||||
def load_and_process(self, image, strength):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
|
||||
with Image.open(image_path) as img:
|
||||
width, height = img.size
|
||||
|
||||
return MfluxImg2ImgPipeline(image_path, strength), width, height
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, image, strength):
|
||||
image_hash = hash(image)
|
||||
strength_rounded = round(float(strength), 2)
|
||||
return (image_hash, strength_rounded)
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, image, strength):
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return "Invalid image file: {}".format(image)
|
||||
|
||||
if not isinstance(strength, (int, float)):
|
||||
return "Strength must be a number"
|
||||
|
||||
return True
|
||||
|
||||
class MfluxLorasPipeline:
|
||||
def __init__(self, lora_paths, lora_scales):
|
||||
self.lora_paths = lora_paths
|
||||
self.lora_scales = lora_scales
|
||||
|
||||
def clear_cache(self):
|
||||
self.lora_paths = []
|
||||
self.lora_scales = []
|
||||
|
||||
class MfluxLorasLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
lora_base_path = folder_paths.models_dir
|
||||
loras_relative = ["None"] + folder_paths.get_filename_list("loras")
|
||||
|
||||
inputs = {
|
||||
"required": {
|
||||
"Lora1": (loras_relative,),
|
||||
"scale1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"Lora2": (loras_relative,),
|
||||
"scale2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"Lora3": (loras_relative,),
|
||||
"scale3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"Loras": ("MfluxLorasPipeline",)
|
||||
}
|
||||
}
|
||||
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("MfluxLorasPipeline",)
|
||||
RETURN_NAMES = ("Loras",)
|
||||
FUNCTION = "lora_stacker"
|
||||
CATEGORY = "MFlux"
|
||||
|
||||
def lora_stacker(self, Loras=None, **kwargs):
|
||||
lora_base_path = folder_paths.models_dir
|
||||
lora_models = [
|
||||
(os.path.join(lora_base_path, "loras", kwargs.get(f"Lora{i}")), kwargs.get(f"scale{i}"))
|
||||
for i in range(1, 4) if kwargs.get(f"Lora{i}") != "None"
|
||||
]
|
||||
|
||||
if Loras is not None and isinstance(Loras, MfluxLorasPipeline):
|
||||
lora_paths = Loras.lora_paths
|
||||
lora_scales = Loras.lora_scales
|
||||
|
||||
if lora_paths and lora_scales:
|
||||
lora_models.extend(zip(lora_paths, lora_scales))
|
||||
|
||||
if lora_models:
|
||||
lora_paths, lora_scales = zip(*lora_models)
|
||||
else:
|
||||
lora_paths, lora_scales = [], []
|
||||
|
||||
return (MfluxLorasPipeline(list(lora_paths), list(lora_scales)),)
|
||||
|
||||
class MfluxControlNetPipeline:
|
||||
def __init__(self, model_selection, control_image_path, control_strength, save_canny=False):
|
||||
self.model_selection = model_selection
|
||||
self.control_image_path = control_image_path
|
||||
self.control_strength = control_strength
|
||||
self.save_canny = save_canny
|
||||
|
||||
|
||||
def clear_cache(self):
|
||||
self.model_selection = None
|
||||
self.control_image_path = None
|
||||
self.control_strength = None
|
||||
self.save_canny = False
|
||||
|
||||
|
||||
class MfluxControlNetLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
controlnet_models = [
|
||||
"InstantX/FLUX.1-dev-Controlnet-Canny",
|
||||
]
|
||||
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"model_selection": (controlnet_models, {"default": "InstantX/FLUX.1-dev-Controlnet-Canny"}),
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"control_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"save_canny": ("BOOLEAN", {"default": False, "label_on": "True", "label_off": "False"}),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CATEGORY = "MFlux"
|
||||
RETURN_TYPES = ("MfluxControlNetPipeline", "INT", "INT", "IMAGE",)
|
||||
RETURN_NAMES = ("ControlNet", "width", "height", "preprocessed_image")
|
||||
FUNCTION = "load_and_select"
|
||||
|
||||
def load_and_select(self, model_selection, image, control_strength, save_canny):
|
||||
|
||||
control_image_path = folder_paths.get_annotated_filepath(image)
|
||||
|
||||
with Image.open(control_image_path) as img:
|
||||
width, height = img.size
|
||||
|
||||
with Image.open(control_image_path) as img:
|
||||
canny_image = ControlnetUtil.preprocess_canny(img)
|
||||
canny_image_np = np.array(canny_image).astype(np.float32) / 255.0
|
||||
canny_tensor = torch.from_numpy(canny_image_np)
|
||||
|
||||
if canny_tensor.dim() == 3:
|
||||
canny_tensor = canny_tensor.unsqueeze(0)
|
||||
|
||||
return MfluxControlNetPipeline(model_selection, control_image_path, control_strength, save_canny), width, height, canny_tensor
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, model_selection, image, control_strength, save_canny):
|
||||
control_image_path = folder_paths.get_annotated_filepath(image)
|
||||
control_strength = round(float(control_strength), 2)
|
||||
control_image_hash = hash(image)
|
||||
return str(control_image_hash) + str(model_selection) + str(control_strength) + str(save_canny)
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, image, model_selection, control_strength, save_canny):
|
||||
|
||||
available_models = [
|
||||
"InstantX/FLUX.1-dev-Controlnet-Canny",
|
||||
]
|
||||
if model_selection not in available_models:
|
||||
return f"Invalid model selection: {model_selection}"
|
||||
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return "Invalid control image file: {}".format(image)
|
||||
|
||||
if not isinstance(control_strength, (int, float)):
|
||||
return "Strength must be a number"
|
||||
|
||||
if not isinstance(save_canny, bool):
|
||||
return "save_canny must be a boolean value"
|
||||
|
||||
|
||||
return True
|
||||
@@ -1,187 +0,0 @@
|
||||
import os
|
||||
import json
|
||||
from pathlib import Path
|
||||
from huggingface_hub import snapshot_download
|
||||
from folder_paths import get_filename_list, get_output_directory, models_dir
|
||||
from mflux.config.model_config import ModelConfig
|
||||
from mflux.flux.flux import Flux1
|
||||
from .Mflux_Core import get_lora_info, generate_image, save_images_with_metadata
|
||||
|
||||
def create_directory(directory):
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
return directory
|
||||
|
||||
mflux_dir = os.path.join(models_dir, "Mflux")
|
||||
create_directory(mflux_dir)
|
||||
|
||||
def get_full_model_path(model_dir, model_name):
|
||||
return os.path.join(model_dir, model_name)
|
||||
|
||||
def download_hg_model(model_version):
|
||||
repo_id = f"madroid/{model_version}" if "4bit" in model_version else f"AITRADER/{model_version}"
|
||||
model_checkpoint = get_full_model_path(mflux_dir, model_version)
|
||||
if not os.path.exists(model_checkpoint):
|
||||
print(f"Downloading model {model_version} to {model_checkpoint}...")
|
||||
try:
|
||||
snapshot_download(repo_id=repo_id, local_dir=model_checkpoint)
|
||||
except Exception as e:
|
||||
print(f"Error downloading model {model_version}: {e}")
|
||||
return None
|
||||
else:
|
||||
print(f"Model {model_version} already exists at {model_checkpoint}. Skipping download.")
|
||||
return model_checkpoint
|
||||
|
||||
class MfluxModelsDownloader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_version": (["flux.1-schnell-mflux-4bit", "flux.1-dev-mflux-4bit", "MFLUX.1-schnell-8-bit", "MFLUX.1-dev-8-bit"], {"default": "flux.1-schnell-mflux-4bit"}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Downloaded_model",)
|
||||
CATEGORY = "MFlux/Air"
|
||||
FUNCTION = "download_model"
|
||||
|
||||
def download_model(self, model_version):
|
||||
model_path = download_hg_model(model_version)
|
||||
if model_path:
|
||||
return (model_path,)
|
||||
return (None,)
|
||||
|
||||
class MfluxCustomModels:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model": (["dev", "schnell"], {"default": "schnell"}),
|
||||
"quantize": (["4", "8"], {"default": "4"}),
|
||||
},
|
||||
"optional": {
|
||||
"Loras": ("MfluxLorasPipeline",),
|
||||
"custom_identifier": ("STRING", {"default": "", "tooltip": "Custom identifier for the model."}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Custom_model",)
|
||||
CATEGORY = "MFlux/Air"
|
||||
FUNCTION = "save_model"
|
||||
|
||||
def save_model(self, model, quantize, Loras=None, custom_identifier=""):
|
||||
identifier = custom_identifier if custom_identifier else "default"
|
||||
save_dir = get_full_model_path(mflux_dir, f"Mflux-{model}-{quantize}bit-{identifier}")
|
||||
create_directory(save_dir)
|
||||
print(f"Saving model: {model}, quantize: {quantize}, save_dir: {save_dir}")
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
if lora_paths:
|
||||
print(f"LoRA paths: {lora_paths}")
|
||||
print(f"LoRA scales: {lora_scales}")
|
||||
model_config = ModelConfig.from_alias(model)
|
||||
flux = Flux1(model_config=model_config, quantize=int(quantize), lora_paths=lora_paths, lora_scales=lora_scales)
|
||||
flux.save_model(save_dir)
|
||||
print(f"Model saved to {save_dir}")
|
||||
return (save_dir,)
|
||||
|
||||
class MfluxModelsLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"model_name": (cls.get_sorted_model_paths() or ["None"], {"default": cls.get_sorted_model_paths()[0] if cls.get_sorted_model_paths() else "None"}),
|
||||
},
|
||||
"optional": {
|
||||
"free_path": ("STRING", {"default": "", "tooltip": "Free model path. If provided, this will override the model_name."}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("PATH",)
|
||||
RETURN_NAMES = ("Local_model",)
|
||||
CATEGORY = "MFlux/Air"
|
||||
FUNCTION = "load"
|
||||
|
||||
@classmethod
|
||||
def get_sorted_model_paths(cls):
|
||||
model_paths = [p.name for p in Path(mflux_dir).iterdir() if p.is_dir()]
|
||||
return sorted(model_paths, key=lambda x: x.lower())
|
||||
|
||||
def load(self, model_name="", free_path=""):
|
||||
if free_path:
|
||||
full_model_path = free_path
|
||||
if not os.path.exists(full_model_path):
|
||||
raise ValueError(f"Provided custom path does not exist: {full_model_path}")
|
||||
elif model_name and model_name != "None":
|
||||
full_model_path = get_full_model_path(mflux_dir, model_name)
|
||||
else:
|
||||
raise ValueError("Either 'model_name' must be provided or 'free_path' must be used.")
|
||||
|
||||
return (full_model_path,)
|
||||
|
||||
class QuickMfluxNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING", {"multiline": True, "dynamicPrompts": True, "tooltip": "The text to be encoded.", "default": "Luxury food photograph"}),
|
||||
"model": (["dev", "schnell"], {"default": "schnell"}),
|
||||
"quantize": (["None", "4", "8"], {"default": "4"}),
|
||||
"seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}),
|
||||
"width": ("INT", {"default": 512}),
|
||||
"height": ("INT", {"default": 512}),
|
||||
"steps": ("INT", {"default": 2, "min": 1}),
|
||||
"guidance": ("FLOAT", {"default": 3.5, "min": 0.0}),
|
||||
"metadata": ("BOOLEAN", {"default": True, "label_on": "True", "label_off": "False"}),
|
||||
},
|
||||
"optional": {
|
||||
"Local_model": ("PATH",),
|
||||
"Loras": ("MfluxLorasPipeline",),
|
||||
"img2img": ("MfluxImg2ImgPipeline",),
|
||||
"ControlNet": ("MfluxControlNetPipeline",),
|
||||
},
|
||||
"hidden": {
|
||||
"full_prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO"
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
CATEGORY = "MFlux/Air"
|
||||
FUNCTION = "generate"
|
||||
|
||||
def generate(self, prompt, model, seed, width, height, steps, guidance, quantize="None", metadata=True, Local_model="", img2img=None, Loras=None, ControlNet=None, full_prompt=None, extra_pnginfo=None):
|
||||
generated_images = generate_image(
|
||||
prompt, model, seed, width, height, steps, guidance, quantize, metadata, Local_model, img2img, Loras, ControlNet
|
||||
)
|
||||
|
||||
image_path = img2img.image_path if img2img else None
|
||||
image_strength = img2img.image_strength if img2img else None
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
|
||||
if metadata:
|
||||
save_images_params = {
|
||||
"images": generated_images,
|
||||
"prompt": prompt,
|
||||
"model": "dev" if "dev" in Local_model.lower() else "schnell" if "schnell" in Local_model.lower() else model,
|
||||
"quantize": quantize,
|
||||
"Local_model": Local_model,
|
||||
"seed": seed,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"steps": steps,
|
||||
"guidance": guidance,
|
||||
"image_path": image_path,
|
||||
"image_strength": image_strength,
|
||||
"lora_paths": lora_paths,
|
||||
"lora_scales": lora_scales,
|
||||
"filename_prefix": "Mflux",
|
||||
"full_prompt": full_prompt,
|
||||
"extra_pnginfo": extra_pnginfo,
|
||||
}
|
||||
|
||||
result = save_images_with_metadata(**save_images_params)
|
||||
counter = result["counter"]
|
||||
|
||||
return generated_images
|
||||
@@ -1,225 +0,0 @@
|
||||
import random
|
||||
import json
|
||||
import os
|
||||
import numpy as np
|
||||
import torch
|
||||
from tqdm import tqdm
|
||||
from PIL import Image
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import comfy.utils as utils
|
||||
import folder_paths
|
||||
|
||||
import mlx.core as mx
|
||||
from mflux.config.model_config import ModelConfig
|
||||
from mflux.config.config import Config, ConfigControlnet
|
||||
from mflux.flux.flux import Flux1
|
||||
from mflux.controlnet.flux_controlnet import Flux1Controlnet
|
||||
from mflux.config.runtime_config import RuntimeConfig
|
||||
from mflux.post_processing.array_util import ArrayUtil
|
||||
from mflux.post_processing.image_util import ImageUtil
|
||||
from mflux.post_processing.generated_image import GeneratedImage
|
||||
from mflux.latent_creator.latent_creator import LatentCreator
|
||||
from mflux.post_processing.stepwise_handler import StepwiseHandler
|
||||
from mflux.controlnet.controlnet_util import ControlnetUtil
|
||||
from mflux.models.vae.vae import VAE
|
||||
from .Mflux_Pro import MfluxControlNetPipeline
|
||||
|
||||
flux_cache = {}
|
||||
|
||||
def load_or_create_flux(model, quantize, path, lora_paths, lora_scales, is_controlnet=False):
|
||||
key = (model, quantize, path, tuple(lora_paths), tuple(lora_scales), is_controlnet)
|
||||
if key not in flux_cache:
|
||||
flux_cache.clear()
|
||||
FluxClass = Flux1Controlnet if is_controlnet else Flux1
|
||||
flux_cache[key] = FluxClass(
|
||||
model_config=ModelConfig.from_alias(model),
|
||||
quantize=quantize,
|
||||
local_path=path,
|
||||
lora_paths=lora_paths,
|
||||
lora_scales=lora_scales,
|
||||
)
|
||||
# This code is licensed under the Apache 2.0 License.
|
||||
# Portions of this code are derived from the work of CharafChnioune at https://github.com/CharafChnioune/MFLUX-WEBUI
|
||||
return flux_cache[key]
|
||||
|
||||
def get_lora_info(Loras):
|
||||
if Loras:
|
||||
return Loras.lora_paths, Loras.lora_scales
|
||||
return [], []
|
||||
|
||||
def generate_image(prompt, model, seed, width, height, steps, guidance, quantize="None", metadata=True, Local_model="", image=None, Loras=None, ControlNet=None):
|
||||
model = "dev" if "dev" in Local_model.lower() else "schnell" if "schnell" in Local_model.lower() else model
|
||||
print(f"Using model: {model}")
|
||||
image_path = image.image_path if image else None
|
||||
image_strength = image.image_strength if image else None
|
||||
|
||||
lora_paths, lora_scales = get_lora_info(Loras)
|
||||
if Loras:
|
||||
print(f"LoRA paths: {lora_paths}")
|
||||
print(f"LoRA scales: {lora_scales}")
|
||||
|
||||
ControlNet_model_selection, control_image_path, controlnet_strength, save_canny = None, None, None, None
|
||||
if ControlNet and isinstance(ControlNet, MfluxControlNetPipeline):
|
||||
ControlNet_model_selection = ControlNet.model_selection
|
||||
save_canny = ControlNet.save_canny
|
||||
control_strength = ControlNet.control_strength
|
||||
control_image_path = ControlNet.control_image_path
|
||||
|
||||
quantize = None if quantize == "None" else int(quantize)
|
||||
seed = random.randint(0, 0xffffffffffffffff) if seed == -1 else int(seed)
|
||||
print(f"Using seed: {seed}")
|
||||
# steps = min(max(steps, 2), 4) if model == "schnell" else steps
|
||||
|
||||
flux = load_or_create_flux(model, quantize, Local_model if Local_model else None, lora_paths, lora_scales, ControlNet is not None)
|
||||
|
||||
config_class = ConfigControlnet if ControlNet else Config
|
||||
config = RuntimeConfig(
|
||||
config=config_class(
|
||||
num_inference_steps=steps,
|
||||
height=height,
|
||||
width=width,
|
||||
guidance=guidance,
|
||||
**({"controlnet_strength": control_strength} if ControlNet else {
|
||||
"init_image_path": image_path,
|
||||
"init_image_strength": image_strength
|
||||
})
|
||||
),
|
||||
model_config=flux.model_config
|
||||
)
|
||||
|
||||
time_steps = tqdm(range(config.init_time_step if not ControlNet else 0, config.num_inference_steps))
|
||||
|
||||
stepwise_handler = StepwiseHandler(
|
||||
flux=flux,
|
||||
config=config,
|
||||
seed=seed,
|
||||
prompt=prompt,
|
||||
time_steps=time_steps,
|
||||
output_dir=None
|
||||
)
|
||||
|
||||
t5_tokens = flux.t5_tokenizer.tokenize(prompt)
|
||||
clip_tokens = flux.clip_tokenizer.tokenize(prompt)
|
||||
prompt_embeds = flux.t5_text_encoder.forward(t5_tokens)
|
||||
pooled_prompt_embeds = flux.clip_text_encoder.forward(clip_tokens)
|
||||
|
||||
if ControlNet:
|
||||
control_image = ImageUtil.load_image(control_image_path)
|
||||
control_image = ControlnetUtil.scale_image(config.height, config.width, control_image)
|
||||
control_image = ControlnetUtil.preprocess_canny(control_image)
|
||||
controlnet_cond = ImageUtil.to_array(control_image)
|
||||
controlnet_cond = flux.vae.encode(controlnet_cond)
|
||||
controlnet_cond = (controlnet_cond / flux.vae.scaling_factor) + flux.vae.shift_factor
|
||||
controlnet_cond = ArrayUtil.pack_latents(latents=controlnet_cond, height=config.height, width=config.width)
|
||||
|
||||
latents = LatentCreator.create(seed=seed, height=config.height, width=config.width)
|
||||
else:
|
||||
latents = LatentCreator.create_for_txt2img_or_img2img(seed, config, flux.vae)
|
||||
|
||||
pbar = None
|
||||
for gen_step, t in enumerate(time_steps, 1):
|
||||
if gen_step == 2:
|
||||
pbar = utils.ProgressBar(total=steps)
|
||||
|
||||
predict_args = {
|
||||
't': t,
|
||||
'prompt_embeds': prompt_embeds,
|
||||
'pooled_prompt_embeds': pooled_prompt_embeds,
|
||||
'hidden_states': latents,
|
||||
'config': config,
|
||||
}
|
||||
|
||||
if ControlNet:
|
||||
controlnet_block_samples, controlnet_single_block_samples = flux.transformer_controlnet.forward(
|
||||
t=t,
|
||||
prompt_embeds=prompt_embeds,
|
||||
pooled_prompt_embeds=pooled_prompt_embeds,
|
||||
hidden_states=latents,
|
||||
controlnet_cond=controlnet_cond,
|
||||
config=config,
|
||||
)
|
||||
|
||||
predict_args['controlnet_block_samples'] = controlnet_block_samples
|
||||
predict_args['controlnet_single_block_samples'] = controlnet_single_block_samples
|
||||
|
||||
noise = flux.transformer.predict(**predict_args)
|
||||
dt = config.sigmas[t + 1] - config.sigmas[t]
|
||||
latents += noise * dt
|
||||
|
||||
stepwise_handler.process_step(gen_step, latents)
|
||||
|
||||
if pbar:
|
||||
pbar.update(1)
|
||||
|
||||
mx.eval(latents)
|
||||
|
||||
if pbar:
|
||||
pbar.update(1)
|
||||
|
||||
latents = ArrayUtil.unpack_latents(latents=latents, height=config.height, width=config.width)
|
||||
decoded = flux.vae.decode(latents)
|
||||
normalized_latents = ImageUtil._denormalize(decoded)
|
||||
|
||||
numpy_image = ImageUtil._to_numpy(normalized_latents)
|
||||
tensor_image = torch.from_numpy(numpy_image)
|
||||
|
||||
if tensor_image.dim() == 3:
|
||||
tensor_image = tensor_image.unsqueeze(0)
|
||||
|
||||
return (tensor_image,)
|
||||
|
||||
def save_images_with_metadata(images, prompt, model, quantize, Local_model, seed, height, width, steps, guidance, lora_paths, lora_scales, image_path, image_strength, filename_prefix="Mflux", full_prompt=None, extra_pnginfo=None):
|
||||
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename_prefix, output_dir, images[0].shape[1], images[0].shape[0])
|
||||
mflux_output_folder = os.path.join(full_output_folder, "MFlux")
|
||||
os.makedirs(mflux_output_folder, exist_ok=True)
|
||||
existing_files = os.listdir(mflux_output_folder)
|
||||
existing_counters = [
|
||||
int(f.split("_")[-1].split(".")[0])
|
||||
for f in existing_files
|
||||
if f.startswith(filename_prefix) and f.endswith(".png")
|
||||
]
|
||||
counter = max(existing_counters, default=0) + 1
|
||||
|
||||
results = list()
|
||||
for image in images:
|
||||
i = 255. * image.cpu().numpy().squeeze()
|
||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||
metadata = None
|
||||
if full_prompt is not None or extra_pnginfo is not None:
|
||||
metadata = PngInfo()
|
||||
if full_prompt is not None:
|
||||
metadata.add_text("full_prompt", json.dumps(full_prompt))
|
||||
if extra_pnginfo is not None:
|
||||
for x in extra_pnginfo:
|
||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||
image_file = f"{filename_prefix}_{counter:05}.png"
|
||||
img.save(os.path.join(mflux_output_folder, image_file), pnginfo=metadata, compress_level=4)
|
||||
results.append({
|
||||
"filename": image_file,
|
||||
"subfolder": subfolder,
|
||||
"type": "output"
|
||||
})
|
||||
|
||||
metadata_jsonfile = os.path.join(mflux_output_folder, f"{filename_prefix}_{counter:05}.json")
|
||||
json_dict = {
|
||||
"prompt": prompt,
|
||||
"model": model,
|
||||
"quantize": quantize,
|
||||
"seed": seed,
|
||||
"height": height,
|
||||
"width": width,
|
||||
"steps": steps,
|
||||
"guidance": guidance if model == "dev" else None,
|
||||
"Local_model": Local_model,
|
||||
"init_image_path": image_path,
|
||||
"init_image_strength": image_strength,
|
||||
"lora_paths": lora_paths,
|
||||
"lora_scales": lora_scales,
|
||||
}
|
||||
with open(metadata_jsonfile, 'w') as metadata_file:
|
||||
json.dump(json_dict, metadata_file, indent=4)
|
||||
counter += 1
|
||||
return {"ui": {"images": results}, "counter": counter}
|
||||
@@ -1,199 +0,0 @@
|
||||
import os
|
||||
from PIL import Image
|
||||
import folder_paths
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
from mflux.controlnet.controlnet_util import ControlnetUtil
|
||||
|
||||
class MfluxImg2ImgPipeline:
|
||||
def __init__(self, image_path, image_strength):
|
||||
self.image_path = image_path
|
||||
self.image_strength = image_strength
|
||||
|
||||
def clear_cache(self):
|
||||
self.image_path = None
|
||||
self.image_strength = None
|
||||
|
||||
class MfluxImg2Img:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"image_strength": ("FLOAT", {"default": 0.4, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = "MFlux/Pro"
|
||||
RETURN_TYPES = ("MfluxImg2ImgPipeline", "INT", "INT")
|
||||
RETURN_NAMES = ("img2img", "width", "height")
|
||||
FUNCTION = "load_and_process"
|
||||
|
||||
def load_and_process(self, image, image_strength):
|
||||
image_path = folder_paths.get_annotated_filepath(image)
|
||||
|
||||
with Image.open(image_path) as img:
|
||||
width, height = img.size
|
||||
|
||||
return MfluxImg2ImgPipeline(image_path, image_strength), width, height
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, image, image_strength):
|
||||
image_hash = hash(image)
|
||||
strength_rounded = round(float(image_strength), 2)
|
||||
return (image_hash, strength_rounded)
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, image, image_strength):
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return "Invalid image file: {}".format(image)
|
||||
|
||||
if not isinstance(image_strength, (int, float)):
|
||||
return "Strength must be a number"
|
||||
|
||||
return True
|
||||
|
||||
class MfluxLorasPipeline:
|
||||
def __init__(self, lora_paths, lora_scales):
|
||||
self.lora_paths = lora_paths
|
||||
self.lora_scales = lora_scales
|
||||
|
||||
def clear_cache(self):
|
||||
self.lora_paths = []
|
||||
self.lora_scales = []
|
||||
|
||||
class MfluxLorasLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
lora_base_path = folder_paths.models_dir
|
||||
loras_relative = ["None"] + folder_paths.get_filename_list("loras")
|
||||
|
||||
inputs = {
|
||||
"required": {
|
||||
"Lora1": (loras_relative,),
|
||||
"scale1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"Lora2": (loras_relative,),
|
||||
"scale2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"Lora3": (loras_relative,),
|
||||
"scale3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
},
|
||||
"optional": {
|
||||
"Loras": ("MfluxLorasPipeline",)
|
||||
}
|
||||
}
|
||||
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("MfluxLorasPipeline",)
|
||||
RETURN_NAMES = ("Loras",)
|
||||
FUNCTION = "lora_stacker"
|
||||
CATEGORY = "MFlux/Pro"
|
||||
|
||||
def lora_stacker(self, Loras=None, **kwargs):
|
||||
lora_base_path = folder_paths.models_dir
|
||||
lora_models = [
|
||||
(os.path.join(lora_base_path, "loras", kwargs.get(f"Lora{i}")), kwargs.get(f"scale{i}"))
|
||||
for i in range(1, 4) if kwargs.get(f"Lora{i}") != "None"
|
||||
]
|
||||
|
||||
if Loras is not None and isinstance(Loras, MfluxLorasPipeline):
|
||||
lora_paths = Loras.lora_paths
|
||||
lora_scales = Loras.lora_scales
|
||||
|
||||
if lora_paths and lora_scales:
|
||||
lora_models.extend(zip(lora_paths, lora_scales))
|
||||
|
||||
if lora_models:
|
||||
lora_paths, lora_scales = zip(*lora_models)
|
||||
else:
|
||||
lora_paths, lora_scales = [], []
|
||||
|
||||
return (MfluxLorasPipeline(list(lora_paths), list(lora_scales)),)
|
||||
|
||||
class MfluxControlNetPipeline:
|
||||
def __init__(self, model_selection, control_image_path, control_strength, save_canny=False):
|
||||
self.model_selection = model_selection
|
||||
self.control_image_path = control_image_path
|
||||
self.control_strength = control_strength
|
||||
self.save_canny = save_canny
|
||||
|
||||
|
||||
def clear_cache(self):
|
||||
self.model_selection = None
|
||||
self.control_image_path = None
|
||||
self.control_strength = None
|
||||
self.save_canny = False
|
||||
|
||||
|
||||
class MfluxControlNetLoader:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
controlnet_models = [
|
||||
"InstantX/FLUX.1-dev-Controlnet-Canny",
|
||||
]
|
||||
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"model_selection": (controlnet_models, {"default": "InstantX/FLUX.1-dev-Controlnet-Canny"}),
|
||||
"image": (sorted(files), {"image_upload": True}),
|
||||
"control_strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
|
||||
"save_canny": ("BOOLEAN", {"default": False, "label_on": "True", "label_off": "False"}),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CATEGORY = "MFlux/Pro"
|
||||
RETURN_TYPES = ("MfluxControlNetPipeline", "INT", "INT", "IMAGE",)
|
||||
RETURN_NAMES = ("ControlNet", "width", "height", "preprocessed_image")
|
||||
FUNCTION = "load_and_select"
|
||||
|
||||
def load_and_select(self, model_selection, image, control_strength, save_canny):
|
||||
|
||||
control_image_path = folder_paths.get_annotated_filepath(image)
|
||||
|
||||
with Image.open(control_image_path) as img:
|
||||
width, height = img.size
|
||||
|
||||
with Image.open(control_image_path) as img:
|
||||
canny_image = ControlnetUtil.preprocess_canny(img)
|
||||
canny_image_np = np.array(canny_image).astype(np.float32) / 255.0
|
||||
canny_tensor = torch.from_numpy(canny_image_np)
|
||||
|
||||
if canny_tensor.dim() == 3:
|
||||
canny_tensor = canny_tensor.unsqueeze(0)
|
||||
|
||||
return MfluxControlNetPipeline(model_selection, control_image_path, control_strength, save_canny), width, height, canny_tensor
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, model_selection, image, control_strength, save_canny):
|
||||
control_image_path = folder_paths.get_annotated_filepath(image)
|
||||
control_strength = round(float(control_strength), 2)
|
||||
control_image_hash = hash(image)
|
||||
return str(control_image_hash) + str(model_selection) + str(control_strength) + str(save_canny)
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, image, model_selection, control_strength, save_canny):
|
||||
|
||||
available_models = [
|
||||
"InstantX/FLUX.1-dev-Controlnet-Canny",
|
||||
]
|
||||
if model_selection not in available_models:
|
||||
return f"Invalid model selection: {model_selection}"
|
||||
|
||||
if not folder_paths.exists_annotated_filepath(image):
|
||||
return "Invalid control image file: {}".format(image)
|
||||
|
||||
if not isinstance(control_strength, (int, float)):
|
||||
return "Strength must be a number"
|
||||
|
||||
if not isinstance(save_canny, bool):
|
||||
return "save_canny must be a boolean value"
|
||||
|
||||
return True
|
||||
@@ -7,7 +7,7 @@
|
||||
# Quick Mflux on Comfyui
|
||||
|
||||
## Introduction
|
||||
Simple use of Mflux in ComfyUI, suitable for users who are not familiar with terminal usage. Only for MacOS.
|
||||
Simple use of Mflux in ComfyUI, suitable for users who are not familiar with terminal usage. Only for MacOS. This is a simplified branch of the original MFlux ComfyUI plugin which is maintained by InformEthics.
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
@@ -22,17 +22,17 @@ Thanks also to **@CharafChnioune**, the author of [**MFLUX-WEBUI**](https://gith
|
||||
1. `cd /path/to/your_ComfyUI`
|
||||
2. Activate the virtual environment
|
||||
3. `cd custom_nodes`
|
||||
4. `git clone https://github.com/raysers/Mflux-ComfyUI.git`
|
||||
5. `pip install mflux==0.4.1`
|
||||
4. `git clone https://github.com/raysers/Mflux-ComfyUI/ComfyMFlux.git`
|
||||
5. `pip install mflux==0.5.1`
|
||||
6. Restart ComfyUI
|
||||
|
||||
Alternatively, you can search for "Mflux-ComfyUI" in ComfyUI-Manager for a quick installation.
|
||||
|
||||
## Update Announcement
|
||||
|
||||
### **About This Update:**
|
||||
|
||||
- The ControlNet node integrates images and intensity to distinguish it from img2img — contribution from @InformEthics.
|
||||
(contributions from @InformEthics)
|
||||
- The Nodes are all in one directory instead of many (Air / Pro etc..)
|
||||
- The whole node is unified in one python file ComfyMFlux.py for easier overview and programming
|
||||
- The ControlNet node integrates images and intensity to distinguish it from img2img
|
||||
|
||||
This is the first time other contributors have participated in updating this plugin. Thanks @InformEthics.
|
||||
|
||||
@@ -56,7 +56,7 @@ If you'd like to experience the original mflux, this provides an additional opti
|
||||
- Customizable file paths.
|
||||
- Image-to-Image generation.
|
||||
|
||||
mflux has been updated to version 0.4.1. To experience image-to-image generation, please upgrade in ComfyUI with:
|
||||
mflux has been updated to version 0.5.1. Please upgrade in ComfyUI with:
|
||||
|
||||
`pip install --upgrade mflux`
|
||||
|
||||
@@ -64,15 +64,12 @@ mflux has been updated to version 0.4.1. To experience image-to-image generation
|
||||
|
||||
Right-click to create nodes:
|
||||
|
||||
Under **MFlux/Air**:
|
||||
Under **MFlux/**:
|
||||
|
||||
- **Quick MFlux Generation**
|
||||
- **MFlux Models Loader**
|
||||
- **MFlux Models Downloader**
|
||||
- **MFlux Custom Models**
|
||||
|
||||
Under **MFlux/Pro**:
|
||||
|
||||
- **Mflux Img2Img**
|
||||
- **MFlux Loras Loader**
|
||||
- **MFlux ControlNet Loader**
|
||||
@@ -116,7 +113,7 @@ Additionally, here is the FLUX.1-dev-Controlnet-Canny model from the InstantX te
|
||||
|
||||
## Workflow
|
||||
|
||||
### **Mflux Air:**
|
||||
### **Mflux:**
|
||||
|
||||
#### text2img:
|
||||
|
||||
@@ -163,9 +160,6 @@ This update also adds the option to manually input paths. Alternatively, you can
|
||||
I am still exploring the specific usage methods. If you have experiences worth sharing, please feel free to start a discussion in the issues.
|
||||
|
||||
|
||||
### **Mflux Pro:**
|
||||
|
||||
|
||||
#### Loras:
|
||||
|
||||

|
||||
@@ -197,31 +191,6 @@ The advantage of this 4-step LoRA model is that it still belongs to the DEV mode
|
||||
|
||||
|
||||
|
||||
### **Mflux Plus:**
|
||||
|
||||

|
||||
|
||||
A must-have for English beginners.
|
||||
|
||||
|
||||

|
||||
|
||||
Image reverse generation, using the MiaoshouAI/Florence-2-large-PromptGen-v1.5 visual model here.
|
||||
|
||||
All these processes can be dragged directly into ComfyUI from the workflows folder.
|
||||
|
||||
!!! If nodes are highlighted in red, use ComfyUI-Manager's "One-click Install Missing Nodes."
|
||||
!!! Please note that all processes at the end use preview nodes, which do not automatically save. You need to manually save the generated images you are satisfied with or simply replace the preview nodes with save nodes.
|
||||
|
||||
### **Possible Explorations**
|
||||
|
||||
#### **Mflux MAX:**
|
||||
|
||||
......
|
||||
|
||||
#### **Mflux Ultra:**
|
||||
|
||||
......
|
||||
|
||||
Here, I hope everyone shares their workflows more, fully promoting the spirit of sharing on the internet. Knowledge for payment? No, I advocate cooperation and sharing for mutual benefit.
|
||||
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
from .Mflux_Comfy.Mflux_Air import QuickMfluxNode, MfluxModelsLoader, MfluxModelsDownloader, MfluxCustomModels
|
||||
from .Mflux_Comfy.Mflux_Pro import MfluxImg2Img, MfluxLorasLoader, MfluxControlNetLoader # 仅导入所需的非实验节点
|
||||
from .ComfyMflux import QuickMfluxNode, MfluxModelsLoader, MfluxModelsDownloader, MfluxCustomModels, MfluxImg2Img, MfluxLorasLoader, MfluxControlNetLoader
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"QuickMfluxNode": QuickMfluxNode,
|
||||
"MfluxModelsLoader": MfluxModelsLoader,
|
||||
"MfluxModelsDownloader": MfluxModelsDownloader,
|
||||
"MfluxCustomModels": MfluxCustomModels,
|
||||
"MfluxImg2Img": MfluxImg2Img,
|
||||
"MfluxLorasLoader": MfluxLorasLoader,
|
||||
"MfluxImg2Img": MfluxImg2Img,
|
||||
"MfluxControlNetLoader": MfluxControlNetLoader,
|
||||
}
|
||||
|
||||
@@ -16,7 +15,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"MfluxModelsLoader": "MFlux Models Loader",
|
||||
"MfluxModelsDownloader": "MFlux Models Downloader",
|
||||
"MfluxCustomModels": "MFlux Custom Models",
|
||||
"MfluxImg2Img": "Mflux Img2Img",
|
||||
"MfluxLorasLoader": "MFlux Loras Loader",
|
||||
"MfluxImg2Img": "MFlux Image2Image",
|
||||
"MfluxControlNetLoader": "MFlux ControlNet Loader",
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.4 MiB |
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "mflux-comfyui"
|
||||
description = "Simple use of [a/Mflux](https://github.com/filipstrand/mflux) in ComfyUI, suitable for users who are not familiar with terminal usage.\nNOTE: A MLX port of FLUX based on the Huggingface Diffusers implementation."
|
||||
version = "1.2.5"
|
||||
version = "1.2.4"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = ["mflux==0.4.1"]
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
mflux==0.4.1
|
||||
mflux==0.5.1
|
||||
@@ -1,309 +0,0 @@
|
||||
{
|
||||
"last_node_id": 5,
|
||||
"last_link_id": 4,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3,
|
||||
"type": "LoadImage",
|
||||
"pos": {
|
||||
"0": 22,
|
||||
"1": 53
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 314
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"shape": 3,
|
||||
"label": "图像"
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3,
|
||||
"label": "遮罩"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"IMG_1281.jpeg",
|
||||
"image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "DownloadAndLoadFlorence2Model",
|
||||
"pos": {
|
||||
"0": 25,
|
||||
"1": 422
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "lora",
|
||||
"type": "PEFTLORA",
|
||||
"link": null,
|
||||
"label": "LoRA"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "florence2_model",
|
||||
"type": "FL2MODEL",
|
||||
"links": [
|
||||
1
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3,
|
||||
"label": "Florence2"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "DownloadAndLoadFlorence2Model"
|
||||
},
|
||||
"widgets_values": [
|
||||
"MiaoshouAI/Florence-2-large-PromptGen-v1.5",
|
||||
"fp16",
|
||||
"sdpa"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "Florence2Run",
|
||||
"pos": {
|
||||
"0": 365,
|
||||
"1": 58
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 352
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 2,
|
||||
"label": "图像"
|
||||
},
|
||||
{
|
||||
"name": "florence2_model",
|
||||
"type": "FL2MODEL",
|
||||
"link": 1,
|
||||
"label": "Florence2"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"links": null,
|
||||
"shape": 3,
|
||||
"label": "图像"
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3,
|
||||
"label": "遮罩"
|
||||
},
|
||||
{
|
||||
"name": "caption",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"slot_index": 2,
|
||||
"shape": 3,
|
||||
"label": "caption"
|
||||
},
|
||||
{
|
||||
"name": "data",
|
||||
"type": "JSON",
|
||||
"links": null,
|
||||
"shape": 3,
|
||||
"label": "json数据"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Florence2Run"
|
||||
},
|
||||
"widgets_values": [
|
||||
"",
|
||||
"more_detailed_caption",
|
||||
true,
|
||||
false,
|
||||
1024,
|
||||
3,
|
||||
true,
|
||||
"",
|
||||
945771744623101,
|
||||
"randomize",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 791,
|
||||
"1": 60
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 336
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "lora_files",
|
||||
"type": "LIST",
|
||||
"link": null,
|
||||
"label": "lora_files"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"label": "ControlNet"
|
||||
},
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"link": 3,
|
||||
"widget": {
|
||||
"name": "prompt"
|
||||
},
|
||||
"label": "prompt"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
4
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3,
|
||||
"label": "IMAGE"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Luxury food photograph",
|
||||
"schnell",
|
||||
"flux.1-schnell-mflux-4bit",
|
||||
682107544996648,
|
||||
"randomize",
|
||||
512,
|
||||
512,
|
||||
2,
|
||||
3.5,
|
||||
"false",
|
||||
"",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1220,
|
||||
"1": 61
|
||||
},
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 4,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
"FL2MODEL"
|
||||
],
|
||||
[
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
3,
|
||||
2,
|
||||
2,
|
||||
4,
|
||||
2,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
4,
|
||||
4,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.1167815779424797,
|
||||
"offset": [
|
||||
14.59059871397305,
|
||||
25.485045382690416
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,367 +0,0 @@
|
||||
{
|
||||
"last_node_id": 20,
|
||||
"last_link_id": 46,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 10,
|
||||
"type": "MfluxModelsLoader",
|
||||
"pos": {
|
||||
"0": 444,
|
||||
"1": -8
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "Local_model",
|
||||
"type": "PATH",
|
||||
"links": [
|
||||
17
|
||||
],
|
||||
"label": "Local_model"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxModelsLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Mflux-dev-4bit-LoRA4step"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"type": "MfluxControlNetLoader",
|
||||
"pos": {
|
||||
"0": 459,
|
||||
"1": 239
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 102
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "MfluxImagePipeline",
|
||||
"link": 44,
|
||||
"shape": 7,
|
||||
"label": "image"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"links": [
|
||||
36
|
||||
],
|
||||
"slot_index": 0,
|
||||
"label": "ControlNet"
|
||||
},
|
||||
{
|
||||
"name": "preprocessed_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
38
|
||||
],
|
||||
"slot_index": 1,
|
||||
"label": "preprocessed_image"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxControlNetLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"InstantX/FLUX.1-dev-Controlnet-Canny",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 465,
|
||||
"1": 413
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 38,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 1298,
|
||||
"1": 47
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 19,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 844,
|
||||
"1": 58
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 377
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Local_model",
|
||||
"type": "PATH",
|
||||
"link": 17,
|
||||
"shape": 7,
|
||||
"label": "Local_model"
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "MfluxImagePipeline",
|
||||
"link": 43,
|
||||
"shape": 7,
|
||||
"label": "image"
|
||||
},
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": 36,
|
||||
"shape": 7,
|
||||
"label": "ControlNet"
|
||||
},
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 45,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
},
|
||||
"label": "width"
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 46,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
},
|
||||
"label": "height"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
19
|
||||
],
|
||||
"slot_index": 0,
|
||||
"label": "IMAGE"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"1girl, ",
|
||||
"schnell",
|
||||
"4",
|
||||
801448522607649,
|
||||
"randomize",
|
||||
512,
|
||||
512,
|
||||
4,
|
||||
3.5,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "MfluxLodeImage",
|
||||
"pos": {
|
||||
"0": 44,
|
||||
"1": 67
|
||||
},
|
||||
"size": [
|
||||
315,
|
||||
358
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "MfluxImagePipeline",
|
||||
"links": [
|
||||
43,
|
||||
44
|
||||
],
|
||||
"label": "image",
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
45
|
||||
],
|
||||
"label": "width",
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
46
|
||||
],
|
||||
"label": "height",
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxLodeImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"图像.jpeg",
|
||||
0.4,
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
17,
|
||||
10,
|
||||
0,
|
||||
9,
|
||||
0,
|
||||
"PATH"
|
||||
],
|
||||
[
|
||||
19,
|
||||
9,
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
36,
|
||||
17,
|
||||
0,
|
||||
9,
|
||||
3,
|
||||
"MfluxControlNetPipeline"
|
||||
],
|
||||
[
|
||||
38,
|
||||
17,
|
||||
1,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
43,
|
||||
20,
|
||||
0,
|
||||
9,
|
||||
1,
|
||||
"MfluxImagePipeline"
|
||||
],
|
||||
[
|
||||
44,
|
||||
20,
|
||||
0,
|
||||
17,
|
||||
0,
|
||||
"MfluxImagePipeline"
|
||||
],
|
||||
[
|
||||
45,
|
||||
20,
|
||||
1,
|
||||
9,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
46,
|
||||
20,
|
||||
2,
|
||||
9,
|
||||
5,
|
||||
"INT"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.0152559799477054,
|
||||
"offset": [
|
||||
65.60323847105522,
|
||||
65.08020389873201
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
{
|
||||
"last_node_id": 33,
|
||||
"last_link_id": 13,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 32,
|
||||
"type": "MfluxLorasLoader",
|
||||
"pos": {
|
||||
"0": 33,
|
||||
"1": 59
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 178
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"links": [
|
||||
12
|
||||
],
|
||||
"label": "Loras"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxLorasLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Mflux/aidmaImageUpgrader-FLUX-V0.2.safetensors",
|
||||
0.8,
|
||||
"Mflux/Flux_1_Dev_LoRA_Paper-Cutout-Style.safetensors",
|
||||
1,
|
||||
"None",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 378,
|
||||
"1": 60
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 356
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Local_models",
|
||||
"type": "PATH",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Local_models"
|
||||
},
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": 12,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "ControlNet"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"slot_index": 0,
|
||||
"label": "IMAGE"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"There, at the very end of the earth In an unprecedented blue distance Hearing the sounds of unprecedented words Sweetly and sweetly the blood stops There the winds fly touching the stars Where trees are not afraid of thunderstorms There ships are roaming the ocean There, at the very end of the earth, glitter, hyper detailed, ornate, professional photo, photorealism, vivid color, chiaroscuro",
|
||||
"schnell",
|
||||
"4",
|
||||
1115004813920489,
|
||||
"randomize",
|
||||
1216,
|
||||
832,
|
||||
4,
|
||||
3.5,
|
||||
"true",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 819,
|
||||
"1": 63
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 6,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
6,
|
||||
25,
|
||||
0,
|
||||
23,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
12,
|
||||
32,
|
||||
0,
|
||||
25,
|
||||
1,
|
||||
"MfluxLorasPipeline"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.116781577942483,
|
||||
"offset": [
|
||||
48.91843793267095,
|
||||
51.73609488467785
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
{
|
||||
"last_node_id": 7,
|
||||
"last_link_id": 6,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 3,
|
||||
"type": "MfluxModelsLoader",
|
||||
"pos": {
|
||||
"0": 29,
|
||||
"1": 57
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "Local_model",
|
||||
"type": "PATH",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"label": "Local_model"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxModelsLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux.1-dev-mflux-4bit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 376,
|
||||
"1": 58
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 357
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Local_model",
|
||||
"type": "PATH",
|
||||
"link": 2,
|
||||
"shape": 7,
|
||||
"label": "Local_model"
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "MfluxImagePipeline",
|
||||
"link": 6,
|
||||
"shape": 7,
|
||||
"label": "image"
|
||||
},
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "ControlNet"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"slot_index": 0,
|
||||
"label": "IMAGE"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Luxury food photograph",
|
||||
"schnell",
|
||||
"4",
|
||||
1125899906842624,
|
||||
"fixed",
|
||||
512,
|
||||
512,
|
||||
30,
|
||||
3.5,
|
||||
true,
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 810,
|
||||
"1": 60
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 3,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "MfluxLodeImage",
|
||||
"pos": {
|
||||
"0": 34,
|
||||
"1": 187
|
||||
},
|
||||
"size": [
|
||||
315,
|
||||
358
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "MfluxImagePipeline",
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"label": "image",
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"label": "width"
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"label": "height"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxLodeImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"IMG_0198.jpeg",
|
||||
0.24,
|
||||
"image"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
2,
|
||||
3,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
"PATH"
|
||||
],
|
||||
[
|
||||
3,
|
||||
1,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
6,
|
||||
7,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
"MfluxImagePipeline"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.0152559799477054,
|
||||
"offset": [
|
||||
65.60323847105522,
|
||||
65.08020389873201
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,160 +0,0 @@
|
||||
{
|
||||
"last_node_id": 4,
|
||||
"last_link_id": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 372,
|
||||
"1": 60
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 332
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Local_models",
|
||||
"type": "PATH",
|
||||
"link": 1,
|
||||
"shape": 7,
|
||||
"label": "Local_models"
|
||||
},
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "ControlNet"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"label": "IMAGE",
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Luxury food photograph",
|
||||
"schnell",
|
||||
"None",
|
||||
166089506520211,
|
||||
"randomize",
|
||||
512,
|
||||
512,
|
||||
2,
|
||||
3.5,
|
||||
"false",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 800,
|
||||
"1": 62
|
||||
},
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 2,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "MfluxModelsLoader",
|
||||
"pos": {
|
||||
"0": 28,
|
||||
"1": 57
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "Local_models",
|
||||
"type": "PATH",
|
||||
"links": [
|
||||
1
|
||||
],
|
||||
"label": "Local_models"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "MfluxModelsLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux.1-schnell-mflux-4bit"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
3,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"PATH"
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
4,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.3513057093104006,
|
||||
"offset": [
|
||||
34.263450117431965,
|
||||
33.06811734068839
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
{
|
||||
"last_node_id": 9,
|
||||
"last_link_id": 6,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 32,
|
||||
"1": 59
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 332
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "Local_models",
|
||||
"type": "PATH",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Local_models"
|
||||
},
|
||||
{
|
||||
"name": "Loras",
|
||||
"type": "MfluxLorasPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "Loras"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"shape": 7,
|
||||
"label": "ControlNet"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
5
|
||||
],
|
||||
"label": "IMAGE",
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Luxury food photograph",
|
||||
"schnell",
|
||||
"4",
|
||||
92876610272781,
|
||||
"randomize",
|
||||
512,
|
||||
512,
|
||||
2,
|
||||
3.5,
|
||||
"false",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 465,
|
||||
"1": 60
|
||||
},
|
||||
"size": [
|
||||
210,
|
||||
246
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 5,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
5,
|
||||
7,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.2284597357367277,
|
||||
"offset": [
|
||||
24.56912269045404,
|
||||
25.075847400889575
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,168 +0,0 @@
|
||||
{
|
||||
"last_node_id": 3,
|
||||
"last_link_id": 2,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"type": "QuickMfluxNode",
|
||||
"pos": {
|
||||
"0": 463,
|
||||
"1": 61
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 336
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "lora_files",
|
||||
"type": "LIST",
|
||||
"link": null,
|
||||
"label": "lora_files"
|
||||
},
|
||||
{
|
||||
"name": "ControlNet",
|
||||
"type": "MfluxControlNetPipeline",
|
||||
"link": null,
|
||||
"label": "ControlNet"
|
||||
},
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"link": 1,
|
||||
"widget": {
|
||||
"name": "prompt"
|
||||
},
|
||||
"label": "prompt"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3,
|
||||
"label": "IMAGE"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "QuickMfluxNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Luxury food photograph",
|
||||
"schnell",
|
||||
"flux.1-schnell-mflux-4bit",
|
||||
656585466532514,
|
||||
"randomize",
|
||||
512,
|
||||
512,
|
||||
2,
|
||||
3.5,
|
||||
"false",
|
||||
"",
|
||||
true
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "PreviewImage",
|
||||
"pos": {
|
||||
"0": 901,
|
||||
"1": 60
|
||||
},
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 2,
|
||||
"label": "图像"
|
||||
}
|
||||
],
|
||||
"outputs": [],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "ArgosTranslateTextNode",
|
||||
"pos": {
|
||||
"0": 27,
|
||||
"1": 57
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 200
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
1
|
||||
],
|
||||
"slot_index": 0,
|
||||
"shape": 3,
|
||||
"label": "文本"
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ArgosTranslateTextNode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"chinese",
|
||||
"english",
|
||||
"明月松间照,清泉石上流。",
|
||||
true
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
2,
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 1.2284597357367277,
|
||||
"offset": [
|
||||
25.90558087874367,
|
||||
28.985363606324412
|
||||
]
|
||||
}
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||