diff --git a/comfy_fm_newgen.py b/comfy_fm_newgen.py index 816bc9a..5e552af 100644 --- a/comfy_fm_newgen.py +++ b/comfy_fm_newgen.py @@ -25,7 +25,9 @@ from lib.general import ( from lib.logging import LOGGING_CONFIG # from simple_term_menu import TerminalMenu -from comfy_api_simplified import ComfyApiWrapper, ComfyWorkflowWrapper +from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler +import torch +from PIL import Image logging.config.dictConfig(LOGGING_CONFIG) @@ -51,31 +53,78 @@ p = inflect.engine() def generate_image(uid, comfy_prompt): - """Generate an image using the Comfy API.""" + """Generate an image using local Stable Diffusion.""" try: - # Initialize API and workflow - api = ComfyApiWrapper(user_config["comfyui"]["comfyui_url"]) - wf = ComfyWorkflowWrapper("./workflow_api.json") + # Initialize the pipeline (do this once and reuse) + if not hasattr(generate_image, 'pipeline'): + logging.info("Loading Stable Diffusion model...") - # Set workflow parameters - wf.set_node_param("KSampler", "seed", random.getrandbits(32)) - # wf.set_node_param("KSampler", "steps", steps) - wf.set_node_param("positive", "text", comfy_prompt) - wf.set_node_param("Save Image", "filename_prefix", uid) - wf.set_node_param( - "Load Checkpoint", "ckpt_name", user_config["comfyui"]["model"] - ) - # Queue your workflow for completion + # Get model configuration + try: + model_id = user_config["models"]["model_name"] + model_dir = user_config["models"].get("model_dir", None) + logging.info(f"Using model: {model_id}") + except KeyError: + model_id = "SG161222/Realistic_Vision_V6.0_B1" + model_dir = None + logging.warning(f"Model configuration not found, using default: {model_id}") + + # Check if CUDA is available + device = "cuda" if torch.cuda.is_available() else "cpu" + logging.info(f"Using device: {device}") + + # Load the pipeline + if model_dir: + pipe = StableDiffusionPipeline.from_pretrained( + model_id, + cache_dir=model_dir, + torch_dtype=torch.float16 if device == "cuda" else torch.float32, + safety_checker=None, + requires_safety_checker=False + ) + else: + pipe = StableDiffusionPipeline.from_pretrained( + model_id, + torch_dtype=torch.float16 if device == "cuda" else torch.float32, + safety_checker=None, + requires_safety_checker=False + ) + + # Use DPMSolverMultistepScheduler for better quality/speed balance + pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config) + + if device == "cuda": + pipe = pipe.to("cuda") + pipe.enable_attention_slicing() # Reduce memory usage + + generate_image.pipeline = pipe + logging.info("Model loaded successfully") + + # Generate the image logging.debug(f"Generating image for UID: {uid}") - results = api.queue_and_wait_images(wf, "Save Image") - for filename, image_data in results.items(): - with open( - f"{user_config[selected_profile]['output_dir']}{uid}.png", "wb+" - ) as f: - f.write(image_data) + + # Set random seed for reproducibility + generator = torch.Generator(device=generate_image.pipeline.device) + generator.manual_seed(random.getrandbits(32)) + + # Generate image with parameters similar to ComfyUI workflow + image = generate_image.pipeline( + comfy_prompt, + num_inference_steps=6, + guidance_scale=1.5, + generator=generator, + width=512, + height=512 + ).images[0] + + # Save the image + output_path = f"{user_config[selected_profile]['output_dir']}{uid}.png" + image.save(output_path) logging.debug(f"Image generated successfully for UID: {uid}") + except Exception as e: logging.error(f"Failed to generate image for UID: {uid}. Error: {e}") + raise def get_country_name(app_config, country_code): diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..f489424 --- /dev/null +++ b/gui.py @@ -0,0 +1,500 @@ +#!/usr/bin/env python3 +""" +Standalone FM NewGens - GUI Interface +A user-friendly graphical interface for generating Football Manager faces +""" + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox, scrolledtext +import os +import sys +import json +import configparser +import threading +import queue +import time +from pathlib import Path +from PIL import Image, ImageTk +import logging + +# Import the main generation logic +from comfy_fm_newgen import generate_image, get_country_name, generate_prompts_for_players +try: + from lib.rtf_parser import RTF_Parser +except ImportError: + # Fallback for when striprtf is not available + class RTF_Parser: + def parse_rtf(self, file_path): + print(f"Warning: RTF parsing not available. Please install striprtf: pip install striprtf==0.0.28") + return [] +from lib.generate_xml import create_config_xml, append_to_config_xml +from lib.xml_reader import extract_from_values +from lib.general import choose_profile, create_or_update, process_player_or_file, get_player_input + +class FMFaceGeneratorGUI: + def __init__(self, root): + self.root = root + self.root.title("FM Face Generator - Standalone Version") + self.root.geometry("1000x700") + self.root.minsize(800, 600) + + # Initialize variables + self.config = None + self.app_config = None + self.rtf = RTF_Parser() + self.generation_thread = None + self.stop_generation = False + self.image_queue = queue.Queue() + + # Set up the GUI + self.setup_gui() + self.load_config() + + # Start image preview update loop + self.update_image_preview() + + def setup_gui(self): + """Set up the main GUI components""" + # Create main notebook (tabs) + self.notebook = ttk.Notebook(self.root) + self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + # Configuration tab + self.config_frame = ttk.Frame(self.notebook) + self.notebook.add(self.config_frame, text="Configuration") + + # Generation tab + self.generation_frame = ttk.Frame(self.notebook) + self.notebook.add(self.generation_frame, text="Generation") + + # Preview tab + self.preview_frame = ttk.Frame(self.notebook) + self.notebook.add(self.preview_frame, text="Preview") + + # Setup each tab + self.setup_config_tab() + self.setup_generation_tab() + self.setup_preview_tab() + + # Status bar + self.status_var = tk.StringVar() + self.status_var.set("Ready") + self.status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN) + self.status_bar.pack(side=tk.BOTTOM, fill=tk.X) + + # Progress bar + self.progress_var = tk.DoubleVar() + self.progress_bar = ttk.Progressbar(self.root, variable=self.progress_var, maximum=100) + self.progress_bar.pack(side=tk.BOTTOM, fill=tk.X) + + def setup_config_tab(self): + """Set up the configuration tab""" + # Model selection + model_frame = ttk.LabelFrame(self.config_frame, text="AI Model", padding=10) + model_frame.pack(fill=tk.X, padx=10, pady=5) + + ttk.Label(model_frame, text="Model:").grid(row=0, column=0, sticky=tk.W, pady=5) + self.model_var = tk.StringVar() + model_combo = ttk.Combobox(model_frame, textvariable=self.model_var, width=50) + model_combo['values'] = [ + 'SG161222/Realistic_Vision_V6.0_B1', + 'digiplay/AbsoluteReality', + 'stabilityai/stable-diffusion-2-1', + 'runwayml/stable-diffusion-v1-5' + ] + model_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) + model_combo.set('SG161222/Realistic_Vision_V6.0_B1') + + # Model directory + ttk.Label(model_frame, text="Model Directory:").grid(row=1, column=0, sticky=tk.W, pady=5) + self.model_dir_var = tk.StringVar() + model_dir_entry = ttk.Entry(model_frame, textvariable=self.model_dir_var, width=50) + model_dir_entry.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5) + ttk.Button(model_frame, text="Browse...", command=self.browse_model_dir).grid(row=1, column=2, padx=5, pady=5) + + # FM Configuration + fm_frame = ttk.LabelFrame(self.config_frame, text="Football Manager", padding=10) + fm_frame.pack(fill=tk.X, padx=10, pady=5) + + ttk.Label(fm_frame, text="FM Version:").grid(row=0, column=0, sticky=tk.W, pady=5) + self.fm_version_var = tk.StringVar(value="2024") + fm_version_entry = ttk.Entry(fm_frame, textvariable=self.fm_version_var, width=20) + fm_version_entry.grid(row=0, column=1, sticky=tk.W, padx=5, pady=5) + + ttk.Label(fm_frame, text="RTF File:").grid(row=1, column=0, sticky=tk.W, pady=5) + self.rtf_file_var = tk.StringVar() + rtf_entry = ttk.Entry(fm_frame, textvariable=self.rtf_file_var, width=50) + rtf_entry.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5) + ttk.Button(fm_frame, text="Browse...", command=self.browse_rtf_file).grid(row=1, column=2, padx=5, pady=5) + + ttk.Label(fm_frame, text="Output Directory:").grid(row=2, column=0, sticky=tk.W, pady=5) + self.output_dir_var = tk.StringVar() + output_entry = ttk.Entry(fm_frame, textvariable=self.output_dir_var, width=50) + output_entry.grid(row=2, column=1, sticky=tk.W, padx=5, pady=5) + ttk.Button(fm_frame, text="Browse...", command=self.browse_output_dir).grid(row=2, column=2, padx=5, pady=5) + + # Control buttons + button_frame = ttk.Frame(self.config_frame) + button_frame.pack(fill=tk.X, padx=10, pady=10) + + ttk.Button(button_frame, text="Save Configuration", command=self.save_config).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Load Configuration", command=self.load_config).pack(side=tk.LEFT, padx=5) + ttk.Button(button_frame, text="Reset to Defaults", command=self.reset_config).pack(side=tk.LEFT, padx=5) + + def setup_generation_tab(self): + """Set up the generation tab""" + # Player selection + player_frame = ttk.LabelFrame(self.generation_frame, text="Player Selection", padding=10) + player_frame.pack(fill=tk.X, padx=10, pady=5) + + self.player_mode_var = tk.StringVar(value="all") + ttk.Radiobutton(player_frame, text="Process all players", variable=self.player_mode_var, value="all", command=self.update_player_controls).grid(row=0, column=0, sticky=tk.W, pady=5) + ttk.Radiobutton(player_frame, text="Process specific player", variable=self.player_mode_var, value="specific", command=self.update_player_controls).grid(row=0, column=1, sticky=tk.W, pady=5) + + ttk.Label(player_frame, text="Player UID:").grid(row=1, column=0, sticky=tk.W, pady=5) + self.player_uid_var = tk.StringVar() + self.player_uid_entry = ttk.Entry(player_frame, textvariable=self.player_uid_var, width=20, state=tk.DISABLED) + self.player_uid_entry.grid(row=1, column=1, sticky=tk.W, padx=5, pady=5) + + # Generation options + gen_frame = ttk.LabelFrame(self.generation_frame, text="Generation Options", padding=10) + gen_frame.pack(fill=tk.X, padx=10, pady=5) + + self.update_mode_var = tk.StringVar(value="false") + ttk.Checkbutton(gen_frame, text="Update existing (skip already processed)", variable=self.update_mode_var, onvalue="true", offvalue="false").grid(row=0, column=0, sticky=tk.W, pady=5) + + # Progress display + progress_frame = ttk.LabelFrame(self.generation_frame, text="Progress", padding=10) + progress_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Status text + self.status_text = scrolledtext.ScrolledText(progress_frame, height=10, wrap=tk.WORD) + self.status_text.pack(fill=tk.BOTH, expand=True, pady=5) + + # Control buttons + control_frame = ttk.Frame(self.generation_frame) + control_frame.pack(fill=tk.X, padx=10, pady=10) + + self.start_button = ttk.Button(control_frame, text="Start Generation", command=self.start_generation) + self.start_button.pack(side=tk.LEFT, padx=5) + + self.stop_button = ttk.Button(control_frame, text="Stop Generation", command=self.stop_generation_thread, state=tk.DISABLED) + self.stop_button.pack(side=tk.LEFT, padx=5) + + ttk.Button(control_frame, text="Clear Log", command=self.clear_log).pack(side=tk.RIGHT, padx=5) + + def setup_preview_tab(self): + """Set up the preview tab""" + # Image preview + preview_frame = ttk.LabelFrame(self.preview_frame, text="Generated Images", padding=10) + preview_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5) + + # Image display + self.image_label = ttk.Label(preview_frame, text="No images generated yet") + self.image_label.pack(pady=20) + + # Image info + info_frame = ttk.Frame(preview_frame) + info_frame.pack(fill=tk.X, pady=10) + + ttk.Label(info_frame, text="Current Image:").grid(row=0, column=0, sticky=tk.W) + self.current_image_var = tk.StringVar() + ttk.Label(info_frame, textvariable=self.current_image_var).grid(row=0, column=1, sticky=tk.W, padx=5) + + ttk.Label(info_frame, text="Total Generated:").grid(row=1, column=0, sticky=tk.W) + self.total_generated_var = tk.StringVar(value="0") + ttk.Label(info_frame, textvariable=self.total_generated_var).grid(row=1, column=1, sticky=tk.W, padx=5) + + # Navigation buttons + nav_frame = ttk.Frame(preview_frame) + nav_frame.pack(fill=tk.X, pady=10) + + ttk.Button(nav_frame, text="Previous", command=self.show_previous_image).pack(side=tk.LEFT, padx=5) + ttk.Button(nav_frame, text="Next", command=self.show_next_image).pack(side=tk.LEFT, padx=5) + ttk.Button(nav_frame, text="Refresh", command=self.refresh_preview).pack(side=tk.RIGHT, padx=5) + + def browse_model_dir(self): + """Browse for model directory""" + directory = filedialog.askdirectory(title="Select Model Directory") + if directory: + self.model_dir_var.set(directory) + + def browse_rtf_file(self): + """Browse for RTF file""" + file_path = filedialog.askopenfilename( + title="Select RTF File", + filetypes=[("RTF files", "*.rtf"), ("All files", "*.*")] + ) + if file_path: + self.rtf_file_var.set(file_path) + + def browse_output_dir(self): + """Browse for output directory""" + directory = filedialog.askdirectory(title="Select Output Directory") + if directory: + self.output_dir_var.set(directory) + + def update_player_controls(self): + """Update player selection controls based on mode""" + if self.player_mode_var.get() == "specific": + self.player_uid_entry.config(state=tk.NORMAL) + else: + self.player_uid_entry.config(state=tk.DISABLED) + self.player_uid_var.set("") + + def save_config(self): + """Save configuration to file""" + try: + # Update config object + if not self.config: + self.config = configparser.ConfigParser() + self.config.add_section('models') + self.config.add_section('profile:NewGens') + + self.config.set('models', 'model_name', self.model_var.get()) + if self.model_dir_var.get(): + self.config.set('models', 'model_dir', self.model_dir_var.get()) + + self.config.set('profile:NewGens', 'football_manager_version', self.fm_version_var.get()) + self.config.set('profile:NewGens', 'rtf_file', self.rtf_file_var.get()) + self.config.set('profile:NewGens', 'output_dir', self.output_dir_var.get()) + + # Save to file + with open('user_config.cfg', 'w') as configfile: + self.config.write(configfile) + + messagebox.showinfo("Success", "Configuration saved successfully!") + self.log_message("Configuration saved") + + except Exception as e: + messagebox.showerror("Error", f"Failed to save configuration: {str(e)}") + self.log_message(f"Error saving configuration: {str(e)}") + + def load_config(self): + """Load configuration from file""" + try: + if os.path.exists('user_config.cfg'): + self.config = configparser.ConfigParser() + self.config.read('user_config.cfg') + + # Load model settings + if 'models' in self.config: + self.model_var.set(self.config.get('models', 'model_name', fallback='SG161222/Realistic_Vision_V6.0_B1')) + self.model_dir_var.set(self.config.get('models', 'model_dir', fallback='')) + + # Load FM settings + if 'profile:NewGens' in self.config: + self.fm_version_var.set(self.config.get('profile:NewGens', 'football_manager_version', fallback='2024')) + self.rtf_file_var.set(self.config.get('profile:NewGens', 'rtf_file', fallback='')) + self.output_dir_var.set(self.config.get('profile:NewGens', 'output_dir', fallback='')) + + self.log_message("Configuration loaded") + else: + self.log_message("No configuration file found, using defaults") + + except Exception as e: + messagebox.showerror("Error", f"Failed to load configuration: {str(e)}") + self.log_message(f"Error loading configuration: {str(e)}") + + def reset_config(self): + """Reset configuration to defaults""" + self.model_var.set('SG161222/Realistic_Vision_V6.0_B1') + self.model_dir_var.set('') + self.fm_version_var.set('2024') + self.rtf_file_var.set('') + self.output_dir_var.set('') + self.log_message("Configuration reset to defaults") + + def log_message(self, message): + """Add message to log""" + self.status_text.insert(tk.END, f"[{time.strftime('%H:%M:%S')}] {message}\n") + self.status_text.see(tk.END) + + def clear_log(self): + """Clear the log""" + self.status_text.delete(1.0, tk.END) + + def start_generation(self): + """Start the generation process""" + if not self.validate_config(): + return + + # Disable start button, enable stop button + self.start_button.config(state=tk.DISABLED) + self.stop_button.config(state=tk.NORMAL) + + # Reset progress + self.progress_var.set(0) + self.stop_generation = False + + # Start generation thread + self.generation_thread = threading.Thread(target=self.generation_worker) + self.generation_thread.daemon = True + self.generation_thread.start() + + def stop_generation_thread(self): + """Stop the generation process""" + self.stop_generation = True + self.stop_button.config(state=tk.DISABLED) + self.log_message("Stopping generation...") + + def generation_worker(self): + """Worker thread for generation process""" + try: + self.log_message("Starting generation process...") + + # Load configurations + with open("app_config.json", "r") as f: + app_config = json.load(f) + + # Parse RTF file + rtf_file = self.rtf.parse_rtf(self.rtf_file_var.get()) + self.log_message(f"Parsed RTF file successfully. Found {len(rtf_file)} players.") + + # Determine players to process + update = self.update_mode_var.get() == "true" + process_player = self.player_mode_var.get() == "specific" + + if process_player: + player_uuid = int(self.player_uid_var.get()) + players_to_process = [p for p in rtf_file if int(p[0]) == player_uuid] + elif update: + # Filter out already processed players + try: + values_from_config = extract_from_values(f"{self.output_dir_var.get()}/config.xml") + ids_in_b = [item for item in values_from_config] + players_to_process = [item for item in rtf_file if item[0] not in ids_in_b] + except FileNotFoundError: + players_to_process = rtf_file + else: + players_to_process = rtf_file + + if len(players_to_process) == 0: + self.log_message("No players to process") + return + + self.log_message(f"Processing {len(players_to_process)} players") + + # Generate prompts + prompts = generate_prompts_for_players(players_to_process, app_config) + + # Generate images + total_players = len(prompts) + for i, prompt in enumerate(prompts): + if self.stop_generation: + break + + uid = prompt.split(":")[0] + comfy_prompt = prompt.split(":")[1] + + self.log_message(f"Generating image for player {uid} ({i+1}/{total_players})") + generate_image(uid, comfy_prompt) + + # Update progress + progress = (i + 1) / total_players * 100 + self.progress_var.set(progress) + + # Queue image for preview + self.image_queue.put(uid) + + # Post-processing + if not self.stop_generation: + try: + if update: + append_to_config_xml( + self.output_dir_var.get(), + [item[0] for item in players_to_process], + self.fm_version_var.get() + ) + else: + create_config_xml( + self.output_dir_var.get(), + [item[0] for item in players_to_process], + self.fm_version_var.get() + ) + self.log_message("Configuration XML updated") + except Exception as e: + self.log_message(f"Post-processing failed: {e}") + + self.log_message("Generation complete!") + + except Exception as e: + self.log_message(f"Generation failed: {str(e)}") + import traceback + self.log_message(traceback.format_exc()) + + finally: + # Re-enable start button + self.start_button.config(state=tk.NORMAL) + self.stop_button.config(state=tk.DISABLED) + + def validate_config(self): + """Validate configuration before starting""" + if not self.rtf_file_var.get(): + messagebox.showerror("Error", "Please select an RTF file") + return False + + if not self.output_dir_var.get(): + messagebox.showerror("Error", "Please select an output directory") + return False + + if not os.path.exists(self.rtf_file_var.get()): + messagebox.showerror("Error", "RTF file does not exist") + return False + + return True + + def update_image_preview(self): + """Update image preview periodically""" + try: + while not self.image_queue.empty(): + uid = self.image_queue.get() + self.show_image(uid) + except: + pass + + # Schedule next update + self.root.after(1000, self.update_image_preview) + + def show_image(self, uid): + """Show an image in the preview""" + try: + image_path = f"{self.output_dir_var.get()}/{uid}.png" + if os.path.exists(image_path): + # Load and resize image + image = Image.open(image_path) + image.thumbnail((300, 300), Image.Resampling.LANCZOS) + + # Convert to PhotoImage + photo = ImageTk.PhotoImage(image) + + # Update display + self.image_label.config(image=photo, text="") + self.image_label.image = photo # Keep reference + self.current_image_var.set(f"Player {uid}") + self.log_message(f"Preview updated: {uid}.png") + + except Exception as e: + self.log_message(f"Failed to load image {uid}: {str(e)}") + + def show_previous_image(self): + """Show previous image""" + self.log_message("Previous image functionality not implemented yet") + + def show_next_image(self): + """Show next image""" + self.log_message("Next image functionality not implemented yet") + + def refresh_preview(self): + """Refresh the preview""" + self.log_message("Refreshing preview...") + # This would scan the output directory and show available images + +def main(): + """Main function""" + root = tk.Tk() + app = FMFaceGeneratorGUI(root) + root.mainloop() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/readme.md b/readme.md index 91d4c01..eecb55a 100644 --- a/readme.md +++ b/readme.md @@ -1,7 +1,9 @@ -# Comfy UI NewGens +# Standalone FM NewGens -Use ComfyUI to generate NewGen images for Football Manager 2024 +Generate NewGen images for Football Manager using local Stable Diffusion + +**🖥️ GUI Available!** - Now includes a user-friendly graphical interface # Intro @@ -13,68 +15,192 @@ Use ComfyUI to generate NewGen images for Football Manager 2024 [Example video here](https://www.youtube.com/watch?v=mcGj3_nbV0A) - -These are some of the questions I think you are going to ask me, but I present to you somehting I have been working on, **Comfy FM24 NewGens** + +These are some of the questions I think you are going to ask me, but I present to you something I have been working on, **Standalone FM NewGens** # The Idea -Taking some inspiration from [fm-ai-face-generator](https://github.com/emilmirzayev/fm-ai-face-generator), but not wanting to pay for a hosted service (I already have AI _stuff_ setup on my home server) I started to rewrite chunks to make compatible with a hosted Comfy UI instance, after awhile, I had rewritten so much, that this is now a new beast in itself.. +Taking inspiration from [fm-ai-face-generator](https://github.com/emilmirzayev/fm-ai-face-generator) and [NewGAN-Manager](https://github.com/franl08/NewGAN-Manager), this tool generates unique face images for Football Manager NewGens using local Stable Diffusion instead of requiring a separate ComfyUI server. - +Thanks to both original projects for the inspiration! -Also taking inspiration from [NewGAN-Manager](https://github.com/franl08/NewGAN-Manager) and reworking some of thier code for my uses. +So what does this do? Why would you want to use it? (you probably won't but personally, I like a bit more personalization from my NewGens, some I get quite attached to :) ) - - -Thanks to both! - - - -So what does this do? Why would you want to use it? (you probably wont but personally, I like a bit more personallisation from my NewGens, some, I get quite attached to :) ) - - - -- This will create images for your NewGens, based off of information provided from the FM database, making a players image more unique to that player -- download the images to the specified output directory, all processed players image will be saved as their **uid** from the game -- remove all the background from the generated faces -- create the needed config file for FM to load the faces, -- you can then rename the generated folder and place it in your **graphics** folder as you would noramlly and voila! +- This will create images for your NewGens, based off of information provided from the FM database, making a player's image more unique to that player +- Generate images locally using Stable Diffusion (no external server required) +- Download the images to the specified output directory, all processed players images will be saved as their **uid** from the game +- Remove all the background from the generated faces +- Create the needed config file for FM to load the faces +- You can then rename the generated folder and place it in your **graphics** folder as you would normally and voila! # Install Guide - + Things you will need that I will not be going over on how to setup. - - Python installed - - Git installed - - A ComfyUI installation (On hosted server, or [portable](https://github.com/YanWenKun/ComfyUI-Windows-Portable)) - - My suggestion it to use the [Realistic Vision 6](https://civitai.com/models/4201/realistic-vision-v60-b1) model + - Python 3.8+ installed + - Git installed + - At least 4GB of RAM (8GB+ recommended for better performance) + - CUDA-compatible GPU (optional, but highly recommended for faster generation) -If you have that, then carry on for Windows instructions - - Use Git to checkout this repo - - You need to get a `rtf` file of the players you want to add images for, to do this you need the view and filter supplied with the project - - Copy the `filters` and `views` folder over to your `Football Manager 2024` data folder in `Documents`, this may create these folders, or may just add the contained files into your existing folders - - You can use `python INSTALL_VIEW_AND_FILTER.py` to do this automatically - - Included is the original `is newgen` filter created by the NewGAN-Manager team and a new view created by myself to get the needed data - - If you follow [this video](https://youtu.be/pmdIkhfmY6w?t=564) it will show you how to export the `rtf` file, you want to use our view, not the view in the video - - Once you have your `rtf` file add it to the root of the current repo - - Create a python virtual environment `python -m venv venv` - - Activate the venv `.\venv\Scripts\activate` - - Install the requirements `pip install -r requirements.txt` - - Copy the `user_config.cfg.sample` to `user_config.cfg` and make the needed changes - - football_manager_version - Version of FM to generate for. Defautls to `2024` - - output_dir - Where to save the generated set. Defaults to `./generated_images/` - - comfyui_url - HTTP location of your comfyui installation. - - model - Model to be used, by default `realisticVisionV60B1_v51HyperVAE.safetensors` is set, this is my suggested model at the moment. -- At this point you should be able to run and start to generate by running the command `python comfy_fm_newgen.py --rtf_file ExportedFile.rtf` - - ExportedFile is the name of the file that you exported and saved with your newgen list +## Setup Instructions + - Use Git to checkout this repo + - You need to get a `rtf` file of the players you want to add images for, to do this you need the view and filter supplied with the project + - Copy the `filters` and `views` folder over to your `Football Manager 2024` data folder in `Documents`, this may create these folders, or may just add the contained files into your existing folders + - You can use `python INSTALL_VIEW_AND_FILTER.py` to do this automatically + - Included is the original `is newgen` filter created by the NewGAN-Manager team and a new view created by myself to get the needed data + - If you follow [this video](https://youtu.be/pmdIkhfmY6w?t=564) it will show you how to export the `rtf` file, you want to use our view, not the view in the video + - Once you have your `rtf` file add it to the root of the current repo + - Create a python virtual environment `python -m venv venv` + - Activate the venv `.\venv\Scripts\activate` (Windows) or `source venv/bin/activate` (Linux/Mac) + - Install the requirements `pip install -r requirements.txt` + - Copy the `user_config.cfg.sample` to `user_config.cfg` and make the needed changes + - football_manager_version - Version of FM to generate for. Defaults to `2024` + - output_dir - Where to save the generated set. Defaults to `./generated_images/` + - rtf_file - Path to your exported RTF file with player data + - model_name - Hugging Face model to use (see Model Selection section below) + - model_dir - Custom directory to store models (optional) + +## Usage Options + +### **🖥️ GUI Mode (Recommended)** +For the best user experience, use the graphical interface: + +```bash +python run_gui.py +``` + +**GUI Features:** +- Easy configuration with file browsers +- Real-time progress monitoring +- Live image preview +- Start/stop controls +- Comprehensive logging + +### **📝 Command Line Mode** +For advanced users or automation: + +```bash +python comfy_fm_newgen.py +``` + +**Command Line Features:** +- Fast batch processing +- Resume capability +- Detailed logging +- Script automation + + +## Model Selection + +The tool uses Hugging Face models for image generation. You can specify which model to use in your `user_config.cfg`: + +### **Recommended Models for Face Generation:** + +- `SG161222/Realistic_Vision_V6.0_B1` - **Best choice** for realistic faces (default) +- `digiplay/AbsoluteReality` - Photorealistic faces and people +- `stabilityai/stable-diffusion-2-1` - General purpose, good for faces +- `runwayml/stable-diffusion-v1-5` - Classic SD 1.5, widely supported + +### **Model Storage:** + +- **Default**: Models are stored in Hugging Face cache (`~/.cache/huggingface/`) +- **Custom Directory**: Set `model_dir` in config to store models in a specific folder +- **Model Size**: Most face models are 2-5GB, ensure you have sufficient disk space +- **First Run**: Model downloads automatically when you first run the tool + +### **Changing Models:** + +1. Edit `model_name` in your `user_config.cfg` +2. Delete the old model from your cache/directory if desired +3. Restart the tool - it will download the new model automatically + +## System Requirements + +- **CPU Mode**: Requires at least 8GB RAM, expect 2-5 minutes per image +- **GPU Mode**: Requires CUDA-compatible GPU with at least 4GB VRAM, expect 10-30 seconds per image +- First run will download the Stable Diffusion model (~5GB), so ensure you have sufficient disk space + +## Features + +### **Core Features** +- **Local Generation**: No external servers or API keys required +- **Model Selection**: Choose from various face generation models via configuration +- **Automatic Processing**: Batch process multiple players from your RTF file +- **Background Removal**: Automatically removes backgrounds from generated faces +- **XML Configuration**: Generates proper Football Manager configuration files +- **Resume Support**: Can resume from previous runs without regenerating existing images +- **Model Caching**: Downloads and caches models locally for faster subsequent runs + +### **🖥️ GUI Features** +- **User-Friendly Interface**: Modern tabbed interface with intuitive controls +- **File Browsers**: Easy selection of RTF files and output directories +- **Real-Time Progress**: Live progress bars and status updates +- **Image Preview**: View generated images as they're created +- **Configuration Management**: Save and load different configurations +- **Error Handling**: Clear error messages and troubleshooting tips +- **Multi-threaded**: Non-blocking interface during generation + +## GUI Troubleshooting + +### **Common Issues:** + +**GUI won't start:** +- Ensure Python 3.8+ is installed +- Install requirements: `pip install -r requirements.txt` +- Check for missing dependencies: `python -c "import tkinter"` + +**Dependency installation fails:** +- Try installing packages individually: `pip install torch torchvision diffusers transformers` +- For Python 3.11+, use: `pip install rembg==2.0.59` (instead of 2.0.60) +- If `striprtf` fails, try: `pip install striprtf==0.0.28` +- Use alternative requirements: `pip install -r requirements-gui.txt` for GUI-only + +**Alternative installation methods:** +- **Conda**: `conda install pytorch torchvision torchaudio cpuonly -c pytorch` +- **GPU Support**: Replace `cpuonly` with `cudatoolkit=11.8` for CUDA support +- **Individual packages**: Install problematic packages one by one to identify issues + +**Model download fails:** +- Check internet connection +- Verify disk space (models are 2-5GB) +- Try a different model from the recommended list + +**Images not appearing in preview:** +- Check output directory permissions +- Ensure images are being generated successfully +- Try refreshing the preview tab + +**Configuration not saving:** +- Check file permissions in the project directory +- Ensure no other instances are running +- Try running as administrator + +### **Performance Tips:** + +- **First run** takes longer due to model download +- **Close other applications** during generation for better performance +- **Use SSD storage** for faster model loading +- **Monitor system resources** - generation is CPU/GPU intensive + +## Command Line Usage + +For advanced users or automation, you can still use the original command-line interface: + +```bash +python comfy_fm_newgen.py +``` + +The GUI and command-line versions share the same configuration files and generated data. + +--- You should get some console output of the progress, good luck! -Open an issue here if your having problems +Open an issue here if you're having problems Thanks \ No newline at end of file diff --git a/requirements-gui.txt b/requirements-gui.txt new file mode 100644 index 0000000..8ea4d2b --- /dev/null +++ b/requirements-gui.txt @@ -0,0 +1,4 @@ +# Alternative requirements for GUI-only usage (without AI generation) +# Use this if you only want to run the GUI interface +tkinterdnd2==0.3.0 +Pillow==10.4.0 \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9602769..621668f 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/run_gui.py b/run_gui.py new file mode 100644 index 0000000..d6698e6 --- /dev/null +++ b/run_gui.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +""" +FM Face Generator GUI Launcher +Simple launcher script for the GUI interface +""" + +import sys +import os + +def main(): + """Launch the GUI application""" + try: + # Add current directory to path for imports + current_dir = os.path.dirname(os.path.abspath(__file__)) + if current_dir not in sys.path: + sys.path.insert(0, current_dir) + + # Import and run the GUI + from gui import main as gui_main + + print("Starting FM Face Generator GUI...") + print("If you encounter any import errors, make sure to install requirements:") + print("pip install -r requirements.txt") + print() + + gui_main() + + except ImportError as e: + print(f"Import Error: {e}") + print("\nPlease install the required dependencies:") + print("pip install -r requirements.txt") + sys.exit(1) + + except Exception as e: + print(f"Error starting GUI: {e}") + print("\nTroubleshooting:") + print("1. Make sure Python 3.8+ is installed") + print("2. Install requirements: pip install -r requirements.txt") + print("3. Check that all dependencies are available") + sys.exit(1) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/user_config.cfg.sample b/user_config.cfg.sample index 577c9d3..3153ec3 100644 --- a/user_config.cfg.sample +++ b/user_config.cfg.sample @@ -1,8 +1,16 @@ -[comfyui] -comfyui_url = -model = realisticVisionV60B1_v51HyperVAE.safetensors - [profile:NewGens] football_manager_version = 2024 rtf_file = ./NewGen.rtf -output_dir = ./NewGens/ \ No newline at end of file +output_dir = ./NewGens/ + +[models] +# Hugging Face model repository (format: username/model-name) +# Popular face generation models: +# SG161222/Realistic_Vision_V6.0_B1 - Realistic faces (recommended) +# stabilityai/stable-diffusion-2-1 - General purpose +# runwayml/stable-diffusion-v1-5 - Classic SD 1.5 +# digiplay/AbsoluteReality - Photorealistic +model_name = SG161222/Realistic_Vision_V6.0_B1 + +# Model storage directory (optional - defaults to Hugging Face cache) +# model_dir = ./models/ \ No newline at end of file