PandaArtStation commited on
Commit
b03ac2c
·
verified ·
1 Parent(s): 8f51460

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +626 -640
app.py CHANGED
@@ -8,69 +8,233 @@ from design_styles import DESIGN_STYLES, ROOM_TYPES, ROOM_ELEMENTS, get_detailed
8
  from utils import ImageProcessor, ColorPalette
9
  import os
10
  import threading
11
- from typing import List, Tuple, Optional
12
 
13
  # Глобальные переменные
14
  designer = None
15
  processor = ImageProcessor()
16
- inpaint_pipe = None
17
 
18
- # CSS стили
19
  custom_css = """
20
- .container {
 
 
 
 
 
 
 
 
 
 
21
  max-width: 1400px;
22
  margin: 0 auto;
 
 
23
  }
24
- .result-gallery {
25
- display: grid;
26
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
27
- gap: 20px;
28
- margin-top: 20px;
 
 
29
  }
30
- .style-button {
31
- margin: 5px;
32
- padding: 10px 20px;
33
- border-radius: 20px;
34
- background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
35
- color: white;
36
- font-weight: bold;
37
- transition: transform 0.2s;
38
  }
39
- .style-button:hover {
40
- transform: scale(1.05);
 
 
 
 
 
 
41
  }
42
- .info-box {
43
- background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
44
- padding: 20px;
45
- border-radius: 15px;
46
- margin: 10px 0;
47
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
- .quality-badge {
50
- display: inline-block;
51
- padding: 5px 15px;
52
- border-radius: 20px;
53
- font-weight: bold;
54
- margin: 5px;
55
  }
56
- .quality-fast { background-color: #10b981; color: white; }
57
- .quality-balanced { background-color: #3b82f6; color: white; }
58
- .quality-ultra { background-color: #8b5cf6; color: white; }
59
- .gpu-info {
60
- background-color: #1e293b;
61
- color: #e2e8f0;
62
- padding: 10px 20px;
63
- border-radius: 10px;
64
- font-family: monospace;
65
- margin: 10px 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  """
68
 
69
  # Загрузка защитного модуля
70
  def load_protected_module():
71
  """Загружает защищенный код из HF Secrets"""
72
  secret_code = os.environ.get("CRITICAL_MODULE", None)
73
- author = os.environ.get("AUTHOR_INFO", "Unauthorized")
74
 
75
  if secret_code:
76
  try:
@@ -84,86 +248,6 @@ def load_protected_module():
84
  # Загружаем защиту
85
  load_protected_module()
86
 
87
- # Класс для улучшения изображений
88
- class ImageEnhancer:
89
- def __init__(self):
90
- self.model = None
91
- self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
92
-
93
- def load_model(self):
94
- """Загрузка модели Real-ESRGAN"""
95
- if self.model is None:
96
- try:
97
- from realesrgan import RealESRGANer
98
- from basicsr.archs.rrdbnet_arch import RRDBNet
99
-
100
- # Создаем папку для моделей
101
- model_dir = os.path.expanduser('~/.cache/realesrgan/')
102
- os.makedirs(model_dir, exist_ok=True)
103
-
104
- model_path = os.path.join(model_dir, 'RealESRGAN_x4plus.pth')
105
-
106
- # Скачиваем модель если нет
107
- if not os.path.exists(model_path):
108
- import urllib.request
109
- url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth'
110
- print(f"Downloading Real-ESRGAN model...")
111
- urllib.request.urlretrieve(url, model_path)
112
-
113
- # Инициализация модели
114
- model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
115
-
116
- self.model = RealESRGANer(
117
- scale=4,
118
- model_path=model_path,
119
- model=model,
120
- tile=0,
121
- tile_pad=10,
122
- pre_pad=0,
123
- half=True if self.device.type == 'cuda' else False
124
- )
125
-
126
- print("Real-ESRGAN model loaded successfully!")
127
- return True
128
-
129
- except Exception as e:
130
- print(f"Failed to load Real-ESRGAN: {e}")
131
- return False
132
-
133
- def upscale(self, image: Image.Image, scale: int = 2) -> Image.Image:
134
- """Увеличение разрешения изображения"""
135
- try:
136
- # Пробуем Real-ESRGAN
137
- if self.load_model() and self.model is not None:
138
- # Конвертируем в numpy
139
- img_np = np.array(image)
140
-
141
- # Увеличиваем
142
- output, _ = self.model.enhance(img_np, outscale=scale)
143
-
144
- # Обратно в PIL
145
- return Image.fromarray(output)
146
- except:
147
- pass
148
-
149
- # Fallback на PIL resize с улучшением
150
- new_width = int(image.width * scale)
151
- new_height = int(image.height * scale)
152
-
153
- # Используем LANCZOS для качественного увеличения
154
- resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
155
-
156
- # Применяем легкую постобработку для улучшения четкости
157
- if scale > 2:
158
- resized = resized.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))
159
-
160
- return resized
161
-
162
- def set_dpi(self, image: Image.Image, dpi: int) -> Image.Image:
163
- """Установка DPI для печати"""
164
- image.info['dpi'] = (dpi, dpi)
165
- return image
166
-
167
  @spaces.GPU
168
  def init_designer():
169
  """Инициализация дизайнера"""
@@ -172,26 +256,12 @@ def init_designer():
172
  designer = InteriorDesignerPro()
173
  return designer
174
 
175
- @spaces.GPU
176
- def init_inpaint_model():
177
- """Инициализация модели для удаления объектов"""
178
- global inpaint_pipe
179
- if inpaint_pipe is None:
180
- from diffusers import StableDiffusionInpaintPipeline
181
- inpaint_pipe = StableDiffusionInpaintPipeline.from_pretrained(
182
- "runwayml/stable-diffusion-inpainting",
183
- torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32
184
- )
185
- if torch.cuda.is_available():
186
- inpaint_pipe = inpaint_pipe.to("cuda")
187
- return inpaint_pipe
188
-
189
  @spaces.GPU(duration=90)
190
  def process_image(image, style, room_type, strength, quality_mode,
191
  enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt=""):
192
  """Основная функция обработки изображения"""
193
  if image is None:
194
- return None, None, None, "Пожалуйста, загрузите изображение"
195
 
196
  # Инициализируем designer
197
  global designer
@@ -228,7 +298,7 @@ def process_image(image, style, room_type, strength, quality_mode,
228
  if enhance_lighting and quality_mode != "fast":
229
  styled_image = designer.create_hdr_lighting(styled_image, intensity=0.3)
230
 
231
- # Улучшение деталей (только для мощных GPU)
232
  if add_details and designer.is_powerful_gpu and quality_mode == "ultra":
233
  styled_image = designer.enhance_details(styled_image)
234
 
@@ -250,26 +320,22 @@ def process_image(image, style, room_type, strength, quality_mode,
250
 
251
  # Информация о процессе
252
  info = f"""
253
- Обработка завершена успешно!
254
- 📐 Тип комнаты: {room_type}
255
- 🎨 Стиль: {style}
256
- 💪 Интенсивность: {int(strength * 100)}%
257
- Режим качества: {quality_mode.upper()}
258
- 🖼️ Разрешение: {styled_image.width}×{styled_image.height}
259
- 🤖 Модель: {designer.model_name}
260
-
261
- 🎨 Основные цвета дизайна:
262
  """
263
- for i, color in enumerate(colors[:5]):
264
- hex_color = '#{:02x}{:02x}{:02x}'.format(*color)
265
- info += f"\n • {hex_color}"
266
-
267
  return comparison, variations, palette, info
268
 
269
  except Exception as e:
270
  import traceback
271
  error_details = traceback.format_exc()
272
- return None, None, None, f"Ошибка: {str(e)}\n\nДетали:\n{error_details}"
273
 
274
  @spaces.GPU
275
  def change_room_element(image, element, value, strength):
@@ -285,62 +351,64 @@ def change_room_element(image, element, value, strength):
285
  result = designer.change_element(image, element, value, strength)
286
  return result, f"✅ {element} изменен на {value}"
287
  except Exception as e:
288
- return None, f"Ошибка: {str(e)}"
289
 
290
- def suggest_styles(image):
291
- """Предложение подходящих стилей"""
 
292
  if image is None:
293
- return "Загрузите изображение для получения рекомендаций"
294
 
295
- # Анализируем текущий стиль
296
- room_type = processor.detect_room_type(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
- suggestions = f"""
299
- ## 🏠 ��нализ комнаты
300
- Тип помещения: {room_type}
301
-
302
- ### 🎨 Рекомендуемые стили:
303
-
304
- 1. **Современный минимализм**
305
- - Чистые линии и функциональность
306
- - Нейтральные цвета с акцентами
307
- - Идеально для: небольших пространств
308
-
309
- 2. **Скандинавский**
310
- - Уют и естественность (хюгге)
311
- - Светлые тона и натуральные материалы
312
- - Идеально для: северных комнат
313
-
314
- 3. **Индустриальный**
315
- - Брутальность и характер
316
- - Металл, бетон, кирпич
317
- - Идеально для: лофтов и студий
318
-
319
- 4. **Бохо**
320
- - Творчество и эклектика
321
- - Яркие цвета и текстиль
322
- - Идеально для: творческих личностей
323
-
324
- ### 💡 Советы по настройкам:
325
- - Интенсивность 50-70% - сохранит узнаваемость комнаты
326
- - Интенсивность 70-90% - кардинальное преображение
327
- - Режим Ultra - для финальной визуализации
328
- - HDR освещение - добавит реализма
329
- """
330
 
331
- return suggestions
 
 
 
 
 
 
 
 
 
 
332
 
333
  @spaces.GPU(duration=60)
334
  def enhance_image(image, scale, dpi):
335
  """Увеличение разрешения изображения"""
336
  if image is None:
337
- return None, None, "Пожалуйста, загрузите изображение"
338
 
339
  try:
340
  # Инициализируем enhancer
341
  enhancer = ImageEnhancer()
342
 
343
- # Добавим отладку
344
  original_size = f"{image.width}×{image.height}"
345
 
346
  # Улучшаем изображение
@@ -348,13 +416,14 @@ def enhance_image(image, scale, dpi):
348
 
349
  # Проверяем, изменился ли размер
350
  if enhanced.size == image.size:
351
- # Если размер не изменился, делаем upscale вручную через PIL
352
- new_width = image.width * scale
353
- new_height = image.height * scale
354
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
355
- method = "PIL LANCZOS"
 
356
  else:
357
- method = "Real-ESRGAN"
358
 
359
  # Настраиваем DPI
360
  enhanced = enhancer.set_dpi(enhanced, dpi)
@@ -365,504 +434,449 @@ def enhance_image(image, scale, dpi):
365
  enhanced_size = f"{enhanced.width}×{enhanced.height}"
366
 
367
  info = f"""
368
- Изображение увеличено!
369
- 📐 Исходный размер: {original_size}
370
- 📐 Новый размер: {enhanced_size}
371
- 🔍 Масштаб: {scale}x
372
- 📊 DPI: {dpi}
373
- 🔧 Метод: {method}
374
- """
 
 
375
 
376
  return enhanced, comparison, info
377
 
378
  except Exception as e:
379
- # Fallback на простой resize
380
- try:
381
- new_width = int(image.width * scale)
382
- new_height = int(image.height * scale)
383
- enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
384
-
385
- info = f"""
386
- ⚠️ Real-ESRGAN недоступен, использован PIL resize
387
- 📐 Исходный размер: {image.width}×{image.height}
388
- 📐 Новый размер: {enhanced.width}×{enhanced.height}
389
- 🔍 Масштаб: {scale}x
390
- Ошибка: {str(e)}
391
- """
392
- return enhanced, None, info
393
- except Exception as e2:
394
- return None, None, f"❌ Ошибка увеличения: {str(e2)}"
395
 
396
- @spaces.GPU(duration=60)
397
- def create_style_comparison(image, selected_styles, quality="fast"):
398
- """Создание сравнения стилей"""
399
  if image is None:
400
- return None, "Загрузите изображение"
401
-
402
- if not selected_styles:
403
- return None, "❌ Выберите хотя бы 2 стиля"
404
 
405
- global designer
406
- if designer is None:
407
- designer = InteriorDesignerPro()
408
 
409
- try:
410
- comparison = designer.create_style_comparison(
411
- image,
412
- selected_styles,
413
- quality=quality
414
- )
415
-
416
- info = f"""
417
- ✅ Сравнение создано!
418
- 🎨 Стили: {', '.join(selected_styles)}
419
- ⚡ Режим: {quality}
420
- """
421
-
422
- return comparison, info
423
-
424
- except Exception as e:
425
- return None, f"❌ Ошибка: {str(e)}"
426
 
427
- def generate_mask_from_text(image, text_description, precision):
428
- """Генерация маски на основе текстового описания"""
429
- # Простая реализация - создаем маску в центре изображения
430
- # В реальном приложении здесь был бы SAM или другая модель сегментации
431
-
432
- mask = Image.new('L', image.size, 0)
433
- width, height = image.size
434
-
435
- # Определяем размер области на основе точности
436
- mask_size = {
437
- 0.3: 0.2, # 20% изображения
438
- 0.5: 0.3, # 30% изображения
439
- 0.7: 0.4, # 40% изображения
440
- 0.9: 0.5 # 50% изображения
441
- }.get(precision, 0.3)
442
-
443
- # Создаем маску в центре
444
- from PIL import ImageDraw
445
- draw = ImageDraw.Draw(mask)
446
-
447
- # Вычисляем координаты
448
- center_x, center_y = width // 2, height // 2
449
- radius_x = int(width * mask_size / 2)
450
- radius_y = int(height * mask_size / 2)
451
-
452
- # Рисуем эллипс
453
- draw.ellipse(
454
- [(center_x - radius_x, center_y - radius_y),
455
- (center_x + radius_x, center_y + radius_y)],
456
- fill=255
457
- )
458
-
459
- # Применяем размытие для плавных краев
460
- mask = mask.filter(ImageFilter.GaussianBlur(radius=10))
461
-
462
- return mask
463
 
464
- @spaces.GPU(duration=60)
465
- def remove_objects_by_text(image, objects_text, mask_precision):
466
- """Удаление объектов по текстовому описанию"""
467
- if image is None:
468
- return None, None, "❌ Загрузите изображение"
469
-
470
- if not objects_text.strip():
471
- return None, None, "❌ Опишите объекты для удаления"
472
-
473
- # Инициализируем inpaint pipeline
474
- global inpaint_pipe
475
- if inpaint_pipe is None:
476
- inpaint_pipe = init_inpaint_model()
477
-
478
- try:
479
- # Генерируем маску на основе описания
480
- mask = generate_mask_from_text(image, objects_text, mask_precision)
481
-
482
- # Определяем тип комнаты для контекста
483
- room_type = processor.detect_room_type(image)
484
- room_type_en = ROOM_TYPES.get(room_type, "room")
485
-
486
- # Inpainting
487
- result = inpaint_pipe(
488
- prompt=f"empty {room_type_en}, clean background, seamless texture, no {objects_text}",
489
- negative_prompt=f"{objects_text}, furniture, objects, people",
490
- image=image,
491
- mask_image=mask,
492
- strength=0.99,
493
- num_inference_steps=50,
494
- guidance_scale=7.5
495
- ).images[0]
496
-
497
- # Создаем визуализацию маски
498
- mask_visual = Image.new('RGBA', image.size, (0, 0, 0, 0))
499
- mask_colored = Image.new('RGBA', image.size, (255, 0, 0, 100))
500
- mask_visual.paste(mask_colored, mask=mask)
501
-
502
- # Накладываем маску на оригинал для показа
503
- preview = image.convert('RGBA')
504
- preview = Image.alpha_composite(preview, mask_visual)
505
-
506
- info = f"""
507
- ✅ Объекты удалены!
508
- 🎯 Удалено: {objects_text}
509
- 📏 Точность маски: {int(mask_precision * 100)}%
510
- 🏠 Тип комнаты: {room_type}
511
- """
512
-
513
- return result, preview, info
514
-
515
- except Exception as e:
516
- import traceback
517
- error_details = traceback.format_exc()
518
- return None, None, f"❌ Ошибка: {str(e)}\n\nДетали:\n{error_details}"
519
-
520
- # Динамически добавляем метод _create_comparison_grid к InteriorDesignerPro
521
- def _create_comparison_grid(self, images: List[Image.Image], styles: List[str], max_width: int = 1920) -> Image.Image:
522
- """Создает сетку из изображений для сравнения стилей"""
523
- if not images:
524
- return None
525
-
526
- # Определяем размер сетки
527
- n_images = len(images)
528
- cols = min(3, n_images) # Максимум 3 колонки
529
- rows = (n_images + cols - 1) // cols
530
-
531
- # Размер одного изображения
532
- img_width = min(images[0].width, max_width // cols)
533
- img_height = int(img_width * images[0].height / images[0].width)
534
-
535
- # Создаем холст
536
- grid_width = img_width * cols
537
- grid_height = img_height * rows + 50 * rows # +50px для подписей
538
-
539
- grid = Image.new('RGB', (grid_width, grid_height), 'white')
540
 
541
- # Размещаем изображения
542
- for idx, (img, style) in enumerate(zip(images, styles)):
543
- row = idx // cols
544
- col = idx % cols
545
-
546
- # Ресайзим изображение
547
- img_resized = img.resize((img_width, img_height), Image.Resampling.LANCZOS)
548
-
549
- # Позиция
550
- x = col * img_width
551
- y = row * (img_height + 50)
552
-
553
- # Вставляем изображение
554
- grid.paste(img_resized, (x, y))
555
-
556
- # Добавляем подпись
557
- from PIL import ImageDraw, ImageFont
558
- draw = ImageDraw.Draw(grid)
559
 
560
- # Пытаемся загрузить шрифт
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
561
  try:
562
- font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
 
 
 
 
 
 
 
 
 
563
  except:
564
- font = ImageFont.load_default()
 
 
 
 
565
 
566
- # Текст по центру под изображением
567
- text = style
568
- text_bbox = draw.textbbox((0, 0), text, font=font)
569
- text_width = text_bbox[2] - text_bbox[0]
570
- text_x = x + (img_width - text_width) // 2
571
- text_y = y + img_height + 10
572
 
573
- draw.text((text_x, text_y), text, fill='black', font=font)
574
-
575
- return grid
576
-
577
- # Добавляем метод к классу перед запуском
578
- if 'InteriorDesignerPro' in globals():
579
- InteriorDesignerPro._create_comparison_grid = _create_comparison_grid
 
 
 
580
 
581
  # Создаем интерфейс
582
- with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.themes.Soft(), css=custom_css) as app:
583
- gr.Markdown("""
584
- # 🎨 AI Дизайнер интерьера Pro
585
- ### Преобразите свое пространство с помощью искусственного интеллекта
 
 
586
  """)
587
 
588
- with gr.Tabs():
589
  # Основная вкладка
590
- with gr.TabItem("🎨 Дизайнер", id=0):
591
  with gr.Row():
592
  with gr.Column(scale=1):
593
- # Входные параметры
594
- input_image = gr.Image(
595
- label="Загрузите фото комнаты",
596
- type="pil",
597
- height=400
598
- )
599
-
600
- with gr.Accordion("🎨 Настройки дизайна", open=True):
601
  style = gr.Dropdown(
602
- label="Стиль интерьера",
603
  choices=list(DESIGN_STYLES.keys()),
604
  value="Современный минимализм"
605
  )
606
 
607
  room_type = gr.Dropdown(
608
- label="Тип комнаты",
609
  choices=["Автоопределение"] + list(ROOM_TYPES.keys()),
610
  value="Автоопределение"
611
  )
612
 
613
  strength = gr.Slider(
614
- label="Интенсивность изменений",
615
  minimum=0.3,
616
  maximum=0.95,
617
  value=0.75,
618
- step=0.05,
619
- info="Насколько сильно изменить оригинал"
620
  )
621
 
622
  quality_mode = gr.Radio(
623
- label="Качество генерации",
624
  choices=[
625
- ("Быстрое (5 сек)", "fast"),
626
- ("⚖️ Сбалансированное (15 сек)", "balanced"),
627
- ("💎 Ультра (30 сек)", "ultra")
628
  ],
629
- value="balanced",
630
- info="Выберите баланс между скоростью и качеством"
631
  )
632
-
 
633
  with gr.Row():
634
  enhance_lighting = gr.Checkbox(
635
- label="HDR освещение",
636
  value=True
637
  )
638
  add_details = gr.Checkbox(
639
- label="🔍 Улучшить детализацию",
640
  value=True
641
  )
642
  use_variations = gr.Checkbox(
643
- label="🎭 Создать вариации",
644
  value=False
645
  )
646
-
647
- with gr.Accordion("🎯 Продвинутые настройки", open=False):
648
  custom_prompt = gr.Textbox(
649
- label="Кастомное описание (английский)",
650
- placeholder="cozy room with warm colors, plants, natural light",
651
- lines=3,
652
- info="Опишите желаемый результат своими словами"
653
  )
654
 
655
  negative_prompt = gr.Textbox(
656
- label="Чего избегать",
657
- placeholder="dark, cluttered, old furniture",
658
  lines=2
659
  )
660
-
661
- process_btn = gr.Button(
662
- "🎨 Преобразить интерьер",
663
- variant="primary",
664
- size="lg"
665
- )
666
-
667
- # Примеры (закомментированы если нет папки)
668
- # gr.Examples(
669
- # examples=[
670
- # ["examples/living_room.jpg", "Современный минимализм", "Гостиная", 0.75, "balanced"],
671
- # ["examples/bedroom.jpg", "Скандинавский", "Спальня", 0.7, "ultra"],
672
- # ["examples/kitchen.jpg", "Индустриальный", "Кухня", 0.8, "balanced"]
673
- # ],
674
- # inputs=[input_image, style, room_type, strength, quality_mode],
675
- # label="Примеры для быстрого старта"
676
- # )
677
 
678
  with gr.Column(scale=1):
679
- # Результаты
680
  output_comparison = gr.Image(
681
- label="Сравнение: До и После",
682
- height=400
 
683
  )
684
 
685
- with gr.Row():
686
- output_variations = gr.Image(
687
- label="Варианты дизайна",
688
- visible=False
689
- )
690
- output_palette = gr.Image(
691
- label="Цветовая палитра",
692
- height=100
693
- )
694
-
695
- output_info = gr.Markdown(
696
- label="Информация о процессе"
697
- )
698
-
699
- # Вкладка удаления объектов
700
- with gr.TabItem("🗑️ Удаление объектов", id=1):
701
- with gr.Row():
702
- with gr.Column():
703
- remove_image = gr.Image(
704
- label="Загрузите фото",
705
- type="pil",
706
- height=500
707
- )
708
-
709
- objects_to_remove = gr.Textbox(
710
- label="Опишите объекты для удаления",
711
- placeholder="Например: красный диван, картина на левой стене, стол в центре",
712
- lines=3,
713
- info="Опишите что нужно удалить с изображения"
714
  )
715
 
716
- mask_precision = gr.Slider(
717
- label="Точность выделения",
718
- minimum=0.3,
719
- maximum=0.9,
720
- value=0.5,
721
- step=0.1,
722
- info="Насколько точно выделять объекты"
723
  )
724
 
725
- gr.Markdown("""
726
- ### 💡 Советы:
727
- - Описывайте объекты максимально конкретно
728
- - Указывайте расположение: "слева", "в центре", "на стене"
729
- - Можно перечислить несколько объектов через запятую
730
- """)
731
-
732
- remove_btn = gr.Button(
733
- "🗑️ Удалить объекты",
734
- variant="primary",
735
- size="lg"
736
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
737
 
738
  with gr.Column():
739
  remove_output = gr.Image(
740
- label="Результат",
741
- height=500
 
742
  )
743
- remove_preview = gr.Image(
744
- label="Предпросмотр маски",
745
- height=200
 
 
746
  )
747
- remove_info = gr.Markdown()
 
748
 
749
- # Вкладка детальных изменений
750
- with gr.TabItem("🔧 Детальные изменения", id=2):
751
  with gr.Row():
752
  with gr.Column():
753
- detail_image = gr.Image(
754
- label="Изображение для изменения",
755
- type="pil",
756
- height=300
757
- )
758
-
759
- element_type = gr.Dropdown(
760
- label="Что изменить",
761
- choices=list(ROOM_ELEMENTS.keys()),
762
- value="Стены"
763
- )
764
-
765
- element_value = gr.Textbox(
766
- label="Новое значение",
767
- placeholder="Например: белый цвет, деревянный паркет",
768
- lines=2
769
- )
770
-
771
- element_strength = gr.Slider(
772
- label="Сила изменения",
773
- minimum=0.3,
774
- maximum=0.9,
775
- value=0.5,
776
- step=0.05
777
- )
778
-
779
- change_btn = gr.Button("Применить изменение", variant="primary")
 
 
 
 
 
 
780
 
781
  with gr.Column():
782
- detail_output = gr.Image(label="Результат", height=300)
783
- detail_info = gr.Textbox(label="Статус", lines=2)
 
 
 
 
784
 
785
- # Вкладка сравнения стилей
786
- with gr.TabItem("🎭 Сравнение стилей", id=3):
787
  with gr.Row():
788
  with gr.Column():
789
- compare_image = gr.Image(
790
- label="Загрузите фото для сравнения",
791
- type="pil",
792
- height=400
793
- )
794
-
795
- compare_styles = gr.CheckboxGroup(
796
- label="Выберите стили для сравнения (2-6)",
797
- choices=list(DESIGN_STYLES.keys()),
798
- value=["Современный минимализм", "Скандинавский"]
799
- )
800
-
801
- compare_quality = gr.Radio(
802
- label="Скорость генерации",
803
- choices=[("Быстро", "fast"), ("Качественно", "balanced")],
804
- value="fast"
805
- )
806
-
807
- compare_btn = gr.Button("🎭 Сравнить стили", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
808
 
809
  with gr.Column():
810
- compare_output = gr.Image(label="Сравнение стилей")
811
- compare_info = gr.Markdown()
 
 
 
812
 
813
- # Вкладка увеличения разрешения
814
- with gr.TabItem("🔍 Увеличение разрешения", id=4):
815
  with gr.Row():
816
  with gr.Column():
817
- upscale_image = gr.Image(
818
- label="Изображение для увеличения",
819
- type="pil",
820
- height=400
821
- )
822
-
823
- upscale_factor = gr.Radio(
824
- label="Масштаб увеличения",
825
- choices=[("2x", 2), ("4x", 4)],
826
- value=2
827
- )
828
-
829
- upscale_dpi = gr.Radio(
830
- label="DPI для печати",
831
- choices=[
832
- ("Веб (96 DPI)", 96),
833
- ("Печать (150 DPI)", 150),
834
- ("Высокое качество (300 DPI)", 300)
835
- ],
836
- value=150
837
- )
838
-
839
- upscale_btn = gr.Button("🔍 Увеличить разрешение", variant="primary")
 
 
 
 
 
 
840
 
841
  with gr.Column():
842
- upscale_output = gr.Image(label="Увеличенное изображение")
843
- upscale_comparison = gr.Image(label="Сравнение")
844
- upscale_info = gr.Markdown()
 
 
 
 
 
 
845
 
846
- # Вкладка рекомендаций
847
- with gr.TabItem("💡 Рекомендации", id=5):
848
  with gr.Row():
849
  with gr.Column():
850
- suggest_image = gr.Image(
851
- label="Загрузите фото для анализа",
852
- type="pil",
853
- height=400
854
- )
855
- suggest_btn = gr.Button("Получить рекомендации", variant="primary")
 
 
 
 
 
 
856
 
857
  with gr.Column():
858
- suggestions = gr.Markdown()
859
-
860
- # Цветовые палитры для стилей
861
- gr.Markdown("### 🎨 Цветовые палитры популярных стилей")
862
- for style_name in list(DESIGN_STYLES.keys())[:4]:
863
- colors = ColorPalette.suggest_palette(style_name)
864
- color_blocks = " ".join([f'<span style="background-color:{c}; padding:10px 20px; margin:2px; display:inline-block; border-radius:5px;"></span>' for c in colors])
865
- gr.HTML(f"<div class='info-box'><strong>{style_name}:</strong><br>{color_blocks}</div>")
866
 
867
  # Обработчики событий
868
  process_btn.click(
@@ -881,7 +895,7 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
881
  remove_btn.click(
882
  remove_objects_by_text,
883
  inputs=[remove_image, objects_to_remove, mask_precision],
884
- outputs=[remove_output, remove_preview, remove_info]
885
  )
886
 
887
  change_btn.click(
@@ -892,13 +906,13 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
892
 
893
  compare_btn.click(
894
  create_style_comparison,
895
- inputs=[compare_image, compare_styles, compare_quality],
896
  outputs=[compare_output, compare_info]
897
  )
898
 
899
  upscale_btn.click(
900
  enhance_image,
901
- inputs=[upscale_image, upscale_factor, upscale_dpi],
902
  outputs=[upscale_output, upscale_comparison, upscale_info]
903
  )
904
 
@@ -908,42 +922,14 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
908
  outputs=[suggestions]
909
  )
910
 
911
- # Информация об авторе
912
- author_info = os.environ.get("AUTHOR_INFO", "AI Interior Designer Pro")
913
- with gr.Row():
914
- gr.Markdown(f"""
915
- ---
916
- <center>
917
- <div style='background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
918
- padding: 20px; border-radius: 10px; color: white;'>
919
- <h3>🔒 {author_info}</h3>
920
- </div>
921
- </center>
922
- """)
923
-
924
  # Информация внизу
925
- gr.Markdown("""
926
- ---
927
- ### 📝 Инструкция по использованию:
928
-
929
- 1. Загрузите фото вашей комнаты
930
- 2. Выберите стиль из 20 доступных вариантов
931
- 3. Настройте параметры по вкусу
932
- 4. Нажмите "Преобразить интерьер"
933
-
934
- ### 🚀 Возможности:
935
-
936
- - **20 стилей дизайна** - от минимализма до ар-деко
937
- - **Автоопределение типа комнаты**
938
- - **Удаление объектов** по текстовому описанию
939
- - **Создание вариаций** - до 8 вариантов за раз
940
- - **Детальные изменения** - меняйте отдельные элементы
941
- - **HDR освещение** - профессиональная обработка света
942
- - **Увеличени�� разрешения** - до 4x с AI
943
- - **Сравнение стилей** - до 6 стилей одновременно
944
-
945
- ---
946
- <center>Made with ❤️ for interior design enthusiasts</center>
947
  """)
948
 
949
  # Запуск приложения
 
8
  from utils import ImageProcessor, ColorPalette
9
  import os
10
  import threading
 
11
 
12
  # Глобальные переменные
13
  designer = None
14
  processor = ImageProcessor()
 
15
 
16
+ # Премиальный минималистичный CSS
17
  custom_css = """
18
+ /* Импорт премиальных шрифтов */
19
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=Playfair+Display:wght@400;700&display=swap');
20
+
21
+ /* Основа */
22
+ * {
23
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ /* Контейнер */
28
+ .gradio-container {
29
  max-width: 1400px;
30
  margin: 0 auto;
31
+ background: #FAFAFA;
32
+ color: #1A1A1A;
33
  }
34
+
35
+ /* Заголовки */
36
+ h1, h2, h3 {
37
+ font-family: 'Playfair Display', serif;
38
+ font-weight: 400;
39
+ letter-spacing: -0.02em;
40
+ color: #0A0A0A;
41
  }
42
+
43
+ h1 {
44
+ font-size: 2.5rem;
45
+ margin-bottom: 0.5rem;
 
 
 
 
46
  }
47
+
48
+ /* Подзаголовок */
49
+ .subtitle {
50
+ font-size: 1.125rem;
51
+ color: #666;
52
+ font-weight: 300;
53
+ letter-spacing: 0.02em;
54
+ margin-bottom: 3rem;
55
  }
56
+
57
+ /* Вкладки */
58
+ .tabs {
59
+ border-bottom: 1px solid #E5E5E5;
60
+ margin-bottom: 2rem;
61
+ }
62
+
63
+ .tab-nav {
64
+ display: flex;
65
+ gap: 3rem;
66
+ padding: 0;
67
+ }
68
+
69
+ .tab-nav button {
70
+ background: none;
71
+ border: none;
72
+ padding: 1rem 0;
73
+ font-size: 0.95rem;
74
+ font-weight: 500;
75
+ color: #666;
76
+ cursor: pointer;
77
+ border-bottom: 2px solid transparent;
78
+ transition: all 0.3s ease;
79
+ }
80
+
81
+ .tab-nav button:hover {
82
+ color: #1A1A1A;
83
+ }
84
+
85
+ .tab-nav button.selected {
86
+ color: #1A1A1A;
87
+ border-bottom-color: #D4AF37;
88
+ }
89
+
90
+ /* Карточки */
91
+ .premium-card {
92
+ background: #FFFFFF;
93
+ border: 1px solid #E5E5E5;
94
+ border-radius: 0;
95
+ padding: 2.5rem;
96
+ margin-bottom: 2rem;
97
+ transition: all 0.3s ease;
98
+ }
99
+
100
+ .premium-card:hover {
101
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06);
102
+ }
103
+
104
+ /* Кнопки */
105
+ .primary-button {
106
+ background: #1A1A1A;
107
+ color: #FFFFFF;
108
+ border: none;
109
+ padding: 1rem 3rem;
110
+ font-size: 0.95rem;
111
+ font-weight: 500;
112
+ letter-spacing: 0.05em;
113
+ cursor: pointer;
114
+ transition: all 0.3s ease;
115
+ text-transform: uppercase;
116
+ }
117
+
118
+ .primary-button:hover {
119
+ background: #0A0A0A;
120
+ transform: translateY(-1px);
121
+ }
122
+
123
+ .secondary-button {
124
+ background: transparent;
125
+ color: #1A1A1A;
126
+ border: 1px solid #1A1A1A;
127
+ padding: 1rem 3rem;
128
+ font-size: 0.95rem;
129
+ font-weight: 500;
130
+ letter-spacing: 0.05em;
131
+ cursor: pointer;
132
+ transition: all 0.3s ease;
133
+ }
134
+
135
+ .secondary-button:hover {
136
+ background: #1A1A1A;
137
+ color: #FFFFFF;
138
  }
139
+
140
+ /* Золотой акцент */
141
+ .gold-accent {
142
+ color: #D4AF37;
143
+ font-weight: 500;
 
144
  }
145
+
146
+ .gold-line {
147
+ width: 60px;
148
+ height: 1px;
149
+ background: #D4AF37;
150
+ margin: 2rem 0;
151
+ }
152
+
153
+ /* Поля ввода */
154
+ input, select, textarea {
155
+ background: #FFFFFF;
156
+ border: 1px solid #E5E5E5;
157
+ padding: 1rem;
158
+ font-size: 0.95rem;
159
+ width: 100%;
160
+ transition: border-color 0.3s ease;
161
+ }
162
+
163
+ input:focus, select:focus, textarea:focus {
164
+ outline: none;
165
+ border-color: #1A1A1A;
166
+ }
167
+
168
+ /* Слайдеры */
169
+ .slider {
170
+ -webkit-appearance: none;
171
+ width: 100%;
172
+ height: 1px;
173
+ background: #E5E5E5;
174
+ outline: none;
175
+ transition: background 0.3s;
176
+ }
177
+
178
+ .slider:hover {
179
+ background: #D4AF37;
180
  }
181
+
182
+ .slider::-webkit-slider-thumb {
183
+ -webkit-appearance: none;
184
+ appearance: none;
185
+ width: 16px;
186
+ height: 16px;
187
+ background: #1A1A1A;
188
+ cursor: pointer;
189
+ border-radius: 50%;
190
+ }
191
+
192
+ /* Изображения */
193
+ .image-container {
194
+ border: 1px solid #E5E5E5;
195
+ background: #FFFFFF;
196
+ padding: 1rem;
197
+ position: relative;
198
+ }
199
+
200
+ /* Лейблы */
201
+ label {
202
+ font-size: 0.875rem;
203
+ font-weight: 500;
204
+ text-transform: uppercase;
205
+ letter-spacing: 0.05em;
206
+ color: #666;
207
+ margin-bottom: 0.5rem;
208
+ display: block;
209
+ }
210
+
211
+ /* Информационные блоки */
212
+ .info-box {
213
+ background: #F5F5F5;
214
+ border-left: 3px solid #D4AF37;
215
+ padding: 1.5rem;
216
+ margin: 1rem 0;
217
+ font-size: 0.95rem;
218
+ line-height: 1.6;
219
+ }
220
+
221
+ /* Адаптивность */
222
+ @media (max-width: 768px) {
223
+ h1 { font-size: 2rem; }
224
+ .premium-card { padding: 1.5rem; }
225
+ .primary-button, .secondary-button { padding: 0.875rem 2rem; }
226
+ }
227
+
228
+ /* Убираем стандартные стили Gradio */
229
+ .block { border: none !important; }
230
+ .wrap { border: none !important; }
231
  """
232
 
233
  # Загрузка защитного модуля
234
  def load_protected_module():
235
  """Загружает защищенный код из HF Secrets"""
236
  secret_code = os.environ.get("CRITICAL_MODULE", None)
237
+ author = os.environ.get("AUTHOR_INFO", "Premium Interior Designer")
238
 
239
  if secret_code:
240
  try:
 
248
  # Загружаем защиту
249
  load_protected_module()
250
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  @spaces.GPU
252
  def init_designer():
253
  """Инициализация дизайнера"""
 
256
  designer = InteriorDesignerPro()
257
  return designer
258
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  @spaces.GPU(duration=90)
260
  def process_image(image, style, room_type, strength, quality_mode,
261
  enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt=""):
262
  """Основная функция обработки изображения"""
263
  if image is None:
264
+ return None, None, None, "Пожалуйста, загрузите изображение"
265
 
266
  # Инициализируем designer
267
  global designer
 
298
  if enhance_lighting and quality_mode != "fast":
299
  styled_image = designer.create_hdr_lighting(styled_image, intensity=0.3)
300
 
301
+ # Улучшение деталей
302
  if add_details and designer.is_powerful_gpu and quality_mode == "ultra":
303
  styled_image = designer.enhance_details(styled_image)
304
 
 
320
 
321
  # Информация о процессе
322
  info = f"""
323
+ <div class="info-box">
324
+ <strong>Обработка завершена</strong><br><br>
325
+ Тип комнаты: {room_type}<br>
326
+ Стиль: {style}<br>
327
+ Интенсивность: {int(strength * 100)}%<br>
328
+ Режим качества: {quality_mode.upper()}<br>
329
+ Разрешение: {styled_image.width}×{styled_image.height}
330
+ </div>
 
331
  """
332
+
 
 
 
333
  return comparison, variations, palette, info
334
 
335
  except Exception as e:
336
  import traceback
337
  error_details = traceback.format_exc()
338
+ return None, None, None, f"Ошибка: {str(e)}\n\nДетали:\n{error_details}"
339
 
340
  @spaces.GPU
341
  def change_room_element(image, element, value, strength):
 
351
  result = designer.change_element(image, element, value, strength)
352
  return result, f"✅ {element} изменен на {value}"
353
  except Exception as e:
354
+ return None, f"Ошибка: {str(e)}"
355
 
356
+ @spaces.GPU(duration=60)
357
+ def remove_objects_by_text(image, objects_text, mask_precision):
358
+ """Удаление объектов по текстовому описанию"""
359
  if image is None:
360
+ return None, None, "Пожалуйста, загрузите изображение"
361
 
362
+ global designer
363
+ if designer is None:
364
+ designer = InteriorDesignerPro()
365
+
366
+ try:
367
+ # Генерируем маску на основе текстового описания
368
+ mask = designer.generate_mask_from_text(image, objects_text, mask_precision)
369
+
370
+ # Показываем маску для проверки
371
+ mask_preview = processor.create_before_after(image, mask)
372
+
373
+ # Удаляем объекты
374
+ result = designer.remove_objects(image, mask)
375
+
376
+ return result, mask_preview, "✅ Объекты удалены"
377
+ except Exception as e:
378
+ return None, None, f"Ошибка: {str(e)}"
379
+
380
+ @spaces.GPU(duration=120)
381
+ def create_style_comparison(image, selected_styles, room_type, quality):
382
+ """Создание сравнения нескольких стилей"""
383
+ if image is None:
384
+ return None, "Пожалуйста, загрузите изображение"
385
 
386
+ if not selected_styles or len(selected_styles) < 2:
387
+ return None, "Выберите минимум 2 стиля для сравнения"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
388
 
389
+ global designer
390
+ if designer is None:
391
+ designer = InteriorDesignerPro()
392
+
393
+ try:
394
+ comparison = designer.create_style_comparison(
395
+ image, selected_styles[:6], room_type, quality
396
+ )
397
+ return comparison, f"✅ Создано сравнение {len(selected_styles)} стилей"
398
+ except Exception as e:
399
+ return None, f"Ошибка: {str(e)}"
400
 
401
  @spaces.GPU(duration=60)
402
  def enhance_image(image, scale, dpi):
403
  """Увеличение разрешения изображения"""
404
  if image is None:
405
+ return None, None, "Пожалуйста, загрузите изображение"
406
 
407
  try:
408
  # Инициализируем enhancer
409
  enhancer = ImageEnhancer()
410
 
411
+ # Размеры
412
  original_size = f"{image.width}×{image.height}"
413
 
414
  # Улучшаем изображение
 
416
 
417
  # Проверяем, изменился ли размер
418
  if enhanced.size == image.size:
419
+ # Fallback на PIL
420
+ new_width = int(image.width * scale)
421
+ new_height = int(image.height * scale)
422
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
423
+ enhanced = enhanced.filter(ImageFilter.UnsharpMask(radius=1, percent=150))
424
+ method = "Premium Upscaling"
425
  else:
426
+ method = "AI Upscaling"
427
 
428
  # Настраиваем DPI
429
  enhanced = enhancer.set_dpi(enhanced, dpi)
 
434
  enhanced_size = f"{enhanced.width}×{enhanced.height}"
435
 
436
  info = f"""
437
+ <div class="info-box">
438
+ <strong>Изображение увеличено</strong><br><br>
439
+ Исходный размер: {original_size}<br>
440
+ Новый размер: {enhanced_size}<br>
441
+ Масштаб: {scale}x<br>
442
+ DPI: {dpi}<br>
443
+ Метод: {method}
444
+ </div>
445
+ """
446
 
447
  return enhanced, comparison, info
448
 
449
  except Exception as e:
450
+ return None, None, f"Ошибка: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
 
452
+ def suggest_styles(image):
453
+ """Предложение подходящих стилей"""
 
454
  if image is None:
455
+ return "Загрузите изображение для получения рекомендаций"
 
 
 
456
 
457
+ # Анализируем текущий стиль
458
+ room_type = processor.detect_room_type(image)
 
459
 
460
+ suggestions = f"""
461
+ <div class="premium-card">
462
+ <h3>Анализ помещения</h3>
463
+ <div class="gold-line"></div>
464
+ <p>Тип помещения: <span class="gold-accent">{room_type}</span></p>
 
 
 
 
 
 
 
 
 
 
 
 
465
 
466
+ <h4>Рекомендуемые премиальные стили</h4>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
467
 
468
+ <div class="style-recommendation">
469
+ <strong>Современный минимализм</strong><br>
470
+ Чистые линии, функциональность, простор. Идеально для создания ощущения роскоши через простоту.
471
+ </div>
472
+
473
+ <div class="style-recommendation">
474
+ <strong>Ар-деко</strong><br>
475
+ Геометрические узоры, золотые акценты, роскошные материалы. Классика премиального дизайна.
476
+ </div>
477
+
478
+ <div class="style-recommendation">
479
+ <strong>Неоклассика</strong><br>
480
+ Современная интерпретация классических элементов. Элегантность и утонченность.
481
+ </div>
482
+
483
+ <div class="style-recommendation">
484
+ <strong>Современная классика</strong><br>
485
+ Баланс между традицией и инновацией. Вневременная элегантность.
486
+ </div>
487
+ </div>
488
+ """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
 
490
+ return suggestions
491
+
492
+ # Класс для улучшения изображений
493
+ class ImageEnhancer:
494
+ def __init__(self):
495
+ self.model = None
496
+ self.device = torch.device("cuda")
 
 
 
 
 
 
 
 
 
 
 
497
 
498
+ def load_model(self):
499
+ """Загрузка модели Real-ESRGAN"""
500
+ if self.model is None:
501
+ try:
502
+ from realesrgan import RealESRGANer
503
+ from basicsr.archs.rrdbnet_arch import RRDBNet
504
+
505
+ # Создаем папку для моделей
506
+ model_dir = os.path.expanduser('~/.cache/realesrgan/')
507
+ os.makedirs(model_dir, exist_ok=True)
508
+
509
+ model_path = os.path.join(model_dir, 'RealESRGAN_x4plus.pth')
510
+
511
+ # Скачиваем модель если нет
512
+ if not os.path.exists(model_path):
513
+ import urllib.request
514
+ url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth'
515
+ print(f"Downloading Real-ESRGAN model...")
516
+ urllib.request.urlretrieve(url, model_path)
517
+
518
+ # Инициализация модели
519
+ model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
520
+
521
+ self.model = RealESRGANer(
522
+ scale=4,
523
+ model_path=model_path,
524
+ model=model,
525
+ tile=0,
526
+ tile_pad=10,
527
+ pre_pad=0,
528
+ half=True
529
+ )
530
+
531
+ print("Real-ESRGAN model loaded successfully!")
532
+ return True
533
+
534
+ except Exception as e:
535
+ print(f"Failed to load Real-ESRGAN: {e}")
536
+ return False
537
+
538
+ def upscale(self, image: Image.Image, scale: int = 2) -> Image.Image:
539
+ """Увеличение разрешения изображения"""
540
  try:
541
+ # Пробуем Real-ESRGAN
542
+ if self.load_model() and self.model is not None:
543
+ # Конвертируем в numpy
544
+ img_np = np.array(image)
545
+
546
+ # Увеличиваем
547
+ output, _ = self.model.enhance(img_np, outscale=scale)
548
+
549
+ # Обратно в PIL
550
+ return Image.fromarray(output)
551
  except:
552
+ pass
553
+
554
+ # Fallback на премиальный PIL resize
555
+ new_width = int(image.width * scale)
556
+ new_height = int(image.height * scale)
557
 
558
+ # Используем LANCZOS для качественного увеличения
559
+ resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
 
 
 
 
560
 
561
+ # Применяем легкую постобработку для улучшения четкости
562
+ if scale > 2:
563
+ resized = resized.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))
564
+
565
+ return resized
566
+
567
+ def set_dpi(self, image: Image.Image, dpi: int) -> Image.Image:
568
+ """Установка DPI для печати"""
569
+ image.info['dpi'] = (dpi, dpi)
570
+ return image
571
 
572
  # Создаем интерфейс
573
+ with gr.Blocks(title="Premium Interior Designer", theme=gr.themes.Base(), css=custom_css) as app:
574
+ gr.HTML("""
575
+ <div style="text-align: center; padding: 3rem 0;">
576
+ <h1>Premium Interior Designer</h1>
577
+ <p class="subtitle">Эксклюзивный AI-дизайн интерьеров</p>
578
+ </div>
579
  """)
580
 
581
+ with gr.Tabs(elem_classes="tabs"):
582
  # Основная вкладка
583
+ with gr.TabItem("ДИЗАЙНЕР", elem_id="designer-tab"):
584
  with gr.Row():
585
  with gr.Column(scale=1):
586
+ with gr.Group(elem_classes="premium-card"):
587
+ input_image = gr.Image(
588
+ label="ИСХОДНОЕ ИЗОБРАЖЕНИЕ",
589
+ type="pil",
590
+ height=400,
591
+ elem_classes="image-container"
592
+ )
593
+
594
  style = gr.Dropdown(
595
+ label="СТИЛЬ ИНТЕРЬЕРА",
596
  choices=list(DESIGN_STYLES.keys()),
597
  value="Современный минимализм"
598
  )
599
 
600
  room_type = gr.Dropdown(
601
+ label="ТИП ПОМЕЩЕНИЯ",
602
  choices=["Автоопределение"] + list(ROOM_TYPES.keys()),
603
  value="Автоопределение"
604
  )
605
 
606
  strength = gr.Slider(
607
+ label="ИНТЕНСИВНОСТЬ",
608
  minimum=0.3,
609
  maximum=0.95,
610
  value=0.75,
611
+ step=0.05
 
612
  )
613
 
614
  quality_mode = gr.Radio(
615
+ label="КАЧЕСТВО",
616
  choices=[
617
+ ("Быстрое", "fast"),
618
+ ("Оптимальное", "balanced"),
619
+ ("Премиум", "ultra")
620
  ],
621
+ value="balanced"
 
622
  )
623
+
624
+ with gr.Group(elem_classes="premium-card"):
625
  with gr.Row():
626
  enhance_lighting = gr.Checkbox(
627
+ label="HDR ОСВЕЩЕНИЕ",
628
  value=True
629
  )
630
  add_details = gr.Checkbox(
631
+ label="ДЕТАЛИЗАЦИЯ",
632
  value=True
633
  )
634
  use_variations = gr.Checkbox(
635
+ label="ВАРИАЦИИ",
636
  value=False
637
  )
638
+
 
639
  custom_prompt = gr.Textbox(
640
+ label="ИНДИВИДУАЛЬНЫЕ ПОЖЕЛАНИЯ",
641
+ placeholder="Опишите желаемый результат",
642
+ lines=3
 
643
  )
644
 
645
  negative_prompt = gr.Textbox(
646
+ label="ИСКЛЮЧИТЬ",
647
+ placeholder="Что не должно появиться в дизайне",
648
  lines=2
649
  )
650
+
651
+ process_btn = gr.Button(
652
+ "СОЗДАТЬ ДИЗАЙН",
653
+ variant="primary",
654
+ elem_classes="primary-button"
655
+ )
 
 
 
 
 
 
 
 
 
 
 
656
 
657
  with gr.Column(scale=1):
 
658
  output_comparison = gr.Image(
659
+ label="РЕЗУЛЬТАТ",
660
+ height=400,
661
+ elem_classes="image-container"
662
  )
663
 
664
+ output_variations = gr.Image(
665
+ label="ВАРИАЦИИ ДИЗАЙНА",
666
+ visible=False,
667
+ elem_classes="image-container"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
668
  )
669
 
670
+ output_palette = gr.Image(
671
+ label="ЦВЕТОВАЯ ПАЛИТРА",
672
+ height=100,
673
+ elem_classes="image-container"
 
 
 
674
  )
675
 
676
+ output_info = gr.HTML(
677
+ label="ИНФОРМАЦИЯ"
 
 
 
 
 
 
 
 
 
678
  )
679
+
680
+ # Удаление объектов
681
+ with gr.TabItem("УДАЛЕНИЕ ОБЪЕКТОВ", elem_id="remove-tab"):
682
+ with gr.Row():
683
+ with gr.Column():
684
+ with gr.Group(elem_classes="premium-card"):
685
+ remove_image = gr.Image(
686
+ label="ЗАГРУЗИТЕ ИЗОБРАЖЕНИЕ",
687
+ type="pil",
688
+ height=400,
689
+ elem_classes="image-container"
690
+ )
691
+
692
+ objects_to_remove = gr.Textbox(
693
+ label="ЧТО УДАЛИТЬ",
694
+ placeholder="Например: диван, стол, картина на стене",
695
+ lines=3
696
+ )
697
+
698
+ mask_precision = gr.Slider(
699
+ label="ТОЧНОСТЬ ВЫДЕЛЕНИЯ",
700
+ minimum=0.5,
701
+ maximum=0.95,
702
+ value=0.8,
703
+ step=0.05
704
+ )
705
+
706
+ remove_btn = gr.Button(
707
+ "УДАЛИТЬ ОБЪЕКТЫ",
708
+ variant="primary",
709
+ elem_classes="primary-button"
710
+ )
711
 
712
  with gr.Column():
713
  remove_output = gr.Image(
714
+ label="РЕЗУЛЬТАТ",
715
+ height=400,
716
+ elem_classes="image-container"
717
  )
718
+
719
+ mask_preview = gr.Image(
720
+ label="ПРЕДПРОСМОТР МАСКИ",
721
+ height=200,
722
+ elem_classes="image-container"
723
  )
724
+
725
+ remove_info = gr.HTML()
726
 
727
+ # Детальные изменения
728
+ with gr.TabItem("ДЕТАЛЬНЫЕ ИЗМЕНЕНИЯ", elem_id="details-tab"):
729
  with gr.Row():
730
  with gr.Column():
731
+ with gr.Group(elem_classes="premium-card"):
732
+ detail_image = gr.Image(
733
+ label="ИЗОБРАЖЕНИЕ",
734
+ type="pil",
735
+ height=300,
736
+ elem_classes="image-container"
737
+ )
738
+
739
+ element_type = gr.Dropdown(
740
+ label="ЭЛЕМЕНТ",
741
+ choices=list(ROOM_ELEMENTS.keys()),
742
+ value="Стены"
743
+ )
744
+
745
+ element_value = gr.Textbox(
746
+ label="НОВОЕ ЗНАЧЕНИЕ",
747
+ placeholder="Например: белый цвет, деревянный паркет",
748
+ lines=2
749
+ )
750
+
751
+ element_strength = gr.Slider(
752
+ label="ИНТЕНСИВНОСТЬ",
753
+ minimum=0.3,
754
+ maximum=0.9,
755
+ value=0.5,
756
+ step=0.05
757
+ )
758
+
759
+ change_btn = gr.Button(
760
+ "ПРИМЕНИТЬ",
761
+ variant="primary",
762
+ elem_classes="primary-button"
763
+ )
764
 
765
  with gr.Column():
766
+ detail_output = gr.Image(
767
+ label="РЕЗУЛЬТАТ",
768
+ height=300,
769
+ elem_classes="image-container"
770
+ )
771
+ detail_info = gr.Textbox(label="СТАТУС", lines=2)
772
 
773
+ # Сравнение стилей
774
+ with gr.TabItem("СРАВНЕНИЕ СТИЛЕЙ", elem_id="compare-tab"):
775
  with gr.Row():
776
  with gr.Column():
777
+ with gr.Group(elem_classes="premium-card"):
778
+ compare_image = gr.Image(
779
+ label="ЗАГРУЗИТЕ ИЗОБРАЖЕНИЕ",
780
+ type="pil",
781
+ height=400,
782
+ elem_classes="image-container"
783
+ )
784
+
785
+ compare_styles = gr.CheckboxGroup(
786
+ label="ВЫБЕРИТЕ СТИЛИ (2-6)",
787
+ choices=list(DESIGN_STYLES.keys()),
788
+ value=[]
789
+ )
790
+
791
+ compare_room_type = gr.Dropdown(
792
+ label="ТИП ПОМЕЩЕНИЯ",
793
+ choices=["Автоопределение"] + list(ROOM_TYPES.keys()),
794
+ value="Автоопределение"
795
+ )
796
+
797
+ compare_quality = gr.Radio(
798
+ label="КАЧЕСТВО",
799
+ choices=[("Быстрое", "fast"), ("Премиум", "balanced")],
800
+ value="fast"
801
+ )
802
+
803
+ compare_btn = gr.Button(
804
+ "СРАВНИТЬ СТИЛИ",
805
+ variant="primary",
806
+ elem_classes="primary-button"
807
+ )
808
 
809
  with gr.Column():
810
+ compare_output = gr.Image(
811
+ label="СРАВНЕНИЕ СТИЛЕЙ",
812
+ elem_classes="image-container"
813
+ )
814
+ compare_info = gr.HTML()
815
 
816
+ # Увеличение разрешения
817
+ with gr.TabItem("УВЕЛИЧЕНИЕ РАЗРЕШЕНИЯ", elem_id="upscale-tab"):
818
  with gr.Row():
819
  with gr.Column():
820
+ with gr.Group(elem_classes="premium-card"):
821
+ upscale_image = gr.Image(
822
+ label="ИЗОБРАЖЕНИЕ",
823
+ type="pil",
824
+ height=400,
825
+ elem_classes="image-container"
826
+ )
827
+
828
+ scale_factor = gr.Radio(
829
+ label="МАСШТАБ",
830
+ choices=[("2x", 2), ("4x", 4)],
831
+ value=2
832
+ )
833
+
834
+ dpi_setting = gr.Radio(
835
+ label="DPI ДЛЯ ПЕЧАТИ",
836
+ choices=[
837
+ ("Экран (96 DPI)", 96),
838
+ ("Печать (150 DPI)", 150),
839
+ ("Премиум печать (300 DPI)", 300)
840
+ ],
841
+ value=150
842
+ )
843
+
844
+ upscale_btn = gr.Button(
845
+ "УВЕЛИЧИТЬ РАЗРЕШЕНИЕ",
846
+ variant="primary",
847
+ elem_classes="primary-button"
848
+ )
849
 
850
  with gr.Column():
851
+ upscale_output = gr.Image(
852
+ label="РЕЗУЛЬТАТ",
853
+ elem_classes="image-container"
854
+ )
855
+ upscale_comparison = gr.Image(
856
+ label="СРАВНЕНИЕ",
857
+ elem_classes="image-container"
858
+ )
859
+ upscale_info = gr.HTML()
860
 
861
+ # Рекомендации
862
+ with gr.TabItem("РЕКОМЕНДАЦИИ", elem_id="recommend-tab"):
863
  with gr.Row():
864
  with gr.Column():
865
+ with gr.Group(elem_classes="premium-card"):
866
+ suggest_image = gr.Image(
867
+ label="ЗАГРУЗИТЕ ФОТО ДЛЯ АНАЛИЗА",
868
+ type="pil",
869
+ height=400,
870
+ elem_classes="image-container"
871
+ )
872
+ suggest_btn = gr.Button(
873
+ "ПОЛУЧИТЬ РЕКОМЕНДАЦИИ",
874
+ variant="primary",
875
+ elem_classes="primary-button"
876
+ )
877
 
878
  with gr.Column():
879
+ suggestions = gr.HTML()
 
 
 
 
 
 
 
880
 
881
  # Обработчики событий
882
  process_btn.click(
 
895
  remove_btn.click(
896
  remove_objects_by_text,
897
  inputs=[remove_image, objects_to_remove, mask_precision],
898
+ outputs=[remove_output, mask_preview, remove_info]
899
  )
900
 
901
  change_btn.click(
 
906
 
907
  compare_btn.click(
908
  create_style_comparison,
909
+ inputs=[compare_image, compare_styles, compare_room_type, compare_quality],
910
  outputs=[compare_output, compare_info]
911
  )
912
 
913
  upscale_btn.click(
914
  enhance_image,
915
+ inputs=[upscale_image, scale_factor, dpi_setting],
916
  outputs=[upscale_output, upscale_comparison, upscale_info]
917
  )
918
 
 
922
  outputs=[suggestions]
923
  )
924
 
 
 
 
 
 
 
 
 
 
 
 
 
 
925
  # Информация внизу
926
+ gr.HTML("""
927
+ <div style="text-align: center; padding: 3rem 0; border-top: 1px solid #E5E5E5; margin-top: 4rem;">
928
+ <div class="gold-line" style="margin: 0 auto;"></div>
929
+ <p style="color: #666; font-size: 0.875rem; margin-top: 2rem;">
930
+ PREMIUM INTERIOR DESIGNER EXCLUSIVE AI TECHNOLOGY
931
+ </p>
932
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
933
  """)
934
 
935
  # Запуск приложения