PandaArtStation commited on
Commit
7af4b63
·
verified ·
1 Parent(s): 10f680f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +387 -473
app.py CHANGED
@@ -1,43 +1,20 @@
1
  import gradio as gr
2
  import numpy as np
3
- from PIL import Image, ImageFilter, ImageDraw, ImageFont
4
  import torch
5
  import spaces
6
  from models import InteriorDesignerPro
7
- from design_styles import DESIGN_STYLES, ROOM_TYPES, ROOM_ELEMENTS
8
  from utils import ImageProcessor, ColorPalette
9
  import os
10
  import urllib.request
11
  from typing import List, Tuple, Optional
12
- import hashlib
13
  import cv2
14
 
15
- # Загрузка защищенных настроек
16
- AUTHOR_INFO = os.environ.get("AUTHOR_INFO", "© 2024 AI Interior Designer Pro")
17
- SPACE_ID = os.environ.get("SPACE_ID", "default-space")
18
-
19
- # Критическая инициализация из secrets
20
- def load_critical_module():
21
- """Загружает защищенный код из HF Secrets"""
22
- secret_code = os.environ.get("CRITICAL_MODULE", None)
23
- if secret_code:
24
- try:
25
- exec(secret_code, globals())
26
- print("✅ Critical module loaded successfully")
27
- except Exception as e:
28
- print(f"⚠️ Critical module failed: {e}")
29
- else:
30
- print("⚠️ Running in unprotected mode")
31
- # Базовая инициализация для разработки
32
- global SECURITY_KEY
33
- SECURITY_KEY = hashlib.sha256(f"{SPACE_ID}_{AUTHOR_INFO}".encode()).hexdigest()
34
-
35
- # Загружаем критический модуль
36
- load_critical_module()
37
-
38
  # Глобальные переменные
39
  designer = None
40
  processor = ImageProcessor()
 
41
 
42
  # CSS стили
43
  custom_css = """
@@ -88,32 +65,110 @@ custom_css = """
88
  font-family: monospace;
89
  margin: 10px 0;
90
  }
