Slait commited on
Commit
af5c770
·
verified ·
1 Parent(s): 1b05a8c

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +659 -0
app.py ADDED
@@ -0,0 +1,659 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import torch
3
+ from transformers import Qwen3VLForConditionalGeneration, AutoProcessor
4
+ from qwen_vl_utils import process_vision_info
5
+ from PIL import Image
6
+ import random
7
+ import os
8
+ import warnings
9
+ from typing import List, Tuple, Optional
10
+
11
+ # Suppress specific warnings
12
+ warnings.filterwarnings('ignore', message='.*meta device.*')
13
+
14
+ # Multi-language support
15
+ TRANSLATIONS = {
16
+ "en": {
17
+ "title": "Qwen VL Image Description Generator",
18
+ "header": "🖼️ Image Description Generator based on Qwen Vision Language Models",
19
+ "subtitle": "Upload an image and enter a prompt to generate a description using Qwen VL models.",
20
+ "language": "Language",
21
+ "language_info": "Select language",
22
+ "model_selection": "Model Selection",
23
+ "model_info": "Select a model for generating descriptions",
24
+ "advanced_params": "⚙️ Advanced Parameters",
25
+ "max_tokens": "Max New Tokens",
26
+ "max_tokens_info": "Maximum number of tokens to generate",
27
+ "temperature": "Temperature",
28
+ "temperature_info": "Controls randomness of generation",
29
+ "top_p": "Top-p (nucleus sampling)",
30
+ "top_p_info": "Probability threshold for token sampling",
31
+ "top_k": "Top-k",
32
+ "top_k_info": "Number of most probable tokens to consider",
33
+ "seed": "Seed",
34
+ "seed_info": "Seed for reproducibility (-1 for random)",
35
+ "random_seed_btn": "🎲 Random Seed",
36
+ "single_processing": "📄 Single Processing",
37
+ "batch_processing": "📚 Batch Processing",
38
+ "upload_image": "Upload Image",
39
+ "prompt": "Prompt",
40
+ "prompt_placeholder": "For example: Create a product description for online store",
41
+ "generate_btn": "🚀 Generate Description",
42
+ "result": "Result",
43
+ "upload_images": "Upload Images",
44
+ "prompts_multiline": "Prompts (one per line)",
45
+ "prompts_placeholder": "Create a product description for online store\nCreate SEO Description for product\n...",
46
+ "prompts_info": "Specify one prompt for all images or one prompt per image",
47
+ "process_batch_btn": "🚀 Process Batch",
48
+ "results": "Results",
49
+ "examples_title": "💡 Example Prompts:",
50
+ "example_1": "Create a product description for online store",
51
+ "example_2": "Create an SEO description for a product with a maximum of 160 characters.",
52
+ "example_3": "Create an attractive product description for marketplace",
53
+ "example_4": "Describe image in detail for product catalog",
54
+ "error_no_image": "Please upload an image",
55
+ "error_no_prompt": "Please enter a prompt",
56
+ "error_no_images": "Please upload images",
57
+ "error_no_prompts": "Please enter prompts (one per line)",
58
+ "error_prompt_mismatch": "Number of prompts ({}) does not match number of images ({}). Specify either one prompt for all images or one prompt per image.",
59
+ "error_generation": "Error generating description: {}",
60
+ "loading_model": "Loading model: {}",
61
+ "model_loaded": "Model {} successfully loaded on {}",
62
+ "image_label": "=== Image {}: {} ===",
63
+ "prompt_label": "Prompt: {}",
64
+ "result_label": "Result: {}",
65
+ "model_size_warning": "⚠️ Note: Large models (8B+) may use CPU offloading if GPU memory is insufficient, which can slow down generation."
66
+ },
67
+ "ru": {
68
+ "title": "Генератор описаний изображений Qwen VL",
69
+ "header": "🖼️ Генератор описаний изображений на основе Qwen Vision Language Models",
70
+ "subtitle": "Загрузите изображение и введите промт для генерации описания с помощью моделей Qwen VL.",
71
+ "language": "Язык",
72
+ "language_info": "Выберите язык",
73
+ "model_selection": "Выбор модели",
74
+ "model_info": "Выберите модель для генерации описаний",
75
+ "advanced_params": "⚙️ Расширенные параметры",
76
+ "max_tokens": "Макс. количество новых токенов",
77
+ "max_tokens_info": "Максимальное количество токенов для генерации",
78
+ "temperature": "Температура",
79
+ "temperature_info": "Контролирует случайность генерации",
80
+ "top_p": "Top-p (nucleus sampling)",
81
+ "top_p_info": "Вероятностный порог для выборки токенов",
82
+ "top_k": "Top-k",
83
+ "top_k_info": "Количество наиболее вероятных токенов для рассмотрения",
84
+ "seed": "Seed",
85
+ "seed_info": "Seed для воспроизводимости (-1 для случайного)",
86
+ "random_seed_btn": "🎲 Случайный seed",
87
+ "single_processing": "📄 Одиночная обработка",
88
+ "batch_processing": "📚 Пакетная обработка",
89
+ "upload_image": "Загрузите изображение",
90
+ "prompt": "Промт",
91
+ "prompt_placeholder": "Например: Создать описание товара для онлайн магазина",
92
+ "generate_btn": "🚀 Генерировать описание",
93
+ "result": "Результат",
94
+ "upload_images": "Загрузите изображения",
95
+ "prompts_multiline": "Промты (по одному на строку)",
96
+ "prompts_placeholder": "Создать описание товара для онлайн магазина\nСоздать SEO Description для товара\n...",
97
+ "prompts_info": "Укажите один промт для всех изображений или по одному промту на каждое изображение",
98
+ "process_batch_btn": "🚀 Обработать пакет",
99
+ "results": "Результаты",
100
+ "examples_title": "💡 Примеры промтов:",
101
+ "example_1": "Создать описание товара '' на русском языке",
102
+ "example_2": "Создать SEO Description для товара максимум 160 символов на русском языке",
103
+ "example_3": "Создать привлекательное описание продукта для маркетплейса на русском языке",
104
+ "example_4": "Детально описать изображение для каталога товаров на русском языке",
105
+ "error_no_image": "Пожалуйста, загрузите изображение",
106
+ "error_no_prompt": "Пожалуйста, введите промт",
107
+ "error_no_images": "Пожалуйста, загрузите изображения",
108
+ "error_no_prompts": "Пожалуйста, введите промты (по одному на строку)",
109
+ "error_prompt_mismatch": "Количество промтов ({}) не совпадает с количеством изображений ({}). Укажите либо один промт для всех изображений, либо по одному промту на каждое изображение.",
110
+ "error_generation": "Ошибка при генерации описания: {}",
111
+ "loading_model": "Загрузка модели: {}",
112
+ "model_loaded": "Модель {} успешно загружена на {}",
113
+ "image_label": "=== Изображение {}: {} ===",
114
+ "prompt_label": "Промт: {}",
115
+ "result_label": "Результат: {}",
116
+ "model_size_warning": "⚠️ Примечание: Большие модели (8B+) могут использовать выгрузку на CPU при недостатке памяти GPU, что может замедлить генерацию."
117
+ },
118
+ "zh": {
119
+ "title": "Qwen VL 图像描述生成器",
120
+ "header": "🖼️ 基于 Qwen Vision Language Models 的图像描述生成器",
121
+ "subtitle": "上传图像并输入提示词,使用 Qwen VL 模型生成描述。",
122
+ "language": "语言",
123
+ "language_info": "选择语言",
124
+ "model_selection": "模型选择",
125
+ "model_info": "选择用于生成描述的模型",
126
+ "advanced_params": "⚙️ 高级参数",
127
+ "max_tokens": "最大新令牌数",
128
+ "max_tokens_info": "生成的最大令牌数",
129
+ "temperature": "温度",
130
+ "temperature_info": "控制生成的随机性",
131
+ "top_p": "Top-p(核采样)",
132
+ "top_p_info": "令牌采样的概率阈值",
133
+ "top_k": "Top-k",
134
+ "top_k_info": "考虑的最可能令牌数",
135
+ "seed": "随机种子",
136
+ "seed_info": "用于可重现性的种子(-1 表示随机)",
137
+ "random_seed_btn": "🎲 随机种子",
138
+ "single_processing": "📄 单张处理",
139
+ "batch_processing": "📚 批量处理",
140
+ "upload_image": "上传图像",
141
+ "prompt": "提示词",
142
+ "prompt_placeholder": "例如:为在线商店创建产品描述",
143
+ "generate_btn": "🚀 生成描述",
144
+ "result": "结果",
145
+ "upload_images": "上传图像",
146
+ "prompts_multiline": "提示词(每行一个)",
147
+ "prompts_placeholder": "为在线商店创建产品描述\n为产品创建SEO描述\n...",
148
+ "prompts_info": "为所有图像指定一个提示词,或为每个图像指定一个提示词",
149
+ "process_batch_btn": "🚀 处理批次",
150
+ "results": "结果",
151
+ "examples_title": "💡 示例提示词:",
152
+ "example_1": "为在线商店创建产品描述",
153
+ "example_2": "为产品创建SEO描述最多 160 个字符",
154
+ "example_3": "为市场创建有吸引力的产品描述",
155
+ "example_4": "详细描述产品目录的图像",
156
+ "error_no_image": "请上传图像",
157
+ "error_no_prompt": "请输入提示词",
158
+ "error_no_images": "请上传图像",
159
+ "error_no_prompts": "请输入提示词(每行一个)",
160
+ "error_prompt_mismatch": "提示词数量({})与图像数量({})不匹配。请为所有图像指定一个提示词,或为每个图像指定一个提示词。",
161
+ "error_generation": "生成描述时出错:{}",
162
+ "loading_model": "正在加载模型:{}",
163
+ "model_loaded": "模型 {} 已成功加载到 {}",
164
+ "image_label": "=== 图像 {}: {} ===",
165
+ "prompt_label": "提示词:{}",
166
+ "result_label": "结果:{}",
167
+ "model_size_warning": "⚠️ 注意:如果 GPU 内存不足,大型模型(8B+)可能会使用 CPU 卸载,这可能会减慢生成速度。"
168
+ }
169
+ }
170
+
171
+ # Default language
172
+ current_language = "en"
173
+
174
+ def get_text(key: str) -> str:
175
+ """Get translated text for the current language"""
176
+ return TRANSLATIONS[current_language].get(key, key)
177
+
178
+ class ImageDescriptionGenerator:
179
+ def __init__(self):
180
+ self.model = None
181
+ self.processor = None
182
+ self.current_model_name = None
183
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
184
+
185
+ def load_model(self, model_name: str):
186
+ """Загрузка модели только если она еще не загружена или изменилась"""
187
+ if self.current_model_name == model_name and self.model is not None:
188
+ return
189
+
190
+ print(get_text("loading_model").format(model_name))
191
+
192
+ # Предупреждение о больших моделях
193
+ if "8B" in model_name or "4B" in model_name:
194
+ print(get_text("model_size_warning"))
195
+
196
+ # Освобождаем память от предыдущей модели
197
+ if self.model is not None:
198
+ del self.model
199
+ del self.processor
200
+ torch.cuda.empty_cache() if torch.cuda.is_available() else None
201
+
202
+ # Загружаем новую модель с подавлением предупреждений
203
+ with warnings.catch_warnings():
204
+ warnings.filterwarnings('ignore')
205
+ self.model = Qwen3VLForConditionalGeneration.from_pretrained(
206
+ model_name,
207
+ dtype=torch.bfloat16 if torch.cuda.is_available() else torch.float32,
208
+ device_map="auto"
209
+ )
210
+ self.processor = AutoProcessor.from_pretrained(model_name)
211
+
212
+ self.current_model_name = model_name
213
+ print(get_text("model_loaded").format(model_name, self.device))
214
+
215
+ def generate_description(
216
+ self,
217
+ image_path: str,
218
+ prompt: str,
219
+ model_name: str,
220
+ max_new_tokens: int = 1024,
221
+ temperature: float = 0.6,
222
+ top_p: float = 0.9,
223
+ top_k: int = 50,
224
+ seed: int = -1
225
+ ) -> str:
226
+ """Генерация описания для одного изображения"""
227
+ try:
228
+ # Загружаем модель если необходимо
229
+ self.load_model(model_name)
230
+
231
+ # Устанавливаем seed если указан
232
+ if seed != -1:
233
+ torch.manual_seed(seed)
234
+ if torch.cuda.is_available():
235
+ torch.cuda.manual_seed(seed)
236
+
237
+ # Подготавливаем сообщения для модели
238
+ messages = [
239
+ {
240
+ "role": "user",
241
+ "content": [
242
+ {
243
+ "type": "image",
244
+ "image": image_path,
245
+ },
246
+ {"type": "text", "text": prompt},
247
+ ],
248
+ }
249
+ ]
250
+
251
+ # Подготавливаем текст для модели
252
+ text = self.processor.apply_chat_template(
253
+ messages, tokenize=False, add_generation_prompt=True
254
+ )
255
+
256
+ # Обрабатываем изображение и текст
257
+ image_inputs, video_inputs = process_vision_info(messages)
258
+ inputs = self.processor(
259
+ text=[text],
260
+ images=image_inputs,
261
+ videos=video_inputs,
262
+ padding=True,
263
+ return_tensors="pt",
264
+ )
265
+ inputs = inputs.to(self.device)
266
+
267
+ # Генерируем ответ
268
+ with torch.no_grad():
269
+ generated_ids = self.model.generate(
270
+ **inputs,
271
+ max_new_tokens=max_new_tokens,
272
+ temperature=temperature,
273
+ top_p=top_p,
274
+ top_k=top_k,
275
+ do_sample=True if temperature > 0 else False
276
+ )
277
+
278
+ # Декодируем результат
279
+ generated_ids_trimmed = [
280
+ out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
281
+ ]
282
+ output_text = self.processor.batch_decode(
283
+ generated_ids_trimmed,
284
+ skip_special_tokens=True,
285
+ clean_up_tokenization_spaces=False
286
+ )
287
+
288
+ return output_text[0]
289
+
290
+ except Exception as e:
291
+ return get_text("error_generation").format(str(e))
292
+
293
+ # Создаем глобальный экземпляр генератора
294
+ generator = ImageDescriptionGenerator()
295
+
296
+ def process_single_image(
297
+ image,
298
+ prompt: str,
299
+ model_name: str,
300
+ max_new_tokens: int,
301
+ temperature: float,
302
+ top_p: float,
303
+ top_k: int,
304
+ seed: int
305
+ ) -> str:
306
+ """Обработка одного изображения"""
307
+ if image is None:
308
+ return get_text("error_no_image")
309
+
310
+ if not prompt.strip():
311
+ return get_text("error_no_prompt")
312
+
313
+ # Сохраняем временное изображение если это numpy array
314
+ if hasattr(image, 'shape'):
315
+ temp_path = "temp_image.jpg"
316
+ Image.fromarray(image).save(temp_path)
317
+ image_path = temp_path
318
+ else:
319
+ image_path = image
320
+
321
+ result = generator.generate_description(
322
+ image_path=image_path,
323
+ prompt=prompt,
324
+ model_name=model_name,
325
+ max_new_tokens=max_new_tokens,
326
+ temperature=temperature,
327
+ top_p=top_p,
328
+ top_k=top_k,
329
+ seed=seed
330
+ )
331
+
332
+ # Удаляем временный файл
333
+ if hasattr(image, 'shape') and os.path.exists(temp_path):
334
+ os.remove(temp_path)
335
+
336
+ return result
337
+
338
+ def process_batch_images(
339
+ files: List,
340
+ prompts_text: str,
341
+ model_name: str,
342
+ max_new_tokens: int,
343
+ temperature: float,
344
+ top_p: float,
345
+ top_k: int,
346
+ seed: int
347
+ ) -> str:
348
+ """Обработка пакета изображений"""
349
+ if not files:
350
+ return get_text("error_no_images")
351
+
352
+ if not prompts_text.strip():
353
+ return get_text("error_no_prompts")
354
+
355
+ # Разбиваем промты по строкам
356
+ prompts = [p.strip() for p in prompts_text.split('\n') if p.strip()]
357
+
358
+ if len(prompts) == 1:
359
+ # Если один промт, используем его для всех изображений
360
+ prompts = prompts * len(files)
361
+ elif len(prompts) != len(files):
362
+ return get_text("error_prompt_mismatch").format(len(prompts), len(files))
363
+
364
+ results = []
365
+ for idx, (file, prompt) in enumerate(zip(files, prompts), 1):
366
+ image_path = file.name if hasattr(file, 'name') else file
367
+ result = generator.generate_description(
368
+ image_path=image_path,
369
+ prompt=prompt,
370
+ model_name=model_name,
371
+ max_new_tokens=max_new_tokens,
372
+ temperature=temperature,
373
+ top_p=top_p,
374
+ top_k=top_k,
375
+ seed=seed if seed == -1 else seed + idx - 1 # Разный seed для каждого изображения
376
+ )
377
+ results.append(get_text("image_label").format(idx, os.path.basename(image_path)) + "\n")
378
+ results.append(get_text("prompt_label").format(prompt) + "\n")
379
+ results.append(get_text("result_label").format(result) + "\n\n")
380
+
381
+ return "".join(results)
382
+
383
+ def random_seed() -> int:
384
+ """Генерация случайного seed"""
385
+ return random.randint(0, 2**32 - 1)
386
+
387
+ def create_interface():
388
+ """Create Gradio interface with current language"""
389
+ with gr.Blocks(title=get_text("title"), theme=gr.themes.Soft()) as demo:
390
+ # Header that will be updated
391
+ header_md = gr.Markdown(f"""
392
+ # {get_text("header")}
393
+
394
+ {get_text("subtitle")}
395
+ """)
396
+
397
+ # Общие настройки - модель и язык в одном ряду
398
+ with gr.Row():
399
+ model_dropdown = gr.Dropdown(
400
+ choices=[
401
+ "Qwen/Qwen3-VL-2B-Instruct",
402
+ "Qwen/Qwen3-VL-4B-Instruct",
403
+ "Qwen/Qwen3-VL-8B-Instruct",
404
+ "Qwen/Qwen3-VL-32B-Instruct",
405
+ "Qwen/Qwen3-VL-2B-Thinking",
406
+ "Qwen/Qwen3-VL-4B-Thinking",
407
+ "Qwen/Qwen3-VL-8B-Thinking",
408
+ "Qwen/Qwen3-VL-32B-Thinking",
409
+ ],
410
+ value="Qwen/Qwen3-VL-2B-Instruct",
411
+ label=get_text("model_selection"),
412
+ info=get_text("model_info"),
413
+ scale=3
414
+ )
415
+ language_dropdown = gr.Dropdown(
416
+ choices=[("English", "en"), ("Русский", "ru"), ("中文", "zh")],
417
+ value=current_language,
418
+ label=get_text("language"),
419
+ info=get_text("language_info"),
420
+ scale=1
421
+ )
422
+
423
+ # Расширенные параметры
424
+ advanced_accordion = gr.Accordion(get_text("advanced_params"), open=False)
425
+ with advanced_accordion:
426
+ with gr.Row():
427
+ max_tokens_slider = gr.Slider(
428
+ minimum=1,
429
+ maximum=4096,
430
+ value=1024,
431
+ step=1,
432
+ label=get_text("max_tokens"),
433
+ info=get_text("max_tokens_info")
434
+ )
435
+ temperature_slider = gr.Slider(
436
+ minimum=0.1,
437
+ maximum=2.0,
438
+ value=0.6,
439
+ step=0.1,
440
+ label=get_text("temperature"),
441
+ info=get_text("temperature_info")
442
+ )
443
+
444
+ with gr.Row():
445
+ top_p_slider = gr.Slider(
446
+ minimum=0.05,
447
+ maximum=1.0,
448
+ value=0.9,
449
+ step=0.05,
450
+ label=get_text("top_p"),
451
+ info=get_text("top_p_info")
452
+ )
453
+ top_k_slider = gr.Slider(
454
+ minimum=1,
455
+ maximum=1000,
456
+ value=50,
457
+ step=1,
458
+ label=get_text("top_k"),
459
+ info=get_text("top_k_info")
460
+ )
461
+
462
+ with gr.Row():
463
+ seed_number = gr.Number(
464
+ value=-1,
465
+ label=get_text("seed"),
466
+ info=get_text("seed_info"),
467
+ precision=0
468
+ )
469
+ random_seed_btn = gr.Button(get_text("random_seed_btn"), size="sm")
470
+
471
+ # Вкладки для одиночной и пакетной обработки
472
+ tabs = gr.Tabs()
473
+ with tabs:
474
+ # Вкладка одиночной обработки
475
+ single_tab = gr.TabItem(get_text("single_processing"))
476
+ with single_tab:
477
+ with gr.Row():
478
+ with gr.Column(scale=1):
479
+ single_image = gr.Image(
480
+ type="numpy",
481
+ label=get_text("upload_image"),
482
+ height=350
483
+ )
484
+ single_prompt = gr.Textbox(
485
+ label=get_text("prompt"),
486
+ placeholder=get_text("prompt_placeholder"),
487
+ lines=3
488
+ )
489
+ single_submit_btn = gr.Button(get_text("generate_btn"), variant="primary")
490
+
491
+ with gr.Column(scale=1):
492
+ single_output = gr.Textbox(
493
+ label=get_text("result"),
494
+ lines=15,
495
+ show_copy_button=True
496
+ )
497
+
498
+ # Кликабельные примеры промтов
499
+ gr.Markdown(f"### {get_text('examples_title')}")
500
+ single_examples = gr.Examples(
501
+ examples=[
502
+ [get_text("example_1")],
503
+ [get_text("example_2")],
504
+ [get_text("example_3")],
505
+ [get_text("example_4")]
506
+ ],
507
+ inputs=[single_prompt],
508
+ label=""
509
+ )
510
+
511
+ # Вкладка пакетной обработки
512
+ batch_tab = gr.TabItem(get_text("batch_processing"))
513
+ with batch_tab:
514
+ with gr.Row():
515
+ with gr.Column(scale=1):
516
+ batch_images = gr.File(
517
+ file_count="multiple",
518
+ label=get_text("upload_images"),
519
+ file_types=["image"]
520
+ )
521
+ batch_prompts = gr.Textbox(
522
+ label=get_text("prompts_multiline"),
523
+ placeholder=get_text("prompts_placeholder"),
524
+ lines=5,
525
+ info=get_text("prompts_info")
526
+ )
527
+ batch_submit_btn = gr.Button(get_text("process_batch_btn"), variant="primary")
528
+
529
+ with gr.Column(scale=1):
530
+ batch_output = gr.Textbox(
531
+ label=get_text("results"),
532
+ lines=20,
533
+ show_copy_button=True
534
+ )
535
+
536
+ # Примеры использования
537
+ examples_md = gr.Markdown(f"""
538
+ ### {get_text("examples_title")}
539
+ - "{get_text("example_1")}"
540
+ - "{get_text("example_2")}"
541
+ - "{get_text("example_3")}"
542
+ - "{get_text("example_4")}"
543
+ """)
544
+
545
+ # Обра��отчики событий
546
+ def change_language(lang):
547
+ global current_language
548
+ current_language = lang
549
+
550
+ # Return updated text for all components
551
+ return [
552
+ f"""
553
+ # {get_text("header")}
554
+
555
+ {get_text("subtitle")}
556
+ """, # header_md
557
+ gr.update(label=get_text("model_selection"), info=get_text("model_info")), # model_dropdown
558
+ gr.update(label=get_text("language"), info=get_text("language_info")), # language_dropdown
559
+ gr.update(label=get_text("advanced_params")), # advanced_accordion
560
+ gr.update(label=get_text("max_tokens"), info=get_text("max_tokens_info")), # max_tokens_slider
561
+ gr.update(label=get_text("temperature"), info=get_text("temperature_info")), # temperature_slider
562
+ gr.update(label=get_text("top_p"), info=get_text("top_p_info")), # top_p_slider
563
+ gr.update(label=get_text("top_k"), info=get_text("top_k_info")), # top_k_slider
564
+ gr.update(label=get_text("seed"), info=get_text("seed_info")), # seed_number
565
+ gr.update(value=get_text("random_seed_btn")), # random_seed_btn
566
+ gr.update(label=get_text("single_processing")), # single_tab
567
+ gr.update(label=get_text("upload_image")), # single_image
568
+ gr.update(label=get_text("prompt"), placeholder=get_text("prompt_placeholder")), # single_prompt
569
+ gr.update(value=get_text("generate_btn")), # single_submit_btn
570
+ gr.update(label=get_text("result")), # single_output
571
+ gr.update(label=get_text("batch_processing")), # batch_tab
572
+ gr.update(label=get_text("upload_images")), # batch_images
573
+ gr.update(label=get_text("prompts_multiline"), placeholder=get_text("prompts_placeholder"), info=get_text("prompts_info")), # batch_prompts
574
+ gr.update(value=get_text("process_batch_btn")), # batch_submit_btn
575
+ gr.update(label=get_text("results")), # batch_output
576
+ f"""
577
+ ### {get_text("examples_title")}
578
+ - "{get_text("example_1")}"
579
+ - "{get_text("example_2")}"
580
+ - "{get_text("example_3")}"
581
+ - "{get_text("example_4")}"
582
+ """ # examples_md
583
+ ]
584
+
585
+ language_dropdown.change(
586
+ fn=change_language,
587
+ inputs=language_dropdown,
588
+ outputs=[
589
+ header_md,
590
+ model_dropdown,
591
+ language_dropdown,
592
+ advanced_accordion,
593
+ max_tokens_slider,
594
+ temperature_slider,
595
+ top_p_slider,
596
+ top_k_slider,
597
+ seed_number,
598
+ random_seed_btn,
599
+ single_tab,
600
+ single_image,
601
+ single_prompt,
602
+ single_submit_btn,
603
+ single_output,
604
+ batch_tab,
605
+ batch_images,
606
+ batch_prompts,
607
+ batch_submit_btn,
608
+ batch_output,
609
+ examples_md
610
+ ]
611
+ )
612
+
613
+ random_seed_btn.click(
614
+ fn=random_seed,
615
+ outputs=seed_number
616
+ )
617
+
618
+ single_submit_btn.click(
619
+ fn=process_single_image,
620
+ inputs=[
621
+ single_image,
622
+ single_prompt,
623
+ model_dropdown,
624
+ max_tokens_slider,
625
+ temperature_slider,
626
+ top_p_slider,
627
+ top_k_slider,
628
+ seed_number
629
+ ],
630
+ outputs=single_output
631
+ )
632
+
633
+ batch_submit_btn.click(
634
+ fn=process_batch_images,
635
+ inputs=[
636
+ batch_images,
637
+ batch_prompts,
638
+ model_dropdown,
639
+ max_tokens_slider,
640
+ temperature_slider,
641
+ top_p_slider,
642
+ top_k_slider,
643
+ seed_number
644
+ ],
645
+ outputs=batch_output
646
+ )
647
+
648
+ return demo
649
+
650
+ # Создаем интерфейс Gradio
651
+ demo = create_interface()
652
+
653
+ if __name__ == "__main__":
654
+ demo.launch(
655
+ server_name="0.0.0.0",
656
+ server_port=7860,
657
+ share=False,
658
+ show_error=True
659
+ )