Merge f950594e8a into ae05379cc9
This commit is contained in:
commit
4087505661
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"version": "0.0.1",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Fooocus WebUI",
|
||||
"runtimeExecutable": "python",
|
||||
"runtimeArgs": ["launch.py"],
|
||||
"port": 7865
|
||||
},
|
||||
{
|
||||
"name": "Fooocus WebUI (ブラウザ自動起動)",
|
||||
"runtimeExecutable": "python",
|
||||
"runtimeArgs": ["launch.py", "--in-browser"],
|
||||
"port": 7865
|
||||
},
|
||||
{
|
||||
"name": "Fooocus WebUI (アップデート付き起動)",
|
||||
"runtimeExecutable": "python",
|
||||
"runtimeArgs": ["entry_with_update.py"],
|
||||
"port": 7865
|
||||
},
|
||||
{
|
||||
"name": "Prompt Forge Bridge (単独テスト)",
|
||||
"runtimeExecutable": "python",
|
||||
"runtimeArgs": ["-c", "import prompt_forge_bridge; import time; prompt_forge_bridge.start_bridge(); print('Bridge running on http://127.0.0.1:8080'); time.sleep(86400)"],
|
||||
"port": 8080
|
||||
}
|
||||
]
|
||||
}
|
||||
123
css/style.css
123
css/style.css
|
|
@ -415,4 +415,127 @@ div:has(> #positive_prompt) {
|
|||
|
||||
#inpaint_brush_color input[type=color]{
|
||||
background: none;
|
||||
}
|
||||
|
||||
/* ── ブラッシュアップ コンパクトボタン ── */
|
||||
.brushup_btn_col button {
|
||||
padding: 3px 6px !important;
|
||||
font-size: 11px !important;
|
||||
min-height: 28px !important;
|
||||
height: 28px !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
|
||||
#brushup_bar {
|
||||
align-items: flex-start !important;
|
||||
gap: 6px !important;
|
||||
}
|
||||
|
||||
/* ── OpenCV エディットパネル ── */
|
||||
#edit_panel {
|
||||
border-top: 1px solid rgba(255,255,255,0.08) !important;
|
||||
padding-top: 8px !important;
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
/* プリセットボタン */
|
||||
#edit_presets button, .preset_btn button {
|
||||
padding: 3px 5px !important;
|
||||
font-size: 10px !important;
|
||||
min-height: 26px !important;
|
||||
height: 26px !important;
|
||||
line-height: 1.1 !important;
|
||||
}
|
||||
|
||||
/* 回転・反転・B/Aボタン */
|
||||
#edit_panel .gr-row button {
|
||||
padding: 3px 6px !important;
|
||||
font-size: 11px !important;
|
||||
min-height: 26px !important;
|
||||
}
|
||||
|
||||
/* スライダーラベル */
|
||||
#edit_panel label {
|
||||
font-size: 11px !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* スライダー間隔を詰める */
|
||||
#edit_panel .gr-form {
|
||||
gap: 2px !important;
|
||||
}
|
||||
|
||||
/* ══════════════════════════════════════════
|
||||
統一感テーマ — アクセントカラー: #7c4dff (紫)
|
||||
══════════════════════════════════════════ */
|
||||
|
||||
/* ── アコーディオン共通 ── */
|
||||
#pf_accordion > .label-wrap,
|
||||
#user_sp_accordion > .label-wrap {
|
||||
border-left: 3px solid #7c4dff !important;
|
||||
padding-left: 8px !important;
|
||||
}
|
||||
|
||||
/* ── ブラッシュアップバー 背景帯 ── */
|
||||
#brushup_bar {
|
||||
background: rgba(124, 77, 255, 0.04) !important;
|
||||
border-radius: 8px !important;
|
||||
padding: 6px 8px !important;
|
||||
border: 1px solid rgba(124, 77, 255, 0.12) !important;
|
||||
}
|
||||
|
||||
/* ── 微調整・強変更ボタン: プライマリ色をテーマ紫に ── */
|
||||
.brushup_btn_col button.secondary {
|
||||
border-color: rgba(124, 77, 255, 0.4) !important;
|
||||
color: rgba(180, 150, 255, 0.9) !important;
|
||||
}
|
||||
.brushup_btn_col button.secondary:hover {
|
||||
background: rgba(124, 77, 255, 0.15) !important;
|
||||
border-color: #7c4dff !important;
|
||||
}
|
||||
|
||||
/* ── 変更して生成ボタン ── */
|
||||
.brushup_btn_col button.primary {
|
||||
background: linear-gradient(135deg, #7c4dff 0%, #5c35cc 100%) !important;
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* ── エディットパネル ── */
|
||||
#edit_panel {
|
||||
background: rgba(124, 77, 255, 0.03) !important;
|
||||
border-radius: 6px !important;
|
||||
border: 1px solid rgba(124, 77, 255, 0.10) !important;
|
||||
border-top: 1px solid rgba(124, 77, 255, 0.15) !important;
|
||||
padding: 8px !important;
|
||||
}
|
||||
|
||||
/* ── 設定プリセット アコーディオン内 ── */
|
||||
#user_sp_accordion {
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* ── 生成ボタンは維持しつつ補助ボタンをトーン統一 ── */
|
||||
#generate_button {
|
||||
background: linear-gradient(135deg, #7c4dff 0%, #3a1d96 100%) !important;
|
||||
box-shadow: 0 2px 12px rgba(124, 77, 255, 0.35) !important;
|
||||
}
|
||||
|
||||
/* ── セクション区切り (Prompt Forge 上部) ── */
|
||||
#pf_accordion {
|
||||
border-radius: 8px !important;
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
/* ── プリセットボタン ホバー ── */
|
||||
#edit_presets button:hover, .preset_btn button:hover {
|
||||
background: rgba(124, 77, 255, 0.2) !important;
|
||||
border-color: #7c4dff !important;
|
||||
color: #d0b8ff !important;
|
||||
}
|
||||
|
||||
/* ── ステータス HTML テキストカラー ── */
|
||||
#brushup_status_html {
|
||||
color: rgba(200, 185, 255, 0.85) !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
"""OpenCV-based image adjustment utilities for the gallery editor."""
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────
|
||||
# ユーティリティ
|
||||
# ────────────────────────────────────────────────
|
||||
|
||||
def resize_for_preview(img_array, max_size=768):
|
||||
"""プレビュー用にアスペクト比を保ってリサイズ(高速化)。"""
|
||||
if img_array is None:
|
||||
return None
|
||||
h, w = img_array.shape[:2]
|
||||
if max(h, w) <= max_size:
|
||||
return img_array
|
||||
scale = max_size / max(h, w)
|
||||
nw, nh = int(w * scale), int(h * scale)
|
||||
return cv2.resize(img_array, (nw, nh), interpolation=cv2.INTER_AREA)
|
||||
|
||||
|
||||
def rotate_image(img_array, degrees):
|
||||
"""画像を 90 / 180 / 270 度回転(RGB 入出力)。"""
|
||||
if img_array is None:
|
||||
return None
|
||||
degrees = int(degrees) % 360
|
||||
if degrees == 90:
|
||||
return cv2.rotate(img_array, cv2.ROTATE_90_CLOCKWISE)
|
||||
elif degrees == 180:
|
||||
return cv2.rotate(img_array, cv2.ROTATE_180)
|
||||
elif degrees == 270:
|
||||
return cv2.rotate(img_array, cv2.ROTATE_90_COUNTERCLOCKWISE)
|
||||
return img_array
|
||||
|
||||
|
||||
def flip_image(img_array, direction='h'):
|
||||
"""水平('h') または垂直('v') 反転(RGB 入出力)。"""
|
||||
if img_array is None:
|
||||
return None
|
||||
code = 1 if direction == 'h' else 0
|
||||
return cv2.flip(img_array, code)
|
||||
|
||||
|
||||
# ────────────────────────────────────────────────
|
||||
# メイン調整関数
|
||||
# ────────────────────────────────────────────────
|
||||
|
||||
def apply_adjustments(img_array,
|
||||
brightness=0, contrast=1.0,
|
||||
saturation=1.0, hue_shift=0,
|
||||
sharpness=1.0, temperature=0):
|
||||
"""OpenCV で画像調整を適用する。
|
||||
|
||||
Parameters
|
||||
----------
|
||||
img_array : numpy ndarray (H, W, 3) RGB
|
||||
brightness : int -100 … 100 加算
|
||||
contrast : float 0.1 … 3.0 乗算
|
||||
saturation : float 0.0 … 3.0 HSV S 倍率
|
||||
hue_shift : int -90 … 90 HSV H 加算(度)
|
||||
sharpness : float 0.0 … 3.0 1.0=変化なし / >1=鮮明 / <1=ぼかし
|
||||
temperature : int -100 … 100 負=クール(青) / 正=ウォーム(赤)
|
||||
|
||||
Returns
|
||||
-------
|
||||
numpy ndarray (H, W, 3) RGB
|
||||
"""
|
||||
if img_array is None:
|
||||
return None
|
||||
|
||||
img = np.clip(img_array, 0, 255).astype(np.uint8)
|
||||
bgr = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
|
||||
|
||||
# ── 明るさ / コントラスト ──────────────────────
|
||||
if brightness != 0 or contrast != 1.0:
|
||||
bgr = cv2.convertScaleAbs(bgr, alpha=float(contrast), beta=float(brightness))
|
||||
|
||||
# ── 彩度 / 色相 ────────────────────────────────
|
||||
if saturation != 1.0 or hue_shift != 0:
|
||||
hsv = cv2.cvtColor(bgr, cv2.COLOR_BGR2HSV).astype(np.float32)
|
||||
hsv[:, :, 0] = (hsv[:, :, 0] + float(hue_shift)) % 180
|
||||
hsv[:, :, 1] = np.clip(hsv[:, :, 1] * float(saturation), 0, 255)
|
||||
bgr = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)
|
||||
|
||||
# ── 色温度 ─────────────────────────────────────
|
||||
if temperature != 0:
|
||||
b, g, r = cv2.split(bgr.astype(np.float32))
|
||||
t = float(temperature)
|
||||
if t > 0: # ウォーム (赤+, 青-)
|
||||
r = np.clip(r + t * 0.8, 0, 255)
|
||||
b = np.clip(b - t * 0.4, 0, 255)
|
||||
else: # クール (青+, 赤-)
|
||||
r = np.clip(r + t * 0.4, 0, 255)
|
||||
b = np.clip(b - t * 0.8, 0, 255)
|
||||
bgr = cv2.merge([b.astype(np.uint8), g.astype(np.uint8), r.astype(np.uint8)])
|
||||
|
||||
# ── シャープネス(アンシャープマスク)─────────
|
||||
if sharpness != 1.0:
|
||||
blur = cv2.GaussianBlur(bgr, (0, 0), 3.0)
|
||||
strength = float(sharpness) - 1.0
|
||||
bgr = cv2.addWeighted(bgr, 1.0 + strength, blur, -strength, 0)
|
||||
bgr = np.clip(bgr, 0, 255).astype(np.uint8)
|
||||
|
||||
return cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
|
||||
|
|
@ -0,0 +1,323 @@
|
|||
"""
|
||||
Prompt Forge Bridge Server
|
||||
--------------------------
|
||||
Fooocusプロセス内でバックグラウンドスレッドとして動作するHTTPサーバー。
|
||||
- ポート8080でPrompt ForgeのHTMLを配信
|
||||
- /v1/generation/text-to-image REST APIを提供
|
||||
- Fooocusのworkerキューに直接AsyncTaskを投入して画像生成
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import base64
|
||||
import threading
|
||||
import time
|
||||
import io
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from urllib.parse import urlparse
|
||||
|
||||
BRIDGE_PORT = 8080
|
||||
_html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'prompt_forge_v3_11.html')
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# AsyncTask args ビルダー
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
def _build_args(req: dict) -> list:
|
||||
"""リクエストJSONからAsyncTask argsリストを構築する。"""
|
||||
import modules.config as config
|
||||
import modules.flags as flags
|
||||
import args_manager
|
||||
|
||||
# ── ユーザー指定パラメータ ──
|
||||
prompt = req.get('prompt', '')
|
||||
negative_prompt = req.get('negative_prompt', '')
|
||||
styles = req.get('style_selections', list(config.default_styles))
|
||||
if isinstance(styles, str):
|
||||
styles = [styles]
|
||||
performance = req.get('performance_selection', 'Speed')
|
||||
image_number = int(req.get('image_number', 1))
|
||||
output_format = req.get('output_format', 'png')
|
||||
image_seed = int(req.get('image_seed', -1))
|
||||
sharpness = float(req.get('sharpness', config.default_sample_sharpness))
|
||||
guidance_scale = float(req.get('guidance_scale', config.default_cfg_scale))
|
||||
|
||||
# アスペクト比を Fooocus 形式に変換 ("1024*1024" → "1024×1024 | 1:1")
|
||||
aspect_raw = str(req.get('aspect_ratios_selection', '1024*1024'))
|
||||
aspect_raw = aspect_raw.replace('×', '*').replace('x', '*').replace('X', '*')
|
||||
if '*' not in aspect_raw:
|
||||
aspect_raw = '1024*1024'
|
||||
aspect_sel = config.add_ratio(aspect_raw)
|
||||
|
||||
args = [
|
||||
False, # generate_image_grid
|
||||
prompt,
|
||||
negative_prompt,
|
||||
styles,
|
||||
performance,
|
||||
aspect_sel,
|
||||
image_number,
|
||||
output_format,
|
||||
image_seed,
|
||||
False, # read_wildcards_in_order
|
||||
sharpness,
|
||||
guidance_scale,
|
||||
config.default_base_model_name,
|
||||
config.default_refiner_model_name,
|
||||
config.default_refiner_switch,
|
||||
]
|
||||
|
||||
# LoRA (default_max_lora_number x 3: enabled, name, weight)
|
||||
# default_loras は必ず default_max_lora_number 個にパディングされている
|
||||
loras_to_add = config.default_loras[:config.default_max_lora_number]
|
||||
# 不足分を 'None' で補填
|
||||
while len(loras_to_add) < config.default_max_lora_number:
|
||||
loras_to_add.append([True, 'None', 1.0])
|
||||
for lora in loras_to_add:
|
||||
args.extend([bool(lora[0]), str(lora[1]), float(lora[2])])
|
||||
|
||||
args += [
|
||||
False, # input_image_checkbox
|
||||
'uov', # current_tab
|
||||
flags.disabled, # uov_method ('Disabled')
|
||||
None, # uov_input_image
|
||||
[], # outpaint_selections
|
||||
{'image': None, 'mask': None}, # inpaint_input_image
|
||||
'', # inpaint_additional_prompt
|
||||
None, # inpaint_mask_image_upload
|
||||
False, # disable_preview
|
||||
False, # disable_intermediate_results
|
||||
False, # disable_seed_increment
|
||||
config.default_black_out_nsfw,
|
||||
1.5, # adm_scaler_positive
|
||||
0.8, # adm_scaler_negative
|
||||
0.3, # adm_scaler_end
|
||||
config.default_cfg_tsnr, # adaptive_cfg
|
||||
config.default_clip_skip, # clip_skip
|
||||
config.default_sampler, # sampler_name
|
||||
config.default_scheduler, # scheduler_name
|
||||
flags.default_vae, # vae_name
|
||||
-1, # overwrite_step
|
||||
-1, # overwrite_switch
|
||||
-1, # overwrite_width
|
||||
-1, # overwrite_height
|
||||
-1, # overwrite_vary_strength
|
||||
-1, # overwrite_upscale_strength
|
||||
False, # mixing_image_prompt_and_vary_upscale
|
||||
False, # mixing_image_prompt_and_inpaint
|
||||
False, # debugging_cn_preprocessor
|
||||
False, # skipping_cn_preprocessor
|
||||
100, # canny_low_threshold
|
||||
200, # canny_high_threshold
|
||||
flags.refiner_swap_method, # 'joint'
|
||||
0.25, # controlnet_softness
|
||||
False, # freeu_enabled
|
||||
1.01, # freeu_b1
|
||||
1.02, # freeu_b2
|
||||
0.99, # freeu_s1
|
||||
0.95, # freeu_s2
|
||||
# ── inpaint_ctrls ──
|
||||
False, # debugging_inpaint_preprocessor
|
||||
False, # inpaint_disable_initial_latent
|
||||
config.default_inpaint_engine_version, # inpaint_engine
|
||||
1.0, # inpaint_strength
|
||||
0.618, # inpaint_respective_field
|
||||
False, # inpaint_advanced_masking_checkbox
|
||||
False, # invert_mask_checkbox
|
||||
0, # inpaint_erode_or_dilate
|
||||
]
|
||||
|
||||
if not args_manager.args.disable_image_log:
|
||||
args.append(config.default_save_only_final_enhanced_image)
|
||||
if not args_manager.args.disable_metadata:
|
||||
args.append(config.default_save_metadata_to_images)
|
||||
args.append('fooocus') # metadata_scheme
|
||||
|
||||
# ControlNet IP (default_controlnet_image_count x 4)
|
||||
for _ in range(config.default_controlnet_image_count):
|
||||
args += [None, 0.5, 0.6, flags.default_ip] # img, stop, weight, type
|
||||
|
||||
# enhance 制御
|
||||
args += [
|
||||
False, # debugging_dino
|
||||
0, # dino_erode_or_dilate
|
||||
False, # debugging_enhance_masks_checkbox
|
||||
None, # enhance_input_image
|
||||
False, # enhance_checkbox
|
||||
config.default_enhance_uov_method,
|
||||
config.default_enhance_uov_processing_order,
|
||||
config.default_enhance_uov_prompt_type,
|
||||
]
|
||||
|
||||
# enhance タブ (default_enhance_tabs x 16)
|
||||
for _ in range(config.default_enhance_tabs):
|
||||
args += [
|
||||
False, # enhance_enabled
|
||||
'', # enhance_mask_dino_prompt_text
|
||||
'', # enhance_prompt
|
||||
'', # enhance_negative_prompt
|
||||
config.default_enhance_inpaint_mask_model, # 'sam'
|
||||
config.default_inpaint_mask_cloth_category, # 'full'
|
||||
config.default_inpaint_mask_sam_model, # 'vit_b'
|
||||
0.25, # enhance_mask_text_threshold
|
||||
0.3, # enhance_mask_box_threshold
|
||||
config.default_sam_max_detections,
|
||||
False, # enhance_inpaint_disable_initial_latent
|
||||
config.default_inpaint_engine_version,
|
||||
0.5, # enhance_inpaint_strength
|
||||
0.618, # enhance_inpaint_respective_field
|
||||
0, # enhance_inpaint_erode_or_dilate
|
||||
False, # enhance_mask_invert
|
||||
]
|
||||
|
||||
print(f'[PromptForge] args構築完了: {len(args)}個 '
|
||||
f'(max_lora={config.default_max_lora_number}, '
|
||||
f'cn_count={config.default_controlnet_image_count}, '
|
||||
f'enhance_tabs={config.default_enhance_tabs})')
|
||||
return args
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 画像生成メイン
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
def _generate(req: dict) -> list:
|
||||
"""
|
||||
Fooocusのworkerキューに生成タスクを投入し、完成した画像をbase64リストで返す。
|
||||
"""
|
||||
import modules.async_worker as worker
|
||||
from modules.async_worker import AsyncTask
|
||||
|
||||
args = _build_args(req)
|
||||
task = AsyncTask(args=args)
|
||||
worker.async_tasks.append(task)
|
||||
|
||||
print(f'[PromptForge] 生成開始: "{task.prompt[:60]}"')
|
||||
|
||||
timeout = 600 # 10分
|
||||
start = time.time()
|
||||
results_b64 = []
|
||||
|
||||
while time.time() - start < timeout:
|
||||
time.sleep(0.3)
|
||||
|
||||
if task.last_stop:
|
||||
raise RuntimeError('生成がキャンセルされました')
|
||||
|
||||
# yields をすべて処理
|
||||
while task.yields:
|
||||
flag, product = task.yields.pop(0)
|
||||
if flag == 'finish':
|
||||
for img in product:
|
||||
try:
|
||||
if isinstance(img, str) and os.path.exists(img):
|
||||
with open(img, 'rb') as f:
|
||||
b64 = base64.b64encode(f.read()).decode('utf-8')
|
||||
elif hasattr(img, 'save'):
|
||||
buf = io.BytesIO()
|
||||
img.save(buf, format='PNG')
|
||||
b64 = base64.b64encode(buf.getvalue()).decode('utf-8')
|
||||
else:
|
||||
continue
|
||||
results_b64.append({
|
||||
'base64': b64,
|
||||
'url': '',
|
||||
'seed': task.seed if hasattr(task, 'seed') else req.get('image_seed', -1),
|
||||
'finish_reason': 'SUCCESS',
|
||||
'meta': {}
|
||||
})
|
||||
except Exception as e:
|
||||
print(f'[PromptForge] 画像読込エラー: {e}')
|
||||
if results_b64:
|
||||
print(f'[PromptForge] 完了: {len(results_b64)}枚')
|
||||
return results_b64
|
||||
raise RuntimeError('画像データを取得できませんでした')
|
||||
|
||||
raise TimeoutError('生成タイムアウト (600秒)')
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# HTTP ハンドラ
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
class _Handler(BaseHTTPRequestHandler):
|
||||
|
||||
def log_message(self, fmt, *args):
|
||||
pass # コンソールへの詳細ログを抑制
|
||||
|
||||
def _cors(self):
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type, Accept')
|
||||
|
||||
def do_OPTIONS(self):
|
||||
self.send_response(200)
|
||||
self._cors()
|
||||
self.end_headers()
|
||||
|
||||
def do_GET(self):
|
||||
path = urlparse(self.path).path.rstrip('/') or '/'
|
||||
if path in ('/', '/index.html'):
|
||||
if os.path.exists(_html_path):
|
||||
with open(_html_path, 'rb') as f:
|
||||
data = f.read()
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/html; charset=utf-8')
|
||||
self._cors()
|
||||
self.end_headers()
|
||||
self.wfile.write(data)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
self.wfile.write(b'prompt_forge_v3_11.html not found')
|
||||
elif path == '/ping':
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/plain')
|
||||
self._cors()
|
||||
self.end_headers()
|
||||
self.wfile.write(b'pong')
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
def do_POST(self):
|
||||
path = urlparse(self.path).path
|
||||
if path == '/v1/generation/text-to-image':
|
||||
try:
|
||||
length = int(self.headers.get('Content-Length', 0))
|
||||
body = self.rfile.read(length)
|
||||
req = json.loads(body.decode('utf-8')) if body else {}
|
||||
images = _generate(req)
|
||||
resp = json.dumps(images).encode('utf-8')
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self._cors()
|
||||
self.end_headers()
|
||||
self.wfile.write(resp)
|
||||
except Exception as e:
|
||||
import traceback
|
||||
msg = traceback.format_exc()
|
||||
print(f'[PromptForge] エラー:\n{msg}')
|
||||
resp = json.dumps({'error': str(e), 'traceback': msg}).encode('utf-8')
|
||||
self.send_response(500)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
self._cors()
|
||||
self.end_headers()
|
||||
self.wfile.write(resp)
|
||||
else:
|
||||
self.send_response(404)
|
||||
self.end_headers()
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
# 起動関数(webui.py から呼ばれる)
|
||||
# ──────────────────────────────────────────────────────────────────
|
||||
def start_bridge(port: int = BRIDGE_PORT):
|
||||
"""ブリッジサーバーをデーモンスレッドで起動する。"""
|
||||
server = HTTPServer(('127.0.0.1', port), _Handler)
|
||||
t = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
t.start()
|
||||
print(f'\n{"="*55}')
|
||||
print(f' 🎨 Prompt Forge が起動しました!')
|
||||
print(f' ブラウザで以下を開いてください:')
|
||||
print(f' http://127.0.0.1:{port}')
|
||||
print(f'{"="*55}\n')
|
||||
return server, t
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,33 @@
|
|||
@echo off
|
||||
chcp 65001 > nul
|
||||
title FOOOCUS FORGE LAUNCHER
|
||||
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM ── Python を探す (embedded > PATH) ──────────────────────────────
|
||||
if exist "python_embedded\python.exe" (
|
||||
set "PYTHON=python_embedded\python.exe"
|
||||
echo [INFO] Embedded Python を使用します
|
||||
) else (
|
||||
set "PYTHON=python"
|
||||
)
|
||||
|
||||
REM ── tkinter が使えるか確認 ───────────────────────────────────────
|
||||
%PYTHON% -c "import tkinter" 2>nul
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo [ERROR] tkinter が見つかりません。
|
||||
echo Python を公式サイト (python.org) からインストールしてください。
|
||||
echo インストール時に "tcl/tk and IDLE" にチェックを入れてください。
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
REM ── ランチャー起動 ───────────────────────────────────────────────
|
||||
%PYTHON% run_forge_launcher.py
|
||||
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo [ERROR] 起動中にエラーが発生しました。
|
||||
pause
|
||||
)
|
||||
|
|
@ -0,0 +1,433 @@
|
|||
"""
|
||||
FOOOCUS FORGE LAUNCHER
|
||||
tkinter dark-themed GUI launcher for Fooocus + Prompt Forge Bridge
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
import subprocess
|
||||
import socket
|
||||
import time
|
||||
import webbrowser
|
||||
import queue
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, scrolledtext, messagebox
|
||||
|
||||
# ── カラーテーマ ──────────────────────────────────────────────────
|
||||
BG = "#0a0a0f"
|
||||
BG2 = "#12121a"
|
||||
BG3 = "#1a1a28"
|
||||
BORDER = "#2a2a3d"
|
||||
FG = "#e8e8f0"
|
||||
FG2 = "#8888aa"
|
||||
ACCENT = "#7c4dff"
|
||||
ACCENT2 = "#b388ff"
|
||||
ACCENT3 = "#00e5ff"
|
||||
GREEN = "#00e676"
|
||||
RED = "#ff1744"
|
||||
YELLOW = "#ffea00"
|
||||
ORANGE = "#ff6d00"
|
||||
X_BLUE = "#1da1f2"
|
||||
|
||||
# ── ポート定義 ─────────────────────────────────────────────────────
|
||||
PORT_FOOOCUS = 7865
|
||||
PORT_BRIDGE = 8080
|
||||
|
||||
# ── 起動コマンド ───────────────────────────────────────────────────
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
def _py():
|
||||
emb = os.path.join(BASE_DIR, "python_embedded", "python.exe")
|
||||
return emb if os.path.exists(emb) else sys.executable
|
||||
|
||||
def build_fooocus_cmd(preset="", in_browser=False, listen=False):
|
||||
cmd = [_py(), "launch.py"]
|
||||
if preset:
|
||||
cmd += ["--preset", preset]
|
||||
if in_browser:
|
||||
cmd.append("--in-browser")
|
||||
if listen:
|
||||
cmd.append("--listen")
|
||||
return cmd
|
||||
|
||||
def is_port_open(port: int) -> bool:
|
||||
try:
|
||||
with socket.create_connection(("127.0.0.1", port), timeout=0.5):
|
||||
return True
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
# ══════════════════════════════════════════════════════════════════
|
||||
class LauncherApp(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.title("FOOOCUS FORGE LAUNCHER")
|
||||
self.configure(bg=BG)
|
||||
self.resizable(True, True)
|
||||
self.minsize(680, 520)
|
||||
|
||||
# プロセス管理
|
||||
self._proc_fooocus: subprocess.Popen | None = None
|
||||
self._log_queue: queue.Queue = queue.Queue()
|
||||
|
||||
# ── ウィジェット構築 ──
|
||||
self._build_header()
|
||||
self._build_status_bar()
|
||||
self._build_options()
|
||||
self._build_controls()
|
||||
self._build_log()
|
||||
self._build_links()
|
||||
|
||||
# ── イベント ──
|
||||
self.bind("<Escape>", lambda _: self._on_close())
|
||||
self.protocol("WM_DELETE_WINDOW", self._on_close)
|
||||
|
||||
# ── ポーリング開始 ──
|
||||
self._poll_status()
|
||||
self._drain_log_queue()
|
||||
|
||||
# ── ヘッダー ──────────────────────────────────────────────────
|
||||
def _build_header(self):
|
||||
f = tk.Frame(self, bg=BG, pady=12)
|
||||
f.pack(fill="x", padx=20)
|
||||
tk.Label(f, text="⚡ FOOOCUS FORGE LAUNCHER",
|
||||
bg=BG, fg=ACCENT2,
|
||||
font=("Consolas", 16, "bold")).pack(side="left")
|
||||
tk.Label(f, text="v1.0",
|
||||
bg=BG, fg=FG2,
|
||||
font=("Consolas", 10)).pack(side="left", padx=(8, 0), pady=4)
|
||||
|
||||
# ── ステータスバー ────────────────────────────────────────────
|
||||
def _build_status_bar(self):
|
||||
outer = tk.Frame(self, bg=BG3, bd=0, highlightbackground=BORDER,
|
||||
highlightthickness=1)
|
||||
outer.pack(fill="x", padx=20, pady=(0, 8))
|
||||
|
||||
inner = tk.Frame(outer, bg=BG3, pady=8)
|
||||
inner.pack(fill="x", padx=12)
|
||||
|
||||
# Fooocus
|
||||
fc = tk.Frame(inner, bg=BG3)
|
||||
fc.pack(side="left", padx=(0, 24))
|
||||
self._dot_fooocus = tk.Label(fc, text="●", fg=RED, bg=BG3,
|
||||
font=("Consolas", 14))
|
||||
self._dot_fooocus.pack(side="left")
|
||||
tk.Label(fc, text=f" Fooocus :{PORT_FOOOCUS}",
|
||||
bg=BG3, fg=FG, font=("Consolas", 11)).pack(side="left")
|
||||
self._lbl_fooocus = tk.Label(fc, text="停止中",
|
||||
bg=BG3, fg=RED,
|
||||
font=("Consolas", 10))
|
||||
self._lbl_fooocus.pack(side="left", padx=(6, 0))
|
||||
|
||||
sep = tk.Frame(inner, bg=BORDER, width=1, height=24)
|
||||
sep.pack(side="left", padx=12)
|
||||
|
||||
# Bridge
|
||||
bc = tk.Frame(inner, bg=BG3)
|
||||
bc.pack(side="left")
|
||||
self._dot_bridge = tk.Label(bc, text="●", fg=RED, bg=BG3,
|
||||
font=("Consolas", 14))
|
||||
self._dot_bridge.pack(side="left")
|
||||
tk.Label(bc, text=f" Bridge :{PORT_BRIDGE}",
|
||||
bg=BG3, fg=FG, font=("Consolas", 11)).pack(side="left")
|
||||
self._lbl_bridge = tk.Label(bc, text="停止中",
|
||||
bg=BG3, fg=RED,
|
||||
font=("Consolas", 10))
|
||||
self._lbl_bridge.pack(side="left", padx=(6, 0))
|
||||
|
||||
# ── 起動オプション ────────────────────────────────────────────
|
||||
def _build_options(self):
|
||||
outer = tk.Frame(self, bg=BG2, bd=0, highlightbackground=BORDER,
|
||||
highlightthickness=1)
|
||||
outer.pack(fill="x", padx=20, pady=(0, 8))
|
||||
|
||||
inner = tk.Frame(outer, bg=BG2, pady=8, padx=12)
|
||||
inner.pack(fill="x")
|
||||
|
||||
tk.Label(inner, text="起動オプション", bg=BG2, fg=FG2,
|
||||
font=("Consolas", 9, "bold")).grid(
|
||||
row=0, column=0, columnspan=6, sticky="w", pady=(0, 6))
|
||||
|
||||
# プリセット
|
||||
tk.Label(inner, text="プリセット:", bg=BG2, fg=FG,
|
||||
font=("Consolas", 10)).grid(row=1, column=0, sticky="w", padx=(0, 6))
|
||||
self._preset_var = tk.StringVar(value="なし")
|
||||
preset_cb = ttk.Combobox(inner, textvariable=self._preset_var,
|
||||
values=["なし", "anime", "realistic", "lcm"],
|
||||
state="readonly", width=12,
|
||||
font=("Consolas", 10))
|
||||
preset_cb.grid(row=1, column=1, sticky="w", padx=(0, 20))
|
||||
self._style_combobox(preset_cb)
|
||||
|
||||
# ブラウザ自動起動
|
||||
self._browser_var = tk.BooleanVar(value=False)
|
||||
chk_browser = tk.Checkbutton(inner, text="ブラウザ自動起動",
|
||||
variable=self._browser_var,
|
||||
bg=BG2, fg=FG, selectcolor=BG3,
|
||||
activebackground=BG2, activeforeground=ACCENT2,
|
||||
font=("Consolas", 10))
|
||||
chk_browser.grid(row=1, column=2, sticky="w", padx=(0, 20))
|
||||
|
||||
# --listen
|
||||
self._listen_var = tk.BooleanVar(value=False)
|
||||
chk_listen = tk.Checkbutton(inner, text="--listen (LAN公開)",
|
||||
variable=self._listen_var,
|
||||
bg=BG2, fg=FG, selectcolor=BG3,
|
||||
activebackground=BG2, activeforeground=ACCENT2,
|
||||
font=("Consolas", 10))
|
||||
chk_listen.grid(row=1, column=3, sticky="w")
|
||||
|
||||
# ── コントロールボタン ────────────────────────────────────────
|
||||
def _build_controls(self):
|
||||
f = tk.Frame(self, bg=BG, pady=4)
|
||||
f.pack(fill="x", padx=20)
|
||||
|
||||
btn_cfg = dict(font=("Consolas", 11, "bold"), cursor="hand2",
|
||||
relief="flat", bd=0, padx=18, pady=7)
|
||||
|
||||
self._btn_start = tk.Button(
|
||||
f, text="▶ 起動", bg=ACCENT, fg="white",
|
||||
activebackground=ACCENT2, activeforeground="white",
|
||||
command=self._start_all, **btn_cfg)
|
||||
self._btn_start.pack(side="left", padx=(0, 8))
|
||||
|
||||
self._btn_stop = tk.Button(
|
||||
f, text="■ 停止", bg=BG3, fg=RED,
|
||||
activebackground=BORDER, activeforeground=RED,
|
||||
command=self._stop_all, state="disabled", **btn_cfg)
|
||||
self._btn_stop.pack(side="left", padx=(0, 8))
|
||||
|
||||
self._btn_restart = tk.Button(
|
||||
f, text="↺ 再起動", bg=BG3, fg=YELLOW,
|
||||
activebackground=BORDER, activeforeground=YELLOW,
|
||||
command=self._restart_all, state="disabled", **btn_cfg)
|
||||
self._btn_restart.pack(side="left", padx=(0, 8))
|
||||
|
||||
tk.Button(f, text="ログクリア", bg=BG3, fg=FG2,
|
||||
activebackground=BORDER, activeforeground=FG,
|
||||
command=self._clear_log, **btn_cfg).pack(side="right")
|
||||
|
||||
# ── ログ ──────────────────────────────────────────────────────
|
||||
def _build_log(self):
|
||||
f = tk.Frame(self, bg=BG)
|
||||
f.pack(fill="both", expand=True, padx=20, pady=(8, 4))
|
||||
|
||||
tk.Label(f, text="ログ", bg=BG, fg=FG2,
|
||||
font=("Consolas", 9, "bold")).pack(anchor="w", pady=(0, 3))
|
||||
|
||||
self._log = scrolledtext.ScrolledText(
|
||||
f, bg=BG2, fg=FG, insertbackground=FG,
|
||||
font=("Consolas", 9), relief="flat",
|
||||
bd=0, highlightbackground=BORDER, highlightthickness=1,
|
||||
state="disabled", wrap="word")
|
||||
self._log.pack(fill="both", expand=True)
|
||||
|
||||
# タグ色
|
||||
self._log.tag_config("info", foreground=FG2)
|
||||
self._log.tag_config("ok", foreground=GREEN)
|
||||
self._log.tag_config("warn", foreground=YELLOW)
|
||||
self._log.tag_config("err", foreground=RED)
|
||||
self._log.tag_config("accent", foreground=ACCENT3)
|
||||
|
||||
# ── クイックリンク ────────────────────────────────────────────
|
||||
def _build_links(self):
|
||||
f = tk.Frame(self, bg=BG, pady=8)
|
||||
f.pack(fill="x", padx=20)
|
||||
|
||||
link_cfg = dict(font=("Consolas", 10), cursor="hand2",
|
||||
relief="flat", bd=0, padx=12, pady=5)
|
||||
|
||||
tk.Button(f, text="🌐 Prompt Forge http://127.0.0.1:8080",
|
||||
bg=BG3, fg=ACCENT3,
|
||||
activebackground=BORDER, activeforeground=ACCENT3,
|
||||
command=lambda: webbrowser.open("http://127.0.0.1:8080"),
|
||||
**link_cfg).pack(side="left", padx=(0, 8))
|
||||
|
||||
tk.Button(f, text="🌐 Fooocus UI http://127.0.0.1:7865",
|
||||
bg=BG3, fg=ACCENT2,
|
||||
activebackground=BORDER, activeforeground=ACCENT2,
|
||||
command=lambda: webbrowser.open("http://127.0.0.1:7865"),
|
||||
**link_cfg).pack(side="left")
|
||||
|
||||
# ── Combobox スタイル ─────────────────────────────────────────
|
||||
def _style_combobox(self, cb):
|
||||
style = ttk.Style(self)
|
||||
style.theme_use("default")
|
||||
style.configure("TCombobox",
|
||||
fieldbackground=BG3,
|
||||
background=BG3,
|
||||
foreground=FG,
|
||||
selectbackground=ACCENT,
|
||||
selectforeground="white",
|
||||
arrowcolor=FG2)
|
||||
style.map("TCombobox",
|
||||
fieldbackground=[("readonly", BG3)],
|
||||
foreground=[("readonly", FG)],
|
||||
background=[("readonly", BG3)])
|
||||
self.option_add("*TCombobox*Listbox.background", BG3)
|
||||
self.option_add("*TCombobox*Listbox.foreground", FG)
|
||||
self.option_add("*TCombobox*Listbox.selectBackground", ACCENT)
|
||||
self.option_add("*TCombobox*Listbox.font", ("Consolas", 10))
|
||||
|
||||
# ── ログ書き込み ──────────────────────────────────────────────
|
||||
def _log_write(self, text: str, tag: str = "info"):
|
||||
self._log.configure(state="normal")
|
||||
self._log.insert("end", text + "\n", tag)
|
||||
self._log.see("end")
|
||||
self._log.configure(state="disabled")
|
||||
|
||||
def _clear_log(self):
|
||||
self._log.configure(state="normal")
|
||||
self._log.delete("1.0", "end")
|
||||
self._log.configure(state="disabled")
|
||||
|
||||
# ── ログキューをUIスレッドで消費 ──────────────────────────────
|
||||
def _drain_log_queue(self):
|
||||
try:
|
||||
while True:
|
||||
text, tag = self._log_queue.get_nowait()
|
||||
self._log_write(text, tag)
|
||||
except queue.Empty:
|
||||
pass
|
||||
self.after(100, self._drain_log_queue)
|
||||
|
||||
def _queue_log(self, text: str, tag: str = "info"):
|
||||
self._log_queue.put((text, tag))
|
||||
|
||||
# ── ステータスポーリング ──────────────────────────────────────
|
||||
def _poll_status(self):
|
||||
fooocus_up = is_port_open(PORT_FOOOCUS)
|
||||
bridge_up = is_port_open(PORT_BRIDGE)
|
||||
|
||||
# Fooocus
|
||||
if fooocus_up:
|
||||
self._dot_fooocus.config(fg=GREEN)
|
||||
self._lbl_fooocus.config(text="起動中", fg=GREEN)
|
||||
else:
|
||||
if self._proc_fooocus and self._proc_fooocus.poll() is None:
|
||||
self._dot_fooocus.config(fg=YELLOW)
|
||||
self._lbl_fooocus.config(text="起動中...", fg=YELLOW)
|
||||
else:
|
||||
self._dot_fooocus.config(fg=RED)
|
||||
self._lbl_fooocus.config(text="停止中", fg=RED)
|
||||
|
||||
# Bridge (Fooocusの中で起動されるためプロセスを別管理しない)
|
||||
if bridge_up:
|
||||
self._dot_bridge.config(fg=GREEN)
|
||||
self._lbl_bridge.config(text="起動中", fg=GREEN)
|
||||
else:
|
||||
self._dot_bridge.config(fg=RED)
|
||||
self._lbl_bridge.config(text="停止中", fg=RED)
|
||||
|
||||
# ボタン状態
|
||||
running = (self._proc_fooocus is not None and
|
||||
self._proc_fooocus.poll() is None)
|
||||
self._btn_start.config(state="disabled" if running else "normal")
|
||||
self._btn_stop.config(state="normal" if running else "disabled")
|
||||
self._btn_restart.config(state="normal" if running else "disabled")
|
||||
|
||||
self.after(2000, self._poll_status)
|
||||
|
||||
# ── 起動 ──────────────────────────────────────────────────────
|
||||
def _start_all(self):
|
||||
preset = self._preset_var.get()
|
||||
browser = self._browser_var.get()
|
||||
listen = self._listen_var.get()
|
||||
|
||||
cmd = build_fooocus_cmd(
|
||||
preset = "" if preset == "なし" else preset,
|
||||
in_browser = browser,
|
||||
listen = listen
|
||||
)
|
||||
|
||||
self._log_write(f"▶ 起動コマンド: {' '.join(cmd)}", "accent")
|
||||
|
||||
try:
|
||||
self._proc_fooocus = subprocess.Popen(
|
||||
cmd,
|
||||
cwd=BASE_DIR,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
errors="replace",
|
||||
bufsize=1
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
self._log_write(f"[ERROR] Python が見つかりません: {e}", "err")
|
||||
return
|
||||
|
||||
self._log_write(f"PID: {self._proc_fooocus.pid}", "info")
|
||||
threading.Thread(target=self._stream_output,
|
||||
args=(self._proc_fooocus,), daemon=True).start()
|
||||
|
||||
def _stream_output(self, proc: subprocess.Popen):
|
||||
try:
|
||||
for line in proc.stdout:
|
||||
line = line.rstrip("\n")
|
||||
if not line:
|
||||
continue
|
||||
low = line.lower()
|
||||
if any(k in low for k in ("error", "exception", "traceback", "failed")):
|
||||
tag = "err"
|
||||
elif any(k in low for k in ("warning", "warn")):
|
||||
tag = "warn"
|
||||
elif any(k in low for k in ("running on", "http://", "startup", "ready", "started")):
|
||||
tag = "ok"
|
||||
else:
|
||||
tag = "info"
|
||||
self._queue_log(line, tag)
|
||||
except Exception:
|
||||
pass
|
||||
self._queue_log("── プロセス終了 ──", "warn")
|
||||
|
||||
# ── 停止 ──────────────────────────────────────────────────────
|
||||
def _stop_all(self):
|
||||
if self._proc_fooocus and self._proc_fooocus.poll() is None:
|
||||
self._log_write("■ 停止中...", "warn")
|
||||
try:
|
||||
self._proc_fooocus.terminate()
|
||||
try:
|
||||
self._proc_fooocus.wait(timeout=8)
|
||||
except subprocess.TimeoutExpired:
|
||||
self._proc_fooocus.kill()
|
||||
self._log_write("停止しました。", "ok")
|
||||
except Exception as e:
|
||||
self._log_write(f"[ERROR] 停止失敗: {e}", "err")
|
||||
self._proc_fooocus = None
|
||||
|
||||
# ── 再起動 ────────────────────────────────────────────────────
|
||||
def _restart_all(self):
|
||||
self._log_write("↺ 再起動中...", "accent")
|
||||
self._stop_all()
|
||||
time.sleep(1)
|
||||
self._start_all()
|
||||
|
||||
# ── 終了 ──────────────────────────────────────────────────────
|
||||
def _on_close(self):
|
||||
if (self._proc_fooocus and self._proc_fooocus.poll() is None):
|
||||
if messagebox.askyesno(
|
||||
"確認",
|
||||
"Fooocus が起動中です。終了しますか?\n(プロセスも停止されます)",
|
||||
parent=self
|
||||
):
|
||||
self._stop_all()
|
||||
else:
|
||||
return
|
||||
self.destroy()
|
||||
|
||||
|
||||
# ── エントリーポイント ─────────────────────────────────────────────
|
||||
if __name__ == "__main__":
|
||||
app = LauncherApp()
|
||||
# ウィンドウを画面中央に
|
||||
app.update_idletasks()
|
||||
w, h = 700, 580
|
||||
sw = app.winfo_screenwidth()
|
||||
sh = app.winfo_screenheight()
|
||||
x = (sw - w) // 2
|
||||
y = (sh - h) // 2
|
||||
app.geometry(f"{w}x{h}+{x}+{y}")
|
||||
app.mainloop()
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"path_outputs": "E:\\マイドライブ\\AI画像入れ"
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"アルビノ": {
|
||||
"prompt": "white skin,clause up,__pf_angle__,masterpiece, high score, great score, absurdres, very aesthetic, smooth hair, neat hair, tidy hair, clean lineart, crisp detail, official art, highres, well-groomed hair, sleek hair, perfect hair, styled hair, hair in place, pure white background, simple background, white background, 1girl, looking at viewer, sensitive,red eyes, distinct pupils, detailed eyes, beautiful eyes, half-closed eyes, white skin, albino, porcelain skin, enigmatic, mysterious, distant, gloomy, white hair, __pf_hairstyle__,detailed white eyelashes,__pf_fetish__,__pf_costume_casual__",
|
||||
"negative_prompt": "lowres, bad anatomy, bad hands, text, error, missing fingers, extra digits, fewer digits, cropped, worst quality, low quality, low score, bad score, average score, signature, watermark, username, blurry, sketch, doodle, rough draft, messy lines, rough lines, traditional media, watercolor, pencil, background, scenery, building, nature, detailed background, head out of frame, no sclera, colored sclera, solid eyes, frizzy hair, messy hair, flyaway hair, bad hair day, ahoge, stray hair, unruly hair, wild hair,sunmissing_limb,lace fabric,background,bad eyes, deformed eyes, thick eyelashes, clumped eyelashes, fused eyelashes, solid eyelashes, heavy eyelashes, messy eyelashes, overly dramatic eyelashes, clumpy mascara",
|
||||
"styles": "[]",
|
||||
"performance": "Quality",
|
||||
"resolution": "(1024, 1024)",
|
||||
"image_number": 1,
|
||||
"guidance_scale": 6.0,
|
||||
"sharpness": 2.0,
|
||||
"base_model": "animagineXL40_v4Opt.safetensors",
|
||||
"refiner_model": "None",
|
||||
"refiner_switch": 0.5,
|
||||
"sampler": "dpmpp_2m_sde_gpu",
|
||||
"scheduler": "karras",
|
||||
"vae": "Default (model)",
|
||||
"adm_guidance": "(1.5, 0.8, 0.3)",
|
||||
"refiner_swap_method": "joint",
|
||||
"adaptive_cfg": 7.0,
|
||||
"clip_skip": 2,
|
||||
"input_image_enabled": false,
|
||||
"enhance_enabled": true,
|
||||
"uov_method": "Disabled",
|
||||
"lora_combined_1": "True : add-detail-xl.safetensors : 0.58",
|
||||
"lora_combined_2": "True : SDXL_FILM_PHOTOGRAPHY_STYLE_V1.safetensors : 0.38",
|
||||
"lora_combined_3": "True : sexy_details_v4.safetensors : 0.22",
|
||||
"lora_combined_4": "True : None : 1",
|
||||
"lora_combined_5": "True : None : 1"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue