Spaces:
Running
Running
File size: 20,527 Bytes
a20b7df 88aafac a20b7df 3b3e0c8 9cf392a a20b7df 46b912b 00e31d3 a6ac76e 00e31d3 a20b7df fb13f87 a20b7df a6ac76e a20b7df a6ac76e a20b7df a6ac76e d9fb779 a6ac76e d9fb779 a6ac76e d9fb779 a20b7df 3b3e0c8 a20b7df 3b3e0c8 a20b7df 3b3e0c8 a20b7df a6ac76e 88aafac a6ac76e 88aafac a6ac76e 88aafac 00e31d3 3b3e0c8 a6ac76e 88aafac 3b3e0c8 88aafac 46b912b a20b7df 38f52fd a20b7df 9cf392a 46b912b a6ac76e 88aafac 9cf392a 38f52fd 9cf392a 9180f04 88aafac a6ac76e 9cf392a 88aafac 9cf392a 38f52fd a20b7df 88aafac 9cf392a a20b7df 46b912b 3b3e0c8 00e31d3 38f52fd a6ac76e 3b3e0c8 a20b7df a6ac76e a20b7df 93bbea8 a20b7df a6ac76e a20b7df a6ac76e 00e31d3 a6ac76e 00e31d3 a6ac76e 00e31d3 a6ac76e a20b7df a6ac76e 38f52fd a20b7df a6ac76e 88aafac 9730020 a20b7df 46b912b a6ac76e 46b912b a20b7df a6ac76e a20b7df a6ac76e a20b7df a6ac76e 38f52fd a6ac76e 38f52fd a6ac76e a20b7df 63c2525 a6ac76e 88aafac a6ac76e a20b7df a6ac76e 3b3e0c8 63c2525 a6ac76e 3b3e0c8 46b912b a6ac76e 46b912b a6ac76e 46b912b a6ac76e 46b912b a6ac76e 46b912b a6ac76e a20b7df 46b912b a20b7df fb13f87 |
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 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
import gradio as gr
import random
import glob
import os
import requests
from openai import OpenAI
from dotenv import load_dotenv
# 加载环境变量
load_dotenv()
# ========== 默认选项和数据 ==========
EXPRESSIONS = [
"smiling", "determined", "surprised", "serene", "smug", "thinking",
"looking back", "laughing", "angry", "pensive", "confident",
"grinning", "thoughtful", "sad tears", "bewildered"
]
ITEMS = [
"magic wand", "sword", "flower", "book of spells", "earrings", "loincloth",
"slippers", "ancient scroll", "music instrument", "shield", "dagger",
"headband", "leg ties", "staff", "potion", "crystal ball", "anklet",
"ribbon", "lantern", "amulet", "ring"
]
OTHER_DETAILS = [
"sparkles", "magical aura", "lens flare", "fireworks in the background",
"smoke effects", "light trails", "falling leaves", "glowing embers",
"floating particles", "rays of light", "shimmering mist", "ethereal glow"
]
SCENES = [
"sunset beach", "rainy city street at night", "floating ash land",
"particles magic world", "high blue sky", "top of the building",
"fantasy forest with glowing mushrooms", "futuristic skyline at dawn",
"abandoned castle", "snowy mountain peak", "desert ruins", "underwater city",
"enchanted meadow", "haunted mansion", "steampunk marketplace", "glacial cavern"
]
CAMERA_ANGLES = [
"low-angle shot", "close-up shot", "bird's-eye view", "wide-angle shot",
"over-the-shoulder shot", "extreme close-up", "panoramic view",
"dynamic tracking shot", "fisheye view", "point-of-view shot"
]
QUALITY_PROMPTS = [
"cinematic lighting", "sharp shadow", "award-winning", "masterpiece",
"vivid colors", "high dynamic range", "immersive", "studio quality",
"fine art", "dreamlike", "8K", "HD", "high quality", "best quality",
"artistic", "vibrant"
]
# Hugging Face DTR 数据集路径(示例,若不可用请忽略)
DTR_DATASET_PATTERN = "https://huggingface.co/datasets/X779/Danbooruwildcards/resolve/main/*DTR*.txt"
# ========== 工具函数 ==========
def load_candidates_from_files(files, excluded_tags=None):
"""
从多个文件中加载候选项,同时排除用户不想要的标签(精确匹配)。
"""
if excluded_tags is None:
excluded_tags = set()
all_lines = []
if files:
for file in files:
if isinstance(file, str):
# 说明是路径字符串
with open(file, "r", encoding="utf-8") as f:
lines = [line.strip() for line in f if line.strip()]
filtered = [l for l in lines if l not in excluded_tags]
all_lines.extend(filtered)
else:
# 说明是一个上传的 file-like 对象
file_data = file.read().decode("utf-8", errors="ignore")
lines = [line.strip() for line in file_data.splitlines() if line.strip()]
filtered = [l for l in lines if l not in excluded_tags]
all_lines.extend(filtered)
return all_lines
def get_random_items(candidates, num_items=1):
"""
从候选项中随机选取指定数量的选项。
"""
return random.sample(candidates, min(num_items, len(candidates))) if candidates else []
def load_dtr_from_huggingface(excluded_tags=None):
"""
从 Hugging Face 数据集中加载所有包含 "DTR" 的文件内容,同时排除不需要的tag。
"""
if excluded_tags is None:
excluded_tags = set()
try:
response = requests.get(DTR_DATASET_PATTERN)
response.raise_for_status()
lines = response.text.splitlines()
# 只过滤精确匹配
lines = [l for l in lines if l not in excluded_tags]
return lines
except Exception as e:
print(f"Error loading DTR dataset: {e}")
return []
def generate_natural_language_description(tags, api_key=None, base_url=None, model="gpt-4"):
"""
使用 OpenAI GPT 或 DeepSeek API 生成自然语言描述。
"""
if not api_key:
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
return "Error: No API Key provided and none found in environment variables."
# 将 dict 转成可读字符串
tag_descriptions = "\n".join([
f"{key}: {', '.join(value) if isinstance(value, list) else value}"
for key, value in tags.items() if value
])
try:
client = OpenAI(api_key=api_key, base_url=base_url) if base_url else OpenAI(api_key=api_key)
response = client.chat.completions.create(
messages=[
{
"role": "system",
"content": (
"You are a creative assistant that generates detailed and imaginative scene descriptions for AI generation prompts. "
"Focus on the details provided and incorporate them into a cohesive narrative. "
"Use at least three sentences but no more than five sentences."
),
},
{
"role": "user",
"content": f"Here are the tags and details:\n{tag_descriptions}\nPlease generate a vivid, imaginative scene description.",
},
],
model=model,
)
return response.choices[0].message.content.strip()
except Exception as e:
return f"GPT generation failed. Error: {e}"
# ========== 核心函数:随机生成 prompt ==========
def generate_prompt(
action_file, style_file, artist_files, character_files, dtr_enabled, api_key, selected_categories,
expression_count, item_count, detail_count, scene_count, angle_count, quality_count, action_count, style_count,
artist_count, use_deepseek, deepseek_key, user_custom_tags, excluded_tags
):
"""
生成随机提示词和描述。
"""
# 处理排除 Tags(逗号分隔 -> 去重 set)
excluded_set = set(
[tag.strip() for tag in excluded_tags.split(",") if tag.strip()]
) if excluded_tags else set()
# 从文件中加载可选 action、style、artist、character
actions = get_random_items(load_candidates_from_files([action_file], excluded_set) if action_file else [], action_count)
styles = get_random_items(load_candidates_from_files([style_file], excluded_set) if style_file else [], style_count)
artists = get_random_items(load_candidates_from_files(artist_files, excluded_set) if artist_files else [], artist_count)
characters = get_random_items(load_candidates_from_files(character_files, excluded_set) if character_files else [], 1)
# 处理 DTR
dtr_candidates = get_random_items(load_dtr_from_huggingface(excluded_set) if dtr_enabled else [], 1)
# 处理预设列表中的随机筛选
filtered_expressions = [e for e in EXPRESSIONS if e not in excluded_set]
filtered_items = [i for i in ITEMS if i not in excluded_set]
filtered_details = [d for d in OTHER_DETAILS if d not in excluded_set]
filtered_scenes = [s for s in SCENES if s not in excluded_set]
filtered_angles = [c for c in CAMERA_ANGLES if c not in excluded_set]
filtered_quality = [q for q in QUALITY_PROMPTS if q not in excluded_set]
# 随机抽取
random_expression = get_random_items(filtered_expressions, expression_count)
random_items = get_random_items(filtered_items, item_count)
random_details = get_random_items(filtered_details, detail_count)
random_scenes = get_random_items(filtered_scenes, scene_count)
random_angles = get_random_items(filtered_angles, angle_count)
random_quality = get_random_items(filtered_quality, quality_count)
number_of_characters = ", ".join(selected_categories) if selected_categories else []
# 整理为字典
tags = {
"number_of_characters": [number_of_characters] if number_of_characters else [],
"character_name": characters,
"artist_prompt": [f"(artist:{', '.join(artists)})"] if artists else [],
"style": styles,
"scene": random_scenes,
"camera_angle": random_angles,
"action": actions,
"expression": random_expression,
"items": random_items,
"other_details": random_details,
"quality_prompts": random_quality,
"dtr": dtr_candidates,
}
# 如果用户有自定义输入
if user_custom_tags.strip():
tags["custom_tags"] = [t.strip() for t in user_custom_tags.split(",") if t.strip()]
# 生成自然语言描述
if use_deepseek:
description = generate_natural_language_description(tags, api_key=deepseek_key, base_url="https://api.deepseek.com", model="deepseek-chat")
else:
description = generate_natural_language_description(tags, api_key=api_key)
# 整理最终 Tags(flatten 并去重)
tags_list = []
for v in tags.values():
if isinstance(v, list):
tags_list.extend(v)
else:
tags_list.append(v)
# 去重保持顺序
seen = set()
final_tags_list = []
for t in tags_list:
if t not in seen and t:
seen.add(t)
final_tags_list.append(t)
final_tags = ", ".join(final_tags_list)
# 默认 Combined = Tags + Description
combined_output = f"{final_tags}\n\n{description}"
return final_tags, description, combined_output
# ========== 部分更新:只根据用户修改后的 tags_text 生成新的描述和合并输出 ==========
def update_description(tags_text, api_key, use_deepseek, deepseek_key):
"""
只根据用户提供的 tags_text 生成描述和合并输出。
不再重新随机抽取,以免破坏用户手动修改过的 Tags。
"""
if not api_key and not deepseek_key:
# 没有提供任意可用 API Key
return "(No API Key provided)", f"{tags_text}\n\n(No API Key provided)"
# 构造给 GPT 的 prompt
user_prompt = (
"You are a creative assistant that generates detailed, imaginative scene descriptions for AI generation.\n"
"Below is the user's current tags (prompt elements). "
"Generate a new descriptive text (3-5 sentences) that incorporates these tags.\n\n"
f"User Tags: {tags_text}\n"
"Please generate a vivid, imaginative scene description."
)
try:
if use_deepseek:
# 调用 DeepSeek
client = OpenAI(api_key=deepseek_key, base_url="https://api.deepseek.com")
model = "deepseek-chat"
else:
# 调用 OpenAI
client = OpenAI(api_key=api_key)
model = "gpt-4" # 或其他可用模型,比如 "gpt-3.5-turbo"
response = client.chat.completions.create(
messages=[
{
"role": "system",
"content": "You are a creative assistant that generates imaginative scene descriptions..."
},
{
"role": "user",
"content": user_prompt,
},
],
model=model,
)
new_description = response.choices[0].message.content.strip()
except Exception as e:
new_description = f"(GPT generation failed: {e})"
new_combined_output = f"{tags_text}\n\n{new_description}"
return new_description, new_combined_output
# ========== 翻译功能:将 combined_output 翻译成用户选定语言 ==========
def translate_combined_output(combined_text, target_language, api_key, use_deepseek, deepseek_key):
"""
使用 GPT 或 DeepSeek API,将 combined_text 翻译成 target_language。
"""
if not api_key and not deepseek_key:
return "(No API Key provided)"
# 简单用 GPT 做翻译,也可改成其他翻译 API
translation_prompt = (
f"You are a professional translator. Please translate the following text into {target_language}.\n\n"
f"{combined_text}"
)
try:
if use_deepseek:
# 调用 DeepSeek
client = OpenAI(api_key=deepseek_key, base_url="https://api.deepseek.com")
model = "deepseek-chat"
else:
# 调用 OpenAI
client = OpenAI(api_key=api_key)
model = "gpt-3.5-turbo" # 或者别的模型
response = client.chat.completions.create(
messages=[
{"role": "system", "content": "You are a professional translator."},
{"role": "user", "content": translation_prompt},
],
model=model,
)
translated_text = response.choices[0].message.content.strip()
except Exception as e:
translated_text = f"(Translation failed: {e})"
return translated_text
# ========== 收藏功能:最多存 3 条 ==========
def add_to_favorites(combined_output, current_favorites):
"""
将当前生成的 combined_output 添加到收藏列表中(最多存 3 条)。
"""
current_favorites.append(combined_output)
# 如果超过3条,移除最早的一条
if len(current_favorites) > 3:
current_favorites.pop(0)
# 格式化输出
favorites_text = "\n\n".join(
[f"[Favorite {i+1}]\n{fav}" for i, fav in enumerate(current_favorites)]
)
return favorites_text, current_favorites
# ========== Gradio 界面 ==========
def gradio_interface():
"""
定义 Gradio 应用界面。
"""
with gr.Blocks() as demo:
gr.Markdown("## Random Prompt Generator with Adjustable Tag Counts (Enhanced)")
# 用于存储收藏内容的状态(最多缓存3条)
favorites_state = gr.State([])
with gr.Row():
# 左侧:文件上传、参数选择、排除/自定义输入
with gr.Column(scale=1):
api_key_input = gr.Textbox(
label="OpenAI API Key (可选)",
placeholder="sk-...",
type="password"
)
deepseek_key_input = gr.Textbox(
label="DeepSeek API Key (可选)",
placeholder="sk-...",
type="password"
)
use_deepseek = gr.Checkbox(label="Use DeepSeek API")
dtr_enabled = gr.Checkbox(label="Enable DTR (如不可用请勿勾选)")
with gr.Group():
gr.Markdown("**上传文件 (可选):**")
action_file = gr.File(label="Action File", file_types=[".txt"])
style_file = gr.File(label="Style File", file_types=[".txt"])
artist_files = gr.Files(label="Artist Files", file_types=[".txt"])
character_files = gr.Files(label="Character Files", file_types=[".txt"])
selected_categories = gr.CheckboxGroup(
["1boy", "1girl", "furry", "mecha", "fantasy monster", "animal", "still life"],
label="Choose Character Categories"
)
excluded_tags = gr.Textbox(
label="排除 Tags (逗号分隔)",
placeholder="如:angry, sword"
)
user_custom_tags = gr.Textbox(
label="自定义附加 Tags (逗号分隔)",
placeholder="如:glowing eyes, giant wings"
)
with gr.Group():
gr.Markdown("**随机数量设置:**")
expression_count = gr.Slider(label="Number of Expressions", minimum=0, maximum=10, step=1, value=1)
item_count = gr.Slider(label="Number of Items", minimum=0, maximum=10, step=1, value=1)
detail_count = gr.Slider(label="Number of Other Details", minimum=0, maximum=10, step=1, value=1)
scene_count = gr.Slider(label="Number of Scenes", minimum=0, maximum=10, step=1, value=1)
angle_count = gr.Slider(label="Number of Camera Angles", minimum=0, maximum=10, step=1, value=1)
quality_count = gr.Slider(label="Number of Quality Prompts", minimum=0, maximum=10, step=1, value=1)
action_count = gr.Slider(label="Number of Actions", minimum=1, maximum=10, step=1, value=1)
style_count = gr.Slider(label="Number of Styles", minimum=1, maximum=10, step=1, value=1)
artist_count = gr.Slider(label="Number of Artists", minimum=1, maximum=10, step=1, value=1)
# 右侧:生成按钮 + 生成结果 + 收藏 + 翻译
with gr.Column(scale=2):
generate_button = gr.Button("Generate Prompt", variant="primary")
tags_output = gr.Textbox(
label="Generated Tags",
placeholder="等待生成...",
lines=4,
interactive=True
)
description_output = gr.Textbox(
label="Generated Description",
placeholder="等待生成...",
lines=4,
interactive=True
)
combined_output = gr.Textbox(
label="Combined Output: Tags + Description",
placeholder="等待生成...",
lines=6
)
# 新增一个按钮,只更新 description 和 combined
update_desc_button = gr.Button("Update Description Only")
# 翻译相关
with gr.Row():
target_language = gr.Dropdown(
choices=["English", "Chinese", "Japanese"],
value="English",
label="Target Language"
)
translate_button = gr.Button("Translate to selected language")
translated_output = gr.Textbox(
label="Translated Output",
placeholder="等待翻译...",
lines=6
)
# 收藏
with gr.Row():
favorite_button = gr.Button("收藏本次结果")
favorites_box = gr.Textbox(
label="收藏夹 (最多 3 条)",
placeholder="暂无收藏",
lines=6
)
# 点击“Generate Prompt”按钮
generate_button.click(
generate_prompt,
inputs=[
action_file, style_file, artist_files, character_files,
dtr_enabled, api_key_input, selected_categories,
expression_count, item_count, detail_count, scene_count,
angle_count, quality_count, action_count, style_count,
artist_count, use_deepseek, deepseek_key_input,
user_custom_tags, excluded_tags
],
outputs=[tags_output, description_output, combined_output],
)
# 点击“Update Description Only”按钮
update_desc_button.click(
update_description,
inputs=[
tags_output, # 用户在文本框里编辑后的 Tags
api_key_input,
use_deepseek,
deepseek_key_input,
],
outputs=[description_output, combined_output],
)
# 点击“Translate to selected language”按钮
translate_button.click(
fn=translate_combined_output,
inputs=[
combined_output, # 要翻译的源文本
target_language,
api_key_input,
use_deepseek,
deepseek_key_input
],
outputs=[translated_output],
)
# 收藏按钮点击事件
favorite_button.click(
fn=add_to_favorites,
inputs=[combined_output, favorites_state],
outputs=[favorites_box, favorites_state],
)
return demo
# 启动 Gradio 应用
if __name__ == "__main__":
gradio_interface().launch() |