diff --git a/README.md b/README.md index 8fdc598..b07e0a0 100644 --- a/README.md +++ b/README.md @@ -185,9 +185,9 @@ mflux-generate --model dev --prompt "Luxury food photograph" --steps 25 --seed 2 - **`--controlnet-save-canny`** (optional, bool, default: False): If set, saves the Canny edge detection reference image used by ControlNet. -- **`--init-image-path`** (optional, `str`, default: `None`): Local path to the initial image for image-to-image generation. +- **`--image-path`** (optional, `str`, default: `None`): Local path to the initial image for image-to-image generation. -- **`--init-image-strength`** (optional, `float`, default: `0.4`): Controls how strongly the initial image influences the output image. A value of `0.0` means no influence. (Default is `0.4`) +- **`--image-strength`** (optional, `float`, default: `0.4`): Controls how strongly the initial image influences the output image. A value of `0.0` means no influence. (Default is `0.4`) - **`--config-from-metadata`** or **`-C`** (optional, `str`): [EXPERIMENTAL] Path to a prior file saved via `--metadata`, or a compatible handcrafted config file adhering to the expected args schema. @@ -514,15 +514,15 @@ processed a bit differently, which is why we require this structure above.* ### 🎨 Image-to-Image One way to condition the image generation is by starting from an existing image and let MFLUX produce new variations. -Use the `--init-image-path` flag to specify the reference image, and the `--init-image-strength` to control how much the reference +Use the `--image-path` flag to specify the reference image, and the `--image-strength` to control how much the reference image should guide the generation. For example, given the reference image below, the following command produced the first image using the [Sketching](https://civitai.com/models/803456/sketching?modelVersionId=898364) LoRA: ```sh mflux-generate \ --prompt "sketching of an Eiffel architecture, masterpiece, best quality. The site is lit by lighting professionals, creating a subtle illumination effect. Ink on paper with very fine touches with colored markers, (shadings:1.1), loose lines, Schematic, Conceptual, Abstract, Gestural. Quick sketches to explore ideas and concepts." \ ---init-image-path "reference.png" \ ---init-image-strength 0.3 \ +--image-path "reference.png" \ +--image-strength 0.3 \ --lora-paths Architectural_Sketching.safetensors \ --lora-scales 1.0 \ --model dev \ diff --git a/src/mflux/config/config.py b/src/mflux/config/config.py index c77147c..b42dbc3 100644 --- a/src/mflux/config/config.py +++ b/src/mflux/config/config.py @@ -15,8 +15,8 @@ class Config: width: int = 1024, height: int = 1024, guidance: float = 4.0, - init_image_path: Path | None = None, - init_image_strength: float | None = None, + image_path: Path | None = None, + image_strength: float | None = None, controlnet_strength: float | None = None, ): if width % 16 != 0 or height % 16 != 0: @@ -25,6 +25,6 @@ class Config: self.height = 16 * (height // 16) self.num_inference_steps = num_inference_steps self.guidance = guidance - self.init_image_path = init_image_path - self.init_image_strength = init_image_strength + self.image_path = image_path + self.image_strength = image_strength self.controlnet_strength = controlnet_strength diff --git a/src/mflux/config/runtime_config.py b/src/mflux/config/runtime_config.py index 5c7d9d2..51a3a77 100644 --- a/src/mflux/config/runtime_config.py +++ b/src/mflux/config/runtime_config.py @@ -44,12 +44,12 @@ class RuntimeConfig: return self.model_config.num_train_steps @property - def init_image_path(self) -> str: - return self.config.init_image_path + def image_path(self) -> str: + return self.config.image_path @property - def init_image_strength(self) -> float: - return self.config.init_image_strength + def image_strength(self) -> float | None: + return self.config.image_strength @property def init_time_step(self) -> int: diff --git a/src/mflux/flux/flux.py b/src/mflux/flux/flux.py index 78cfd59..784c4d8 100644 --- a/src/mflux/flux/flux.py +++ b/src/mflux/flux/flux.py @@ -62,7 +62,7 @@ class Flux1(nn.Module): vae=self.vae, sigmas=config.sigmas, init_time_step=config.init_time_step, - init_image_path=config.init_image_path, + image_path=config.image_path, ), ) @@ -141,8 +141,8 @@ class Flux1(nn.Module): quantization=self.bits, lora_paths=self.lora_paths, lora_scales=self.lora_scales, - init_image_path=config.init_image_path, - init_image_strength=config.init_image_strength, + image_path=config.image_path, + image_strength=config.image_strength, generation_time=time_steps.format_dict["elapsed"], ) diff --git a/src/mflux/generate.py b/src/mflux/generate.py index ca2f209..5a443e1 100644 --- a/src/mflux/generate.py +++ b/src/mflux/generate.py @@ -50,8 +50,8 @@ def main(): height=args.height, width=args.width, guidance=args.guidance, - init_image_path=args.init_image_path, - init_image_strength=args.init_image_strength, + image_path=args.image_path, + image_strength=args.image_strength, ), ) # 4. Save the image diff --git a/src/mflux/latent_creator/latent_creator.py b/src/mflux/latent_creator/latent_creator.py index addb970..58e4d24 100644 --- a/src/mflux/latent_creator/latent_creator.py +++ b/src/mflux/latent_creator/latent_creator.py @@ -11,12 +11,12 @@ class Img2Img: vae: VAE, sigmas: mx.array, init_time_step: int, - init_image_path: int, + image_path: int, ): self.vae = vae self.sigmas = sigmas self.init_time_step = init_time_step - self.init_image_path = init_image_path + self.image_path = image_path class LatentCreator: @@ -39,7 +39,7 @@ class LatentCreator: img2img: Img2Img, ) -> mx.array: # 0. Determine type of image generation - is_text2img = img2img.init_image_path is None + is_text2img = img2img.image_path is None if is_text2img: # 1. Create the pure noise diff --git a/src/mflux/post_processing/generated_image.py b/src/mflux/post_processing/generated_image.py index 33f8859..48f0b2a 100644 --- a/src/mflux/post_processing/generated_image.py +++ b/src/mflux/post_processing/generated_image.py @@ -25,8 +25,8 @@ class GeneratedImage: lora_scales: list[float], controlnet_image_path: str | pathlib.Path | None = None, controlnet_strength: float | None = None, - init_image_path: str | pathlib.Path | None = None, - init_image_strength: float | None = None, + image_path: str | pathlib.Path | None = None, + image_strength: float | None = None, ): self.image = image self.model_config = model_config @@ -41,8 +41,8 @@ class GeneratedImage: self.lora_scales = lora_scales self.controlnet_image_path = controlnet_image_path self.controlnet_strength = controlnet_strength - self.init_image_path = init_image_path - self.init_image_strength = init_image_strength + self.image_path = image_path + self.image_strength = image_strength def save( self, @@ -72,8 +72,8 @@ class GeneratedImage: "generation_time_seconds": round(self.generation_time, 2), "lora_paths": [str(p) for p in self.lora_paths] if self.lora_paths else None, "lora_scales": [round(scale, 2) for scale in self.lora_scales] if self.lora_scales else None, - "init_image_path": str(self.init_image_path) if self.init_image_path else None, - "init_image_strength": self.init_image_strength if self.init_image_path else None, + "image_path": str(self.image_path) if self.image_path else None, + "image_strength": self.image_strength if self.image_path else None, "controlnet_image_path": str(self.controlnet_image_path) if self.controlnet_image_path else None, "controlnet_strength": round(self.controlnet_strength, 2) if self.controlnet_strength else None, "prompt": self.prompt, diff --git a/src/mflux/post_processing/image_util.py b/src/mflux/post_processing/image_util.py index eae15c1..563d9fa 100644 --- a/src/mflux/post_processing/image_util.py +++ b/src/mflux/post_processing/image_util.py @@ -26,8 +26,8 @@ class ImageUtil: lora_paths: list[str], lora_scales: list[float], controlnet_image_path: str | None = None, - init_image_path: str | None = None, - init_image_strength: float | None = None, + image_path: str | None = None, + image_strength: float | None = None, ) -> GeneratedImage: normalized = ImageUtil._denormalize(decoded_latents) normalized_numpy = ImageUtil._to_numpy(normalized) @@ -44,8 +44,8 @@ class ImageUtil: generation_time=generation_time, lora_paths=lora_paths, lora_scales=lora_scales, - init_image_path=init_image_path, - init_image_strength=init_image_strength, + image_path=image_path, + image_strength=image_strength, controlnet_image_path=controlnet_image_path, controlnet_strength=config.controlnet_strength, ) diff --git a/src/mflux/ui/cli/parsers.py b/src/mflux/ui/cli/parsers.py index e6ec546..59e930f 100644 --- a/src/mflux/ui/cli/parsers.py +++ b/src/mflux/ui/cli/parsers.py @@ -69,8 +69,8 @@ class CommandLineParser(argparse.ArgumentParser): def add_image_to_image_arguments(self, required=False) -> None: self.supports_image_to_image = True - self.add_argument("--init-image-path", type=Path, required=required, default=None, help="Local path to init image") - self.add_argument("--init-image-strength", type=float, required=False, default=ui_defaults.INIT_IMAGE_STRENGTH, help=f"Controls how strongly the init image influences the output image. A value of 0.0 means no influence. (Default is {ui_defaults.INIT_IMAGE_STRENGTH})") + self.add_argument("--image-path", type=Path, required=required, default=None, help="Local path to init image") + self.add_argument("--image-strength", type=float, required=False, default=ui_defaults.IMAGE_STRENGTH, help=f"Controls how strongly the init image influences the output image. A value of 0.0 means no influence. (Default is {ui_defaults.IMAGE_STRENGTH})") def add_batch_image_generator_arguments(self) -> None: self.add_argument("--prompts-file", type=Path, required=True, default=argparse.SUPPRESS, help="Local path for a file that holds a batch of prompts.") @@ -152,10 +152,10 @@ class CommandLineParser(argparse.ArgumentParser): namespace.lora_scales = prior_gen_metadata.get("lora_scales", []) + namespace.lora_scales if self.supports_image_to_image: - if namespace.init_image_path is None: - namespace.init_image_path = prior_gen_metadata.get("init_image_path", None) - if namespace.init_image_strength == self.get_default("init_image_strength") and (init_img_strength_from_metadata := prior_gen_metadata.get("init_image_strength", None)): - namespace.init_image_strength = init_img_strength_from_metadata + if namespace.image_path is None: + namespace.image_path = prior_gen_metadata.get("image_path", None) + if namespace.image_strength == self.get_default("image_strength") and (img_strength_from_metadata := prior_gen_metadata.get("image_strength", None)): + namespace.image_strength = img_strength_from_metadata if self.supports_controlnet: if namespace.controlnet_image_path is None: diff --git a/src/mflux/ui/defaults.py b/src/mflux/ui/defaults.py index 31a307f..1c12e28 100644 --- a/src/mflux/ui/defaults.py +++ b/src/mflux/ui/defaults.py @@ -1,7 +1,7 @@ CONTROLNET_STRENGTH = 0.4 GUIDANCE_SCALE = 3.5 HEIGHT, WIDTH = 1024, 1024 -INIT_IMAGE_STRENGTH = 0.4 # for image-to-image init_image +IMAGE_STRENGTH = 0.4 MODEL_CHOICES = ["dev", "schnell"] MODEL_INFERENCE_STEPS = { "dev": 14, diff --git a/tests/arg_parser/test_cli_argparser.py b/tests/arg_parser/test_cli_argparser.py index 56bee4d..fcc91f1 100644 --- a/tests/arg_parser/test_cli_argparser.py +++ b/tests/arg_parser/test_cli_argparser.py @@ -75,8 +75,8 @@ def base_metadata_dict() -> dict: "generation_time_seconds": 42.0, "lora_paths": None, "lora_scales": None, - "init_image": None, - "init_image_strength": None, + "image": None, + "image_strength": None, "controlnet_image": None, "controlnet_strength": None, "controlnet_save_canny": False, @@ -300,32 +300,32 @@ def test_image_to_image_args(mflux_generate_parser, mflux_generate_minimal_argv, metadata_file = temp_dir / "image_to_image.json" test_path = "/some/awesome/image.png" with metadata_file.open("wt") as m: - base_metadata_dict["init_image_path"] = test_path + base_metadata_dict["image_path"] = test_path json.dump(base_metadata_dict, m, indent=4) # test user default value with patch("sys.argv", mflux_generate_minimal_argv + ["-m", "dev"]): args = mflux_generate_parser.parse_args() - assert args.init_image_path is None - assert args.init_image_strength == 0.4 # default + assert args.image_path is None + assert args.image_strength == 0.4 # default # test metadata config accepted with patch('sys.argv', mflux_generate_minimal_argv + ['--config-from-metadata', metadata_file.as_posix()]): # fmt: off args = mflux_generate_parser.parse_args() - assert args.init_image_path == test_path - assert args.init_image_strength == 0.4 # default + assert args.image_path == test_path + assert args.image_strength == 0.4 # default # test strength override - with patch('sys.argv', mflux_generate_minimal_argv + ['--init-image-strength', '0.7', '--config-from-metadata', metadata_file.as_posix()]): # fmt: off + with patch('sys.argv', mflux_generate_minimal_argv + ['--image-strength', '0.7', '--config-from-metadata', metadata_file.as_posix()]): # fmt: off args = mflux_generate_parser.parse_args() - assert args.init_image_path == test_path - assert args.init_image_strength == 0.7 + assert args.image_path == test_path + assert args.image_strength == 0.7 # test image path override - with patch('sys.argv', mflux_generate_minimal_argv + ['--init-image-path', '/some/better/image.png', '--config-from-metadata', metadata_file.as_posix()]): # fmt: off + with patch('sys.argv', mflux_generate_minimal_argv + ['--image-path', '/some/better/image.png', '--config-from-metadata', metadata_file.as_posix()]): # fmt: off args = mflux_generate_parser.parse_args() - assert args.init_image_path == Path("/some/better/image.png") - assert args.init_image_strength == 0.4 # default + assert args.image_path == Path("/some/better/image.png") + assert args.image_strength == 0.4 # default def test_controlnet_args(mflux_generate_controlnet_parser, mflux_generate_controlnet_minimal_argv, base_metadata_dict, temp_dir): # fmt: off diff --git a/tests/image_generation/helpers/image_generation_test_helper.py b/tests/image_generation/helpers/image_generation_test_helper.py index 6718dc1..f816e44 100644 --- a/tests/image_generation/helpers/image_generation_test_helper.py +++ b/tests/image_generation/helpers/image_generation_test_helper.py @@ -18,8 +18,8 @@ class ImageGeneratorTestHelper: seed: int, height: int = None, width: int = None, - init_image_path: str | None = None, - init_image_strength: float | None = None, + image_path: str | None = None, + image_strength: float | None = None, lora_paths: list[str] | None = None, lora_scales: list[float] | None = None, ): @@ -42,8 +42,8 @@ class ImageGeneratorTestHelper: prompt=prompt, config=Config( num_inference_steps=steps, - init_image_path=ImageGeneratorTestHelper.resolve_path(init_image_path), - init_image_strength=init_image_strength, + image_path=ImageGeneratorTestHelper.resolve_path(image_path), + image_strength=image_strength, height=height, width=width, ), diff --git a/tests/image_generation/test_generate_image.py b/tests/image_generation/test_generate_image.py index d45fca0..2ba5f94 100644 --- a/tests/image_generation/test_generate_image.py +++ b/tests/image_generation/test_generate_image.py @@ -60,8 +60,8 @@ class TestImageGenerator: def test_image_generation_dev_image_to_image(self): ImageGeneratorTestHelper.assert_matches_reference_image( reference_image_path="reference_dev_image_to_image_result.png", - init_image_path="reference_dev_lora.png", - init_image_strength=0.4, + image_path="reference_dev_lora.png", + image_strength=0.4, output_image_path=TestImageGenerator.OUTPUT_IMAGE_FILENAME, model_config=ModelConfig.dev(), steps=8,