91
- .author-badge {
92
- background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
93
- color: white;
94
- padding: 15px 30px;
95
- border-radius: 10px;
96
- text-align: center;
97
- font-weight: bold;
98
- margin: 20px 0;
99
- box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
100
- }
101
  """
102
 
103
- # Функция добавления водяного знака (если определена в CRITICAL_MODULE)
104
- def add_watermark(image):
105
- """Добавляет водяной знак на изображение"""
106
- if 'SecurityValidator' in globals():
107
- return SecurityValidator.inject_watermark(image)
108
- # Базовый водяной знак
109
- draw = ImageDraw.Draw(image)
110
- text = AUTHOR_INFO.split("|")[0].strip()
111
- try:
112
- font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 20)
113
- except:
114
- font = None
115
- draw.text((10, image.height - 30), text, fill=(255, 255, 255, 128), font=font)
116
- return image
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  # Класс для улучшения изображений
119
  class ImageEnhancer:
@@ -128,16 +183,19 @@ class ImageEnhancer:
128
  from realesrgan import RealESRGANer
129
  from basicsr.archs.rrdbnet_arch import RRDBNet
130
 
 
131
  model_dir = os.path.expanduser('~/.cache/realesrgan/')
132
  os.makedirs(model_dir, exist_ok=True)
133
 
134
  model_path = os.path.join(model_dir, 'RealESRGAN_x4plus.pth')
135
 
 
136
  if not os.path.exists(model_path):
137
  url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth'
138
  print(f"Downloading Real-ESRGAN model...")
139
  urllib.request.urlretrieve(url, model_path)
140
 
 
141
  model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
142
 
143
  self.model = RealESRGANer(
@@ -160,19 +218,27 @@ class ImageEnhancer:
160
  def upscale(self, image: Image.Image, scale: int = 2) -> Image.Image:
161
  """Увеличение разрешения изображения"""
162
  try:
 
163
  if self.load_model() and self.model is not None:
 
164
  img_np = np.array(image)
 
 
165
  output, _ = self.model.enhance(img_np, outscale=scale)
 
 
166
  return Image.fromarray(output)
167
  except:
168
  pass
169
 
170
- # Fallback на PIL resize
171
  new_width = int(image.width * scale)
172
  new_height = int(image.height * scale)
173
 
 
174
  resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
175
 
 
176
  if scale > 2:
177
  resized = resized.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))
178
 
@@ -183,178 +249,41 @@ class ImageEnhancer:
183
  image.info['dpi'] = (dpi, dpi)
184
  return image
185
 
186
- # Класс для LAMA удаления объектов
187
- class LamaObjectRemover:
188
- def __init__(self):
189
- self.pipe = None
190
- self.device = "cuda" if torch.cuda.is_available() else "cpu"
191
-
192
- def load_model(self):
193
- """Загрузка модели для inpainting"""
194
- if self.pipe is None:
195
- try:
196
- from diffusers import StableDiffusionInpaintPipeline
197
- self.pipe = StableDiffusionInpaintPipeline.from_pretrained(
198
- "runwayml/stable-diffusion-inpainting",
199
- torch_dtype=torch.float16 if self.device == "cuda" else torch.float32,
200
- variant="fp16" if self.device == "cuda" else None
201
- )
202
- self.pipe = self.pipe.to(self.device)
203
- print("LAMA model loaded successfully!")
204
- return True
205
- except Exception as e:
206
- print(f"Failed to load LAMA model: {e}")
207
- return False
208
 
209
- @spaces.GPU
210
- def remove_objects(self, image: Image.Image, mask: Image.Image) -> Image.Image:
211
- """Удаление объектов с изображения"""
212
- try:
213
- # Пробуем загрузить модель
214
- if self.load_model() and self.pipe is not None:
215
- # Приводим размеры к кратным 8
216
- w, h = image.size
217
- new_w = (w // 8) * 8
218
- new_h = (h // 8) * 8
219
-
220
- image_resized = image.resize((new_w, new_h), Image.Resampling.LANCZOS)
221
- mask_resized = mask.resize((new_w, new_h), Image.Resampling.LANCZOS)
222
-
223
- # Inpainting с минимальными изменениями
224
- result = self.pipe(
225
- prompt="empty space, clean background, seamless texture",
226
- negative_prompt="furniture, objects, people, decorations",
227
- image=image_resized,
228
- mask_image=mask_resized,
229
- height=new_h,
230
- width=new_w,
231
- strength=0.99,
232
- guidance_scale=3.0,
233
- num_inference_steps=50
234
- ).images[0]
235
-
236
- # Возвращаем к исходному размеру
237
- return result.resize((w, h), Image.Resampling.LANCZOS)
238
-
239
- except Exception as e:
240
- print(f"Model-based inpainting failed: {e}")
241
-
242
- # Fallback на OpenCV inpainting
243
  try:
244
- img_np = np.array(image)
245
- mask_np = np.array(mask.convert('L'))
246
-
247
- # Расширяем маску для лучшего результата
248
- kernel = np.ones((5,5), np.uint8)
249
- mask_np = cv2.dilate(mask_np, kernel, iterations=1)
250
-
251
- # Применяем inpainting
252
- result = cv2.inpaint(img_np, mask_np, 3, cv2.INPAINT_TELEA)
253
-
254
- return Image.fromarray(result)
255
-
256
  except Exception as e:
257
- print(f"OpenCV inpainting failed: {e}")
258
- return image
259
-
260
- # Инициализация глобальных объектов
261
- lama_remover = LamaObjectRemover()
262
-
263
- # Добавление метода _create_comparison_grid к классу InteriorDesignerPro
264
- def _create_comparison_grid(self, images: List[Image.Image], styles: List[str],
265
- original: Optional[Image.Image] = None) -> Image.Image:
266
- """Создает сетку сравнения стилей"""
267
- if not images:
268
- return None
269
-
270
- # Размеры для сетки
271
- n_images = len(images) + (1 if original else 0)
272
- cols = min(3, n_images)
273
- rows = (n_images + cols - 1) // cols
274
-
275
- # Размер одного изображения в сетке
276
- img_width = 512
277
- img_height = 512
278
- padding = 20
279
- text_height = 60
280
-
281
- # Создаем холст
282
- grid_width = cols * img_width + (cols - 1) * padding
283
- grid_height = rows * (img_height + text_height) + (rows - 1) * padding
284
-
285
- grid = Image.new('RGB', (grid_width, grid_height), 'white')
286
- draw = ImageDraw.Draw(grid)
287
-
288
- # Шрифт для подписей
289
- try:
290
- font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20)
291
- except:
292
- font = None
293
-
294
- # Добавляем изображения
295
- all_images = []
296
- all_labels = []
297
-
298
- if original:
299
- all_images.append(original)
300
- all_labels.append("Оригинал")
301
-
302
- all_images.extend(images)
303
- all_labels.extend(styles)
304
-
305
- for idx, (img, label) in enumerate(zip(all_images, all_labels)):
306
- row = idx // cols
307
- col = idx % cols
308
-
309
- # Позиция изображения
310
- x = col * (img_width + padding)
311
- y = row * (img_height + text_height + padding)
312
-
313
- # Изменяем размер и вставляем
314
- img_resized = img.resize((img_width, img_height), Image.Resampling.LANCZOS)
315
- grid.paste(img_resized, (x, y))
316
-
317
- # Добавляем подпись
318
- text_y = y + img_height + 10
319
- # Центрируем текст
320
- if font:
321
- bbox = draw.textbbox((0, 0), label, font=font)
322
- text_width = bbox[2] - bbox[0]
323
- else:
324
- text_width = len(label) * 6
325
-
326
- text_x = x + (img_width - text_width) // 2
327
-
328
- # Фон для текста
329
- draw.rectangle(
330
- [x, y + img_height, x + img_width, y + img_height + text_height],
331
- fill='#f0f0f0'
332
- )
333
-
334
- # Текст
335
- draw.text(
336
- (text_x, text_y),
337
- label,
338
- fill='black',
339
- font=font
340
- )
341
-
342
- return grid
343
 
344
- # Динамически добавляем метод к классу
345
- InteriorDesignerPro._create_comparison_grid = _create_comparison_grid
346
 
347
  @spaces.GPU
348
  def init_designer():
349
  """Инициализация дизайнера"""
350
  global designer
351
  if designer is None:
352
- # Проверка безопасности если определена
353
- if 'critical_init' in globals():
354
- critical_init()
355
  designer = InteriorDesignerPro()
356
  return designer
357
 
 
 
 
 
 
 
 
 
 
358
  @spaces.GPU(duration=90)
359
  def process_image(image, style, room_type, strength, quality_mode,
360
  enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt=""):
@@ -377,6 +306,7 @@ def process_image(image, style, room_type, strength, quality_mode,
377
 
378
  # Применяем стиль с выбранным качеством
379
  if custom_prompt:
 
380
  quality_boost = "masterpiece, best quality, ultra-detailed, 8k uhd, " if quality_mode == "ultra" else ""
381
  styled_image = designer.pipe(
382
  prompt=f"{quality_boost}{room_type_en}, {custom_prompt}, interior design, high quality",
@@ -387,21 +317,25 @@ def process_image(image, style, room_type, strength, quality_mode,
387
  guidance_scale={"fast": 7.5, "balanced": 8.5, "ultra": 10}[quality_mode]
388
  ).images[0]
389
  else:
390
- styled_image = designer.apply_style_pro(
391
- image, style, room_type_en, strength, quality=quality_mode
392
- )
 
 
 
 
 
 
 
393
 
394
  # HDR освещение
395
  if enhance_lighting and quality_mode != "fast":
396
  styled_image = designer.create_hdr_lighting(styled_image, intensity=0.3)
397
 
398
- # Улучшение деталей
399
  if add_details and designer.is_powerful_gpu and quality_mode == "ultra":
400
  styled_image = designer.enhance_details(styled_image)
401
 
402
- # Добавляем водяной знак
403
- styled_image = add_watermark(styled_image)
404
-
405
  # Создаем сравнение до/после
406
  comparison = processor.create_before_after(image, styled_image)
407
 
@@ -410,8 +344,6 @@ def process_image(image, style, room_type, strength, quality_mode,
410
  if use_variations:
411
  num_var = 8 if designer.is_powerful_gpu else 4
412
  var_images = designer.create_variations(image, num_variations=num_var)
413
- # Добавляем водяные знаки на вариации
414
- var_images = [add_watermark(img) for img in var_images]
415
  variations = processor.create_grid(
416
  var_images,
417
  titles=[f"Вариант {i+1}" for i in range(len(var_images))]
@@ -429,14 +361,13 @@ def process_image(image, style, room_type, strength, quality_mode,
429
  ⚡ Режим качества: {quality_mode.upper()}
430
  🖼️ Разрешение: {styled_image.width}×{styled_image.height}
431
  🤖 Модель: {designer.model_name}
432
- 🔒 Protected by: {AUTHOR_INFO.split('|')[0]}
433
 
434
  🎨 Основные цвета дизайна:
435
  """
436
  for i, color in enumerate(colors[:5]):
437
  hex_color = '#{:02x}{:02x}{:02x}'.format(*color)
438
- info += f"\n • {hex_color}"
439
-
440
  return comparison, variations, palette, info
441
 
442
  except Exception as e:
@@ -449,73 +380,23 @@ def change_room_element(image, element, value, strength):
449
  """Изменение отдельного элемента"""
450
  if image is None:
451
  return None, "Загрузите изображение"
452
-
453
  global designer
454
  if designer is None:
455
  designer = InteriorDesignerPro()
456
-
457
  try:
458
  result = designer.change_element(image, element, value, strength)
459
- result = add_watermark(result)
460
  return result, f"✅ {element} изменен на {value}"
461
  except Exception as e:
462
  return None, f"❌ Ошибка: {str(e)}"
463
 
