Spaces:
Build error
Build error
Upload folder using huggingface_hub
Browse files- README.md +117 -0
- SD3 Batch Imagine.rar +3 -0
- app.py +138 -61
README.md
CHANGED
|
@@ -4,3 +4,120 @@ app_file: app.py
|
|
| 4 |
sdk: gradio
|
| 5 |
sdk_version: 4.31.5
|
| 6 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
sdk: gradio
|
| 5 |
sdk_version: 4.31.5
|
| 6 |
---
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
Sure, here is a detailed README for your script:
|
| 10 |
+
|
| 11 |
+
GroOvy SD3 Batch Imagine
|
| 12 |
+
Overview
|
| 13 |
+
The GroOvy SD3 Batch Imagine tool is designed for batch image generation using the SD3 Medium model. This tool allows users to input multiple prompts and generate images in batches, with options for negative prompts, and automated zipping of generated images for easy download. The integration with ComfyUI ensures a seamless workflow for AI-powered image generation.
|
| 14 |
+
|
| 15 |
+
Features
|
| 16 |
+
Batch Image Generation: Enter multiple prompts and generate images in bulk.
|
| 17 |
+
Negative Prompts: Specify negative prompts to refine image outputs.
|
| 18 |
+
Zip Interval: Set the interval at which images are zipped.
|
| 19 |
+
Real-time Progress Monitoring: Watch the progress of image generation in real-time.
|
| 20 |
+
Automated File Management: Generated images are automatically copied to user-specific folders and zipped at specified intervals.
|
| 21 |
+
Detailed Logging: Monitor detailed logs of the image generation process.
|
| 22 |
+
Prerequisites
|
| 23 |
+
Python 3.7 or higher
|
| 24 |
+
Required Python packages: requests, gradio, logging, shutil, zipfile, os, json, random, time
|
| 25 |
+
Installation
|
| 26 |
+
Clone the repository:
|
| 27 |
+
bash
|
| 28 |
+
Copy code
|
| 29 |
+
git clone https://github.com/your-repo/groovy-sd3-batch-imagine.git
|
| 30 |
+
Navigate to the project directory:
|
| 31 |
+
bash
|
| 32 |
+
Copy code
|
| 33 |
+
cd groovy-sd3-batch-imagine
|
| 34 |
+
Install the required packages:
|
| 35 |
+
bash
|
| 36 |
+
Copy code
|
| 37 |
+
pip install -r requirements.txt
|
| 38 |
+
Usage
|
| 39 |
+
Running the Application
|
| 40 |
+
To run the application, execute the following command in your terminal:
|
| 41 |
+
|
| 42 |
+
bash
|
| 43 |
+
Copy code
|
| 44 |
+
python app.py
|
| 45 |
+
Accessing the Interface
|
| 46 |
+
Once the application is running, you can access the Gradio interface via the provided local URL (http://127.0.0.1:7860) or a public URL if you choose to share it.
|
| 47 |
+
|
| 48 |
+
Interface Overview
|
| 49 |
+
Developer Information
|
| 50 |
+
This section provides information about the developer, BeWiZ, including contact details and social media links.
|
| 51 |
+
|
| 52 |
+
About SD3 Batch Imagine
|
| 53 |
+
Learn about the purpose and features of the GroOvy SD3 Batch Imagine tool.
|
| 54 |
+
|
| 55 |
+
Instructions
|
| 56 |
+
Follow these instructions to use the tool effectively:
|
| 57 |
+
|
| 58 |
+
Enter your prompts, one per empty line.
|
| 59 |
+
Enter your negative prompts, one per line (optional).
|
| 60 |
+
Set the zip interval to determine how many images will be included in each zip file.
|
| 61 |
+
Click "Process Prompts" to start generating images.
|
| 62 |
+
Click "Cancel Processing" to stop the current batch run.
|
| 63 |
+
Monitor the progress in real-time.
|
| 64 |
+
Download the generated zip files at the end of the process.
|
| 65 |
+
Components
|
| 66 |
+
Enter Prompts
|
| 67 |
+
Prompts: Input your image generation prompts here, one per empty line.
|
| 68 |
+
Negative Prompts: Input negative prompts here, one per line (optional).
|
| 69 |
+
Zip Interval: Specify the number of images per zip file.
|
| 70 |
+
Process Prompts Button: Start the batch image generation process.
|
| 71 |
+
Cancel Processing Button: Cancel the current batch run.
|
| 72 |
+
Progress: Displays real-time progress of image generation.
|
| 73 |
+
Generated Images
|
| 74 |
+
Gallery: Displays the generated images.
|
| 75 |
+
Zip Files
|
| 76 |
+
Zip Files: Displays the generated zip files for download.
|
| 77 |
+
Detailed Logs
|
| 78 |
+
Logs: Displays detailed logs of the image generation process.
|
| 79 |
+
Script Details
|
| 80 |
+
Constants
|
| 81 |
+
COMFYUI_DIR: Directory path for ComfyUI.
|
| 82 |
+
WORKFLOW_PATH: Path to the workflow JSON file.
|
| 83 |
+
OUTPUT_DIR: Directory path for output images.
|
| 84 |
+
URL: URL for sending the workflow to ComfyUI.
|
| 85 |
+
Functions
|
| 86 |
+
read_workflow(file_path)
|
| 87 |
+
Reads and returns the workflow JSON from the specified file path.
|
| 88 |
+
|
| 89 |
+
update_workflow(workflow, prompt, negative_prompt)
|
| 90 |
+
Updates the workflow with new prompts and negative prompts.
|
| 91 |
+
|
| 92 |
+
write_workflow(workflow, file_path)
|
| 93 |
+
Writes the updated workflow back to the specified file path.
|
| 94 |
+
|
| 95 |
+
send_workflow_to_comfyui(workflow, url)
|
| 96 |
+
Sends the updated workflow to ComfyUI and logs the response.
|
| 97 |
+
|
| 98 |
+
monitor_output_images(output_dir, previous_images, timeout=60)
|
| 99 |
+
Monitors the output directory for new images, returning the latest image found within the timeout period.
|
| 100 |
+
|
| 101 |
+
copy_file_with_retry(src, dst_dir, file_index, retries=5, delay=1)
|
| 102 |
+
Copies a file from the source to the destination directory, with retries in case of permission errors.
|
| 103 |
+
|
| 104 |
+
zip_files(output_images: List[str], zip_interval: int, zip_folder: str)
|
| 105 |
+
Creates zip files from the list of output images at specified intervals and saves them to the zip folder.
|
| 106 |
+
|
| 107 |
+
process_prompts(prompts_text, negative_prompt_text, user_folder, zip_interval)
|
| 108 |
+
Processes the prompts, generates images, copies them to the user folder, and zips them at specified intervals.
|
| 109 |
+
|
| 110 |
+
cancel_processing_fn()
|
| 111 |
+
Sets the cancel processing flag to True to stop the current batch run.
|
| 112 |
+
|
| 113 |
+
reset_cancel_processing_fn()
|
| 114 |
+
Resets the cancel processing flag to False.
|
| 115 |
+
|
| 116 |
+
Main Function
|
| 117 |
+
The main() function initializes the Gradio interface and sets up the UI components for entering prompts, monitoring progress, displaying generated images, zip files, and logs.
|
| 118 |
+
|
| 119 |
+
License
|
| 120 |
+
This project is licensed under the MIT License. See the LICENSE file for more details.
|
| 121 |
+
|
| 122 |
+
Contact
|
| 123 |
+
For any queries or support, please contact downlifted@gmail.com or reach out on Twitter @AiAnarchist.
|
SD3 Batch Imagine.rar
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f1cd79d8cb6d2b736058367c93f8a7c4bf137af4782d9624080c4436475ee7ac
|
| 3 |
+
size 7112
|
app.py
CHANGED
|
@@ -11,44 +11,44 @@ from typing import List
|
|
| 11 |
|
| 12 |
# Define constants
|
| 13 |
COMFYUI_DIR = r"C:\Users\bewiz\OneDrive\Desktop\AI\ComfyUI_windows_portable\ComfyUI"
|
| 14 |
-
WORKFLOW_PATH = os.path.join(COMFYUI_DIR, 'sd3.json')
|
| 15 |
OUTPUT_DIR = os.path.join(COMFYUI_DIR, 'output')
|
| 16 |
URL = "http://127.0.0.1:8188/prompt"
|
| 17 |
|
| 18 |
# Configure logging
|
| 19 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 20 |
|
| 21 |
-
#
|
|
|
|
|
|
|
| 22 |
def read_workflow(file_path):
|
| 23 |
logging.info(f"Reading workflow from {file_path}")
|
| 24 |
with open(file_path, 'r') as file:
|
| 25 |
workflow = json.load(file)
|
| 26 |
return workflow
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
workflow["
|
| 32 |
return workflow
|
| 33 |
|
| 34 |
-
# Write the updated workflow back to the JSON file
|
| 35 |
def write_workflow(workflow, file_path):
|
| 36 |
logging.info(f"Writing updated workflow to {file_path}")
|
| 37 |
with open(file_path, 'w') as file:
|
| 38 |
json.dump(workflow, file, indent=4)
|
| 39 |
|
| 40 |
-
# Send the workflow to ComfyUI
|
| 41 |
def send_workflow_to_comfyui(workflow, url):
|
| 42 |
headers = {"Content-Type": "application/json"}
|
| 43 |
try:
|
| 44 |
response = requests.post(url, headers=headers, json={"prompt": workflow})
|
| 45 |
response.raise_for_status()
|
| 46 |
logging.info(f"Workflow sent successfully: {response.status_code}")
|
|
|
|
| 47 |
except requests.exceptions.RequestException as e:
|
| 48 |
logging.error(f"Error sending workflow to ComfyUI: {e}")
|
| 49 |
raise
|
| 50 |
|
| 51 |
-
# Monitor the output directory for new images
|
| 52 |
def monitor_output_images(output_dir, previous_images, timeout=60):
|
| 53 |
start_time = time.time()
|
| 54 |
logging.info(f"Monitoring {output_dir} for new images...")
|
|
@@ -60,78 +60,117 @@ def monitor_output_images(output_dir, previous_images, timeout=60):
|
|
| 60 |
logging.info(f"New image found: {latest_image}")
|
| 61 |
return latest_image
|
| 62 |
time.sleep(1)
|
| 63 |
-
|
|
|
|
| 64 |
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
dst = os.path.join(dst_dir, os.path.basename(src))
|
| 68 |
for _ in range(retries):
|
| 69 |
try:
|
| 70 |
-
|
| 71 |
-
base, ext = os.path.splitext(dst)
|
| 72 |
-
dst = f"{base}_{random.randint(1000, 9999)}{ext}"
|
| 73 |
-
shutil.move(src, dst)
|
| 74 |
return dst
|
| 75 |
except PermissionError:
|
| 76 |
time.sleep(delay)
|
| 77 |
-
raise PermissionError(f"Failed to
|
| 78 |
|
| 79 |
-
# Zip files at specified intervals
|
| 80 |
def zip_files(output_images: List[str], zip_interval: int, zip_folder: str):
|
| 81 |
zip_files = []
|
| 82 |
for i in range(0, len(output_images), zip_interval):
|
| 83 |
-
zip_filename = os.path.join(zip_folder, f"images_{i//zip_interval + 1}.zip")
|
| 84 |
with zipfile.ZipFile(zip_filename, 'w') as zipf:
|
| 85 |
for img in output_images[i:i+zip_interval]:
|
| 86 |
zipf.write(img, os.path.basename(img))
|
| 87 |
zip_files.append(zip_filename)
|
| 88 |
return zip_files
|
| 89 |
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
prompts =
|
|
|
|
| 93 |
output_images = []
|
|
|
|
|
|
|
| 94 |
|
| 95 |
workflow = read_workflow(WORKFLOW_PATH)
|
| 96 |
|
| 97 |
total_prompts = len(prompts)
|
| 98 |
previous_images = os.listdir(OUTPUT_DIR)
|
| 99 |
-
|
|
|
|
| 100 |
|
| 101 |
try:
|
| 102 |
for i, prompt in enumerate(prompts):
|
|
|
|
|
|
|
|
|
|
| 103 |
if not prompt.strip():
|
| 104 |
continue
|
| 105 |
|
| 106 |
-
|
|
|
|
|
|
|
| 107 |
write_workflow(updated_workflow, WORKFLOW_PATH)
|
| 108 |
|
|
|
|
|
|
|
| 109 |
send_workflow_to_comfyui(updated_workflow, URL)
|
| 110 |
-
logging.info(f"Sent workflow to ComfyUI for prompt {i+1}/{total_prompts}")
|
| 111 |
|
| 112 |
-
|
|
|
|
|
|
|
| 113 |
new_image = monitor_output_images(OUTPUT_DIR, previous_images)
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
continue
|
| 120 |
|
| 121 |
-
output_images.append(
|
| 122 |
-
|
| 123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
|
| 130 |
except KeyboardInterrupt:
|
| 131 |
logging.info("Script interrupted by user.")
|
| 132 |
|
| 133 |
return output_images, zip_files_list
|
| 134 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
def main():
|
| 136 |
with gr.Blocks(css="""
|
| 137 |
.gradio-container {font-family: Arial, sans-serif;}
|
|
@@ -151,46 +190,84 @@ def main():
|
|
| 151 |
90% { color: #da70d6; }
|
| 152 |
100% { color: #ff69b4; }
|
| 153 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 154 |
""") as demo:
|
| 155 |
-
gr.Markdown("""
|
| 156 |
-
<h1 class="psychedelic-text">
|
| 157 |
-
<span>G</span><span>r</span><span>o</span><span>o</span><span>v</span><span>y</span>-
|
| 158 |
-
<span>S</span><span>t</span><span>y</span><span>l</span><span>S</span><span>u</span><span>i</span><span>t</span>;
|
| 159 |
-
</h1>
|
| 160 |
-
""")
|
| 161 |
-
|
| 162 |
-
gr.Markdown("ComfyUI Workflow Automation\nWelcome to the Groovy ComfyUI Workflow Automation tool. Enter your prompts below, one per line, and the tool will process each prompt sequentially.")
|
| 163 |
-
|
| 164 |
with gr.Row():
|
| 165 |
with gr.Column(scale=1):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
gr.Markdown("### Enter Prompts")
|
| 167 |
-
prompts_text = gr.Textbox(lines=20, placeholder="Enter your prompts here, one per line.")
|
|
|
|
| 168 |
zip_interval = gr.Number(value=10, label="Zip Interval", precision=0)
|
| 169 |
process_btn = gr.Button("Process Prompts")
|
|
|
|
| 170 |
progress_text = gr.Markdown("Progress")
|
| 171 |
-
|
| 172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
|
| 174 |
def generate_user_folder():
|
| 175 |
-
user_folder = os.path.normpath(os.path.join(OUTPUT_DIR, f'SD3{random.randint(
|
| 176 |
os.makedirs(user_folder, exist_ok=True)
|
| 177 |
logging.info(f"Generated user folder: {user_folder}")
|
| 178 |
return user_folder
|
| 179 |
|
| 180 |
-
def on_click(prompts_text, zip_interval):
|
|
|
|
| 181 |
user_folder = generate_user_folder()
|
| 182 |
output_images, zip_files_list = [], []
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
|
|
|
| 189 |
|
| 190 |
process_btn.click(
|
| 191 |
fn=on_click,
|
| 192 |
-
inputs=[prompts_text, zip_interval],
|
| 193 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
)
|
| 195 |
|
| 196 |
demo.launch(share=True)
|
|
|
|
| 11 |
|
| 12 |
# Define constants
|
| 13 |
COMFYUI_DIR = r"C:\Users\bewiz\OneDrive\Desktop\AI\ComfyUI_windows_portable\ComfyUI"
|
| 14 |
+
WORKFLOW_PATH = os.path.join(COMFYUI_DIR, 'sd3.json')
|
| 15 |
OUTPUT_DIR = os.path.join(COMFYUI_DIR, 'output')
|
| 16 |
URL = "http://127.0.0.1:8188/prompt"
|
| 17 |
|
| 18 |
# Configure logging
|
| 19 |
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
| 20 |
|
| 21 |
+
# Shared state for cancellation
|
| 22 |
+
cancel_processing = False
|
| 23 |
+
|
| 24 |
def read_workflow(file_path):
|
| 25 |
logging.info(f"Reading workflow from {file_path}")
|
| 26 |
with open(file_path, 'r') as file:
|
| 27 |
workflow = json.load(file)
|
| 28 |
return workflow
|
| 29 |
|
| 30 |
+
def update_workflow(workflow, prompt, negative_prompt):
|
| 31 |
+
logging.info(f"Updating workflow with new prompts: {prompt}, negative: {negative_prompt}")
|
| 32 |
+
workflow["6"]["inputs"]["text"] = prompt
|
| 33 |
+
workflow["71"]["inputs"]["text"] = negative_prompt
|
| 34 |
return workflow
|
| 35 |
|
|
|
|
| 36 |
def write_workflow(workflow, file_path):
|
| 37 |
logging.info(f"Writing updated workflow to {file_path}")
|
| 38 |
with open(file_path, 'w') as file:
|
| 39 |
json.dump(workflow, file, indent=4)
|
| 40 |
|
|
|
|
| 41 |
def send_workflow_to_comfyui(workflow, url):
|
| 42 |
headers = {"Content-Type": "application/json"}
|
| 43 |
try:
|
| 44 |
response = requests.post(url, headers=headers, json={"prompt": workflow})
|
| 45 |
response.raise_for_status()
|
| 46 |
logging.info(f"Workflow sent successfully: {response.status_code}")
|
| 47 |
+
logging.debug(f"Response content: {response.content}")
|
| 48 |
except requests.exceptions.RequestException as e:
|
| 49 |
logging.error(f"Error sending workflow to ComfyUI: {e}")
|
| 50 |
raise
|
| 51 |
|
|
|
|
| 52 |
def monitor_output_images(output_dir, previous_images, timeout=60):
|
| 53 |
start_time = time.time()
|
| 54 |
logging.info(f"Monitoring {output_dir} for new images...")
|
|
|
|
| 60 |
logging.info(f"New image found: {latest_image}")
|
| 61 |
return latest_image
|
| 62 |
time.sleep(1)
|
| 63 |
+
logging.info(f"Timeout while waiting for new images in {output_dir}")
|
| 64 |
+
return None
|
| 65 |
|
| 66 |
+
def copy_file_with_retry(src, dst_dir, file_index, retries=5, delay=1):
|
| 67 |
+
dst = os.path.join(dst_dir, f"SD3_{file_index:05d}.png")
|
|
|
|
| 68 |
for _ in range(retries):
|
| 69 |
try:
|
| 70 |
+
shutil.copy(src, dst)
|
|
|
|
|
|
|
|
|
|
| 71 |
return dst
|
| 72 |
except PermissionError:
|
| 73 |
time.sleep(delay)
|
| 74 |
+
raise PermissionError(f"Failed to copy {src} to {dst} after {retries} retries")
|
| 75 |
|
|
|
|
| 76 |
def zip_files(output_images: List[str], zip_interval: int, zip_folder: str):
|
| 77 |
zip_files = []
|
| 78 |
for i in range(0, len(output_images), zip_interval):
|
| 79 |
+
zip_filename = os.path.join(zip_folder, f"images_{i//zip_interval + 1}_{time.time_ns()}.zip")
|
| 80 |
with zipfile.ZipFile(zip_filename, 'w') as zipf:
|
| 81 |
for img in output_images[i:i+zip_interval]:
|
| 82 |
zipf.write(img, os.path.basename(img))
|
| 83 |
zip_files.append(zip_filename)
|
| 84 |
return zip_files
|
| 85 |
|
| 86 |
+
def process_prompts(prompts_text, negative_prompt_text, user_folder, zip_interval):
|
| 87 |
+
global cancel_processing
|
| 88 |
+
prompts = [line.strip() for line in prompts_text.split('\n\n') if line.strip()]
|
| 89 |
+
negative_prompts = [line.strip() for line in negative_prompt_text.split('\n') if line.strip()]
|
| 90 |
output_images = []
|
| 91 |
+
zip_files_list = []
|
| 92 |
+
file_index = 1
|
| 93 |
|
| 94 |
workflow = read_workflow(WORKFLOW_PATH)
|
| 95 |
|
| 96 |
total_prompts = len(prompts)
|
| 97 |
previous_images = os.listdir(OUTPUT_DIR)
|
| 98 |
+
|
| 99 |
+
logs = ""
|
| 100 |
|
| 101 |
try:
|
| 102 |
for i, prompt in enumerate(prompts):
|
| 103 |
+
if cancel_processing:
|
| 104 |
+
logging.info("Processing cancelled by user.")
|
| 105 |
+
break
|
| 106 |
if not prompt.strip():
|
| 107 |
continue
|
| 108 |
|
| 109 |
+
negative_prompt = negative_prompts[i] if i < len(negative_prompts) else ""
|
| 110 |
+
|
| 111 |
+
updated_workflow = update_workflow(workflow, prompt, negative_prompt)
|
| 112 |
write_workflow(updated_workflow, WORKFLOW_PATH)
|
| 113 |
|
| 114 |
+
logging.debug(f"Updated workflow: {json.dumps(updated_workflow, indent=4)}")
|
| 115 |
+
|
| 116 |
send_workflow_to_comfyui(updated_workflow, URL)
|
| 117 |
+
logging.info(f"Sent workflow to ComfyUI for prompt {i + 1}/{total_prompts}")
|
| 118 |
|
| 119 |
+
new_image = None
|
| 120 |
+
retries = 0
|
| 121 |
+
while new_image is None and retries < 5:
|
| 122 |
new_image = monitor_output_images(OUTPUT_DIR, previous_images)
|
| 123 |
+
if new_image is None:
|
| 124 |
+
retries += 1
|
| 125 |
+
logging.warning(f"Retrying ({retries}/5)...")
|
| 126 |
+
time.sleep(5)
|
| 127 |
+
else:
|
| 128 |
+
time.sleep(2)
|
| 129 |
+
|
| 130 |
+
if new_image is None:
|
| 131 |
+
logging.error("Error monitoring output images: Timed out waiting for new image.")
|
| 132 |
+
continue
|
| 133 |
+
|
| 134 |
+
new_image_path = os.path.join(OUTPUT_DIR, new_image)
|
| 135 |
+
try:
|
| 136 |
+
copied_image_path = copy_file_with_retry(new_image_path, user_folder, file_index)
|
| 137 |
+
logging.info(f"New image generated and copied to user folder: {copied_image_path}")
|
| 138 |
+
except PermissionError as e:
|
| 139 |
+
logging.error(f"Failed to copy file after retries: {e}")
|
| 140 |
continue
|
| 141 |
|
| 142 |
+
output_images.append(copied_image_path)
|
| 143 |
+
previous_images.append(new_image)
|
| 144 |
+
file_index += 1
|
| 145 |
+
|
| 146 |
+
if len(output_images) % zip_interval == 0 and not cancel_processing:
|
| 147 |
+
zip_folder = os.path.join(user_folder, "zipped_images")
|
| 148 |
+
os.makedirs(zip_folder, exist_ok=True)
|
| 149 |
+
new_zip_files = zip_files(output_images[-zip_interval:], zip_interval, zip_folder)
|
| 150 |
+
zip_files_list.extend(new_zip_files)
|
| 151 |
|
| 152 |
+
logs += f"Processed {i + 1}/{total_prompts} - Done: {i + 1}, Left: {total_prompts - (i + 1)}\n"
|
| 153 |
+
yield output_images, zip_files_list, logs
|
| 154 |
+
|
| 155 |
+
if cancel_processing or (len(output_images) % zip_interval != 0):
|
| 156 |
+
zip_folder = os.path.join(user_folder, "zipped_images")
|
| 157 |
+
os.makedirs(zip_folder, exist_ok=True)
|
| 158 |
+
new_zip_files = zip_files(output_images, zip_interval, zip_folder)
|
| 159 |
+
zip_files_list.extend(new_zip_files)
|
| 160 |
|
| 161 |
except KeyboardInterrupt:
|
| 162 |
logging.info("Script interrupted by user.")
|
| 163 |
|
| 164 |
return output_images, zip_files_list
|
| 165 |
|
| 166 |
+
def cancel_processing_fn():
|
| 167 |
+
global cancel_processing
|
| 168 |
+
cancel_processing = True
|
| 169 |
+
|
| 170 |
+
def reset_cancel_processing_fn():
|
| 171 |
+
global cancel_processing
|
| 172 |
+
cancel_processing = False
|
| 173 |
+
|
| 174 |
def main():
|
| 175 |
with gr.Blocks(css="""
|
| 176 |
.gradio-container {font-family: Arial, sans-serif;}
|
|
|
|
| 190 |
90% { color: #da70d6; }
|
| 191 |
100% { color: #ff69b4; }
|
| 192 |
}
|
| 193 |
+
.image-container img {
|
| 194 |
+
width: 250px;
|
| 195 |
+
height: 250px;
|
| 196 |
+
}
|
| 197 |
""") as demo:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 198 |
with gr.Row():
|
| 199 |
with gr.Column(scale=1):
|
| 200 |
+
gr.Markdown("### beWiZ's GroOvy SD3 Batch Imagine")
|
| 201 |
+
gr.HTML('<div class="image-container"><img src="https://raw.githubusercontent.com/downlifted/Groovy-StyleSuite/main/groovy.png" alt="GroOvy - SD3 Batch Imagine Logo"></div>')
|
| 202 |
+
with gr.Accordion("Developer Information", open=False):
|
| 203 |
+
gr.Markdown("### Made by BeWiZ")
|
| 204 |
+
gr.Markdown('<div class="image-container"><a href="https://twitter.com/AiAnarchist"><img src="https://raw.githubusercontent.com/downlifted/pictoprompt/master/aia.png" alt="BeWiZ Logo"></a></div>')
|
| 205 |
+
gr.Markdown("Contact: [downlifted@gmail.com](mailto:downlifted@gmail.com)")
|
| 206 |
+
gr.Markdown("Twitter: [@AiAnarchist](https://x.com/AiAnarchist)")
|
| 207 |
+
with gr.Accordion("About SD3 Batch Imagine", open=False):
|
| 208 |
+
gr.Markdown("""
|
| 209 |
+
### SD3 Batch Imagine: Batch Image Generation
|
| 210 |
+
Produce large batches of images using the latest SD3 Medium model. This tool allows you to generate images quickly and efficiently.
|
| 211 |
+
- **ComfyUI**: For seamless integration and image processing.
|
| 212 |
+
- **Hugging Face**: For state-of-the-art language models.
|
| 213 |
+
- **Gradio**: For an intuitive user interface.
|
| 214 |
+
""")
|
| 215 |
+
|
| 216 |
+
with gr.Accordion("Instructions", open=True):
|
| 217 |
+
gr.Markdown("""
|
| 218 |
+
**SD3 Batch Imagine Instructions**
|
| 219 |
+
- Enter your prompts below, one per empty line.
|
| 220 |
+
- Enter your negative prompts below, one per line. (Optional)
|
| 221 |
+
- Set the zip interval to determine how many images will be included in each zip file.
|
| 222 |
+
- Click "Process Prompts" to start generating images.
|
| 223 |
+
- Click "Cancel Processing" to stop the current batch run.
|
| 224 |
+
- Watch the progress as images are generated in real-time.
|
| 225 |
+
- At the end of the process, zip files containing your images will be available for download.
|
| 226 |
+
""")
|
| 227 |
+
|
| 228 |
+
with gr.Column(scale=2):
|
| 229 |
gr.Markdown("### Enter Prompts")
|
| 230 |
+
prompts_text = gr.Textbox(lines=20, placeholder="Enter your prompts here, one per empty line.", label="Prompts")
|
| 231 |
+
negative_prompts_text = gr.Textbox(lines=5, placeholder="Enter your negative prompts here, one per line.", label="Negative Prompts")
|
| 232 |
zip_interval = gr.Number(value=10, label="Zip Interval", precision=0)
|
| 233 |
process_btn = gr.Button("Process Prompts")
|
| 234 |
+
cancel_btn = gr.Button("Cancel Processing")
|
| 235 |
progress_text = gr.Markdown("Progress")
|
| 236 |
+
gallery_output = gr.Gallery(label="Generated Images")
|
| 237 |
+
zip_files_output = gr.Files(label="Zip Files")
|
| 238 |
+
|
| 239 |
+
with gr.Column(scale=1):
|
| 240 |
+
gr.Markdown("### Detailed Logs")
|
| 241 |
+
logs_output = gr.Textbox(lines=10, interactive=False, label="Logs")
|
| 242 |
|
| 243 |
def generate_user_folder():
|
| 244 |
+
user_folder = os.path.normpath(os.path.join(OUTPUT_DIR, f'SD3{random.randint(1000, 9999)}'))
|
| 245 |
os.makedirs(user_folder, exist_ok=True)
|
| 246 |
logging.info(f"Generated user folder: {user_folder}")
|
| 247 |
return user_folder
|
| 248 |
|
| 249 |
+
def on_click(prompts_text, negative_prompts_text, zip_interval):
|
| 250 |
+
reset_cancel_processing_fn()
|
| 251 |
user_folder = generate_user_folder()
|
| 252 |
output_images, zip_files_list = [], []
|
| 253 |
+
logs = ""
|
| 254 |
+
for images, zip_files, log_msg in process_prompts(prompts_text, negative_prompts_text, user_folder, zip_interval):
|
| 255 |
+
output_images = images
|
| 256 |
+
zip_files_list = zip_files
|
| 257 |
+
logs = log_msg
|
| 258 |
+
yield images, zip_files_list, logs
|
| 259 |
+
return output_images, zip_files_list, logs
|
| 260 |
|
| 261 |
process_btn.click(
|
| 262 |
fn=on_click,
|
| 263 |
+
inputs=[prompts_text, negative_prompts_text, zip_interval],
|
| 264 |
+
outputs=[gallery_output, zip_files_output, logs_output]
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
cancel_btn.click(
|
| 268 |
+
fn=cancel_processing_fn,
|
| 269 |
+
inputs=[],
|
| 270 |
+
outputs=[]
|
| 271 |
)
|
| 272 |
|
| 273 |
demo.launch(share=True)
|