Spaces:
Sleeping
Sleeping
File size: 10,759 Bytes
1d6ee52 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
import streamlit as st
import openai
import base64
import requests
import os
import re
import pandas as pd
import random
import json
import subprocess
# Function to save API key and image directory to a file
def save_settings(api_key, image_directory):
with open('settings.txt', 'w') as f:
f.write(f"{api_key}\n{image_directory}")
# Function to load API key and image directory from a file
def load_settings():
try:
with open('settings.txt', 'r') as f:
lines = f.readlines()
api_key = lines[0].strip() if len(lines) > 0 else ''
image_directory = lines[1].strip() if len(lines) > 1 else ''
return api_key, image_directory
except FileNotFoundError:
return '', ''
# Function to initialize OpenAI API key
def init_openai_api_key(api_key):
openai.api_key = api_key
# Function to call GPT API
def call_gpt_api(prompt, temperature=0.7, max_tokens=None):
params = {
"model": "gpt-3.5-turbo-0301",
"messages": [
{"role": "system", "content": "You are a helpful assistant that generates metadata."},
{"role": "user", "content": prompt}
],
"temperature": temperature
}
if max_tokens is not None:
params["max_tokens"] = max_tokens
response = openai.ChatCompletion.create(**params)
return response.choices[0].message['content']
# Function to call GPT-4o for vision capabilities
def call_gpt_4o_vision(image_path, api_key):
with open(image_path, 'rb') as image_file:
image_data = image_file.read()
image_base64 = base64.b64encode(image_data).decode('utf-8')
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {api_key}"
}
payload = {
"model": "gpt-4o",
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "Generate a concise name and detailed context for this image, ensuring response fits within 77 tokens. No copyright, No colon (:), trademarks, privacy rights, property rights, no number, no ensuring organization and clarity. No quotation marks or dashes, using commas for separation. Focuses on straightforward, richly descriptive titles without vague language or mentioning camera specifics or photography techniques. Ensure the response is a single line."},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}}
]
}
],
"max_tokens": 77
}
response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload)
response_json = response.json()
if 'choices' in response_json and len(response_json['choices']) > 0:
return response_json['choices'][0]['message']['content'].replace('\n', ' ')
else:
return "No description available"
# Function to clean and process metadata
def clean_metadata(description):
description = re.split(r'Job ID:|--ar \d+:\d+', description)[0].strip()
description = description.replace('"', '').replace("'", '')
return description
# Function to clean numbers and periods from keywords
def clean_numbers_and_periods(keywords):
cleaned_keywords = []
for keyword in keywords:
cleaned_keyword = re.sub(r'\d+|\.', '', keyword).strip()
if cleaned_keyword:
cleaned_keywords.append(cleaned_keyword)
return cleaned_keywords
# Function to generate keywords with retries
def generate_keywords_with_retries(description, keywords_rule, min_keywords=49, max_keywords=49, retries=1):
keywords_set = set()
for _ in range(retries):
keywords_prompt = f"{keywords_rule}\nDescription: {description}"
keywords = call_gpt_api(keywords_prompt).strip()
keywords_list = [k.strip() for k in keywords.split(',')]
keywords_list = clean_numbers_and_periods(keywords_list)
keywords_set.update(keywords_list)
if len(keywords_set) >= min_keywords:
break
keywords_list = list(keywords_set)
if len(keywords_list) > max_keywords:
keywords_list = random.sample(keywords_list, max_keywords)
elif len(keywords_list) < min_keywords:
additional_keywords_needed = min_keywords - len(keywords_list)
for _ in range(retries):
keywords_prompt = f"{keywords_rule}\nDescription: {description}"
keywords = call_gpt_api(keywords_prompt).strip()
keywords_list.extend([k.strip() for k in keywords.split(',') if k.strip() not in keywords_list])
keywords_list = clean_numbers_and_periods(keywords_list)
if len(keywords_list) >= min_keywords:
break
keywords_list = keywords_list[:max_keywords]
return ', '.join(keywords_list)
# Function to generate concise names and detailed contexts
def generate_concise_names(description):
concise_names_rule = (
"You are a creative assistant. Generate a concise name and detailed context for this image, ensuring response fits within 77 tokens. "
"No copyright, No colon (:), trademarks, privacy rights, property rights, no number, no ensuring organization and clarity. "
"No quotation marks or dashes, using commas for separation. Focuses on straightforward, richly descriptive titles without vague language or mentioning camera specifics or photography techniques. "
"Ensure the response is a single line. if it's too long, make it short and fit."
)
concise_names_prompt = f"{concise_names_rule}\nDescription: {description}"
result = call_gpt_api(concise_names_prompt, temperature=0.7, max_tokens=77).strip()
result = result.replace('Title:', '').strip()
result = result.replace('Name:', '').strip()
result = result.replace('"', '')
result = result.replace('\n', ' ')
return result
# Function to generate metadata
def generate_metadata(description, use_concise_names):
if use_concise_names and not description:
return None
keywords_rule = (
"Generate 40-49 single-word keywords for a microstock image. Ensure diversity by creatively linking "
"first ten must come from input name, the rest is related concepts (e.g., cross leads to religion, christ). For a black cat, use black, cat, etc. Avoid plurals, and it must be on the same horizontal row"
"format with commas. Don't generate numbers such as 1. Butterfly 2. Open 3. Pages 4. White"
"don't use photography techniques. No copyright, No trademarks, "
"No privacy rights, or property rights."
)
title = generate_concise_names(description) if use_concise_names else clean_metadata(description)
if not title:
return None
title = title.replace('\n', ' ')
keywords = generate_keywords_with_retries(description, keywords_rule)
metadata = {'title': title, 'keywords': keywords}
return metadata
def read_image(image_path):
description = ""
try:
if hasattr(sys, '_MEIPASS'):
exiftool_path = os.path.join(sys._MEIPASS, 'exiftool', 'exiftool.exe')
else:
exiftool_path = os.path.join(os.path.dirname(__file__), 'exiftool', 'exiftool.exe')
result = subprocess.run([exiftool_path, '-j', image_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode != 0:
return None, ""
metadata_list = json.loads(result.stdout.decode('utf-8'))
if metadata_list:
metadata = metadata_list[0]
description = (metadata.get("Caption-Abstract") or
metadata.get("ImageDescription") or
metadata.get("Description") or
metadata.get("XPComment") or
metadata.get("UserComment") or "")
if description:
description = re.split(r'Job ID:|--ar \d+:\d+', description)[0].strip()
except Exception as e:
pass
return None, description
def generate_metadata_for_images(directory, use_repeating_mode, use_concise_names, use_gpt_4o, api_key):
metadata_list = []
files = [f for f in os.listdir(directory) if f.lower().endswith(('png', 'jpg', 'jpeg'))]
title_keywords_map = {}
for filename in files:
file_path = os.path.join(directory, filename)
if use_gpt_4o:
description = call_gpt_4o_vision(file_path, api_key)
else:
_, description = read_image(file_path)
if description:
title = generate_concise_names(description) if use_concise_names else clean_metadata(description)
if use_repeating_mode and title in title_keywords_map:
keywords = title_keywords_map[title]
else:
metadata = generate_metadata(description, use_concise_names)
title_keywords_map[title] = metadata['keywords']
keywords = metadata['keywords']
metadata_list.append({'Filename': filename, 'Title': title, 'Keywords': keywords})
return metadata_list
# Streamlit UI
st.title("Metadata Generator by Gasia")
st.write("อย่าลืมเปลี่ยนเป็นภาษาอังกฤษ")
api_key = st.text_input("OpenAI API Key")
image_directory = st.text_input("Images Folder")
use_repeating_mode = st.checkbox("Enable Save Mode", value=True)
use_concise_names = st.checkbox("Generate Concise Names")
use_gpt_4o = st.checkbox("Use GPT-4o Vision")
convert_to_png = st.checkbox("Convert .JPG/.JPEG to .PNG")
if st.button("Submit"):
if not api_key or not image_directory:
st.warning("Please fill in all fields")
else:
save_settings(api_key, image_directory)
init_openai_api_key(api_key)
metadata_list = generate_metadata_for_images(image_directory, use_repeating_mode, use_concise_names, use_gpt_4o, api_key)
if convert_to_png:
for metadata in metadata_list:
if metadata['Filename'].lower().endswith(('.jpg', '.jpeg')):
metadata['Filename'] = re.sub(r'\.(jpg|jpeg)$', '.png', metadata['Filename'], flags=re.IGNORECASE)
if metadata_list:
df = pd.DataFrame(metadata_list)
st.write(df)
csv = df.to_csv(index=False).encode('utf-8')
st.download_button(
label="Download metadata as CSV",
data=csv,
file_name='image_metadata.csv',
mime='text/csv',
)
if st.button("Reset Settings"):
api_key = ''
image_directory = ''
save_settings(api_key, image_directory)
st.success("Settings have been reset to default values")
|