Initial commit

This commit is contained in:
raysers
2024-10-04 02:31:59 -07:00
commit 05bdcb9d3d
6 changed files with 243 additions and 0 deletions
Vendored
BIN
View File
Binary file not shown.
+37
View File
@@ -0,0 +1,37 @@
<h1 align="center">Quick Mflux on Comfyui</h1>
<p align="center">
<br> <font size=5>English | <a href="README_zh.md">中文</a></font>
</p>
## Introduction
Simple use of Mflux in ComfyUI, suitable for users who are not familiar with terminal usage.
## Acknowledgements
Special thanks to the developers of the mflux project, especially the initiator @filipstrand and active contributor @anthonywu, for making it easier and more efficient for Mac users to generate flux model images. Their contributions are truly delightful; appreciate them!
mflux:https://github.com/filipstrand/mflux
I also want to thank @CharafChnioune, the author of MFLUX-WEBUI. I have partially referenced his code, and in accordance with the Apache 2.0 license used in his project, I have added license comments in the referenced sections of my code.
MFLUX-WEBUI:https://github.com/CharafChnioune/MFLUX-WEBUI
## Installation Guide
1. cd /path/to/your_ComfyUI
2. Activate your virtual environment
3. cd custom_nodes
4. git clone https://github.com/raysers/Mflux-ComfyUI.git
5. pip install mflux==0.3.0
6. Restart ComfyUI
## Usage Instructions
Currently, the functionality is relatively simple and only implements text-to-image generation. To use it, simply create a node named **Quick MFlux Generation** (found under **Mflux/Quick**), and then connect it to the built-in preview or save image nodes in ComfyUI. If the Quick MFlux Generation node is connected to a preview node and you are satisfied with the generated image, you will need to right-click to manually save the final result. If it is connected to a save image node, any generated images will be automatically saved to **ComfyUI/output** by the ComfyUI mechanism. Note that in the Quick MFlux Generation node, the metadata option should be set to true (default is false); if true, the generated image will be saved to **ComfyUI/output/Mflux** along with a JSON file of the same name, containing almost all the generation parameters of that image.
PS: Alternatively, you can directly drag the JSON or PNG workflows from this project into ComfyUI, and they will load automatically.
## Contributions
I am a novice coder, and this is my first GitHub project. I initially had grand plans, such as implementing mflux's Lora and ControlNet functionalities, and realizing the essence of ComfyUI—splitting nodes to allow users to delve into the underlying logic of flux. However, my limited coding skills have constrained my vision. If anyone with expertise could help implement these, I would be very grateful. Greetings to all!
## License
I would like to adopt the MIT license, consistent with the mflux project, as a contribution to open source.
+40
View File
@@ -0,0 +1,40 @@
<h1 align="center">Quick Mflux on Comfyui</h1>
<p align="center">
<br> <font size=5>中文 | <a href="README.md">English</a></font>
</p>
# Quick Mflux on Comfyui
## 简介
Mflux在ComfyUI中的简单使用,适合不太懂得使用终端的用户。
## 特别鸣谢
感谢mflux项目的开发者们,特别是项目发起人 @filipstrand 和活跃贡献者 @anthonywu,是他们让Mac用户实现了更方便高效的flux模型生图,这些贡献着实令人轻松愉悦,谢谢他们!
mflux:https://github.com/filipstrand/mflux
同时感谢MFLUX-WEBUI的作者 @CharafChnioune,我部分参考了他的代码,基于他的项目所使用的Apache 2.0许可证的要求,我在代码中的引用段落添加了许可证注释。
MFLUX-WEBUI:https://github.com/CharafChnioune/MFLUX-WEBUI
## 安装指南
1. cd /path/to/your_ComfyUI
2. 激活虚拟环境
3. cd custom_nodes
4. git clone https://github.com/raysers/Mflux-ComfyUI.git
5. pip install mflux==0.3.0
6. 重启ComfyUI
## 使用说明
由于目前的功能相对简单,仅仅实现了文生图,因此只需要新建节点 **Quick MFlux Generation**(在 **Mflux/Quick** 下),再连接ComfyUI自带的预览图像或保存图像节点即可。如果 **Quick MFlux Generation** 连接的是预览图像节点,最终生成结果需要右键手动保存;如果是保存图像节点,任何生成图片都将被ComfyUI的机制自动保存到 **ComfyUI/output** 下。需要注意的是 **Quick MFlux Generation** 节点中的metadata选项,如果它是true值(默认false),那么生成的图像将被保存到 **ComfyUI/output/Mflux** 中,同时带有图像同名的json文件,里面包含了该图像的几乎一切生成参数。
或者,您可以直接将本项目中的JSON或PNG工作流拖入ComfyUI,它们将自动加载。
## 贡献
我是编码新手,这是我的第一个GitHub项目。我原本的规划十分宏大,比如实现mflux的Lora以及ControlNet功能,以及实现ComfyUI的精髓理念——即拆分节点以让用户能够深入了解flux的底层逻辑……但我粗浅的代码能力限制了我的蓝图。如果有高手能够帮忙实现这些,我十分感谢。在此问好。
## 许可证
我想采用和mflux项目一致的MIT麻省理工许可,也算为开源贡献一份心力吧。
+3
View File
@@ -0,0 +1,3 @@
from .nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
+162
View File
@@ -0,0 +1,162 @@
import time
import random
import os
import numpy as np
from mflux.config.model_config import ModelConfig
from mflux.config.config import Config
from mflux.flux.flux import Flux1
from mflux.controlnet.flux_controlnet import Flux1Controlnet
from pathlib import Path
from folder_paths import get_filename_list, get_output_directory, models_dir
from huggingface_hub import snapshot_download
import torch
import json
flux_cache = {}
def download_hg_model(model_id, path, exDir="mflux"):
model_checkpoint = os.path.join(models_dir, exDir, path)
if not os.path.exists(model_checkpoint):
print(f"Downloading model {model_id} to {model_checkpoint}...")
try:
snapshot_download(repo_id=model_id, local_dir=model_checkpoint, local_dir_use_symlinks=False)
except Exception as e:
print(f"Error downloading model {model_id}: {e}")
return None
else:
print(f"Model {model_id} already exists at {model_checkpoint}. Skipping download.")
return model_checkpoint
def load_or_create_flux(model, path, lora_paths, lora_scales, is_controlnet=False):
key = (model, path, tuple(lora_paths), tuple(lora_scales), is_controlnet)
if key not in flux_cache:
FluxClass = Flux1Controlnet if is_controlnet else Flux1
flux_cache[key] = FluxClass(
model_config=ModelConfig.from_alias(model),
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 generate_image(prompt, model, seed, height, width, steps, guidance, path, lora_files, lora_scales_str):
lora_paths = lora_files if lora_files else []
lora_scales = [float(s.strip()) for s in lora_scales_str.split(",")] if lora_scales_str else []
seed = random.randint(0, 0xffffffffffffffff) if seed == -1 else int(seed)
print(f"Using seed: {seed}")
flux = load_or_create_flux(model, path if path else None, lora_paths, lora_scales)
image = flux.generate_image(
seed=seed,
prompt=prompt,
config=Config(
num_inference_steps=steps,
height=height,
width=width,
guidance=guidance,
),
)
inner_image = image.image
inner_image_numpy = np.array(inner_image).astype(np.float32)
normalized = inner_image_numpy / 255.0
tensor_image = torch.from_numpy(normalized).permute(2, 0, 1)
tensor_image = tensor_image.unsqueeze(0)
tensor_image = tensor_image.permute(0, 2, 3, 1)
return inner_image, tensor_image
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"}),
"path": (["flux.1-schnell-mflux-4bit", "flux.1-dev-mflux-4bit"], {"default": "flux.1-schnell-mflux-4bit"}),
"seed": ("INT", {"default": -1, "min": -1, "max": 0xffffffffffffffff}),
"height": ("INT", {"default": 1024, "min": 256, "max": 1024}),
"width": ("INT", {"default": 1024, "min": 256, "max": 1024}),
"steps": ("INT", {"default": 2, "min": 1}),
"guidance": ("FLOAT", {"default": 3.5, "min": 0.0}),
"metadata": (["true", "false"], {"default": "false"}),
},
"optional": {
"lora_files": ("LIST", {"default": []}),
"lora_scales": ("STRING", {"default": ""}),
}
}
RETURN_TYPES = ("IMAGE",)
CATEGORY = "Mflux/Quick"
FUNCTION = "generate"
def generate(self, prompt, model, path, seed, height, width, steps, guidance, metadata="false", lora_files=[], lora_scales=""):
model_id = f"madroid/{path}"
full_model_path = download_hg_model(model_id, path)
if model == "dev":
steps = max(steps, 14)
elif model == "schnell":
steps = min(max(steps, 2), 4)
inner_image, tensor_image = generate_image(prompt, model, seed, height, width, steps, guidance, full_model_path, lora_files, lora_scales)
if metadata == "true":
self.save_images([inner_image], prompt, model, path, seed, height, width, steps, guidance)
print(f"Generated tensor image with shape: {tensor_image.shape}, dtype: {tensor_image.dtype}")
return (tensor_image,)
def save_images(self, images, prompt, model, path, seed, height, width, steps, guidance, filename_prefix="Mflux"):
output_dir = get_output_directory()
mflux_dir = os.path.join(output_dir, "Mflux")
os.makedirs(mflux_dir, exist_ok=True)
results = []
existing_files = os.listdir(mflux_dir)
existing_count = len([f for f in existing_files if f.startswith(filename_prefix)])
for (batch_number, img) in enumerate(images):
filename = f"{filename_prefix}_{existing_count + batch_number:05}.png"
file_path = os.path.join(mflux_dir, filename)
img.save(file_path)
metadata = {
"prompt": prompt,
"model": model,
"path": path,
"seed": seed,
"height": height,
"width": width,
"steps": steps,
"guidance": guidance,
}
metadata_filename = os.path.join(mflux_dir, f"{filename_prefix}_{existing_count + batch_number:05}.json")
with open(metadata_filename, 'w') as metadata_file:
json.dump(metadata, metadata_file)
results.append({
"filename": filename,
"path": file_path,
})
return results
NODE_CLASS_MAPPINGS = {
"QuickMfluxNode": QuickMfluxNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"QuickMfluxNode": "Quick MFlux Generation"
}
+1
View File
@@ -0,0 +1 @@
mflux==0.3.0