Spaces:
Running
on
Zero
Running
on
Zero
Refactor UI in app.py to implement a tabbed layout for the Trainer and Prompt Generator sections, enhancing organization and user experience. The update includes restructured input fields and improved accessibility for image uploads and prompt generation functionalities.
Browse files- QIE_prompt_generator.py +144 -0
- app.py +175 -138
QIE_prompt_generator.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import mimetypes
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import gradio as gr
|
| 5 |
+
from typing import Optional, Tuple
|
| 6 |
+
|
| 7 |
+
# ===== ユーティリティ =====
|
| 8 |
+
|
| 9 |
+
def file_to_data_url(path: str) -> Optional[str]:
|
| 10 |
+
"""画像ファイルを data URL に変換"""
|
| 11 |
+
if not path:
|
| 12 |
+
return None
|
| 13 |
+
p = Path(path)
|
| 14 |
+
if not p.exists():
|
| 15 |
+
return None
|
| 16 |
+
mime, _ = mimetypes.guess_type(str(p))
|
| 17 |
+
if not mime:
|
| 18 |
+
mime = "image/png"
|
| 19 |
+
with open(p, "rb") as f:
|
| 20 |
+
b64 = base64.b64encode(f.read()).decode("utf-8")
|
| 21 |
+
return f"data:{mime};base64,{b64}"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# ===== Meta Prompt =====
|
| 25 |
+
META_PROMPT = """You are an AI prompt generator for image-to-image transformation tasks in art and illustration pipelines.
|
| 26 |
+
Given two images — A (input) and B (output) — and an optional description or notes, your goal is to write a precise, structured English prompt that fully explains how A transforms into B, including both conceptual and visual rules of the transformation.
|
| 27 |
+
|
| 28 |
+
Instructions:
|
| 29 |
+
1) Write 3–6 concise sentences describing the transformation.
|
| 30 |
+
2) Begin with “The [type of A] transforms into …” or “Convert the [A] into …”.
|
| 31 |
+
3) Clearly describe:
|
| 32 |
+
- What disappears or remains (e.g., hair, clothes, shadows, lines).
|
| 33 |
+
- What structural or stylistic simplifications occur (e.g., box, sphere, guide lines).
|
| 34 |
+
- How lines, colors, or lighting change (e.g., colored → lineart, flat → shaded).
|
| 35 |
+
- Any rules for anatomy, proportion, or rendering (e.g., keep pose, maintain base colors).
|
| 36 |
+
- Background or presentation constraints (e.g., white background, no shading).
|
| 37 |
+
4) Integrate any user-provided notes naturally into the text.
|
| 38 |
+
|
| 39 |
+
Output Format:
|
| 40 |
+
- English Prompt: A full paragraph (3–6 sentences) describing the transformation from A to B.
|
| 41 |
+
- Name Suggestions: Propose 3 short, descriptive task-name candidates (e.g., Image2Body, Body2Box, Sketch2Line, Flat2Shade, Guide2Color, Light2Render, etc.).
|
| 42 |
+
- Optional Japanese Translation: Provide a brief Japanese version of the English prompt for understanding if requested.
|
| 43 |
+
|
| 44 |
+
Make the wording professional, objective, and consistent with technical art pipeline prompts.
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# ===== OpenAI 呼び出し =====
|
| 49 |
+
def call_openai_chat(api_key: str, a_data_url: Optional[str], b_data_url: Optional[str], notes: str, want_japanese: bool):
|
| 50 |
+
"""OpenAI GPT-5 API 呼び出し"""
|
| 51 |
+
if not api_key:
|
| 52 |
+
return ("", "", "(エラー:API Key が未入力です)")
|
| 53 |
+
|
| 54 |
+
try:
|
| 55 |
+
from openai import OpenAI
|
| 56 |
+
except Exception:
|
| 57 |
+
return ("", "", "(エラー:openai パッケージが見つかりません。`pip install openai` を実行してください)")
|
| 58 |
+
|
| 59 |
+
client = OpenAI(api_key=api_key)
|
| 60 |
+
model = "gpt-5"
|
| 61 |
+
|
| 62 |
+
user_content = []
|
| 63 |
+
if a_data_url:
|
| 64 |
+
user_content.append({"type": "text", "text": "Image A (input):"})
|
| 65 |
+
user_content.append({"type": "image_url", "image_url": {"url": a_data_url}})
|
| 66 |
+
if b_data_url:
|
| 67 |
+
user_content.append({"type": "text", "text": "Image B (output):"})
|
| 68 |
+
user_content.append({"type": "image_url", "image_url": {"url": b_data_url}})
|
| 69 |
+
user_content.append({
|
| 70 |
+
"type": "text",
|
| 71 |
+
"text": f"Additional notes: {notes or '(none)'}\n{'Include Japanese translation.' if want_japanese else 'No Japanese translation.'}"
|
| 72 |
+
})
|
| 73 |
+
|
| 74 |
+
try:
|
| 75 |
+
resp = client.chat.completions.create(
|
| 76 |
+
model=model,
|
| 77 |
+
messages=[
|
| 78 |
+
{"role": "system", "content": META_PROMPT},
|
| 79 |
+
{"role": "user", "content": user_content},
|
| 80 |
+
]
|
| 81 |
+
)
|
| 82 |
+
text = resp.choices[0].message.content.strip()
|
| 83 |
+
except Exception as e:
|
| 84 |
+
return ("", "", f"(APIエラー:{e})")
|
| 85 |
+
|
| 86 |
+
# 簡易パース
|
| 87 |
+
english, names, japanese = "", "", ""
|
| 88 |
+
lower = text.lower()
|
| 89 |
+
if "name suggestions:" in lower:
|
| 90 |
+
split = text.split("English Prompt:")[1].split("Name Suggestions:")
|
| 91 |
+
english = split[0].strip()
|
| 92 |
+
rest = split[1]
|
| 93 |
+
if "Japanese Translation:" in rest:
|
| 94 |
+
name_part, jp_part = rest.split("Japanese Translation:", 1)
|
| 95 |
+
names = name_part.strip()
|
| 96 |
+
japanese = jp_part.strip()
|
| 97 |
+
else:
|
| 98 |
+
names = rest.strip()
|
| 99 |
+
else:
|
| 100 |
+
english = text
|
| 101 |
+
|
| 102 |
+
if not want_japanese:
|
| 103 |
+
japanese = ""
|
| 104 |
+
|
| 105 |
+
return english, names, japanese
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# ===== Gradio UI =====
|
| 109 |
+
|
| 110 |
+
with gr.Blocks(title="A→B 変換プロンプト自動生成(GPT-5固定)") as demo:
|
| 111 |
+
gr.Markdown("""
|
| 112 |
+
# 🎨 A→B 変換プロンプト自動生成
|
| 113 |
+
画像A(入力)と画像B(出力)、補足説明を入力すると、
|
| 114 |
+
A→B の変換内容を**英語プロンプト**として自動生成し、
|
| 115 |
+
さらに**タスク名候補(3件)**を提案します。
|
| 116 |
+
モデルは `gpt-5` を使用します。
|
| 117 |
+
""")
|
| 118 |
+
|
| 119 |
+
api_key = gr.Textbox(label="OpenAI API Key", type="password", placeholder="sk-...")
|
| 120 |
+
with gr.Row():
|
| 121 |
+
img_a = gr.Image(type="filepath", label="Image A (Input)", height=300)
|
| 122 |
+
img_b = gr.Image(type="filepath", label="Image B (Output)", height=300)
|
| 123 |
+
|
| 124 |
+
notes = gr.Textbox(label="補足説明(日本語可)", lines=4, placeholder="例)髪・服・背景は消す。目は四角。鎖骨はピンク線。など", value="この画像は例であって、汎用的なプロンプトにする")
|
| 125 |
+
want_japanese = gr.Checkbox(label="日本語訳を含める", value=True)
|
| 126 |
+
run_btn = gr.Button("生成する", variant="primary")
|
| 127 |
+
|
| 128 |
+
english_out = gr.Textbox(label="English Prompt", lines=8)
|
| 129 |
+
names_out = gr.Textbox(label="Name Suggestions", lines=4)
|
| 130 |
+
japanese_out = gr.Textbox(label="日本語訳(任意)", lines=8)
|
| 131 |
+
|
| 132 |
+
def on_click(api_key_in, a_path, b_path, notes_in, ja_flag):
|
| 133 |
+
a_url = file_to_data_url(a_path) if a_path else None
|
| 134 |
+
b_url = file_to_data_url(b_path) if b_path else None
|
| 135 |
+
return call_openai_chat(api_key_in, a_url, b_url, notes_in, ja_flag)
|
| 136 |
+
|
| 137 |
+
run_btn.click(
|
| 138 |
+
fn=on_click,
|
| 139 |
+
inputs=[api_key, img_a, img_b, notes, want_japanese],
|
| 140 |
+
outputs=[english_out, names_out, japanese_out],
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
if __name__ == "__main__":
|
| 144 |
+
demo.launch()
|
app.py
CHANGED
|
@@ -11,6 +11,7 @@ import time
|
|
| 11 |
import json
|
| 12 |
|
| 13 |
import gradio as gr
|
|
|
|
| 14 |
import spaces
|
| 15 |
|
| 16 |
# Local modules
|
|
@@ -706,154 +707,190 @@ def build_ui() -> gr.Blocks:
|
|
| 706 |
}
|
| 707 |
"""
|
| 708 |
with gr.Blocks(title="Qwen-Image-Edit: Trainer", css=css) as demo:
|
| 709 |
-
gr.
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
with gr.
|
| 718 |
-
|
| 719 |
-
|
|
|
|
|
|
|
| 720 |
|
| 721 |
-
with gr.Row():
|
| 722 |
-
lr_input = gr.Textbox(label="Learning rate", value="1e-3")
|
| 723 |
-
dim_input = gr.Number(label="Network dim", value=4, precision=0)
|
| 724 |
-
seed_input = gr.Number(label="Seed", value=42, precision=0)
|
| 725 |
-
max_epochs = gr.Number(label="Max epochs", value=100, precision=0)
|
| 726 |
-
save_every = gr.Number(label="Save every N epochs", value=10, precision=0)
|
| 727 |
-
|
| 728 |
-
with gr.Accordion("Target Image", elem_classes=["pad-section_0"]):
|
| 729 |
-
with gr.Group():
|
| 730 |
-
with gr.Row():
|
| 731 |
-
images_input = gr.File(label="Upload target images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 732 |
-
main_gallery = gr.Gallery(label="Target preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 733 |
-
with gr.Column(scale=1):
|
| 734 |
with gr.Row():
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
gr.
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
- Control 0 は必須、Control 1〜7 は任意。コントロール画像が1枚だけのときは、すべてのターゲット画像に適用します。
|
| 744 |
-
""")
|
| 745 |
-
|
| 746 |
-
# control_0 is required and shown outside the accordion
|
| 747 |
-
with gr.Accordion("Control 0", elem_classes=["pad-section_1"]):
|
| 748 |
-
with gr.Group():
|
| 749 |
-
with gr.Row():
|
| 750 |
-
ctrl0_files = gr.File(label="Upload control_0 images (required)", file_count="multiple", type="filepath", height=220, scale=3)
|
| 751 |
-
ctrl0_gallery = gr.Gallery(label="control_0 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 752 |
-
with gr.Column(scale=1):
|
| 753 |
with gr.Row():
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
with gr.Row():
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
|
|
|
|
|
|
| 773 |
with gr.Row():
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
with gr.
|
| 782 |
with gr.Row():
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
| 789 |
-
|
| 790 |
-
with gr.
|
| 791 |
with gr.Row():
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
with gr.
|
| 800 |
with gr.Row():
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
with gr.
|
| 809 |
with gr.Row():
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
with gr.
|
| 818 |
with gr.Row():
|
| 819 |
-
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
| 824 |
-
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
| 831 |
-
|
| 832 |
-
|
| 833 |
-
|
| 834 |
-
|
| 835 |
-
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
|
| 839 |
-
|
| 840 |
-
|
| 841 |
-
|
| 842 |
-
|
| 843 |
-
|
| 844 |
-
|
| 845 |
-
ctrl0_files,
|
| 846 |
-
ctrl1_files,
|
| 847 |
-
ctrl2_files,
|
| 848 |
-
ctrl3_files,
|
| 849 |
-
ctrl4_files,
|
| 850 |
-
ctrl5_files,
|
| 851 |
-
ctrl6_files,
|
| 852 |
-
ctrl7_files,
|
| 853 |
-
|
| 854 |
-
|
| 855 |
-
|
| 856 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 857 |
|
| 858 |
return demo
|
| 859 |
|
|
|
|
| 11 |
import json
|
| 12 |
|
| 13 |
import gradio as gr
|
| 14 |
+
import importlib
|
| 15 |
import spaces
|
| 16 |
|
| 17 |
# Local modules
|
|
|
|
| 707 |
}
|
| 708 |
"""
|
| 709 |
with gr.Blocks(title="Qwen-Image-Edit: Trainer", css=css) as demo:
|
| 710 |
+
with gr.Tabs() as tabs:
|
| 711 |
+
with gr.TabItem("Training"):
|
| 712 |
+
gr.Markdown("""
|
| 713 |
+
# Qwen-Image-Edit Trainer
|
| 714 |
+
学習に使う画像をアップロードし、必要ならファイル名の前後にある共通の文字(prefix/suffix)を指定して、
|
| 715 |
+
自動でデータセットを作成し学習を開始します。難しい操作は不要です。
|
| 716 |
+
""")
|
| 717 |
+
|
| 718 |
+
with gr.Accordion("Settings", elem_classes=["pad-section"]):
|
| 719 |
+
with gr.Group():
|
| 720 |
+
with gr.Row():
|
| 721 |
+
output_name = gr.Textbox(label="OUTPUT NAME", placeholder="my_lora_output", lines=1)
|
| 722 |
+
caption = gr.Textbox(label="CAPTION", placeholder="A photo of ...", lines=2)
|
| 723 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 724 |
with gr.Row():
|
| 725 |
+
lr_input = gr.Textbox(label="Learning rate", value="1e-3")
|
| 726 |
+
dim_input = gr.Number(label="Network dim", value=4, precision=0)
|
| 727 |
+
seed_input = gr.Number(label="Seed", value=42, precision=0)
|
| 728 |
+
max_epochs = gr.Number(label="Max epochs", value=100, precision=0)
|
| 729 |
+
save_every = gr.Number(label="Save every N epochs", value=10, precision=0)
|
| 730 |
+
|
| 731 |
+
with gr.Accordion("Target Image", elem_classes=["pad-section_0"]):
|
| 732 |
+
with gr.Group():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 733 |
with gr.Row():
|
| 734 |
+
images_input = gr.File(label="Upload target images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 735 |
+
main_gallery = gr.Gallery(label="Target preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 736 |
+
with gr.Column(scale=1):
|
| 737 |
+
with gr.Row():
|
| 738 |
+
main_prefix = gr.Textbox(label="Target prefix", placeholder="e.g., IMG_")
|
| 739 |
+
main_suffix = gr.Textbox(label="Target suffix", placeholder="e.g., _v2")
|
| 740 |
+
with gr.Accordion("prefix/sufixについて", open=False):
|
| 741 |
+
gr.Markdown("""
|
| 742 |
+
ファイルの同名判定のため、画像のファイル名から共通の先頭/末尾文字を取り除く指定(例: IMG_ や _v2)
|
| 743 |
+
- まずターゲット画像のファイル名(拡張子なし)から、指定した Target prefix/suffix を取り除いたものを key とします。
|
| 744 |
+
- 各コントロールは「付加」規則で、期待名 = control_prefix_i + key + control_suffix_i + ".png" を探して対応付けます。
|
| 745 |
+
- アップロード時に画像は自動で .png に変換して保存します(元のファイル名のベースは維持)。
|
| 746 |
+
- Control 0 は必須、Control 1〜7 は任意。コントロール画像が1枚だけのときは、すべてのターゲット画像に適用します。
|
| 747 |
+
""")
|
| 748 |
+
|
| 749 |
+
# control_0 is required and shown outside the accordion
|
| 750 |
+
with gr.Accordion("Control 0", elem_classes=["pad-section_1"]):
|
| 751 |
+
with gr.Group():
|
| 752 |
with gr.Row():
|
| 753 |
+
ctrl0_files = gr.File(label="Upload control_0 images (required)", file_count="multiple", type="filepath", height=220, scale=3)
|
| 754 |
+
ctrl0_gallery = gr.Gallery(label="control_0 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 755 |
+
with gr.Column(scale=1):
|
| 756 |
+
with gr.Row():
|
| 757 |
+
ctrl0_prefix = gr.Textbox(label="control_0 prefix", placeholder="e.g., C0_")
|
| 758 |
+
ctrl0_suffix = gr.Textbox(label="control_0 suffix", placeholder="e.g., _mask")
|
| 759 |
+
|
| 760 |
+
# Optional controls start from 1, accordion closed by default
|
| 761 |
+
with gr.Accordion("Control 1", open=False, elem_classes=["pad-section_0"]):
|
| 762 |
+
with gr.Group():
|
| 763 |
with gr.Row():
|
| 764 |
+
ctrl1_files = gr.File(label="Upload control_1 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 765 |
+
ctrl1_gallery = gr.Gallery(label="control_1 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 766 |
+
with gr.Column(scale=1):
|
| 767 |
+
with gr.Row():
|
| 768 |
+
ctrl1_prefix = gr.Textbox(label="control_1 prefix", placeholder="")
|
| 769 |
+
ctrl1_suffix = gr.Textbox(label="control_1 suffix", placeholder="")
|
| 770 |
+
with gr.Accordion("Control 2", open=False, elem_classes=["pad-section_1"]):
|
| 771 |
+
with gr.Group():
|
| 772 |
with gr.Row():
|
| 773 |
+
ctrl2_files = gr.File(label="Upload control_2 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 774 |
+
ctrl2_gallery = gr.Gallery(label="control_2 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 775 |
+
with gr.Column(scale=1):
|
| 776 |
+
with gr.Row():
|
| 777 |
+
ctrl2_prefix = gr.Textbox(label="control_2 prefix", placeholder="")
|
| 778 |
+
ctrl2_suffix = gr.Textbox(label="control_2 suffix", placeholder="")
|
| 779 |
+
with gr.Accordion("Control 3", open=False, elem_classes=["pad-section_0"]):
|
| 780 |
+
with gr.Group():
|
| 781 |
with gr.Row():
|
| 782 |
+
ctrl3_files = gr.File(label="Upload control_3 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 783 |
+
ctrl3_gallery = gr.Gallery(label="control_3 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 784 |
+
with gr.Column(scale=1):
|
| 785 |
+
with gr.Row():
|
| 786 |
+
ctrl3_prefix = gr.Textbox(label="control_3 prefix", placeholder="")
|
| 787 |
+
ctrl3_suffix = gr.Textbox(label="control_3 suffix", placeholder="")
|
| 788 |
+
with gr.Accordion("Control 4", open=False, elem_classes=["pad-section_1"]):
|
| 789 |
+
with gr.Group():
|
| 790 |
with gr.Row():
|
| 791 |
+
ctrl4_files = gr.File(label="Upload control_4 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 792 |
+
ctrl4_gallery = gr.Gallery(label="control_4 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 793 |
+
with gr.Column(scale=1):
|
| 794 |
+
with gr.Row():
|
| 795 |
+
ctrl4_prefix = gr.Textbox(label="control_4 prefix", placeholder="")
|
| 796 |
+
ctrl4_suffix = gr.Textbox(label="control_4 suffix", placeholder="")
|
| 797 |
+
with gr.Accordion("Control 5", open=False, elem_classes=["pad-section_0"]):
|
| 798 |
+
with gr.Group():
|
| 799 |
with gr.Row():
|
| 800 |
+
ctrl5_files = gr.File(label="Upload control_5 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 801 |
+
ctrl5_gallery = gr.Gallery(label="control_5 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 802 |
+
with gr.Column(scale=1):
|
| 803 |
+
with gr.Row():
|
| 804 |
+
ctrl5_prefix = gr.Textbox(label="control_5 prefix", placeholder="")
|
| 805 |
+
ctrl5_suffix = gr.Textbox(label="control_5 suffix", placeholder="")
|
| 806 |
+
with gr.Accordion("Control 6", open=False, elem_classes=["pad-section_1"]):
|
| 807 |
+
with gr.Group():
|
| 808 |
with gr.Row():
|
| 809 |
+
ctrl6_files = gr.File(label="Upload control_6 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 810 |
+
ctrl6_gallery = gr.Gallery(label="control_6 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 811 |
+
with gr.Column(scale=1):
|
| 812 |
+
with gr.Row():
|
| 813 |
+
ctrl6_prefix = gr.Textbox(label="control_6 prefix", placeholder="")
|
| 814 |
+
ctrl6_suffix = gr.Textbox(label="control_6 suffix", placeholder="")
|
| 815 |
+
with gr.Accordion("Control 7", open=False, elem_classes=["pad-section_0"]):
|
| 816 |
+
with gr.Group():
|
| 817 |
+
with gr.Row():
|
| 818 |
+
ctrl7_files = gr.File(label="Upload control_7 images", file_count="multiple", type="filepath", height=220, scale=3)
|
| 819 |
+
ctrl7_gallery = gr.Gallery(label="control_7 preview", columns=4, height=220, object_fit='contain', preview=True, scale=3)
|
| 820 |
+
with gr.Column(scale=1):
|
| 821 |
+
with gr.Row():
|
| 822 |
+
ctrl7_prefix = gr.Textbox(label="control_7 prefix", placeholder="")
|
| 823 |
+
ctrl7_suffix = gr.Textbox(label="control_7 suffix", placeholder="")
|
| 824 |
+
|
| 825 |
+
# Models root / OUTPUT_DIR_BASE / DATASET_CONFIG are auto-resolved at runtime; no user input needed.
|
| 826 |
+
|
| 827 |
+
run_btn = gr.Button("Start Training", variant="primary")
|
| 828 |
+
logs = gr.Textbox(label="Logs", lines=20)
|
| 829 |
+
ckpt_files = gr.Files(label="Checkpoints (live)", interactive=False)
|
| 830 |
+
|
| 831 |
+
# moved max_epochs/save_every above next to OUTPUT NAME
|
| 832 |
+
|
| 833 |
+
# Wire previews
|
| 834 |
+
images_input.change(fn=_files_to_gallery, inputs=images_input, outputs=main_gallery)
|
| 835 |
+
ctrl0_files.change(fn=_files_to_gallery, inputs=ctrl0_files, outputs=ctrl0_gallery)
|
| 836 |
+
ctrl1_files.change(fn=_files_to_gallery, inputs=ctrl1_files, outputs=ctrl1_gallery)
|
| 837 |
+
ctrl2_files.change(fn=_files_to_gallery, inputs=ctrl2_files, outputs=ctrl2_gallery)
|
| 838 |
+
ctrl3_files.change(fn=_files_to_gallery, inputs=ctrl3_files, outputs=ctrl3_gallery)
|
| 839 |
+
ctrl4_files.change(fn=_files_to_gallery, inputs=ctrl4_files, outputs=ctrl4_gallery)
|
| 840 |
+
ctrl5_files.change(fn=_files_to_gallery, inputs=ctrl5_files, outputs=ctrl5_gallery)
|
| 841 |
+
ctrl6_files.change(fn=_files_to_gallery, inputs=ctrl6_files, outputs=ctrl6_gallery)
|
| 842 |
+
ctrl7_files.change(fn=_files_to_gallery, inputs=ctrl7_files, outputs=ctrl7_gallery)
|
| 843 |
+
|
| 844 |
+
run_btn.click(
|
| 845 |
+
fn=run_training,
|
| 846 |
+
inputs=[
|
| 847 |
+
output_name, caption, images_input, main_prefix, main_suffix,
|
| 848 |
+
ctrl0_files, ctrl0_prefix, ctrl0_suffix,
|
| 849 |
+
ctrl1_files, ctrl1_prefix, ctrl1_suffix,
|
| 850 |
+
ctrl2_files, ctrl2_prefix, ctrl2_suffix,
|
| 851 |
+
ctrl3_files, ctrl3_prefix, ctrl3_suffix,
|
| 852 |
+
ctrl4_files, ctrl4_prefix, ctrl4_suffix,
|
| 853 |
+
ctrl5_files, ctrl5_prefix, ctrl5_suffix,
|
| 854 |
+
ctrl6_files, ctrl6_prefix, ctrl6_suffix,
|
| 855 |
+
ctrl7_files, ctrl7_prefix, ctrl7_suffix,
|
| 856 |
+
lr_input, dim_input, seed_input, max_epochs, save_every,
|
| 857 |
+
],
|
| 858 |
+
outputs=[logs, ckpt_files],
|
| 859 |
+
)
|
| 860 |
+
|
| 861 |
+
with gr.TabItem("Prompt Generator"):
|
| 862 |
+
gr.Markdown("""
|
| 863 |
+
# 🎨 A→B 変換プロンプト自動生成
|
| 864 |
+
画像A(入力)と画像B(出力)、補足説明を入力すると、
|
| 865 |
+
A→B の変換内容を英語プロンプトとして自動生成し、タスク名候補(3件)も提案します。
|
| 866 |
+
モデルは `gpt-5` を使用します。
|
| 867 |
+
""")
|
| 868 |
+
|
| 869 |
+
api_key_pg = gr.Textbox(label="OpenAI API Key", type="password", placeholder="sk-...")
|
| 870 |
+
with gr.Row():
|
| 871 |
+
img_a_pg = gr.Image(type="filepath", label="Image A (Input)", height=300)
|
| 872 |
+
img_b_pg = gr.Image(type="filepath", label="Image B (Output)", height=300)
|
| 873 |
+
|
| 874 |
+
notes_pg = gr.Textbox(label="補足説明(日本語可)", lines=4, value="この画像は例であって、汎用的なプロンプトにする")
|
| 875 |
+
want_japanese_pg = gr.Checkbox(label="日本語訳を含める", value=True)
|
| 876 |
+
run_btn_pg = gr.Button("生成する", variant="primary")
|
| 877 |
+
|
| 878 |
+
english_out_pg = gr.Textbox(label="English Prompt", lines=8)
|
| 879 |
+
names_out_pg = gr.Textbox(label="Name Suggestions", lines=4)
|
| 880 |
+
japanese_out_pg = gr.Textbox(label="日本語訳(任意)", lines=8)
|
| 881 |
+
|
| 882 |
+
def _on_click_prompt(api_key_in, a_path, b_path, notes_in, ja_flag):
|
| 883 |
+
# Lazy import to avoid constructing extra Blocks at startup
|
| 884 |
+
qpg = importlib.import_module("QIE_prompt_generator")
|
| 885 |
+
a_url = qpg.file_to_data_url(a_path) if a_path else None
|
| 886 |
+
b_url = qpg.file_to_data_url(b_path) if b_path else None
|
| 887 |
+
return qpg.call_openai_chat(api_key_in, a_url, b_url, notes_in, ja_flag)
|
| 888 |
+
|
| 889 |
+
run_btn_pg.click(
|
| 890 |
+
fn=_on_click_prompt,
|
| 891 |
+
inputs=[api_key_pg, img_a_pg, img_b_pg, notes_pg, want_japanese_pg],
|
| 892 |
+
outputs=[english_out_pg, names_out_pg, japanese_out_pg],
|
| 893 |
+
)
|
| 894 |
|
| 895 |
return demo
|
| 896 |
|