464
- @spaces.GPU
465
- def remove_objects_lama(image, mask, inpaint_prompt=""):
466
- """Удаление объектов с использованием LAMA"""
467
- if image is None:
468
- return None, "❌ Загрузите изображение"
469
-
470
- try:
471
- # Если маска не загружена, возвращаем оригинал
472
- if mask is None:
473
- return image, "⚠️ Загрузите маску для удаления объектов"
474
-
475
- # Применяем LAMA
476
- result = lama_remover.remove_objects(image, mask)
477
- result = add_watermark(result)
478
-
479
- return result, "✅ Объекты успешно удалены"
480
-
481
- except Exception as e:
482
- return None, f"❌ Ошибка: {str(e)}"
483
-
484
- @spaces.GPU
485
- def create_auto_mask(image, objects_text):
486
- """Создание маски по текстовому описанию"""
487
- if image is None:
488
- return None, "❌ Загрузите изображение"
489
-
490
- try:
491
- # Простая демо-маска (в реальности нужен GroundingDINO или SAM)
492
- mask = Image.new('L', image.size, 0)
493
- draw = ImageDraw.Draw(mask)
494
-
495
- # Создаем примерную маску в центре
496
- width, height = image.size
497
- margin = min(width, height) // 4
498
-
499
- if "центр" in objects_text.lower() or "middle" in objects_text.lower():
500
- draw.ellipse([margin, margin, width-margin, height-margin], fill=255)
501
- elif "лев" in objects_text.lower() or "left" in objects_text.lower():
502
- draw.rectangle([0, 0, width//2, height], fill=255)
503
- elif "прав" in objects_text.lower() or "right" in objects_text.lower():
504
- draw.rectangle([width//2, 0, width, height], fill=255)
505
- else:
506
- # По умолчанию - центральная область
507
- draw.ellipse([margin, margin, width-margin, height-margin], fill=255)
508
-
509
- return mask, "✅ Маска создана. Для точного выделения используйте ручное рисование."
510
-
511
- except Exception as e:
512
- return None, f"❌ Ошибка: {str(e)}"
513
-
514
  def suggest_styles(image):
515
  """Предложение подходящих стилей"""
516
  if image is None:
517
  return "Загрузите изображение для получения рекомендаций"
518
-
 
519
  room_type = processor.detect_room_type(image)
520
 
521
  suggestions = f"""
@@ -549,68 +430,21 @@ def suggest_styles(image):
549
  - Интенсивность 70-90% - кардинальное преображение
550
  - Режим Ultra - для финальной визуализации
551
  - HDR освещение - добавит реализма
552
-
553
- 🔒 {AUTHOR_INFO}
554
  """
 
555
  return suggestions
556
 
557
  @spaces.GPU(duration=60)
558
- def create_style_comparison(image, selected_styles, quality="balanced"):
559
- """Создание сравнения стилей"""
560
- if image is None:
561
- return None, "❌ Загрузите изображение"
562
-
563
- if not selected_styles or len(selected_styles) < 2:
564
- return None, "❌ Выберите минимум 2 стиля для сравнения"
565
-
566
- global designer
567
- if designer is None:
568
- designer = InteriorDesignerPro()
569
-
570
- try:
571
- room_type = processor.detect_room_type(image)
572
- room_type_en = ROOM_TYPES.get(room_type, "living room")
573
-
574
- # Генерируем изображения для каждого стиля
575
- styled_images = []
576
- for style in selected_styles[:6]: # Максимум 6 стилей
577
- styled = designer.apply_style_pro(
578
- image, style, room_type_en,
579
- strength=0.75,
580
- quality="fast" if quality == "balanced" else quality
581
- )
582
- styled = add_watermark(styled)
583
- styled_images.append(styled)
584
-
585
- # Создаем сетку сравнения
586
- comparison_grid = designer._create_comparison_grid(
587
- styled_images,
588
- selected_styles[:6],
589
- original=image
590
- )
591
-
592
- info = f"""
593
- ✅ Сравнение стилей готово!
594
- 📐 Тип комнаты: {room_type}
595
- 🎨 Стили: {', '.join(selected_styles[:6])}
596
- 🖼️ Количество вариантов: {len(styled_images)}
597
- 🔒 {AUTHOR_INFO}
598
- """
599
-
600
- return comparison_grid, info
601
-
602
- except Exception as e:
603
- return None, f"❌ Ошибка: {str(e)}"
604
-
605
- @spaces.GPU
606
  def enhance_image(image, scale, dpi):
607
  """Увеличение разрешения изображения"""
608
  if image is None:
609
  return None, None, "❌ Пожалуйста, загрузите изображение"
610
 
611
  try:
 
612
  enhancer = ImageEnhancer()
613
 
 
614
  original_size = f"{image.width}×{image.height}"
615
 
616
  # Улучшаем изображение
@@ -618,6 +452,7 @@ def enhance_image(image, scale, dpi):
618
 
619
  # Проверяем, изменился ли размер
620
  if enhanced.size == image.size:
 
621
  new_width = image.width * scale
622
  new_height = image.height * scale
623
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
@@ -628,9 +463,6 @@ def enhance_image(image, scale, dpi):
628
  # Настраиваем DPI
629
  enhanced = enhancer.set_dpi(enhanced, dpi)
630
 
631
- # Добавляем водяной знак
632
- enhanced = add_watermark(enhanced)
633
-
634
  # Создаем сравнение
635
  comparison = processor.create_before_after(image, enhanced)
636
 
@@ -643,17 +475,16 @@ def enhance_image(image, scale, dpi):
643
  🔍 Масштаб: {scale}x
644
  📊 DPI: {dpi}
645
  🔧 Метод: {method}
646
- 🔒 {AUTHOR_INFO}
647
- """
648
 
649
  return enhanced, comparison, info
650
 
651
  except Exception as e:
 
652
  try:
653
  new_width = int(image.width * scale)
654
  new_height = int(image.height * scale)
655
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
656
- enhanced = add_watermark(enhanced)
657
 
658
  info = f"""
659
  ⚠️ Real-ESRGAN недоступен, использован PIL resize
@@ -661,26 +492,132 @@ def enhance_image(image, scale, dpi):
661
  📐 Новый размер: {enhanced.width}×{enhanced.height}
662
  🔍 Масштаб: {scale}x
663
  Ошибка: {str(e)}
664
- """
665
  return enhanced, None, info
666
  except Exception as e2:
667
  return None, None, f"❌ Ошибка увеличения: {str(e2)}"
668
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
  # Создаем интерфейс
670
  with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.themes.Soft(), css=custom_css) as app:
671
- # Добавляем информацию об авторе вверху
672
- gr.HTML(f"""
673
- <div class="author-badge">
674
- <h3>{AUTHOR_INFO}</h3>
675
- <p>Protected AI Interior Designer Pro | Space ID: {SPACE_ID}</p>
676
- </div>
677
- """)
678
-
679
  gr.Markdown("""
680
  # 🎨 AI Дизайнер интерьера Pro
681
  ### Преобразите свое пространство с помощью искусственного интеллекта
682
  """)
683
 
 
 
 
 
 
 
684
  with gr.Tabs():
685
  # Основная вкладка
686
  with gr.TabItem("🎨 Дизайнер", id=0):
@@ -747,7 +684,6 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
747
  lines=3,
748
  info="Опишите желаемый результат своими словами"
749
  )
750
-
751
  negative_prompt = gr.Textbox(
752
  label="Чего избегать",
753
  placeholder="dark, cluttered, old furniture",
@@ -783,7 +719,6 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
783
  label="Варианты дизайна",
784
  visible=False
785
  )
786
-
787
  output_palette = gr.Image(
788
  label="Цветовая палитра",
789
  height=100
@@ -793,51 +728,83 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
793
  label="Информация о процессе"
794
  )
795
 
796
- # Вкладка удаления объектов с LAMA
797
- with gr.TabItem("🗑️ Удаление объектов (LAMA)", id=1):
798
  with gr.Row():
799
  with gr.Column():
800
- lama_image = gr.Image(
801
- label="Загрузите фото",
802
  type="pil",
803
  height=400
804
  )
805
 
806
- gr.Markdown("""
807
- ### 📝 Как удалить объекты:
 
 
 
808
 
809
- **Вариант 1: Создайте маску вручную**
810
- 1. Сохраните изображение выше
811
- 2. Откройте в любом редакторе (Paint, Photoshop, GIMP)
812
- 3. Закрасьте объекты БЕЛЫМ цветом
813
- 4. Загрузите маску ниже
814
 
815
- **Вариант 2: Автоматическая маска**
816
- - Опишите объекты текстом ниже
817
- """)
 
 
 
 
818
 
819
- lama_mask = gr.Image(
820
- label="Загрузите маску (белый = удалить, черный = оставить)",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
821
  type="pil",
822
- height=400
 
 
823
  )
824
 
825
- with gr.Row():
826
- auto_mask_text = gr.Textbox(
827
- label="Или опишите объекты для удаления",
828
- placeholder="мебель в центре, диван, стол",
829
- lines=2
830
- )
831
-
832
- create_mask_btn = gr.Button("Создать маску автоматически")
833
 
834
- lama_inpaint_prompt = gr.Textbox(
835
- label="Чем заполнить пустоты (опционально)",
836
- placeholder="empty floor, clean wall",
837
- lines=2
838
- )
839
 
840
- remove_btn = gr.Button(
841
  "🗑️ Удалить объекты",
842
  variant="primary",
843
  size="lg"
@@ -846,25 +813,15 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
846
  with gr.Column():
847
  lama_output = gr.Image(
848
  label="Результат",
849
- height=400
850
  )
851
-
852
  lama_info = gr.Textbox(
853
  label="Статус",
854
- lines=3
855
  )
856
-
857
- # Примеры масок
858
- gr.Markdown("""
859
- ### 💡 Советы:
860
- - Чем точнее маска, тем лучше результат
861
- - Для мебели делайте маску чуть больше объекта
862
- - LAMA сохраняет стиль комнаты, только убирает объекты
863
- """)
864
 
865
- # Остальные вкладки...
866
  # Вкладка детальных изменений
867
- with gr.TabItem("🔧 Детальные изменения", id=2):
868
  with gr.Row():
869
  with gr.Column():
870
  detail_image = gr.Image(
@@ -899,51 +856,12 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
899
  detail_output = gr.Image(label="Результат", height=300)
900
  detail_info = gr.Textbox(label="Статус", lines=2)
901
 
902
- # Вкладка сравнения стилей
903
- with gr.TabItem("🎭 Сравнение стилей", id=3):
904
- with gr.Row():
905
- with gr.Column():
906
- compare_image = gr.Image(
907
- label="Загрузите фото для сравнения стилей",
908
- type="pil",
909
- height=400
910
- )
911
-
912
- compare_styles = gr.CheckboxGroup(
913
- label="Выберите стили для сравнения (от 2 до 6)",
914
- choices=list(DESIGN_STYLES.keys()),
915
- value=[]
916
- )
917
-
918
- compare_quality = gr.Radio(
919
- label="Качество сравнения",
920
- choices=[
921
- ("Быстрое", "fast"),
922
- ("Сбалансированное", "balanced")
923
- ],
924
- value="fast"
925
- )
926
-
927
- compare_btn = gr.Button(
928
- "🎭 Сравнить стили",
929
- variant="primary",
930
- size="lg"
931
- )
932
-
933
- with gr.Column():
934
- compare_output = gr.Image(
935
- label="Сравнение стилей",
936
- height=600
937
- )
938
-
939
- compare_info = gr.Markdown()
940
-
941
  # Вкладка увеличения разрешения
942
  with gr.TabItem("🔍 Увеличение разрешения", id=4):
943
  with gr.Row():
944
  with gr.Column():
945
  upscale_image = gr.Image(
946
- label="Загрузите изображение для увеличения",
947
  type="pil",
948
  height=400
949
  )
@@ -957,17 +875,16 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
957
  upscale_dpi = gr.Radio(
958
  label="DPI для печати",
959
  choices=[
960
- ("Экран (96 DPI)", 96),
961
- ("Документ (150 DPI)", 150),
962
- ("Печать (300 DPI)", 300)
963
  ],
964
  value=150
965
  )
966
 
967
  upscale_btn = gr.Button(
968
  "🔍 Увеличить разрешение",
969
- variant="primary",
970
- size="lg"
971
  )
972
 
973
  with gr.Column():
@@ -975,12 +892,10 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
975
  label="Увеличенное изображение",
976
  height=400
977
  )
978
-
979
  upscale_comparison = gr.Image(
980
  label="Сравнение",
981
  height=200
982
  )
983
-
984
  upscale_info = gr.Markdown()
985
 
986
  # Вкладка рекомендаций
@@ -992,7 +907,6 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
992
  type="pil",
993
  height=400
994
  )
995
-
996
  suggest_btn = gr.Button("Получить рекомендации", variant="primary")
997
 
998
  with gr.Column():
@@ -1000,13 +914,9 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
1000
 
1001
  # Цветовые палитры для стилей
1002
  gr.Markdown("### 🎨 Цветовые палитры популярных стилей")
1003
-
1004
  for style_name in list(DESIGN_STYLES.keys())[:4]:
1005
- colors = ColorPalette.suggest_palette(style_name)
1006
- color_blocks = " ".join([
1007
- f'<span style="background-color:{c}; padding:10px 20px; margin:2px; display:inline-block; border-radius:5px;"></span>'
1008
- for c in colors
1009
- ])
1010
  gr.HTML(f"<div class='info-box'><strong>{style_name}:</strong><br>{color_blocks}</div>")
1011
 
1012
  # Обработчики событий
@@ -1023,16 +933,16 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
1023
  outputs=[output_variations]
1024
  )
1025
 
1026
- # LAMA handlers
1027
- create_mask_btn.click(
1028
- create_auto_mask,
1029
- inputs=[lama_image, auto_mask_text],
1030
- outputs=[lama_mask, lama_info]
1031
  )
1032
 
1033
- remove_btn.click(
1034
- remove_objects_lama,
1035
- inputs=[lama_image, lama_mask, lama_inpaint_prompt],
 
1036
  outputs=[lama_output, lama_info]
1037
  )
1038
 
@@ -1042,12 +952,6 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
1042
  outputs=[detail_output, detail_info]
1043
  )
1044
 
1045
- compare_btn.click(
1046
- create_style_comparison,
1047
- inputs=[compare_image, compare_styles, compare_quality],
1048
- outputs=[compare_output, compare_info]
1049
- )
1050
-
1051
  upscale_btn.click(
1052
  enhance_image,
1053
  inputs=[upscale_image, upscale_factor, upscale_dpi],
@@ -1060,31 +964,41 @@ with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.them
1060
  outputs=[suggestions]
1061
  )
1062
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1063
  # Информация внизу
1064
- gr.Markdown(f"""
1065
  ---
1066
  ### 📝 Инструкция по использованию:
1067
- 1. Загрузите фото вашей комнаты
1068
- 2. Выберите стиль из 8 доступных вариантов
1069
- 3. Настройте параметры по вкусу
1070
- 4. Нажмите "Преобразить интерьер"
 
 
1071
 
1072
  ### 🚀 Возможности:
1073
- - 8 стилей дизайна - от минимализма до ар-деко
1074
- - Автоопределение типа комнаты
1075
- - Создание вариаций - до 8 вариантов за раз
1076
- - Удаление объектов с LAMA
1077
- - Детальные изменения - меняйте отдельные элементы
1078
- - HDR освещение - профессиональная обработка света
1079
- - Анализ цветовой палитры
1080
 
1081
  ---
1082
- <center>
1083
- <div class="author-badge">
1084
- <p>{AUTHOR_INFO}</p>
1085
- <p>Made with ❤️ for interior design enthusiasts</p>
1086
- </div>
1087
- </center>
1088
  """)
1089
 
1090
  # ВАЖНО! Запуск приложения
 
1
  import gradio as gr
2
  import numpy as np
3
+ from PIL import Image, ImageFilter, ImageDraw
4
  import torch
5
  import spaces
6
  from models import InteriorDesignerPro
7
+ from design_styles import DESIGN_STYLES, ROOM_TYPES, ROOM_ELEMENTS, get_detailed_prompt
8
  from utils import ImageProcessor, ColorPalette
9
  import os
10
  import urllib.request
11
  from typing import List, Tuple, Optional
 
12
  import cv2
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  # Глобальные переменные
15
  designer = None
16
  processor = ImageProcessor()
17
+ lama_remover = None
18
 
19
  # CSS стили
20
  custom_css = """
 
65
  font-family: monospace;
66
  margin: 10px 0;
67
  }
 
 
 
 
 
 
 
 
 
 
68
  """
69
 
70
+ # Класс для удаления объектов через LAMA
71
+ class LamaObjectRemover:
72
+ def __init__(self):
73
+ self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
74
+ self.pipe = None
75
+
76
+ @spaces.GPU
77
+ def init_model(self):
78
+ """Инициализация модели для inpainting"""
79
+ if self.pipe is None:
80
+ try:
81
+ from diffusers import StableDiffusionInpaintPipeline
82
+ self.pipe = StableDiffusionInpaintPipeline.from_pretrained(
83
+ "runwayml/stable-diffusion-inpainting",
84
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
85
+ safety_checker=None,
86
+ requires_safety_checker=False
87
+ ).to(self.device)
88
+ print("✅ LAMA model loaded successfully")
89
+ except Exception as e:
90
+ print(f"Failed to load inpainting model: {e}")
91
+ self.pipe = None
92
+
93
+ def extract_mask_from_drawing(self, image_with_drawing):
94
+ """Извлекает маску из нарисованного изображения"""
95
+ if isinstance(image_with_drawing, dict):
96
+ # Новый формат Gradio
97
+ original = image_with_drawing.get("background", image_with_drawing.get("image"))
98
+ composite = image_with_drawing.get("composite", image_with_drawing.get("mask"))
99
+
100
+ if original and composite:
101
+ # Создаем маску из разницы
102
+ orig_np = np.array(original)
103
+ comp_np = np.array(composite)
104
+
105
+ # Находим отличия (где рисовали)
106
+ diff = np.any(orig_np != comp_np, axis=-1)
107
+ mask = Image.fromarray((diff * 255).astype(np.uint8))
108
+
109
+ return original, mask
110
+ else:
111
+ # Старый формат или просто изображение с рисунком
112
+ img_array = np.array(image_with_drawing)
113
+
114
+ # Ищем красные области (предполагаем что рисовали красным)
115
+ red_mask = (img_array[:,:,0] > 200) & (img_array[:,:,1] < 100) & (img_array[:,:,2] < 100)
116
+
117
+ # Очищаем оригинал от красных меток
118
+ clean_img = img_array.copy()
119
+ clean_img[red_mask] = [255, 255, 255] # Заменяем на белый
120
+
121
+ mask = Image.fromarray((red_mask * 255).astype(np.uint8))
122
+ original = Image.fromarray(clean_img)
123
+
124
+ return original, mask
125
+
126
+ @spaces.GPU(duration=60)
127
+ def remove_objects(self, image_with_drawing):
128
+ """Удаляет объекты используя LAMA подход"""
129
+ if image_with_drawing is None:
130
+ return None, "❌ Пожалуйста, загрузите изображение"
131
+
132
+ try:
133
+ # Инициализируем модель если нужно
134
+ if self.pipe is None:
135
+ self.init_model()
136
+
137
+ # Извлекаем оригинал и маску
138
+ original, mask = self.extract_mask_from_drawing(image_with_drawing)
139
+
140
+ # Проверяем что маска не пустая
141
+ if np.array(mask).sum() == 0:
142
+ return original, "⚠️ Ничего не выделено для удаления"
143
+
144
+ # Немного расширяем маску для лучшего результата
145
+ mask_np = np.array(mask)
146
+ kernel = np.ones((5,5), np.uint8)
147
+ mask_np = cv2.dilate(mask_np, kernel, iterations=2)
148
+ mask = Image.fromarray(mask_np)
149
+
150
+ if self.pipe:
151
+ # Используем inpainting pipeline
152
+ result = self.pipe(
153
+ prompt="empty space, clean background, seamless texture",
154
+ negative_prompt="furniture, objects, people, decorations",
155
+ image=original,
156
+ mask_image=mask,
157
+ num_inference_steps=30,
158
+ strength=0.99,
159
+ guidance_scale=3.0
160
+ ).images[0]
161
+ else:
162
+ # Fallback на OpenCV inpainting
163
+ img_array = np.array(original)
164
+ mask_array = np.array(mask)
165
+ result_array = cv2.inpaint(img_array, mask_array, 3, cv2.INPAINT_TELEA)
166
+ result = Image.fromarray(result_array)
167
+
168
+ return result, "✅ Объекты успешно удалены!"
169
+
170
+ except Exception as e:
171
+ return None, f"❌ Ошибка: {str(e)}"
172
 
173
  # Класс для улучшения изображений
174
  class ImageEnhancer:
 
183
  from realesrgan import RealESRGANer
184
  from basicsr.archs.rrdbnet_arch import RRDBNet
185
 
186
+ # Создаем папку для моделей
187
  model_dir = os.path.expanduser('~/.cache/realesrgan/')
188
  os.makedirs(model_dir, exist_ok=True)
189
 
190
  model_path = os.path.join(model_dir, 'RealESRGAN_x4plus.pth')
191
 
192
+ # Скачиваем модель если нет
193
  if not os.path.exists(model_path):
194
  url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth'
195
  print(f"Downloading Real-ESRGAN model...")
196
  urllib.request.urlretrieve(url, model_path)
197
 
198
+ # Инициализация модели
199
  model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4)
200
 
201
  self.model = RealESRGANer(
 
218
  def upscale(self, image: Image.Image, scale: int = 2) -> Image.Image:
219
  """Увеличение разрешения изображения"""
220
  try:
221
+ # Пробуем Real-ESRGAN
222
  if self.load_model() and self.model is not None:
223
+ # Конвертируем в numpy
224
  img_np = np.array(image)
225
+
226
+ # Увеличиваем
227
  output, _ = self.model.enhance(img_np, outscale=scale)
228
+
229
+ # Обратно в PIL
230
  return Image.fromarray(output)
231
  except:
232
  pass
233
 
234
+ # Fallback на PIL resize с улучшением
235
  new_width = int(image.width * scale)
236
  new_height = int(image.height * scale)
237
 
238
+ # Используем LANCZOS для качественного увеличения
239
  resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
240
 
241
+ # Применяем легкую постобработку для улучшения четкости
242
  if scale > 2:
243
  resized = resized.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))
244
 
 
249
  image.info['dpi'] = (dpi, dpi)
250
  return image
251
 
252
+ # Загрузка защищенного модуля (если есть)
253
+ def load_protected_module():
254
+ """Загружает защищенный код из HF Secrets"""
255
+ secret_code = os.environ.get("CRITICAL_MODULE", None)
256
+ author = os.environ.get("AUTHOR_INFO", "Development Mode")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
 
258
+ if secret_code:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
259
  try:
260
+ exec(secret_code, globals())
261
+ print(f"✅ Protected version by {author}")
 
 
 
 
 
 
 
 
 
 
262
  except Exception as e:
263
+ print(f"⚠️ Running in unprotected mode: {e}")
264
+ else:
265
+ print("⚠️ Running in development mode (no protection)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
+ # Загружаем защиту
268
+ load_protected_module()
269
 
270
  @spaces.GPU
271
  def init_designer():
272
  """Инициализация дизайнера"""
273
  global designer
274
  if designer is None:
 
 
 
275
  designer = InteriorDesignerPro()
276
  return designer
277
 
278
+ @spaces.GPU
279
+ def init_lama():
280
+ """Инициализация LAMA"""
281
+ global lama_remover
282
+ if lama_remover is None:
283
+ lama_remover = LamaObjectRemover()
284
+ lama_remover.init_model()
285
+ return lama_remover
286
+
287
  @spaces.GPU(duration=90)
288
  def process_image(image, style, room_type, strength, quality_mode,
289
  enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt=""):
 
306
 
307
  # Применяем стиль с выбранным качеством
308
  if custom_prompt:
309
+ # Используем кастомный промпт
310
  quality_boost = "masterpiece, best quality, ultra-detailed, 8k uhd, " if quality_mode == "ultra" else ""
311
  styled_image = designer.pipe(
312
  prompt=f"{quality_boost}{room_type_en}, {custom_prompt}, interior design, high quality",
 
317
  guidance_scale={"fast": 7.5, "balanced": 8.5, "ultra": 10}[quality_mode]
318
  ).images[0]
319
  else:
320
+ # Используем предустановленный стиль с детальным промптом
321
+ detailed_prompt = get_detailed_prompt(style, room_type_en)
322
+ styled_image = designer.pipe(
323
+ prompt=detailed_prompt,
324
+ negative_prompt=DESIGN_STYLES[style].get("negative", ""),
325
+ image=image,
326
+ strength=strength,
327
+ num_inference_steps={"fast": 20, "balanced": 35, "ultra": 50}[quality_mode],
328
+ guidance_scale={"fast": 7.5, "balanced": 8.5, "ultra": 10}[quality_mode]
329
+ ).images[0]
330
 
331
  # HDR освещение
332
  if enhance_lighting and quality_mode != "fast":
333
  styled_image = designer.create_hdr_lighting(styled_image, intensity=0.3)
334
 
335
+ # Улучшение деталей (только для мощных GPU)
336
  if add_details and designer.is_powerful_gpu and quality_mode == "ultra":
337
  styled_image = designer.enhance_details(styled_image)
338
 
 
 
 
339
  # Создаем сравнение до/после
340
  comparison = processor.create_before_after(image, styled_image)
341
 
 
344
  if use_variations:
345
  num_var = 8 if designer.is_powerful_gpu else 4
346
  var_images = designer.create_variations(image, num_variations=num_var)
 
 
347
  variations = processor.create_grid(
348
  var_images,
349
  titles=[f"Вариант {i+1}" for i in range(len(var_images))]
 
361
  ⚡ Режим качества: {quality_mode.upper()}
362
  🖼️ Разрешение: {styled_image.width}×{styled_image.height}
363
  🤖 Модель: {designer.model_name}
 
364
 
365
  🎨 Основные цвета дизайна:
366
  """
367
  for i, color in enumerate(colors[:5]):
368
  hex_color = '#{:02x}{:02x}{:02x}'.format(*color)
369
+ info += f"\n • {hex_color}"
370
+
371
  return comparison, variations, palette, info
372
 
373
  except Exception as e:
 
380
  """Изменение отдельного элемента"""
381
  if image is None:
382
  return None, "Загрузите изображение"
383
+
384
  global designer
385
  if designer is None:
386
  designer = InteriorDesignerPro()
387
+
388
  try:
389
  result = designer.change_element(image, element, value, strength)
 
390
  return result, f"✅ {element} изменен на {value}"
391
  except Exception as e:
392
  return None, f"❌ Ошибка: {str(e)}"
393
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  def suggest_styles(image):
395
  """Предложение подходящих стилей"""
396
  if image is None:
397
  return "Загрузите изображение для получения рекомендаций"
398
+
399
+ # Анализируем текущий стиль
400
  room_type = processor.detect_room_type(image)
401
 
402
  suggestions = f"""
 
430
  - Интенсивность 70-90% - кардинальное преображение
431
  - Режим Ultra - для финальной визуализации
432
  - HDR освещение - добавит реализма
 
 
433
  """
434
+
435
  return suggestions
436
 
437
  @spaces.GPU(duration=60)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438
  def enhance_image(image, scale, dpi):
439
  """Увеличение разрешения изображения"""
440
  if image is None:
441
  return None, None, "❌ Пожалуйста, загрузите изображение"
442
 
443
  try:
444
+ # Инициализируем enhancer
445
  enhancer = ImageEnhancer()
446
 
447
+ # Добавим отладку
448
  original_size = f"{image.width}×{image.height}"
449
 
450
  # Улучшаем изображение
 
452
 
453
  # Проверяем, изменился ли размер
454
  if enhanced.size == image.size:
455
+ # Если размер не изменился, делаем upscale вручную через PIL
456
  new_width = image.width * scale
457
  new_height = image.height * scale
458
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
 
463
  # Настраиваем DPI
464
  enhanced = enhancer.set_dpi(enhanced, dpi)
465
 
 
 
 
466
  # Создаем сравнение
467
  comparison = processor.create_before_after(image, enhanced)
468
 
 
475
  🔍 Масштаб: {scale}x
476
  📊 DPI: {dpi}
477
  🔧 Метод: {method}
478
+ """
 
479
 
480
  return enhanced, comparison, info
481
 
482
  except Exception as e:
483
+ # Fallback на простой resize
484
  try:
485
  new_width = int(image.width * scale)
486
  new_height = int(image.height * scale)
487
  enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
 
488
 
489
  info = f"""
490
  ⚠️ Real-ESRGAN недоступен, использован PIL resize
 
492
  📐 Новый размер: {enhanced.width}×{enhanced.height}
493
  🔍 Масштаб: {scale}x
494
  Ошибка: {str(e)}
495
+ """
496
  return enhanced, None, info
497
  except Exception as e2:
498
  return None, None, f"❌ Ошибка увеличения: {str(e2)}"
499
 
500
+ # Добавляем метод _create_comparison_grid к InteriorDesignerPro если его нет
501
+ if hasattr(InteriorDesignerPro, 'create_style_comparison') and not hasattr(InteriorDesignerPro, '_create_comparison_grid'):
502
+ def _create_comparison_grid(self, images: List[Image.Image], styles: List[str],
503
+ cols: int = 3) -> Image.Image:
504
+ """Создание сетки для сравнения стилей"""
505
+ if not images:
506
+ return None
507
+
508
+ # Размеры
509
+ img_width = 512
510
+ img_height = 512
511
+ padding = 20
512
+ text_height = 40
513
+
514
+ # Изменяем размер всех изображений
515
+ resized_images = []
516
+ for img in images:
517
+ resized = img.resize((img_width, img_height), Image.Resampling.LANCZOS)
518
+ resized_images.append(resized)
519
+
520
+ # Вычисляем размер итогового изображения
521
+ rows = (len(images) + cols - 1) // cols
522
+ total_width = cols * img_width + (cols + 1) * padding
523
+ total_height = rows * (img_height + text_height) + (rows + 1) * padding
524
+
525
+ # Создаем пустое изображение
526
+ grid = Image.new('RGB', (total_width, total_height), color='white')
527
+ draw = ImageDraw.Draw(grid)
528
+
529
+ # Размещаем изображения
530
+ for idx, (img, style) in enumerate(zip(resized_images, styles)):
531
+ row = idx // cols
532
+ col = idx % cols
533
+
534
+ x = col * (img_width + padding) + padding
535
+ y = row * (img_height + text_height + padding) + padding
536
+
537
+ # Вставляем изображение
538
+ grid.paste(img, (x, y))
539
+
540
+ # Добавляем подпись
541
+ text_y = y + img_height + 5
542
+ draw.text((x + img_width//2, text_y), style,
543
+ fill='black', anchor='mt')
544
+
545
+ return grid
546
+
547
+ InteriorDesignerPro._create_comparison_grid = _create_comparison_grid
548
+
549
+ @spaces.GPU(duration=120)
550
+ def create_style_comparison(image, selected_styles, room_type, strength, fast_mode):
551
+ """Создание сравнения нескольких стилей"""
552
+ if image is None:
553
+ return None, "❌ Пожалуйста, загрузите изображение"
554
+
555
+ if not selected_styles:
556
+ return None, "❌ Выберите хотя бы 2 стиля для сравнения"
557
+
558
+ global designer
559
+ if designer is None:
560
+ designer = InteriorDesignerPro()
561
+
562
+ try:
563
+ # Определяем тип комнаты
564
+ if room_type == "Автоопределение":
565
+ room_type = processor.detect_room_type(image)
566
+ room_type_en = ROOM_TYPES.get(room_type, "living room")
567
+ else:
568
+ room_type_en = ROOM_TYPES.get(room_type, "living room")
569
+
570
+ # Настройки качества для режима сравнения
571
+ steps = 20 if fast_mode else 30
572
+
573
+ # Генерируем изображения для каждого стиля
574
+ styled_images = []
575
+ for style in selected_styles:
576
+ detailed_prompt = get_detailed_prompt(style, room_type_en)
577
+
578
+ styled = designer.pipe(
579
+ prompt=detailed_prompt,
580
+ negative_prompt=DESIGN_STYLES[style].get("negative", ""),
581
+ image=image,
582
+ strength=strength,
583
+ num_inference_steps=steps,
584
+ guidance_scale=7.5
585
+ ).images[0]
586
+
587
+ styled_images.append(styled)
588
+
589
+ # Создаем сетку сравнения
590
+ comparison = designer._create_comparison_grid(
591
+ styled_images,
592
+ selected_styles,
593
+ cols=3 if len(selected_styles) > 4 else 2
594
+ )
595
+
596
+ info = f"""
597
+ ✅ Сравнение стилей завершено!
598
+ 📊 Сравнено стилей: {len(selected_styles)}
599
+ 🏠 Тип комнаты: {room_type}
600
+ ⚡ Режим: {'Быстрый' if fast_mode else 'Качественный'}
601
+ """
602
+
603
+ return comparison, info
604
+
605
+ except Exception as e:
606
+ return None, f"❌ Ошибка: {str(e)}"
607
+
608
  # Создаем интерфейс
609
  with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.themes.Soft(), css=custom_css) as app:
 
 
 
 
 
 
 
 
610
  gr.Markdown("""
611
  # 🎨 AI Дизайнер интерьера Pro
612
  ### Преобразите свое пространство с помощью искусственного интеллекта
613
  """)
614
 
615
+ # Инициализация моделей при запуске
616
+ with gr.Row(visible=False):
617
+ init_btn = gr.Button("Init")
618
+ init_btn.click(fn=init_designer, inputs=[], outputs=[])
619
+ init_btn.click(fn=init_lama, inputs=[], outputs=[])
620
+
621
  with gr.Tabs():
622
  # Основная вкладка
623
  with gr.TabItem("🎨 Дизайнер", id=0):
 
684
  lines=3,
685
  info="Опишите желаемый результат своими словами"
686
  )
 
687
  negative_prompt = gr.Textbox(
688
  label="Чего избегать",
689
  placeholder="dark, cluttered, old furniture",
 
719
  label="Варианты дизайна",
720
  visible=False
721
  )
 
722
  output_palette = gr.Image(
723
  label="Цветовая палитра",
724
  height=100
 
728
  label="Информация о процессе"
729
  )
730
 
731
+ # Вкладка сравнения стилей
732
+ with gr.TabItem("🎭 Сравнение стилей", id=1):
733
  with gr.Row():
734
  with gr.Column():
735
+ compare_image = gr.Image(
736
+ label="Загрузите фото для сравнения",
737
  type="pil",
738
  height=400
739
  )
740
 
741
+ compare_styles = gr.CheckboxGroup(
742
+ label="Выберите 2-6 стилей для сравнения",
743
+ choices=list(DESIGN_STYLES.keys()),
744
+ value=["Современный минимализм", "Скандинавский"]
745
+ )
746
 
747
+ compare_room_type = gr.Dropdown(
748
+ label="Тип комнаты",
749
+ choices=["Автоопределение"] + list(ROOM_TYPES.keys()),
750
+ value="Автоопределение"
751
+ )
752
 
753
+ compare_strength = gr.Slider(
754
+ label="Интенсивность",
755
+ minimum=0.3,
756
+ maximum=0.95,
757
+ value=0.7,
758
+ step=0.05
759
+ )
760
 
761
+ compare_fast = gr.Checkbox(
762
+ label=" Быстрый режим",
763
+ value=True,
764
+ info="Быстрее, но немного ниже качество"
765
+ )
766
+
767
+ compare_btn = gr.Button(
768
+ "🎭 Сравнить стили",
769
+ variant="primary"
770
+ )
771
+
772
+ with gr.Column():
773
+ compare_output = gr.Image(
774
+ label="Сравнение стилей",
775
+ height=600
776
+ )
777
+ compare_info = gr.Markdown()
778
+
779
+ # Вкладка удаления объектов с РИСОВАНИЕМ
780
+ with gr.TabItem("🗑️ Удаление объектов (LAMA)", id=2):
781
+ with gr.Row():
782
+ with gr.Column():
783
+ # Используем gr.ImageEditor для рисования маски
784
+ lama_input = gr.Image(
785
+ label="Закрасьте объекты для удаления красным цветом",
786
+ source="upload",
787
+ tool="color-sketch", # Позволяет рисовать
788
  type="pil",
789
+ height=500,
790
+ brush_color="#FF0000", # Красная кисть
791
+ elem_id="lama_canvas"
792
  )
793
 
794
+ gr.Markdown("""
795
+ ### 🖌️ Инструкция:
796
+ 1. Загрузите фото
797
+ 2. Выберите инструмент рисования (кисть)
798
+ 3. Закрасьте объекты КРАСНЫМ цветом
799
+ 4. Нажмите "Удалить объекты"
 
 
800
 
801
+ 💡 Советы:
802
+ - Закрашивайте объекты полностью
803
+ - Используйте увеличение для точности
804
+ - Можно стереть лишнее ластиком
805
+ """)
806
 
807
+ lama_btn = gr.Button(
808
  "🗑️ Удалить объекты",
809
  variant="primary",
810
  size="lg"
 
813
  with gr.Column():
814
  lama_output = gr.Image(
815
  label="Результат",
816
+ height=500
817
  )
 
818
  lama_info = gr.Textbox(
819
  label="Статус",
820
+ lines=2
821
  )
 
 
 
 
 
 
 
 
822
 
 
823
  # Вкладка детальных изменений
824
+ with gr.TabItem("🔧 Детальные изменения", id=3):
825
  with gr.Row():
826
  with gr.Column():
827
  detail_image = gr.Image(
 
856
  detail_output = gr.Image(label="Результат", height=300)
857
  detail_info = gr.Textbox(label="Статус", lines=2)
858
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  # Вкладка увеличения разрешения
860
  with gr.TabItem("🔍 Увеличение разрешения", id=4):
861
  with gr.Row():
862
  with gr.Column():
863
  upscale_image = gr.Image(
864
+ label="Изображение для увеличения",
865
  type="pil",
866
  height=400
867
  )
 
875
  upscale_dpi = gr.Radio(
876
  label="DPI для печати",
877
  choices=[
878
+ ("96 DPI (экран)", 96),
879
+ ("150 DPI (обычная печать)", 150),
880
+ ("300 DPI (высокое качество)", 300)
881
  ],
882
  value=150
883
  )
884
 
885
  upscale_btn = gr.Button(
886
  "🔍 Увеличить разрешение",
887
+ variant="primary"
 
888
  )
889
 
890
  with gr.Column():
 
892
  label="Увеличенное изображение",
893
  height=400
894
  )
 
895
  upscale_comparison = gr.Image(
896
  label="Сравнение",
897
  height=200
898
  )
 
899
  upscale_info = gr.Markdown()
900
 
901
  # Вкладка рекомендаций
 
907
  type="pil",
908
  height=400
909
  )
 
910
  suggest_btn = gr.Button("Получить рекомендации", variant="primary")
911
 
912
  with gr.Column():
 
914
 
915
  # Цветовые палитры для стилей
916
  gr.Markdown("### 🎨 Цветовые палитры популярных стилей")
 
917
  for style_name in list(DESIGN_STYLES.keys())[:4]:
918
+ colors = DESIGN_STYLES[style_name].get("colors", ["#FFFFFF"] * 6)
919
+ 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])
 
 
 
920
  gr.HTML(f"<div class='info-box'><strong>{style_name}:</strong><br>{color_blocks}</div>")
921
 
922
  # Обработчики событий
 
933
  outputs=[output_variations]
934
  )
935
 
936
+ compare_btn.click(
937
+ create_style_comparison,
938
+ inputs=[compare_image, compare_styles, compare_room_type, compare_strength, compare_fast],
939
+ outputs=[compare_output, compare_info]
 
940
  )
941
 
942
+ # Обработчик для LAMA
943
+ lama_btn.click(
944
+ lambda img: lama_remover.remove_objects(img) if lama_remover else (None, "❌ LAMA не инициализирован"),
945
+ inputs=[lama_input],
946
  outputs=[lama_output, lama_info]
947
  )
948
 
 
952
  outputs=[detail_output, detail_info]
953
  )
954
 
 
 
 
 
 
 
955
  upscale_btn.click(
956
  enhance_image,
957
  inputs=[upscale_image, upscale_factor, upscale_dpi],
 
964
  outputs=[suggestions]
965
  )
966
 
967
+ # Информация о защите
968
+ author_info = os.environ.get("AUTHOR_INFO", "Development Mode")
969
+ if "Development" not in author_info:
970
+ with gr.Row():
971
+ gr.Markdown(f"""
972
+ ---
973
+ <center>
974
+ <div style='background: linear-gradient(45deg, #667eea 0%, #764ba2 100%);
975
+ padding: 20px; border-radius: 10px; color: white;'>
976
+ <h3>🔒 {author_info}</h3>
977
+ <p>Protected AI Interior Designer Pro</p>
978
+ </div>
979
+ </center>
980
+ """)
981
+
982
  # Информация внизу
983
+ gr.Markdown("""
984
  ---
985
  ### 📝 Инструкция по использованию:
986
+
987
+ 1. **Основной дизайнер** - загрузите фото и выберите стиль
988
+ 2. **Сравнение стилей** - посмотрите как выглядят разные стили на одном фото
989
+ 3. **Удаление объектов** - закрасьте ненужные предметы красным цветом
990
+ 4. **Детальные изменения** - измените отдельные элементы интерьера
991
+ 5. **Увеличение разрешения** - улучшите качество для печати
992
 
993
  ### 🚀 Возможности:
994
+ - 20+ стилей дизайна с детальными настройками
995
+ - Удаление объектов с помощью LAMA
996
+ - Создание вариаций дизайна
997
+ - HDR освещение и улучшение деталей
998
+ - Увеличение разрешения до 4x
 
 
999
 
1000
  ---
1001
+ <center>Made with ❤️ for interior design enthusiasts</center>
 
 
 
 
 
1002
  """)
1003
 
1004
  # ВАЖНО! Запуск приложения