Opera8's picture
Update app.py
c3ba67c verified
raw
history blame
30.4 kB
import os
import gradio as gr
import numpy as np
import spaces
import torch
import random
from PIL import Image, ImageFilter
from typing import Iterable
from gradio.themes import Soft
from gradio.themes.utils import colors, fonts, sizes
from deep_translator import GoogleTranslator
from transformers import pipeline
from datetime import date
import json
# --- تعریف تم ---
colors.steel_blue = colors.Color(
name="steel_blue",
c50="#EBF3F8",
c100="#D3E5F0",
c200="#A8CCE1",
c300="#7DB3D2",
c400="#529AC3",
c500="#4682B4",
c600="#3E72A0",
c700="#36638C",
c800="#2E5378",
c900="#264364",
c950="#1E3450",
)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# --- تنظیمات سیستم اعتبار ---
USAGE_LIMIT = 5
DATA_FILE = "usage_data.json" # فایل ذخیره اطلاعات
PREMIUM_PAGE_ID = '1149636'
# --- توابع مدیریت فایل JSON ---
def load_usage_data():
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, 'r') as f:
return json.load(f)
except:
return {}
return {}
def save_usage_data(data):
try:
with open(DATA_FILE, 'w') as f:
json.dump(data, f)
except Exception as e:
print(f"Error saving data: {e}")
# بارگذاری اولیه داده‌ها
usage_data_cache = load_usage_data()
# --- بارگذاری مدل تشخیص محتوای نامناسب (NSFW) ---
print("Loading Safety Checker...")
safety_classifier = pipeline("image-classification", model="Falconsai/nsfw_image_detection", device=-1)
def is_image_nsfw(image):
if image is None: return False
try:
results = safety_classifier(image)
for result in results:
if result['label'] == 'nsfw' and result['score'] > 0.75:
return True
return False
except Exception as e:
print(f"Safety check error: {e}")
return False
# --- بارگذاری مدل اصلی ---
from diffusers import FlowMatchEulerDiscreteScheduler
from qwenimage.pipeline_qwenimage_edit_plus import QwenImageEditPlusPipeline
from qwenimage.transformer_qwenimage import QwenImageTransformer2DModel
from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
dtype = torch.bfloat16
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Loading pipeline...")
pipe = QwenImageEditPlusPipeline.from_pretrained(
"Qwen/Qwen-Image-Edit-2509",
transformer=QwenImageTransformer2DModel.from_pretrained(
"linoyts/Qwen-Image-Edit-Rapid-AIO",
subfolder='transformer',
torch_dtype=dtype,
device_map='cuda'
),
torch_dtype=dtype
).to(device)
# بارگذاری LoRA ها
pipe.load_lora_weights("autoweeb/Qwen-Image-Edit-2509-Photo-to-Anime", weight_name="Qwen-Image-Edit-2509-Photo-to-Anime_000001000.safetensors", adapter_name="anime")
pipe.load_lora_weights("dx8152/Qwen-Edit-2509-Multiple-angles", weight_name="镜头转换.safetensors", adapter_name="multiple-angles")
pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Light_restoration", weight_name="移除光影.safetensors", adapter_name="light-restoration")
pipe.load_lora_weights("dx8152/Qwen-Image-Edit-2509-Relight", weight_name="Qwen-Edit-Relight.safetensors", adapter_name="relight")
pipe.load_lora_weights("dx8152/Qwen-Edit-2509-Multi-Angle-Lighting", weight_name="多角度灯光-251116.safetensors", adapter_name="multi-angle-lighting")
pipe.load_lora_weights("tlennon-ie/qwen-edit-skin", weight_name="qwen-edit-skin_1.1_000002750.safetensors", adapter_name="edit-skin")
pipe.load_lora_weights("lovis93/next-scene-qwen-image-lora-2509", weight_name="next-scene_lora-v2-3000.safetensors", adapter_name="next-scene")
pipe.load_lora_weights("vafipas663/Qwen-Edit-2509-Upscale-LoRA", weight_name="qwen-edit-enhance_64-v3_000001000.safetensors", adapter_name="upscale-image")
pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
MAX_SEED = np.iinfo(np.int32).max
LORA_MAPPING = {
"تبدیل عکس به انیمه": "anime",
"تغییر زاویه دید": "multiple-angles",
"اصلاح نور و سایه": "light-restoration",
"نورپردازی مجدد (Relight)": "relight",
"نورپردازی چند زاویه‌ای": "multi-angle-lighting",
"روتوش پوست": "edit-skin",
"صحنه بعدی (سینمایی)": "next-scene",
"افزایش کیفیت (Upscale)": "upscale-image"
}
ASPECT_RATIOS_LIST = [
"خودکار (پیش‌فرض)",
"۱:۱ (مربع - 1024x1024)",
"۱۶:۹ (افقی - 1344x768)",
"۹:۱۶ (عمودی - 768x1344)",
"شخصی‌سازی (Custom)"
]
ASPECT_RATIOS_MAP = {
"خودکار (پیش‌فرض)": "Auto",
"۱:۱ (مربع - 1024x1024)": (1024, 1024),
"۱۶:۹ (افقی - 1344x768)": (1344, 768),
"۹:۱۶ (عمودی - 768x1344)": (768, 1344),
"شخصی‌سازی (Custom)": "Custom"
}
BANNED_WORDS = [
"nude", "naked", "sex", "porn", "undressed", "nsfw", "erotic", "xxx",
"breast", "nipple", "genital", "vagina", "penis", "ass", "butt", "sexual",
"lingerie", "bikini", "swimwear", "underwear", "fetish", "topless",
"exhibitionism", "hentai", "ecchi", "18+"
]
def check_text_safety(text):
text_lower = text.lower()
for word in BANNED_WORDS:
if f" {word} " in f" {text_lower} ":
return False
return True
def translate_prompt(text):
if not text:
return ""
try:
translated = GoogleTranslator(source='auto', target='en').translate(text)
return translated
except Exception as e:
print(f"Translation Error: {e}")
return text
def update_dimensions_on_upload(image):
if image is None:
return 1024, 1024
original_width, original_height = image.size
if original_width > original_height:
new_width = 1024
aspect_ratio = original_height / original_width
new_height = int(new_width * aspect_ratio)
else:
new_height = 1024
aspect_ratio = original_width / original_height
new_width = int(new_height * aspect_ratio)
new_width = (new_width // 8) * 8
new_height = (new_height // 8) * 8
return new_width, new_height
def update_sliders_visibility(choice):
if choice == "شخصی‌سازی (Custom)":
return gr.update(visible=True), gr.update(visible=True)
else:
return gr.update(visible=False), gr.update(visible=False)
def get_error_html(message):
return f"""
<div style="background-color: #fee2e2; border: 1px solid #ef4444; color: #b91c1c; padding: 12px; border-radius: 8px; text-align: center; margin-bottom: 10px; font-weight: bold; display: flex; align-items: center; justify-content: center; gap: 8px;">
<span style="font-size: 1.2em;">⛔</span>
{message}
</div>
"""
def get_success_html(message):
return f"""
<div style="background-color: #dcfce7; border: 1px solid #22c55e; color: #15803d; padding: 12px; border-radius: 8px; text-align: center; margin-bottom: 10px; font-weight: bold; display: flex; align-items: center; justify-content: center; gap: 8px;">
<span style="font-size: 1.2em;">✅</span>
{message}
</div>
"""
def check_quota(fingerprint, subscription_status):
"""
بررسی اعتبار کاربر.
"""
global usage_data_cache
# لاگ برای دیباگ
print(f"CHECK QUOTA: FP={fingerprint}, STATUS={subscription_status}")
if subscription_status == 'paid':
return True, "unlimited"
if not fingerprint or fingerprint == "unknown":
# اگر فینگرپرینت نرسید، موقتا اجازه ندهیم یا یک شناسه موقت بسازیم
# اینجا فرض می‌کنیم کاربر ناشناس است
print("Warning: No fingerprint received.")
return True, 0
today_str = date.today().isoformat()
# رفرش کردن دیتا از فایل (در صورت تغییر توسط ترد دیگر)
usage_data_cache = load_usage_data()
user_record = usage_data_cache.get(fingerprint)
# اگر کاربر جدید است یا روز عوض شده
if not user_record or user_record.get("last_reset") != today_str:
user_record = {"count": 0, "last_reset": today_str}
usage_data_cache[fingerprint] = user_record
# بررسی محدودیت
if user_record["count"] >= USAGE_LIMIT:
return False, user_record["count"]
# کسر اعتبار
user_record["count"] += 1
# ذخیره در فایل
usage_data_cache[fingerprint] = user_record
save_usage_data(usage_data_cache)
return True, user_record["count"]
@spaces.GPU(duration=30)
def infer(
input_image,
prompt,
lora_adapter_persian,
seed,
randomize_seed,
guidance_scale,
steps,
aspect_ratio_selection,
custom_width,
custom_height,
fingerprint,
subscription_status,
progress=gr.Progress(track_tqdm=True)
):
print(f"INFER CALLED: Fingerprint='{fingerprint}', Sub='{subscription_status}'")
# --- بررسی اعتبار ---
is_allowed, usage_count = check_quota(fingerprint, subscription_status)
if not is_allowed:
msg = "شما به محدودیت ۵ تصویر رایگان روزانه خود رسیده‌اید. لطفاً برای استفاده نامحدود حساب خود را ارتقا دهید."
return None, seed, get_error_html(msg)
if input_image is None:
return None, seed, get_error_html("لطفاً ابتدا یک تصویر بارگذاری کنید.")
if is_image_nsfw(input_image):
return None, seed, get_error_html("تصویر ورودی دارای محتوای نامناسب است و پردازش نمی‌شود.")
english_prompt = translate_prompt(prompt)
if not check_text_safety(english_prompt):
return None, seed, get_error_html("متن درخواست شامل کلمات غیرمجاز یا غیراخلاقی است.")
adapter_internal_name = LORA_MAPPING.get(lora_adapter_persian)
if adapter_internal_name:
pipe.set_adapters([adapter_internal_name], adapter_weights=[1.0])
if randomize_seed:
seed = random.randint(0, MAX_SEED)
generator = torch.Generator(device=device).manual_seed(seed)
safety_negative = "nsfw, nude, naked, porn, sexual, xxx, breast, nipple, genital, vagina, penis, ass, lingerie, bikini, swimwear, underwear, fetish, topless, gore, violence, blood"
base_negative = "worst quality, low quality, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, jpeg artifacts, signature, watermark, username, blurry"
final_negative_prompt = f"{safety_negative}, {base_negative}"
original_image = input_image.convert("RGB")
selection_value = ASPECT_RATIOS_MAP.get(aspect_ratio_selection)
if selection_value == "Custom":
width = (int(custom_width) // 8) * 8
height = (int(custom_height) // 8) * 8
elif selection_value == "Auto" or selection_value is None:
width, height = update_dimensions_on_upload(original_image)
else:
width, height = selection_value
try:
result = pipe(
image=original_image,
prompt=english_prompt,
negative_prompt=final_negative_prompt,
height=height,
width=width,
num_inference_steps=steps,
generator=generator,
true_cfg_scale=guidance_scale,
).images[0]
if is_image_nsfw(result):
return None, seed, get_error_html("تصویر تولید شده حاوی محتوای نامناسب بود و حذف شد.")
success_msg = f"تصویر با موفقیت ویرایش شد."
if subscription_status != 'paid':
success_msg += f" (اعتبار باقی‌مانده امروز: {USAGE_LIMIT - usage_count})"
return result, seed, get_success_html(success_msg)
except Exception as e:
error_str = str(e)
if "quota" in error_str.lower() or "exceeded" in error_str.lower():
raise e
return None, seed, get_error_html(f"خطا در پردازش: {error_str}")
@spaces.GPU(duration=30)
def infer_example(input_image, prompt, lora_adapter):
res, s, status = infer(input_image, prompt, lora_adapter, 0, True, 1.0, 4, "خودکار (پیش‌فرض)", 1024, 1024, "example_user", "paid")
return res, s, status
# --- جاوااسکریپت برای دانلود ---
js_download_func = """
async (image) => {
if (!image) {
alert("لطفاً ابتدا تصویر را تولید کنید.");
return;
}
let fileUrl = image.url;
if (fileUrl && !fileUrl.startsWith('http')) {
fileUrl = window.location.origin + fileUrl;
} else if (!fileUrl && image.path) {
fileUrl = window.location.origin + "/file=" + image.path;
}
window.parent.postMessage({
type: 'DOWNLOAD_REQUEST',
url: fileUrl
}, '*');
}
"""
# --- جاوااسکریپت جامع ---
js_global_content = """
<script>
document.addEventListener('DOMContentLoaded', () => {
// ---------------------------------------------
// 1. Fingerprint & Subscription Logic
// ---------------------------------------------
async function getBrowserFingerprint() {
const components = [
navigator.userAgent,
navigator.language,
screen.width + 'x' + screen.height,
new Date().getTimezoneOffset()
];
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.textBaseline = "top"; ctx.font = "14px 'Arial'"; ctx.textBaseline = "alphabetic";
ctx.fillStyle = "#f60"; ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = "#069"; ctx.fillText("a1b2c3d4e5f6g7h8i9j0_!@#$%^&*()", 2, 15);
components.push(canvas.toDataURL());
} catch (e) { components.push("canvas-error"); }
const fingerprintString = components.join('~~~');
let hash = 0;
for (let i = 0; i < fingerprintString.length; i++) {
const char = fingerprintString.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash |= 0;
}
return 'fp_' + Math.abs(hash).toString(16);
}
function isUserPaid(userObject) {
const PREMIUM_PAGE_ID = '1149636';
if (userObject && userObject.isLogin && userObject.accessible_pages) {
if (Array.isArray(userObject.accessible_pages)) {
return userObject.accessible_pages.some(page => String(page) === String(PREMIUM_PAGE_ID));
}
}
return false;
}
function updateHiddenInputs(fingerprint, status) {
const fpInput = document.querySelector('#fingerprint_storage textarea');
const stInput = document.querySelector('#status_storage textarea');
if(fpInput) {
fpInput.value = fingerprint;
fpInput.dispatchEvent(new Event('input', { bubbles: true }));
}
if(stInput) {
stInput.value = status;
stInput.dispatchEvent(new Event('input', { bubbles: true }));
}
}
function updateSubscriptionBadge(status) {
const badge = document.getElementById('user-sub-badge');
if (!badge) return;
if (status === 'paid') {
badge.innerHTML = '✨ اشتراک: <span style="color: #FFD700; font-weight: bold;">نامحدود (PRO)</span>';
badge.style.background = 'linear-gradient(45deg, #1e3a8a, #3b82f6)';
} else {
badge.innerHTML = '👤 اشتراک: <span style="color: #fff; font-weight: bold;">رایگان (۵ اعتبار روزانه)</span>';
badge.style.background = 'linear-gradient(45deg, #4b5563, #6b7280)';
}
badge.style.display = 'inline-block';
// اطمینان از آپدیت شدن اینپوت‌ها هر بار که وضعیت تغییر می‌کند
updateHiddenInputs(window.userFingerprint || 'unknown', status);
}
async function initUserIdentity() {
window.userFingerprint = await getBrowserFingerprint();
window.userStatus = 'free'; // Default
window.parent.postMessage({ type: 'REQUEST_USER_STATUS' }, '*');
updateSubscriptionBadge('free');
// Polling to ensure inputs are populated even if DOM is slow
setInterval(() => {
updateHiddenInputs(window.userFingerprint || 'unknown', window.userStatus || 'free');
}, 2000);
}
window.addEventListener('message', (event) => {
if (event.data && event.data.type === 'USER_STATUS_RESPONSE') {
try {
const userObject = typeof event.data.payload === 'string'
? JSON.parse(event.data.payload)
: event.data.payload;
const status = isUserPaid(userObject) ? 'paid' : 'free';
window.userStatus = status;
updateSubscriptionBadge(status);
} catch (e) {
console.error("Error parsing user status:", e);
updateSubscriptionBadge('free');
}
}
});
initUserIdentity();
// ---------------------------------------------
// 2. UI Helper
// ---------------------------------------------
const forceLight = () => {
const body = document.querySelector('body');
if (body) {
body.classList.remove('dark');
body.style.backgroundColor = '#f5f7fa';
body.style.color = '#333333';
}
document.querySelectorAll('.dark').forEach(el => el.classList.remove('dark'));
};
forceLight();
setInterval(forceLight, 1000);
window.closeErrorModal = function() {
const modal = document.getElementById('custom-quota-modal');
if (modal) modal.remove();
};
const showQuotaModal = () => {
if (document.getElementById('custom-quota-modal')) return;
const modalHtml = `
<div id="custom-quota-modal" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.6); backdrop-filter: blur(5px); z-index: 99999; display: flex; align-items: center; justify-content: center; font-family: 'Vazirmatn', sans-serif;">
<div class="ip-reset-guide-container">
<div class="guide-header">
<div style="font-size: 2rem;">💎</div>
<div>
<h2>پایان اعتبار رایگان امروز</h2>
<p>برای ادامه، حساب خود را ارتقا دهید.</p>
</div>
</div>
<div class="guide-content">
<div class="info-card" style="border-color: #fbbf24; background: #fffbeb;">
<p>شما از ۵ اعتبار رایگان امروز خود استفاده کرده‌اید.</p>
</div>
<div class="video-button-container">
<button onclick="parent.postMessage({ type: 'NAVIGATE_TO_PREMIUM' }, '*')" class="elegant-video-button" style="background: linear-gradient(135deg, #f59e0b, #d97706) !important; color: white !important;">
<span>⭐️ خرید نسخه نامحدود</span>
</button>
</div>
</div>
<div class="guide-actions">
<button class="action-button back-button" onclick="window.closeErrorModal()">
<span>بستن</span>
</button>
</div>
</div>
</div>
`;
document.body.insertAdjacentHTML('beforeend', modalHtml);
};
setInterval(() => {
const potentialErrors = document.querySelectorAll('.toast-body, .error, .toast-wrap, .eta-bar, div[class*="error"]');
potentialErrors.forEach(el => {
const text = el.innerText || "";
if (text.includes('محدودیت') && text.includes('رایگان')) {
showQuotaModal();
el.style.display = 'none';
const parentWrap = el.closest('.toast-wrap');
if(parentWrap) parentWrap.style.display = 'none';
}
});
}, 500);
});
</script>
"""
# --- CSS Updated ---
css_code = """
<style>
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;700&display=swap');
:root, .dark, body, .gradio-container {
--body-background-fill: #f5f7fa !important;
--body-text-color: #1f2937 !important;
font-family: 'Vazirmatn', sans-serif !important;
}
/* Hide the storage inputs but keep them in DOM */
#fingerprint_storage, #status_storage {
display: none !important;
/* We use display:none with !important in CSS instead of visible=False in Python
to ensure the element exists in DOM for JS to find it. */
}
/* Modal CSS */
.ip-reset-guide-container {
text-align: right;
direction: rtl;
background: #fff;
padding: 25px;
border-radius: 16px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
width: 90%;
max-width: 400px;
font-family: 'Vazirmatn', sans-serif !important;
}
.guide-header { display: flex; align-items: center; margin-bottom: 15px; gap: 15px; }
.guide-header h2 { margin: 0; font-size: 1.2rem; color: #1f2937; }
.guide-content { font-size: 0.95rem; color: #4b5563; line-height: 1.6; }
.info-card { padding: 12px; border-radius: 8px; border: 1px solid #e5e7eb; margin-bottom: 15px; }
.elegant-video-button {
display: inline-flex; width: 100%; justify-content: center; padding: 12px;
border-radius: 12px; border: none; font-weight: bold; cursor: pointer; transition: transform 0.2s;
}
.elegant-video-button:hover { transform: translateY(-2px); }
.action-button { width: 100%; padding: 10px; border-radius: 8px; border: 1px solid #d1d5db; background: white; cursor: pointer; }
/* Main App Styles */
#col-container {
margin: 0 auto;
max-width: 980px;
direction: rtl;
text-align: right;
padding: 30px;
background: #ffffff !important;
border-radius: 24px;
box-shadow: 0 10px 40px -10px rgba(0,0,0,0.08);
border: 1px solid rgba(255,255,255,0.8);
}
#main-title h1 {
font-size: 2.4em !important;
text-align: center;
color: #1a202c !important;
margin-bottom: 5px;
font-weight: 800;
background: -webkit-linear-gradient(45deg, #2563eb, #1e40af);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
#main-description {
text-align: center;
font-size: 1.15em;
color: #4b5563 !important;
margin-bottom: 10px;
}
#badge-container {
text-align: center;
margin-bottom: 30px;
height: 30px;
}
.primary-btn {
background: linear-gradient(135deg, #10b981 0%, #059669 100%) !important;
border: none !important;
color: white !important;
font-weight: 700 !important;
padding: 14px 28px !important;
border-radius: 14px !important;
}
</style>
"""
combined_html = css_code + js_global_content
with gr.Blocks() as demo:
gr.HTML(combined_html)
# تغییر مهم: visible=True قرار دادیم تا در DOM باشد، اما با CSS مخفی کردیم
fingerprint_box = gr.Textbox(elem_id="fingerprint_storage", visible=True)
status_box_input = gr.Textbox(elem_id="status_storage", visible=True)
with gr.Column(elem_id="col-container"):
gr.Markdown("# **ویرایشگر هوشمند آلفا**", elem_id="main-title")
gr.Markdown(
"با هوش مصنوعی آلفا تصاویر تونو به مدل های مختلف ویرایش کنید.",
elem_id="main-description"
)
# محل قرارگیری بج وضعیت اشتراک
gr.HTML('<div id="badge-container"><span id="user-sub-badge"></span></div>')
with gr.Row(equal_height=True):
with gr.Column():
input_image = gr.Image(label="بارگذاری تصویر", type="pil", height=320)
prompt = gr.Text(
label="دستور ویرایش (به فارسی)",
show_label=True,
placeholder="مثال: تصویر را به سبک انیمه تبدیل کن...",
rtl=True,
lines=3
)
status_box = gr.HTML(label="وضعیت")
run_button = gr.Button("✨ شروع پردازش و ساخت تصویر", variant="primary", elem_classes="primary-btn", elem_id="run-btn")
with gr.Column():
output_image = gr.Image(label="تصویر نهایی", interactive=False, format="png", height=380)
download_button = gr.Button("📥 دانلود و ذخیره تصویر", variant="secondary", elem_id="download-btn", elem_classes="primary-btn")
with gr.Row():
lora_adapter = gr.Dropdown(
label="انتخاب سبک ویرایش (LoRA)",
choices=list(LORA_MAPPING.keys()),
value="تبدیل عکس به انیمه"
)
with gr.Accordion("تنظیمات پیشرفته", open=False, visible=True):
aspect_ratio_selection = gr.Dropdown(
label="ابعاد تصویر خروجی",
choices=ASPECT_RATIOS_LIST,
value="خودکار (پیش‌فرض)",
interactive=True
)
with gr.Row(visible=False) as custom_dims_row:
custom_width = gr.Slider(
label="عرض دلخواه (Width)",
minimum=256, maximum=2048, step=8, value=1024
)
custom_height = gr.Slider(
label="ارتفاع دلخواه (Height)",
minimum=256, maximum=2048, step=8, value=1024
)
seed = gr.Slider(label="دانه تصادفی (Seed)", minimum=0, maximum=MAX_SEED, step=1, value=0)
randomize_seed = gr.Checkbox(label="استفاده از Seed تصادفی", value=True)
guidance_scale = gr.Slider(label="میزان وفاداری به متن (Guidance Scale)", minimum=1.0, maximum=10.0, step=0.1, value=1.0)
steps = gr.Slider(label="تعداد مراحل پردازش (Steps)", minimum=1, maximum=50, step=1, value=4)
def toggle_row(choice):
if choice == "شخصی‌سازی (Custom)":
return gr.update(visible=True)
return gr.update(visible=False)
aspect_ratio_selection.change(
fn=toggle_row,
inputs=aspect_ratio_selection,
outputs=custom_dims_row
)
gr.Examples(
examples=[
["examples/1.jpg", "تبدیل به انیمه کن.", "تبدیل عکس به انیمه"],
["examples/5.jpg", "سایه‌ها را حذف کن و نورپردازی نرم به تصویر بده.", "اصلاح نور و سایه"],
["examples/4.jpg", "از فیلتر ساعت طلایی با پخش نور ملایم استفاده کن.", "نورپردازی مجدد (Relight)"],
["examples/2.jpeg", "دوربین را ۴۵ درجه به سمت چپ بچرخان.", "تغییر زاویه دید"],
["examples/7.jpg", "منبع نور را از سمت راست عقب قرار بده.", "نورپردازی چند زاویه‌ای"],
["examples/10.jpeg", "کیفیت تصویر را افزایش بده (Upscale).", "افزایش کیفیت (Upscale)"],
["examples/7.jpg", "منبع نور را از پایین بتابان.", "نورپردازی چند زاویه‌ای"],
["examples/2.jpeg", "زاویه دوربین را به نمای بالا گوشه راست تغییر بده.", "تغییر زاویه دید"],
["examples/9.jpg", "دوربین کمی به جلو حرکت می‌کند در حالی که نور خورشید از میان ابرها می‌تابد و درخششی نرم اطراف شبح شخصیت در مه ایجاد می‌کند. سبک سینمایی واقعی.", "صحنه بعدی (سینمایی)"],
["examples/8.jpg", "جزئیات پوست سوژه را برجسته‌تر و طبیعی‌تر کن.", "روتوش پوست"],
["examples/6.jpg", "دوربین را به نمای پایین به بالا تغییر بده.", "تغییر زاویه دید"],
],
inputs=[input_image, prompt, lora_adapter],
outputs=[output_image, seed, status_box],
fn=infer_example,
cache_examples=False,
label="نمونه‌ها (برای تست کلیک کنید)"
)
run_button.click(
fn=infer,
inputs=[
input_image, prompt, lora_adapter, seed, randomize_seed,
guidance_scale, steps, aspect_ratio_selection,
custom_width, custom_height,
fingerprint_box, status_box_input
],
outputs=[output_image, seed, status_box],
api_name="predict"
)
download_button.click(
fn=None,
inputs=[output_image],
outputs=None,
js=js_download_func
)
if __name__ == "__main__":
demo.queue(max_size=30).launch(show_error=True)