Spaces:
Runtime error
Runtime error
rayochoajr
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -1,78 +1,915 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
import shutil
|
4 |
-
import hashlib
|
5 |
-
import subprocess
|
6 |
-
import pandas as pd
|
7 |
from PIL import Image
|
8 |
from datetime import datetime
|
9 |
-
import io
|
10 |
import base64
|
11 |
import requests
|
|
|
12 |
import json
|
13 |
import logging
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
15 |
-
#
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
THUMBS_DOWN_FOLDER = os.path.join(IMAGE_FOLDER, "Thumbs_Down")
|
20 |
-
BACKUP_FOLDER = "Backup_Scripts"
|
21 |
-
ALLOWED_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
|
22 |
-
LOGGING_LEVEL = logging.INFO
|
23 |
-
API_URL = "https://api.openai.com/v1/chat/completions"
|
24 |
|
25 |
-
#
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
|
29 |
-
|
30 |
-
os.makedirs(THUMBS_UP_FOLDER, exist_ok=True)
|
31 |
-
os.makedirs(THUMBS_DOWN_FOLDER, exist_ok=True)
|
32 |
-
os.makedirs(BACKUP_FOLDER, exist_ok=True)
|
33 |
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
def get_image_description(image, custom_prompt):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
if not custom_prompt.strip():
|
39 |
custom_prompt = "Describe this image"
|
40 |
-
|
41 |
-
payload =
|
42 |
"model": "gpt-4-vision-preview",
|
43 |
-
"messages": [
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
|
|
|
|
|
|
49 |
"max_tokens": 300
|
50 |
-
}
|
51 |
-
response = requests.post(
|
52 |
-
|
|
|
|
|
|
|
|
|
53 |
|
54 |
-
def
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
text_file_path = f"{os.path.splitext(image_path)[0]}.txt"
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
with gr.Blocks() as app:
|
65 |
with gr.Row():
|
66 |
upload_btn = gr.File(label="Upload Images", type="binary", file_count='multiple')
|
67 |
gallery = gr.Gallery(label="Uploaded Images Gallery")
|
68 |
-
upload_btn.change(fn=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
|
70 |
-
with gr.Accordion("Training Data"):
|
71 |
-
details_df = gr.Dataframe()
|
72 |
-
thumbs_up_gallery = gr.Gallery(value=load_gallery(THUMBS_UP_FOLDER), label="Thumbs Up Gallery")
|
73 |
-
thumbs_down_gallery = gr.Gallery(value=load_gallery(THUMBS_DOWN_FOLDER), label="Thumbs Down Gallery")
|
74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
refresh_btn = gr.Button("Refresh")
|
76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
-
|
|
|
1 |
+
project_index = 0 # Initialize project_index with a default value
|
2 |
+
|
3 |
+
import gradio as gr,os,shutil,numpy as np,hashlib,subprocess,pandas as pd
|
|
|
|
|
|
|
4 |
from PIL import Image
|
5 |
from datetime import datetime
|
|
|
6 |
import base64
|
7 |
import requests
|
8 |
+
import io
|
9 |
import json
|
10 |
import logging
|
11 |
+
import re
|
12 |
+
|
13 |
+
"""
|
14 |
+
Gradio App Interface Specification:
|
15 |
+
Inputs:
|
16 |
+
- prompt: Text Input. Description: 'Enter a prompt for image generation.'
|
17 |
+
- steps: Slider. Range: [1, 32]. value: 16. Description: 'Number of steps for image generation.'
|
18 |
+
- model: Dropdown. Options: ['TurboAnime.saftensors', 'OtherModel']. value: 'TurboAnime.saftensors'. Description: 'Select the model for image generation.'
|
19 |
+
- styles: CheckboxGroup. Options: ['RayORender', 'OtherStyle']. value: ['RayORender']. Description: 'Select styles for image generation.'
|
20 |
+
|
21 |
+
Outputs:
|
22 |
+
- image: Image. Description: 'Generated image based on the prompt.'
|
23 |
+
- log: Text. Description: 'Log of the image generation process.'
|
24 |
+
"""
|
25 |
+
|
26 |
+
import gradio as gr
|
27 |
+
import subprocess
|
28 |
+
import json
|
29 |
+
import base64
|
30 |
+
from PIL import Image
|
31 |
+
import io
|
32 |
+
import time
|
33 |
+
|
34 |
+
# π Start time for the entire script
|
35 |
+
start_time = time.time()
|
36 |
+
def analyze_all_images(custom_prompt):
|
37 |
+
global file_array
|
38 |
+
descriptions = []
|
39 |
+
for file_info in file_array:
|
40 |
+
file_path = find_file_by_hash(file_info["hash"])
|
41 |
+
if file_path:
|
42 |
+
with Image.open(file_path) as img:
|
43 |
+
description = get_image_description(img, custom_prompt)
|
44 |
+
save_text(file_info["hash"], description, "", "", True) # Save the description to the text file
|
45 |
+
descriptions.append(description)
|
46 |
+
return descriptions
|
47 |
+
|
48 |
+
def generate_status_message(index, total, file_name):
|
49 |
+
return f"Processing {index + 1}/{total}: {file_name}"
|
50 |
+
|
51 |
+
def analyze_thumbs_up_images(custom_prompt):
|
52 |
+
thumbs_up_images = load_thumbs_up_gallery() # Assuming this function returns full paths
|
53 |
+
total_images = len(thumbs_up_images)
|
54 |
+
descriptions = []
|
55 |
+
status_messages = []
|
56 |
+
for index, image_path in enumerate(thumbs_up_images):
|
57 |
+
with Image.open(image_path) as img:
|
58 |
+
description = get_image_description(img, custom_prompt)
|
59 |
+
descriptions.append((os.path.basename(image_path), description))
|
60 |
+
status_messages.append(generate_status_message(index, total_images, os.path.basename(image_path)))
|
61 |
+
save_text(file_info["hash"], description, "", "", True) # Save the description to the text file
|
62 |
+
return descriptions, status_messages
|
63 |
+
|
64 |
+
# π’ Log Status
|
65 |
+
def log_status(progress, message):
|
66 |
+
elapsed_time = time.time() - start_time
|
67 |
+
log_message = {"status": message, "progress": f"{progress}%", "time_elapsed": f"{elapsed_time:.2f} seconds"}
|
68 |
+
return json.dumps(log_message)
|
69 |
+
def process_files(file_info):
|
70 |
+
try:
|
71 |
+
if file_info:
|
72 |
+
return [_process_file(file_path) for file_path in file_info] if isinstance(file_info, list) else [_process_file(file_info)]
|
73 |
+
return []
|
74 |
+
except Exception as e:
|
75 |
+
print(f"Error processing files: {e}")
|
76 |
+
raise e
|
77 |
+
|
78 |
+
def clear_uploads_folder():
|
79 |
+
os.makedirs(recycle_bin_folder, exist_ok=True)
|
80 |
+
for file_name in os.listdir(_get_project_folder()):
|
81 |
+
dest_file_path = os.path.join(recycle_bin_folder, file_name)
|
82 |
+
if os.path.exists(dest_file_path):
|
83 |
+
base, extension = os.path.splitext(file_name)
|
84 |
+
i = 1
|
85 |
+
new_file_name = f"{base}_{i}{extension}"
|
86 |
+
new_dest_file_path = os.path.join(recycle_bin_folder, new_file_name)
|
87 |
+
while os.path.exists(new_dest_file_path):
|
88 |
+
i += 1
|
89 |
+
new_file_name = f"{base}_{i}{extension}"
|
90 |
+
new_dest_file_path = os.path.join(recycle_bin_folder, new_file_name)
|
91 |
+
dest_file_path = new_dest_file_path
|
92 |
+
shutil.move(os.path.join(_get_project_folder(), file_name), dest_file_path)
|
93 |
+
return "Uploads folder cleared!"
|
94 |
+
|
95 |
+
def undo_last_deletion():
|
96 |
+
file_list = os.listdir(recycle_bin_folder)
|
97 |
+
if file_list:
|
98 |
+
last_deleted_file = file_list[-1]
|
99 |
+
shutil.move(os.path.join(recycle_bin_folder, last_deleted_file), _get_project_folder())
|
100 |
+
return f"Restored: {last_deleted_file}"
|
101 |
+
return "No files to restore"
|
102 |
+
|
103 |
+
def next_session():
|
104 |
+
global project_index
|
105 |
+
project_index += 1
|
106 |
+
_init_project_directory()
|
107 |
+
return f"Switched to the next session! Current session index: {project_index}"
|
108 |
+
|
109 |
+
def previous_session():
|
110 |
+
global project_index
|
111 |
+
if project_index > 0: project_index -= 1
|
112 |
+
return f"Switched to the previous session! Current session index: {project_index}"
|
113 |
+
|
114 |
+
def get_next_image():
|
115 |
+
global current_image
|
116 |
+
images = get_images()
|
117 |
+
if images:
|
118 |
+
current_image = images[0] if current_image is None else images[(images.index(current_image) + 1) % len(images)]
|
119 |
+
else:
|
120 |
+
current_image = default_image
|
121 |
+
return current_image
|
122 |
+
|
123 |
+
def get_previous_image():
|
124 |
+
global current_image
|
125 |
+
images = get_images()
|
126 |
+
if images:
|
127 |
+
current_image = images[-1] if current_image is None else images[(images.index(current_image) - 1) % len(images)]
|
128 |
+
else:
|
129 |
+
current_image = default_image
|
130 |
+
return current_image
|
131 |
+
|
132 |
+
def process_all_zips():
|
133 |
+
for file_name in os.listdir(_get_project_folder()):
|
134 |
+
file_path = os.path.join(_get_project_folder(), file_name)
|
135 |
+
if zipfile.is_zipfile(file_path): _process_file(file_path)
|
136 |
+
return "All zip files processed!"
|
137 |
+
|
138 |
+
# π API Call
|
139 |
+
def make_api_call(url, payload):
|
140 |
+
try:
|
141 |
+
response = subprocess.run(["curl", "-X", "POST", url, "-H", "Content-Type: application/json", "--data", json.dumps(payload)], capture_output=True, text=True)
|
142 |
+
if response.returncode == 0:
|
143 |
+
return json.loads(response.stdout), None
|
144 |
+
else:
|
145 |
+
return None, log_status(progress, f"API call failed with return code: {response.returncode}")
|
146 |
+
except Exception as e:
|
147 |
+
return None, log_status(progress, f"API call failed with exception: {str(e)}")
|
148 |
|
149 |
+
# πΌοΈ Save Image
|
150 |
+
def save_image(image_data):
|
151 |
+
image = Image.open(io.BytesIO(base64.b64decode(image_data)))
|
152 |
+
return image
|
|
|
|
|
|
|
|
|
|
|
153 |
|
154 |
+
# π Main Execution Function
|
155 |
+
def generate_image(prompt, steps, model, styles):
|
156 |
+
base_url = "http://73.255.78.150:7909"
|
157 |
+
base_payload = {
|
158 |
+
"prompt": prompt,
|
159 |
+
"steps": steps,
|
160 |
+
"model": model,
|
161 |
+
"styles": styles,
|
162 |
+
"negative_prompt": "album, duplicate, crowded, multiple, stuff, messy, photo, collage, doll, caricature, render. mannequin.",
|
163 |
+
"seed": -1,
|
164 |
+
"height": 768,
|
165 |
+
"width": 1280,
|
166 |
+
"sampler_index": "DPM++ 2M SDE Karras",
|
167 |
+
"restore_faces": False,
|
168 |
+
"tiling": False,
|
169 |
+
"n_iter": 1,
|
170 |
+
"batch_size": 1,
|
171 |
+
"cfg_scale": 2.0,
|
172 |
+
"subseed": -1,
|
173 |
+
"subseed_strength": 0.0,
|
174 |
+
"seed_resize_from_h": -1,
|
175 |
+
"seed_resize_from_w": -1,
|
176 |
+
"seed_enable_extras": False,
|
177 |
+
"enable_hr": False,
|
178 |
+
"denoising_strength": 0.0,
|
179 |
+
"hr_scale": 2.0,
|
180 |
+
"hr_upscaler": "Latent",
|
181 |
+
"hr_second_pass_steps": 0,
|
182 |
+
"hr_resize_x": 0,
|
183 |
+
"hr_resize_y": 0,
|
184 |
+
"hr_sampler_index": "",
|
185 |
+
"hr_prompt": "",
|
186 |
+
"hr_negative_prompt": "",
|
187 |
+
"override_settings_texts": ""
|
188 |
+
|
189 |
+
}
|
190 |
|
191 |
+
progress = 0
|
192 |
+
log = log_status(progress, "Starting image generation...")
|
|
|
|
|
|
|
193 |
|
194 |
+
response, error_log = make_api_call(f"{base_url}/sdapi/v1/txt2img", base_payload)
|
195 |
+
if error_log:
|
196 |
+
return None, error_log
|
197 |
+
|
198 |
+
if response and 'images' in response:
|
199 |
+
image_data = response['images'][0]
|
200 |
+
image = save_image(image_data)
|
201 |
+
log = log_status(100, "Image generated successfully.")
|
202 |
+
return image, log
|
203 |
+
else:
|
204 |
+
log = log_status(progress, "Failed to generate image.")
|
205 |
+
return None, log
|
206 |
+
|
207 |
+
max_image_size = 768
|
208 |
+
image_folder = "/Users/gev7418/Library/CloudStorage/OneDrive-Personal/Gradio Script Building Blocks/HatchDuo-Gradio/Images"
|
209 |
+
thumbs_up_folder = os.path.join(image_folder, "Thumbs_Up")
|
210 |
+
thumbs_down_folder = os.path.join(image_folder, "Thumbs_Down")
|
211 |
+
allowed_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
|
212 |
+
folders_cycle = [image_folder, thumbs_up_folder, thumbs_down_folder]
|
213 |
+
# Set up logging to print to the terminal
|
214 |
+
logging.basicConfig(level=logging.INFO)
|
215 |
+
|
216 |
+
# OpenAI API Key
|
217 |
+
api_key = "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" # Replace with your OpenAI API Key
|
218 |
+
import subprocess
|
219 |
+
|
220 |
+
def convert_and_rename_files_in_directory(directory, original_extension, new_extension, new_base_name):
|
221 |
+
"""
|
222 |
+
Converts all files in the specified directory from the original extension to PNG, and renames them to a new base name followed by a number.
|
223 |
+
"""
|
224 |
+
files = [f for f in os.listdir(directory) if f.endswith(original_extension)]
|
225 |
+
for index, file in enumerate(sorted(files)):
|
226 |
+
new_name = f"{new_base_name}_{index}{new_extension}"
|
227 |
+
original_file_path = os.path.join(directory, file)
|
228 |
+
new_file_path = os.path.join(directory, new_name)
|
229 |
+
# Convert to PNG using a subprocess call to a command line tool like ImageMagick
|
230 |
+
subprocess.run(['convert', original_file_path, new_file_path])
|
231 |
+
os.rename(new_file_path, os.path.join(directory, new_name))
|
232 |
+
logging.info(f"All {original_extension} files in {directory} have been converted to PNG and renamed to {new_base_name} format.")
|
233 |
+
|
234 |
+
|
235 |
+
def encode_image(image):
|
236 |
+
"""
|
237 |
+
This function converts the image to bytes and returns the base64 encoding of the image file
|
238 |
+
"""
|
239 |
+
logging.info("Encoding image to base64")
|
240 |
+
image_bytes = io.BytesIO()
|
241 |
+
if image.mode == 'RGBA':
|
242 |
+
# Convert RGBA to RGB
|
243 |
+
image = image.convert('RGB')
|
244 |
+
image.save(image_bytes, format='JPEG')
|
245 |
+
image_bytes = image_bytes.getvalue()
|
246 |
+
base64_image = base64.b64encode(image_bytes).decode('utf-8')
|
247 |
+
|
248 |
+
return base64_image
|
249 |
|
250 |
def get_image_description(image, custom_prompt):
|
251 |
+
"""
|
252 |
+
This function sends the image to the OpenAI API and returns the response.
|
253 |
+
It now checks if there's custom prompt content before sending it to the API.
|
254 |
+
"""
|
255 |
+
logging.info("Getting image description from OpenAI API")
|
256 |
+
base64_image = encode_image(image)
|
257 |
+
headers = {"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}
|
258 |
+
|
259 |
+
# Check if custom_prompt is not empty, otherwise use a default prompt
|
260 |
if not custom_prompt.strip():
|
261 |
custom_prompt = "Describe this image"
|
262 |
+
|
263 |
+
payload = {
|
264 |
"model": "gpt-4-vision-preview",
|
265 |
+
"messages": [
|
266 |
+
{
|
267 |
+
"role": "user",
|
268 |
+
"content": [
|
269 |
+
{"type": "text", "text": custom_prompt},
|
270 |
+
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
|
271 |
+
]
|
272 |
+
}
|
273 |
+
],
|
274 |
"max_tokens": 300
|
275 |
+
}
|
276 |
+
response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
|
277 |
+
response_payload = json.loads(response.text)
|
278 |
+
content = response_payload['choices'][0]['message']['content']
|
279 |
+
|
280 |
+
logging.info("Received response from OpenAI API")
|
281 |
+
return content
|
282 |
|
283 |
+
def create_directories():
|
284 |
+
print("Creating directories...")
|
285 |
+
os.makedirs(thumbs_up_folder, exist_ok=True)
|
286 |
+
os.makedirs(thumbs_down_folder, exist_ok=True)
|
287 |
+
|
288 |
+
create_directories()
|
289 |
+
|
290 |
+
def get_files(folder):
|
291 |
+
print(f"Getting files from {folder}...")
|
292 |
+
return [os.path.join(dp, f) for dp, dn, filenames in os.walk(folder) for f in filenames if os.path.splitext(f)[1].lower() in allowed_extensions]
|
293 |
+
|
294 |
+
def hash_image_pixels(file_path):
|
295 |
+
"""
|
296 |
+
Generates a hash for an image based on its pixel content.
|
297 |
+
"""
|
298 |
+
if os.path.splitext(file_path)[1].lower() in allowed_extensions:
|
299 |
+
with Image.open(file_path) as img:
|
300 |
+
# Convert the image to RGBA (to standardize if images are in different modes)
|
301 |
+
img_rgba = img.convert('RGBA')
|
302 |
+
# Calculate the new height and width to maintain aspect ratio
|
303 |
+
aspect_ratio = img_rgba.width / img_rgba.height
|
304 |
+
new_height = min(max_image_size, img_rgba.height)
|
305 |
+
new_width = int(aspect_ratio * new_height)
|
306 |
+
if img_rgba.height > max_image_size:
|
307 |
+
new_height = max_image_size
|
308 |
+
new_width = int(new_height * aspect_ratio)
|
309 |
+
# Use Image.Resampling.LANCZOS for better quality resizing
|
310 |
+
img_resized = img_rgba.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
311 |
+
# Get the bytes of the resized image data
|
312 |
+
img_bytes = io.BytesIO()
|
313 |
+
img_resized.save(img_bytes, format='PNG') # PNG format to ensure consistency across platforms
|
314 |
+
img_bytes = img_bytes.getvalue()
|
315 |
+
# Generate a hash of the resized image bytes
|
316 |
+
hash_obj = hashlib.sha256(img_bytes)
|
317 |
+
return hash_obj.hexdigest()
|
318 |
+
else:
|
319 |
+
return None
|
320 |
+
|
321 |
+
# Global cache for hashes to file paths
|
322 |
+
hash_to_path_cache = {}
|
323 |
+
|
324 |
+
def update_hash_to_path_cache():
|
325 |
+
global hash_to_path_cache
|
326 |
+
hash_to_path_cache.clear()
|
327 |
+
for folder in folders_cycle:
|
328 |
+
for file_path in get_files(folder):
|
329 |
+
file_hash = hash_image_pixels(file_path)
|
330 |
+
hash_to_path_cache[file_hash] = file_path
|
331 |
+
|
332 |
+
def find_file_by_hash(file_hash):
|
333 |
+
# Use the cache to find the file path
|
334 |
+
file_path = hash_to_path_cache.get(file_hash, None)
|
335 |
+
if file_path and os.path.exists(file_path):
|
336 |
+
return file_path
|
337 |
+
# If the file was not found or doesn't exist at the cached location,
|
338 |
+
# search in the thumbs up and thumbs down folders
|
339 |
+
for folder in [thumbs_up_folder, thumbs_down_folder]:
|
340 |
+
for dp, dn, filenames in os.walk(folder):
|
341 |
+
for f in filenames:
|
342 |
+
potential_path = os.path.join(dp, f)
|
343 |
+
if os.path.splitext(f)[1].lower() in allowed_extensions:
|
344 |
+
if hash_image_pixels(potential_path) == file_hash:
|
345 |
+
# Update the cache with the new location
|
346 |
+
hash_to_path_cache[file_hash] = potential_path
|
347 |
+
return potential_path
|
348 |
+
# If the file is not found in any of the locations, return None
|
349 |
+
return None
|
350 |
+
|
351 |
+
def build_file_array():
|
352 |
+
print("Building file array...")
|
353 |
+
files = []
|
354 |
+
for folder in folders_cycle:
|
355 |
+
for file_path in get_files(folder):
|
356 |
+
file_hash = hash_image_pixels(file_path)
|
357 |
+
status = "π Rated Up" if folder == thumbs_up_folder else "π Rated Down" if folder == thumbs_down_folder else "β³ Pending"
|
358 |
+
files.append({"path": file_path, "hash": file_hash, "status": status, "date_modified": os.path.getmtime(file_path)})
|
359 |
+
|
360 |
+
# Create text files for any images missing their text file counterpart
|
361 |
+
text_file_path = file_path.replace(os.path.splitext(file_path)[1], '.txt')
|
362 |
+
if not os.path.exists(text_file_path):
|
363 |
+
with open(text_file_path, 'w') as text_file:
|
364 |
+
text_file.write("") # Create an empty text file
|
365 |
+
|
366 |
+
files = sorted(files, key=lambda x: x["date_modified"], reverse=True)
|
367 |
+
return files
|
368 |
+
|
369 |
+
def refresh_file_array_and_hashes():
|
370 |
+
global file_array, hash_list
|
371 |
+
file_array = build_file_array()
|
372 |
+
hash_list = [file['hash'] for file in file_array]
|
373 |
+
print("File array and hash list updated.")
|
374 |
+
|
375 |
+
update_hash_to_path_cache()
|
376 |
+
refresh_file_array_and_hashes()
|
377 |
+
current_index = 0
|
378 |
+
def save_text(file_hash, text, prepend_text="", append_text="", save_and_overwrite_changes=True):
|
379 |
+
if not save_and_overwrite_changes:
|
380 |
+
print("Skipping saving due to user preference.")
|
381 |
+
return
|
382 |
+
file_path = find_file_by_hash(file_hash)
|
383 |
+
if file_path:
|
384 |
+
text_file_path = file_path.replace(os.path.splitext(file_path)[1], '.txt')
|
385 |
+
final_text = ""
|
386 |
+
if not save_and_overwrite_changes and os.path.exists(text_file_path):
|
387 |
+
with open(text_file_path, 'r') as existing_file:
|
388 |
+
existing_content = existing_file.read()
|
389 |
+
final_text = (prepend_text + ", " if prepend_text else "") + existing_content + (", " + append_text if append_text else "")
|
390 |
+
else:
|
391 |
+
final_text = (prepend_text + ", " if prepend_text else "") + text + (", " + append_text if append_text else "")
|
392 |
+
print(f"Saving text for file hash {file_hash} to {text_file_path}...")
|
393 |
+
with open(text_file_path, 'w') as text_file:
|
394 |
+
text_file.write(final_text)
|
395 |
+
|
396 |
+
def get_text(file_hash):
|
397 |
+
file_path = find_file_by_hash(file_hash)
|
398 |
+
if file_path:
|
399 |
+
text_file_path = file_path.replace(os.path.splitext(file_path)[1], '.txt')
|
400 |
+
if os.path.exists(text_file_path):
|
401 |
+
print(f"Getting text for file hash {file_hash} from {text_file_path}...")
|
402 |
+
with open(text_file_path) as text_file:
|
403 |
+
return text_file.read()
|
404 |
+
return ""
|
405 |
+
|
406 |
+
def update_index_for_navigation(current_text, prepend_text, append_text, navigate_forward=True, save_and_overwrite_changes=False):
|
407 |
+
global current_index, file_array, hash_list
|
408 |
+
if current_text or not save_and_overwrite_changes: # Save only if there's something to save or if saving changes is not required
|
409 |
+
file_hash = hash_list[current_index]
|
410 |
+
save_text(file_hash, current_text, prepend_text, append_text, save_and_overwrite_changes)
|
411 |
+
if navigate_forward:
|
412 |
+
current_index = (current_index + 1) % len(hash_list) # Cycle to the first item if at the end
|
413 |
+
else:
|
414 |
+
current_index = (current_index - 1) % len(hash_list) # Cycle to the last item if at the beginning
|
415 |
+
def get_file(navigate_forward, current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt):
|
416 |
+
global current_index, file_array, hash_list
|
417 |
+
print(f"Getting file, navigate_forward: {navigate_forward}...")
|
418 |
+
update_index_for_navigation(current_text, prepend_text, append_text, navigate_forward, save_and_overwrite_changes)
|
419 |
+
if current_index < len(hash_list):
|
420 |
+
current_hash = hash_list[current_index]
|
421 |
+
file_info = next((item for item in file_array if item["hash"] == current_hash), None)
|
422 |
+
if file_info:
|
423 |
+
file_path = find_file_by_hash(current_hash)
|
424 |
+
if file_path:
|
425 |
+
with Image.open(file_path) as img:
|
426 |
+
# Calculate the new height and width to maintain aspect ratio, with a max height of 768 for display in gradio
|
427 |
+
aspect_ratio = img.width / img.height
|
428 |
+
new_height = min(768, img.height) # Set max height to 768 for gradio display
|
429 |
+
new_width = int(aspect_ratio * new_height)
|
430 |
+
# Use Image.Resampling.LANCZOS for better quality resizing
|
431 |
+
img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
|
432 |
+
img_resized_bytes = io.BytesIO()
|
433 |
+
img_resized.save(img_resized_bytes, format='PNG')
|
434 |
+
img_resized_bytes = img_resized_bytes.getvalue()
|
435 |
+
text = get_text(current_hash) # Load text file contents into textbox
|
436 |
+
if not pause_api_call:
|
437 |
+
text = get_image_description(img, custom_prompt) # Get new description from OpenAI
|
438 |
+
save_text(current_hash, text, prepend_text, append_text, save_and_overwrite_changes) # Optionally save the new description
|
439 |
+
# Convert the resized image to a numpy array for display
|
440 |
+
img_resized_np = np.array(Image.open(io.BytesIO(img_resized_bytes)))
|
441 |
+
return img_resized_np, current_hash, file_info["status"], text, os.path.basename(file_path), file_path, os.path.relpath(file_path, start=image_folder), os.path.basename(file_path).replace(os.path.splitext(os.path.basename(file_path))[1], '.txt'), find_file_by_hash(current_hash).replace(os.path.splitext(find_file_by_hash(current_hash))[1], '.txt'), os.path.relpath(find_file_by_hash(current_hash).replace(os.path.splitext(find_file_by_hash(current_hash))[1], '.txt'), start=image_folder)
|
442 |
+
return None, "File not found", "β³ Pending", "", "", "", "", "", "", ""
|
443 |
+
def move_file(direction, current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt):
|
444 |
+
global current_index, file_array, hash_list
|
445 |
+
print(f"Moving file in direction {direction}...")
|
446 |
+
if current_index < len(hash_list):
|
447 |
+
current_hash = hash_list[current_index]
|
448 |
+
file_info = next((item for item in file_array if item["hash"] == current_hash), None)
|
449 |
+
if file_info:
|
450 |
+
source_file = find_file_by_hash(current_hash)
|
451 |
+
if source_file:
|
452 |
+
destination = thumbs_up_folder if direction == "up" else thumbs_down_folder if direction == "down" else None
|
453 |
+
new_status = "π Rated Up" if direction == "up" else "π Rated Down" if direction == "down" else None
|
454 |
+
if destination:
|
455 |
+
shutil.move(source_file, os.path.join(destination, os.path.basename(source_file)))
|
456 |
+
file_info["status"] = new_status
|
457 |
+
# Get new description and save it
|
458 |
+
with Image.open(source_file) as img:
|
459 |
+
description = get_image_description(img, custom_prompt)
|
460 |
+
save_text(current_hash, description, prepend_text, append_text, save_and_overwrite_changes)
|
461 |
+
return get_file(True, current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt)
|
462 |
+
return None, "Invalid direction or file not found", "β³ Pending", "", "", "", "", "", "", ""
|
463 |
+
|
464 |
+
def reset_files(current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt):
|
465 |
+
global file_array, current_index, hash_list
|
466 |
+
print("Resetting files and clearing text...")
|
467 |
+
for file_info in file_array:
|
468 |
+
source_file = find_file_by_hash(file_info["hash"])
|
469 |
+
if source_file and os.path.exists(source_file):
|
470 |
+
source_text = source_file.replace(os.path.splitext(source_file)[1], '.txt')
|
471 |
+
if os.path.exists(source_text):
|
472 |
+
shutil.move(source_text, os.path.join(image_folder, os.path.basename(source_text)))
|
473 |
+
shutil.move(source_file, os.path.join(image_folder, os.path.basename(source_file)))
|
474 |
+
with open(os.path.join(image_folder, os.path.basename(source_text)), 'w') as text_file:
|
475 |
+
text_file.write("")
|
476 |
+
file_info["status"] = "β³ Pending"
|
477 |
+
file_array = build_file_array()
|
478 |
+
hash_list = [file['hash'] for file in file_array]
|
479 |
+
update_hash_to_path_cache()
|
480 |
+
return get_file(True, current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt)
|
481 |
+
|
482 |
+
def delete_file(current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt):
|
483 |
+
global current_index, file_array, hash_list
|
484 |
+
print("Deleting file...")
|
485 |
+
if current_index < len(hash_list):
|
486 |
+
current_hash = hash_list[current_index]
|
487 |
+
source_file = find_file_by_hash(current_hash)
|
488 |
+
if source_file:
|
489 |
+
source_text = source_file.replace(os.path.splitext(source_file)[1], '.txt')
|
490 |
+
os.remove(source_file)
|
491 |
+
os.remove(source_text)
|
492 |
+
hash_list.remove(current_hash) # Remove the hash from the hash_list
|
493 |
+
file_array = [file for file in file_array if file["hash"] != current_hash] # Rebuild file_array without the deleted file
|
494 |
+
if current_index >= len(hash_list): current_index = len(hash_list) - 1
|
495 |
+
update_hash_to_path_cache()
|
496 |
+
return get_file(True, current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt)
|
497 |
+
|
498 |
+
def load_image_details():
|
499 |
+
print("Loading image details...")
|
500 |
+
image_details = []
|
501 |
+
for folder in folders_cycle:
|
502 |
+
for file_path in get_files(folder):
|
503 |
+
file_hash = hash_image_pixels(file_path)
|
504 |
+
status = "π Rated Up" if folder == thumbs_up_folder else "π Rated Down" if folder == thumbs_down_folder else "β³ Pending"
|
505 |
+
text_file_path = file_path.replace(os.path.splitext(file_path)[1], '.txt')
|
506 |
+
text_content = ""
|
507 |
+
if os.path.exists(text_file_path):
|
508 |
+
with open(text_file_path, 'r') as text_file:
|
509 |
+
text_content = text_file.read()
|
510 |
+
image_details.append({
|
511 |
+
"Text Content": text_content,
|
512 |
+
"Rating Status": status,
|
513 |
+
"File Hash": file_hash,
|
514 |
+
"Image Path": file_path,
|
515 |
+
"Text Path": text_file_path if os.path.exists(text_file_path) else "N/A"
|
516 |
+
})
|
517 |
+
return pd.DataFrame(image_details)
|
518 |
+
|
519 |
+
def load_text_files_as_df():
|
520 |
+
print("Loading text files into dataframe...")
|
521 |
+
text_files = []
|
522 |
+
for file in os.listdir(image_folder):
|
523 |
+
if file.endswith('.txt'):
|
524 |
+
file_path = os.path.join(image_folder, file)
|
525 |
+
with open(file_path, 'r') as text_file:
|
526 |
+
text_content = text_file.read()
|
527 |
+
text_files.append({
|
528 |
+
"File Name": file.replace('.txt', ''),
|
529 |
+
"Content": text_content,
|
530 |
+
"Date Modified": datetime.fromtimestamp(os.path.getmtime(file_path)).strftime('%Y-%m-%d %H:%M:%S')
|
531 |
+
})
|
532 |
+
return pd.DataFrame(text_files)
|
533 |
+
|
534 |
+
# Function to merge text_files_df and image_details_df into a single dataframe
|
535 |
+
def load_combined_details():
|
536 |
+
print("Loading text file details...")
|
537 |
+
text_df = load_text_files_as_df()
|
538 |
+
image_details_df = load_image_details()
|
539 |
+
# Ensure the 'File Name' column exists in image_details_df by extracting it from 'Image Path'
|
540 |
+
image_details_df['File Name'] = image_details_df['Image Path'].apply(lambda x: os.path.basename(x).replace(os.path.splitext(os.path.basename(x))[1], ''))
|
541 |
+
combined_df = pd.merge(text_df, image_details_df, on="File Name", how="outer")
|
542 |
+
# Reorder dataframe fields according to specified order: Date Modified, Rating Status, File Name, Text Content, File Hash, Text Path, Image Path
|
543 |
+
combined_df = combined_df[['Date Modified', 'Rating Status', 'File Name', 'Text Content', 'File Hash', 'Text Path', 'Image Path']]
|
544 |
+
return combined_df
|
545 |
+
|
546 |
+
def load_gallery():
|
547 |
+
print("Loading gallery...")
|
548 |
+
return sorted([os.path.join(image_folder, f) for f in os.listdir(image_folder) if os.path.splitext(f)[1].lower() in allowed_extensions], key=lambda x: os.path.basename(x).lower())
|
549 |
+
|
550 |
+
def load_thumbs_up_gallery():
|
551 |
+
print("Loading Thumbs Up gallery...")
|
552 |
+
return sorted([os.path.join(thumbs_up_folder, f) for f in os.listdir(thumbs_up_folder) if os.path.splitext(f)[1].lower() in allowed_extensions], key=lambda x: os.path.basename(x).lower())
|
553 |
|
554 |
+
def load_thumbs_down_gallery():
|
555 |
+
print("Loading Thumbs Down gallery...")
|
556 |
+
return sorted([os.path.join(thumbs_down_folder, f) for f in os.listdir(thumbs_down_folder) if os.path.splitext(f)[1].lower() in allowed_extensions], key=lambda x: os.path.basename(x).lower())
|
557 |
+
|
558 |
+
def load_all_folders_gallery():
|
559 |
+
print("Loading all folders gallery...")
|
560 |
+
all_images = []
|
561 |
+
for folder in folders_cycle:
|
562 |
+
all_images.extend(sorted([os.path.join(folder, f) for f in os.listdir(folder) if os.path.splitext(f)[1].lower() in allowed_extensions], key=lambda x: os.path.basename(x).lower()))
|
563 |
+
return all_images
|
564 |
+
|
565 |
+
def load_text_files():
|
566 |
+
print("Loading text files...")
|
567 |
+
return sorted([[file, open(os.path.join(image_folder, file),'r').read()] for file in os.listdir(image_folder) if file.endswith('.txt')], key=lambda x: x[0].lower())
|
568 |
+
|
569 |
+
def update_text_content(df):
|
570 |
+
global text_files_df
|
571 |
+
print("Updating text content...")
|
572 |
+
for row in df:
|
573 |
+
if len(row) == 3:
|
574 |
+
file_name, new_content, date_added = row
|
575 |
+
file_path = os.path.join(image_folder, file_name)
|
576 |
+
if os.path.exists(file_path):
|
577 |
+
file_hash = hash_file(file_path)
|
578 |
+
save_text(file_hash, new_content)
|
579 |
+
else:
|
580 |
+
print("Error: Unexpected number of values in row. Expected 3 values per row.")
|
581 |
+
|
582 |
+
# Step 1: Define a function to update the dataframe
|
583 |
+
def refresh_text_files():
|
584 |
+
print("Refreshing text files...")
|
585 |
+
updated_text_files = sorted([[file, open(os.path.join(image_folder, file),'r').read()] for file in os.listdir(image_folder) if file.endswith('.txt')], key=lambda x: x[0].lower())
|
586 |
+
return updated_text_files
|
587 |
+
def save_uploaded_image(image_file):
|
588 |
+
"""
|
589 |
+
Attempts to save the uploaded image to the designated image folder. Returns the path of the saved image or None if an error occurs.
|
590 |
+
"""
|
591 |
+
try:
|
592 |
+
os.makedirs(image_folder, exist_ok=True) # Ensure the image folder exists
|
593 |
+
image_path = os.path.join(image_folder, image_file.name)
|
594 |
+
with open(image_path, "wb") as file:
|
595 |
+
file.write(image_file.content) # Write the byte content of the file
|
596 |
+
print(f"Image saved successfully: {image_path}")
|
597 |
+
return image_path
|
598 |
+
except Exception as e:
|
599 |
+
print(f"Error saving image: {e}")
|
600 |
+
return None
|
601 |
+
|
602 |
+
def create_text_file_for_image(image_path):
|
603 |
+
"""
|
604 |
+
Generates an empty text file corresponding to an uploaded image, using the same base name.
|
605 |
+
"""
|
606 |
text_file_path = f"{os.path.splitext(image_path)[0]}.txt"
|
607 |
+
open(text_file_path, 'w').close() # Efficiently create an empty text file
|
608 |
+
return text_file_path
|
609 |
+
|
610 |
+
def handle_image_upload(uploaded_files):
|
611 |
+
status_messages = []
|
612 |
+
for uploaded_file in uploaded_files:
|
613 |
+
file_extension = os.path.splitext(uploaded_file.name)[1].lower()
|
614 |
+
if file_extension in allowed_extensions:
|
615 |
+
# Ensure the image folder exists
|
616 |
+
os.makedirs(image_folder, exist_ok=True)
|
617 |
+
# Save the file with a unique name based on its hash
|
618 |
+
unique_name = hashlib.sha256(uploaded_file.name.encode()).hexdigest() + file_extension
|
619 |
+
save_path = os.path.join(image_folder, unique_name)
|
620 |
+
with open(save_path, "wb") as file:
|
621 |
+
file.write(uploaded_file.content)
|
622 |
+
# Optionally, create a corresponding text file
|
623 |
+
text_file_path = save_path + ".txt"
|
624 |
+
with open(text_file_path, "w") as text_file:
|
625 |
+
text_file.write(f"Image file: {unique_name} uploaded successfully.")
|
626 |
+
status_messages.append(f"Uploaded and saved {uploaded_file.name} successfully.")
|
627 |
+
else:
|
628 |
+
status_messages.append(f"File {uploaded_file.name} has an unsupported extension ({file_extension}) and was not uploaded.")
|
629 |
+
return "\n".join(status_messages)
|
630 |
+
|
631 |
+
def refresh_image_description(current_text, prepend_text, append_text, pause_api_call, save_and_overwrite_changes, custom_prompt):
|
632 |
+
global current_index, file_array
|
633 |
+
if not pause_api_call:
|
634 |
+
file_info = file_array[current_index]
|
635 |
+
file_path = find_file_by_hash(file_info["hash"])
|
636 |
+
if file_path:
|
637 |
+
with Image.open(file_path) as img:
|
638 |
+
# Use the custom_prompt parameter when calling get_image_description
|
639 |
+
text = get_image_description(img, custom_prompt)
|
640 |
+
save_text(file_info["hash"], text, prepend_text, append_text, save_and_overwrite_changes)
|
641 |
+
return np.array(img), file_info["hash"], file_info["status"], text, os.path.basename(file_path), file_path, os.path.relpath(file_path, start=image_folder), os.path.basename(file_path).replace(os.path.splitext(os.path.basename(file_path))[1], '.txt'), find_file_by_hash(file_info["hash"]).replace(os.path.splitext(find_file_by_hash(file_info["hash"]))[1], '.txt'), os.path.relpath(find_file_by_hash(file_info["hash"]).replace(os.path.splitext(find_file_by_hash(file_info["hash"]))[1], '.txt'), start=image_folder)
|
642 |
+
# If API call is paused or file not found, return current state without changes
|
643 |
+
return None, file_info["hash"], file_info["status"], current_text, os.path.basename(file_path), file_path, os.path.relpath(file_path, start=image_folder), os.path.basename(file_path).replace(os.path.splitext(os.path.basename(file_path))[1], '.txt'), find_file_by_hash(file_info["hash"]).replace(os.path.splitext(find_file_by_hash(file_info["hash"]))[1], '.txt'), os.path.relpath(find_file_by_hash(file_info["hash"]).replace(os.path.splitext(find_file_by_hash(file_info["hash"]))[1], '.txt'), start=image_folder)
|
644 |
+
# Backup functionality for successful app launches
|
645 |
+
backup_folder = "Backup_Scripts"
|
646 |
+
os.makedirs(backup_folder, exist_ok=True)
|
647 |
+
|
648 |
+
def backup_script():
|
649 |
+
"""
|
650 |
+
This function creates a backup of the current script in the designated backup folder.
|
651 |
+
"""
|
652 |
+
current_script_path = os.path.realpath(__file__)
|
653 |
+
backup_script_path = os.path.join(backup_folder, f"backup_{os.path.basename(current_script_path)}_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.py")
|
654 |
+
shutil.copy2(current_script_path, backup_script_path)
|
655 |
+
print(f"Backup of the script created at {backup_script_path}")
|
656 |
+
|
657 |
+
def app_launch_success():
|
658 |
+
"""
|
659 |
+
This function is called when the app successfully launches.
|
660 |
+
It triggers the backup of the current script.
|
661 |
+
"""
|
662 |
+
print("App successfully launched. Creating a backup of the script...")
|
663 |
+
backup_script()
|
664 |
+
|
665 |
+
def refresh_all():
|
666 |
+
combined_details = load_combined_details()
|
667 |
+
image_gallery_content = load_gallery()
|
668 |
+
thumbs_up_gallery_content = load_thumbs_up_gallery()
|
669 |
+
# Reinitialize the hash array and the index for new images
|
670 |
+
update_hash_to_path_cache()
|
671 |
+
refresh_file_array_and_hashes()
|
672 |
+
current_index = 0 # Reset the index to start from the first image
|
673 |
+
return combined_details, image_gallery_content, thumbs_up_gallery_content
|
674 |
+
|
675 |
+
def combined_action(prompt, steps, model, styles, custom_prompt):
|
676 |
+
# Trigger the OpenAI API for description and wait for the response
|
677 |
+
description, status = trigger_openai_api(custom_prompt)
|
678 |
+
if status == "success":
|
679 |
+
# Once the response is received, generate the image based on the prompt and other parameters
|
680 |
+
generated_image, log = generate_image(prompt, steps, model, styles)
|
681 |
+
else:
|
682 |
+
generated_image, log = None, "Failed to generate image due to API call failure."
|
683 |
+
return generated_image, log, description
|
684 |
+
|
685 |
+
|
686 |
+
# π Setup Environment π
|
687 |
+
import gradio as gr
|
688 |
+
import os
|
689 |
+
from pathlib import Path
|
690 |
+
import subprocess
|
691 |
+
|
692 |
+
# Define the directory to store uploaded images
|
693 |
+
image_folder = "./Images"
|
694 |
+
os.makedirs(image_folder, exist_ok=True) # Create the directory if it doesn't exist
|
695 |
+
|
696 |
+
# π Function Definitions π
|
697 |
+
def save_and_show_images(uploaded_files):
|
698 |
+
images_to_display = []
|
699 |
+
# Process each uploaded file
|
700 |
+
for index, uploaded_file in enumerate(uploaded_files):
|
701 |
+
# Define a base file name with index
|
702 |
+
base_file_name = f"Img-{index}"
|
703 |
+
# Check for existing files and increment index to avoid overwriting
|
704 |
+
existing_files = [f for f in Path(image_folder).rglob('*') if f.is_file() and f.suffix in ['.jpg', '.png', '.txt']]
|
705 |
+
while any(base_file_name in file.name for file in existing_files):
|
706 |
+
index += 1
|
707 |
+
base_file_name = f"Img-{index}"
|
708 |
+
|
709 |
+
# Determine the file extension (jpg or png)
|
710 |
+
file_extension = "jpg" if uploaded_file[:3] == b'\xff\xd8\xff' else "png"
|
711 |
+
final_file_name = f"{base_file_name}.{file_extension}"
|
712 |
+
image_path = os.path.join(image_folder, final_file_name)
|
713 |
+
|
714 |
+
# Write the bytes to a new file in the specified directory
|
715 |
+
with open(image_path, "wb") as file:
|
716 |
+
file.write(uploaded_file)
|
717 |
+
|
718 |
+
# Create a corresponding text file for the image
|
719 |
+
text_file_path = os.path.join(image_folder, f"{base_file_name}.txt")
|
720 |
+
with open(text_file_path, "w") as text_file:
|
721 |
+
text_file.write(f"Image file: {final_file_name}")
|
722 |
+
|
723 |
+
# Add the path (or ideally, a URL to the file) to the list to display
|
724 |
+
images_to_display.append(image_path)
|
725 |
+
|
726 |
+
# Ensure all images have a corresponding text file
|
727 |
+
for image_file_path in Path(image_folder).rglob('*'):
|
728 |
+
if image_file_path.suffix in ['.jpg', '.png']:
|
729 |
+
base_name = os.path.splitext(image_file_path.name)[0]
|
730 |
+
text_file_path = os.path.join(image_folder, f"{base_name}.txt")
|
731 |
+
if not os.path.exists(text_file_path):
|
732 |
+
with open(text_file_path, "w") as text_file:
|
733 |
+
text_file.write(f"Image file: {image_file_path.name}")
|
734 |
+
|
735 |
+
return images_to_display
|
736 |
+
|
737 |
+
|
738 |
|
739 |
with gr.Blocks() as app:
|
740 |
with gr.Row():
|
741 |
upload_btn = gr.File(label="Upload Images", type="binary", file_count='multiple')
|
742 |
gallery = gr.Gallery(label="Uploaded Images Gallery")
|
743 |
+
upload_btn.change(fn=save_and_show_images, inputs=upload_btn, outputs=gallery)
|
744 |
+
with gr.Row():
|
745 |
+
with gr.Column():
|
746 |
+
gr.Markdown("## Image Viewer")
|
747 |
+
with gr.Column():
|
748 |
+
# Add an upload button for uploading files to the images folder
|
749 |
+
|
750 |
+
image = gr.Image(height=768, label="Current Image", image_mode="RGBA", width="max", elem_id="current_image", type="numpy")
|
751 |
+
with gr.Row():
|
752 |
+
thumbs_down_btn = gr.Button("π Rate Down")
|
753 |
+
thumbs_up_btn = gr.Button("π Rate Up")
|
754 |
+
with gr.Row():
|
755 |
+
prev_btn = gr.Button("β¬
Previous")
|
756 |
+
next_btn = gr.Button("Next β‘")
|
757 |
+
with gr.Row():
|
758 |
+
status_display = gr.Textbox(label="Rating Status", interactive=False)
|
759 |
+
image_path_display = gr.Textbox(label="Image Path", interactive=False)
|
760 |
+
|
761 |
+
|
762 |
+
|
763 |
+
with gr.Column():
|
764 |
+
# Existing setup for checkboxes
|
765 |
+
with gr.Row():
|
766 |
+
pause_api_call_checkbox = gr.Checkbox(label="Pause OpenAI API Calls", value=True)
|
767 |
+
save_and_overwrite_changes_checkbox = gr.Checkbox(label="Save and Overwrite Changes", value=True)
|
768 |
+
|
769 |
+
# New textbox for custom OpenAI API instructions
|
770 |
+
custom_instructions = gr.Textbox(label="Custom Instructions", value="Use the art of deduction and creativity to generate a persona profile and 3 inspiration words to describe the image without describing the subject. Wrap it up with a concise 1 sentence caption of the image with the subject in high detail. JSON Format needed: Futuristic Persona Profile: The subject exudes a sense of readiness and authority, dressed in attire that hints at a future dominated by advanced technology and interstellar travel. This character could be envisioned as a commander or a pioneer in a futuristic saga, marked by their composed nature and the streamlined design of their gear. Futuristic Caption: A resolute figure of tomorrow, standing firm with a serene resolve, garbed in a sleek, technologically superior suit that narrates tales of distant realms and adventures. Poised Persona Profile: The individual appears as a beacon of confidence and tactical acumen. Their outfit suggests a world of progressive technology and potential space conquests. This persona might be a strategist or a guardian in a speculative fiction setting, distinguished by their steady presence and the modernistic cut of their uniform. Poised Caption: A visionary sentinel of the cosmos, poised with a calm yet assertive demeanor, dressed in an advanced, form-enhancing suit that hints at the mysteries of the universe yet to unfold. Advanced Persona Profile: The figure stands as a symbol of assurance and innovation, clad in a costume that forecasts an era of sophisticated technology and cosmic exploration. This character could represent an expert or a defender in a futuristic tale, highlighted by their tranquil posture and the elegant configuration of their attire. Advanced Caption: An unwavering pioneer of the future, the individual poses with a composed assurance, enveloped in a cutting-edge suit that speaks of advanced civilizations and uncharted frontiers.", placeholder="Enter custom instructions...", lines=2, max_lines=5)
|
771 |
+
with gr.Accordion("π Thumbs Up Gallery", open=False):
|
772 |
+
thumbs_up_gallery = gr.Gallery(label="Thumbs Up Image Gallery", value=load_thumbs_up_gallery(), every=5, columns=3, rows=1, object_fit="contain", height="auto")
|
773 |
|
|
|
|
|
|
|
|
|
774 |
|
775 |
+
with gr.Row():
|
776 |
+
text_display = prompt = gr.Textbox(label="Image Analysis", lines=3, max_lines=10, interactive=True, placeholder="API call paused. Manually enter description.")
|
777 |
+
output_image = gr.Image(type="pil", label="Visual Analysis Analog")
|
778 |
+
|
779 |
+
with gr.Row():
|
780 |
+
trigger_api_btn = gr.Button("Analyze Aesthetic!")
|
781 |
+
combined_btn = gr.Button("Analyze & Generate") # New combined action button
|
782 |
+
generate_btn = gr.Button("Generate")
|
783 |
+
|
784 |
+
analyze_all_btn = gr.Button("Analyze All Thumbs Up")
|
785 |
+
descriptions_display = gr.Dataframe()
|
786 |
+
status_display = gr.Textbox(label="Status", lines=10, interactive=False)
|
787 |
+
analyze_all_btn.click(analyze_thumbs_up_images, inputs=[custom_instructions], outputs=[descriptions_display, status_display])
|
788 |
+
|
789 |
+
with gr.Row():
|
790 |
+
analyze_everything_btn = gr.Button("Analyze All Images")
|
791 |
+
|
792 |
+
analyze_all_btn.click(analyze_all_images, inputs=[custom_instructions], outputs=[descriptions_display, status_display])
|
793 |
+
analyze_everything_btn.click(analyze_all_images, inputs=[custom_instructions], outputs=[descriptions_display, status_display])
|
794 |
+
|
795 |
+
|
796 |
+
with gr.Row():
|
797 |
+
gr.Image(type="pil")
|
798 |
+
gr.Image(type="pil")
|
799 |
+
gr.Image(type="pil")
|
800 |
+
|
801 |
+
with gr.Accordion("Append / Prepend", open=False):
|
802 |
+
with gr.Row():
|
803 |
+
prepend_text = gr.Textbox(label="Prepend", lines=2, max_lines=5, placeholder="Enter text to prepend...")
|
804 |
+
append_text = gr.Textbox(label="Append", lines=2, max_lines=5, placeholder="Enter text to append...")
|
805 |
+
|
806 |
+
|
807 |
+
def trigger_openai_api(custom_prompt):
|
808 |
+
global current_index, file_array
|
809 |
+
if current_index < len(file_array):
|
810 |
+
file_info = file_array[current_index]
|
811 |
+
file_path = find_file_by_hash(file_info["hash"])
|
812 |
+
if file_path:
|
813 |
+
with Image.open(file_path) as img:
|
814 |
+
# Use the custom_prompt parameter when calling get_image_description
|
815 |
+
text = get_image_description(img, custom_prompt)
|
816 |
+
save_text(file_info["hash"], text, "", "", True) # Assuming you want to save and overwrite changes by default
|
817 |
+
return text, "Triggered OpenAI API successfully."
|
818 |
+
return "", "Failed to trigger OpenAI API."
|
819 |
+
trigger_api_btn.click(trigger_openai_api, inputs=[custom_instructions], outputs=[text_display, status_display])
|
820 |
+
|
821 |
+
|
822 |
+
with gr.Row():
|
823 |
+
delete_btn = gr.Button("ποΈ Delete")
|
824 |
+
reset_btn = gr.Button("π Reset All Ratings ππ")
|
825 |
+
|
826 |
+
with gr.Accordion(label="Edit Project", open=False):
|
827 |
+
with gr.Column(scale=0):
|
828 |
+
file_input = gr.File(file_count="multiple", type="binary", label="Upload Images or Zip Files")
|
829 |
+
with gr.Row():
|
830 |
+
previous_session_button = gr.Button("π Previous Project")
|
831 |
+
next_session_button = gr.Button("Next Projectπ")
|
832 |
+
with gr.Row():
|
833 |
+
next_img = gr.Button("πΌοΈ Show Next Image")
|
834 |
+
prev_img = gr.Button("πΌοΈ Show Previous Image")
|
835 |
+
with gr.Row():
|
836 |
+
with gr.Row():
|
837 |
+
clear_button = gr.Button("ποΈ Clear Uploads Folder")
|
838 |
+
undo_button = gr.Button("β©οΈ Undo Last Deletion")
|
839 |
+
process_zip_button = gr.Button("π Process Zip Files")
|
840 |
+
with gr.Column():
|
841 |
+
randomize_checkbox = gr.Checkbox(label="π Randomize Every 2 Seconds", value=True)
|
842 |
+
refresh_time_input = gr.Number(label="β±οΈ Refresh Time", value=2)
|
843 |
+
index_display = gr.Textbox(value=str(project_index), label="π’ Session Index", every=5)
|
844 |
+
file_input.change(process_files, inputs=[file_input], outputs=[])
|
845 |
+
clear_button.click(clear_uploads_folder, inputs=[], outputs=[])
|
846 |
+
undo_button.click(undo_last_deletion, inputs=[], outputs=[])
|
847 |
+
next_session_button.click(next_session, inputs=[], outputs=[index_display])
|
848 |
+
previous_session_button.click(previous_session, inputs=[], outputs=[index_display])
|
849 |
+
next_img.click(get_next_image, inputs=[], outputs=[gallery])
|
850 |
+
prev_img.click(get_previous_image, inputs=[], outputs=[gallery])
|
851 |
+
process_zip_button.click(process_all_zips, inputs=[], outputs=[])
|
852 |
+
|
853 |
+
with gr.Accordion("Advanced Generation Settings", open=False):
|
854 |
+
with gr.Column():
|
855 |
+
steps = gr.Slider(minimum=1, maximum=32, value=16, label="Steps")
|
856 |
+
model = gr.Dropdown(choices=['TurboAnime.saftensors', 'OtherModel'], value='TurboAnime.saftensors', label="Model")
|
857 |
+
styles = gr.CheckboxGroup(choices=['RayORender', 'OtherStyle'], value=['RayORender'], label="Styles")
|
858 |
+
output_log = gr.Textbox(label="Log")
|
859 |
+
# Step 2: Add a button to trigger the update
|
860 |
+
|
861 |
+
generate_btn.click(fn=generate_image, inputs=[prompt, steps, model, styles], outputs=[output_image, output_log])
|
862 |
+
|
863 |
+
# Set the click action for the combined button
|
864 |
+
combined_btn.click(combined_action, inputs=[prompt, steps, model, styles, custom_instructions], outputs=[output_image, output_log, text_display])
|
865 |
+
|
866 |
+
|
867 |
+
with gr.Accordion("Training Data", open=True):
|
868 |
refresh_btn = gr.Button("Refresh")
|
869 |
+
with gr.Row():
|
870 |
+
combined_details_df = gr.Dataframe(label="Combined Text and Image Details", value=load_combined_details(), headers=["Date Modified", "Rating Status", "File Name", "Text Content", "File Hash", "Text Path", "Image Path"], datatype=["str", "str", "str", "str", "str", "str", "str"], every=5)
|
871 |
+
gallery = gr.Gallery(label="Image Gallery", value=load_gallery(), every=5)
|
872 |
+
with gr.Accordion("File Info", open=False):
|
873 |
+
file_hash_display = gr.Textbox(label="File Hash", interactive=False)
|
874 |
+
text_path_display = gr.Textbox(label="Text Path", interactive=False)
|
875 |
+
def delete_all_text_files_content():
|
876 |
+
text_files = [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.endswith('.txt')]
|
877 |
+
for file_path in text_files:
|
878 |
+
with open(file_path, 'w') as f:
|
879 |
+
f.write('') # Clear the content of the text file
|
880 |
+
return "All text files' content deleted."
|
881 |
+
with gr.Row():
|
882 |
+
# Add a button to manually trigger a backup of the script
|
883 |
+
backup_btn = gr.Button("Backup Script")
|
884 |
+
backup_btn.click(backup_script, inputs=[], outputs=[])
|
885 |
+
|
886 |
+
delete_all_text_btn = gr.Button("Delete All Text Files Content")
|
887 |
+
delete_all_text_btn.click(delete_all_text_files_content, inputs=[], outputs=status_display)
|
888 |
+
|
889 |
+
# Modify the button click actions to include the pause_api_call_checkbox state and the save_and_overwrite_changes_checkbox state as arguments
|
890 |
+
reset_btn.click(lambda t, pt, at, p, s, ci: reset_files(t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
891 |
+
prev_btn.click(lambda t, pt, at, p, s, ci: get_file(False, t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
892 |
+
next_btn.click(lambda t, pt, at, p, s, ci: get_file(True, t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
893 |
+
thumbs_up_btn.click(lambda t, pt, at, p, s, ci: move_file("up", t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
894 |
+
thumbs_down_btn.click(lambda t, pt, at, p, s, ci: move_file("down", t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
895 |
+
delete_btn.click(lambda t, pt, at, p, s, ci: delete_file(t, pt, at, p, s, ci), inputs=[text_display, prepend_text, append_text, pause_api_call_checkbox, save_and_overwrite_changes_checkbox, custom_instructions], outputs=[image, file_hash_display, status_display, text_display, image_path_display, text_path_display])
|
896 |
+
|
897 |
+
refresh_btn.click(refresh_all, inputs=[], outputs=[combined_details_df, gallery, thumbs_up_gallery])
|
898 |
+
|
899 |
+
|
900 |
+
# Check for successful app launch and backup the script
|
901 |
+
try:
|
902 |
+
app.launch(share=True, server_port=7866, server_name="0.0.0.0")
|
903 |
+
app_launch_success()
|
904 |
+
except Exception as e:
|
905 |
+
print(f"Error launching the app: {e}")
|
906 |
+
|
907 |
+
|
908 |
+
|
909 |
+
|
910 |
+
|
911 |
+
======
|
912 |
+
|
913 |
+
refactor using programmer golf. keep all hardcoded values, inputs, outputs, and features. Optimize for UI / UX intuitiveness. move all hardcoded values to the top of the script without breaking anything. refactor and simplify with the goal of brininging ui ux principles to coding
|
914 |
|
915 |
+
Use ui as guide
|