├── .gitignore ├── LICENSE ├── README.md ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | *.png 3 | output/ 4 | prompts.txt 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Replicate 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Run latent consistency models on your Mac 2 | 3 | Latent consistency models (LCMs) are based on Stable Diffusion, but they can generate images much faster, needing only 4 to 8 steps for a good image (compared to 25 to 50 steps). [Simian Luo et al](https://arxiv.org/abs/2310.04378) released the first Stable Diffusion distilled model. It’s distilled from the Dreamshaper fine-tune by incorporating classifier-free guidance into the model’s input. 4 | 5 | You can [run Latent Consistency Models in the cloud on Replicate](https://replicate.com/luosiallen/latent-consistency-model), but it's also possible to run it locally. 6 | 7 | ## Prerequisites 8 | 9 | You’ll need: 10 | 11 | - a Mac with an M1 or M2 chip 12 | - 16GB RAM or more 13 | - macOS 13.0 or higher 14 | - Python 3.10 or above 15 | 16 | ## Install 17 | 18 | Run this to clone the repo: 19 | 20 | git clone https://github.com/replicate/latent-consistency-model.git 21 | cd latent-consistency-model 22 | 23 | Set up a virtualenv to install the dependencies: 24 | 25 | python3 -m pip install virtualenv 26 | python3 -m virtualenv venv 27 | 28 | Activate the virtualenv: 29 | 30 | source venv/bin/activate 31 | 32 | (You'll need to run this command again any time you want to run the script.) 33 | 34 | Then, install the dependencies: 35 | 36 | pip install -r requirements.txt 37 | 38 | ## Run 39 | 40 | The script will automatically download the [`SimianLuo/LCM_Dreamshaper_v7`](https://huggingface.co/SimianLuo/LCM_Dreamshaper_v7) (3.44 GB) and [safety checker](https://huggingface.co/CompVis/stable-diffusion-safety-checker) (1.22 GB) models from HuggingFace. 41 | 42 | ```sh 43 | python main.py \ 44 | "a beautiful apple floating in outer space, like a planet" \ 45 | --steps 4 --width 512 --height 512 46 | ``` 47 | 48 | You’ll see an output like this: 49 | 50 | ```sh 51 | Output image saved to: output/out-20231026-144506.png 52 | Using seed: 48404 53 | 100%|███████████████████████████| 4/4 [00:00<00:00, 5.54it/s] 54 | ``` 55 | 56 | ## Options 57 | 58 | | Parameter | Type | Default | Description | 59 | |---------------|-------|---------|---------------------------------------------------------------| 60 | | prompt | str | N/A | A text string for image generation. | 61 | | --width | int | 512 | The width of the generated image. | 62 | | --height | int | 512 | The height of the generated image. | 63 | | --steps | int | 8 | The number of inference steps. | 64 | | --seed | int | None | Seed for random number generation. | 65 | | --continuous | flag | False | Enable continuous generation. | 66 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | import argparse 4 | import time 5 | from diffusers import DiffusionPipeline 6 | 7 | class Predictor: 8 | def __init__(self): 9 | self.pipe = self._load_model() 10 | 11 | def _load_model(self): 12 | model = DiffusionPipeline.from_pretrained( 13 | "SimianLuo/LCM_Dreamshaper_v7", 14 | ) 15 | model.to(torch_device="cpu", torch_dtype=torch.float32).to('mps:0') 16 | return model 17 | 18 | def predict(self, prompt: str, width: int, height: int, steps: int, seed: int = None) -> str: 19 | seed = seed or int.from_bytes(os.urandom(2), "big") 20 | print(f"Using seed: {seed}") 21 | torch.manual_seed(seed) 22 | 23 | result = self.pipe( 24 | prompt=prompt, width=width, height=height, 25 | guidance_scale=8.0, num_inference_steps=steps, 26 | num_images_per_prompt=1, lcm_origin_steps=50, 27 | output_type="pil" 28 | ).images[0] 29 | 30 | return self._save_result(result) 31 | 32 | def predict_from_file(self, prompt_file: str, width: int, height: int, steps: int, seed: int = None, continuous: bool = False): 33 | with open(prompt_file, 'r') as file: 34 | prompts = file.readlines() 35 | 36 | while True: 37 | for prompt in prompts: 38 | prompt = prompt.strip() 39 | output_path = self.predict(prompt, width, height, steps, seed) 40 | print(f"Output image saved for '{prompt}' to: {output_path}") 41 | 42 | if not continuous: 43 | return 44 | 45 | def _save_result(self, result): 46 | timestamp = time.strftime("%Y%m%d-%H%M%S") 47 | output_dir = "output" 48 | if not os.path.exists(output_dir): 49 | os.makedirs(output_dir) 50 | output_path = os.path.join(output_dir, f"out-{timestamp}.png") 51 | result.save(output_path) 52 | return output_path 53 | 54 | def main(): 55 | args = parse_args() 56 | predictor = Predictor() 57 | 58 | if args.prompts: 59 | predictor.predict_from_file(args.prompts, args.width, args.height, args.steps, args.seed, args.continuous) 60 | else: 61 | output_path = predictor.predict(args.prompt, args.width, args.height, args.steps, args.seed) 62 | print(f"Output image saved to: {output_path}") 63 | 64 | def parse_args(): 65 | parser = argparse.ArgumentParser(description="Generate images based on text prompts.") 66 | parser.add_argument("prompt", type=str, help="A single text prompt for image generation.", nargs='?') 67 | parser.add_argument("--prompts", type=str, help="A file containing text prompts for image generation, one per line.") 68 | parser.add_argument("--width", type=int, default=512, help="The width of the generated image.") 69 | parser.add_argument("--height", type=int, default=512, help="The height of the generated image.") 70 | parser.add_argument("--steps", type=int, default=8, help="The number of inference steps.") 71 | parser.add_argument("--seed", type=int, default=None, help="Seed for random number generation.") 72 | parser.add_argument("--continuous", action='store_true', help="Enable continuous generation.") 73 | return parser.parse_args() 74 | 75 | if __name__ == "__main__": 76 | main() 77 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | accelerate==0.23.0 2 | torch==2.0.1 3 | torchvision==0.15.2 4 | diffusers==0.23.0 5 | Pillow==10.1.0 6 | transformers==4.34.1 7 | --------------------------------------------------------------------------------