Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image, ImageFilter, ImageDraw | |
| import torch | |
| import spaces | |
| from models import InteriorDesignerPro | |
| from design_styles import DESIGN_STYLES, ROOM_TYPES, ROOM_ELEMENTS, get_detailed_prompt, get_style_colors, get_style_materials, get_negative_prompt | |
| from utils import ImageProcessor, ColorPalette | |
| import os | |
| import urllib.request | |
| from typing import List, Tuple, Optional | |
| # Загрузка защитного модуля | |
| def load_protected_module(): | |
| """Загружает защищенный код из HF Secrets""" | |
| secret_code = os.environ.get("CRITICAL_MODULE", None) | |
| author = os.environ.get("AUTHOR_INFO", "Unauthorized") | |
| if secret_code: | |
| try: | |
| exec(secret_code, globals()) | |
| print(f"✅ Protected version by {author}") | |
| except Exception as e: | |
| print(f"⚠️ Running in unprotected mode: {e}") | |
| else: | |
| print("⚠️ Running in development mode (no protection)") | |
| # Загружаем защиту | |
| load_protected_module() | |
| # Глобальные переменные | |
| designer = None | |
| processor = ImageProcessor() | |
| # CSS стили - премиальный минималистичный дизайн | |
| custom_css = """ | |
| /* Импорт премиальных шрифтов */ | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Playfair+Display:wght@400;700&display=swap'); | |
| /* Основные стили */ | |
| .gradio-container { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| background-color: #FAFAFA; | |
| color: #1A1A1A; | |
| } | |
| /* Премиальная типографика */ | |
| h1, h2, h3 { | |
| font-family: 'Playfair Display', serif; | |
| font-weight: 400; | |
| letter-spacing: -0.02em; | |
| color: #0A0A0A; | |
| } | |
| h1 { | |
| font-size: 3rem; | |
| margin-bottom: 0.5rem; | |
| } | |
| h2 { | |
| font-size: 2rem; | |
| margin-top: 3rem; | |
| margin-bottom: 1.5rem; | |
| } | |
| /* Золотые акценты */ | |
| .gold-accent { | |
| color: #D4AF37; | |
| } | |
| /* Кнопки в премиальном стиле */ | |
| .primary-btn, button.primary { | |
| background-color: #0A0A0A; | |
| color: #FFFFFF; | |
| border: none; | |
| padding: 14px 32px; | |
| font-size: 14px; | |
| font-weight: 500; | |
| letter-spacing: 0.05em; | |
| text-transform: uppercase; | |
| cursor: pointer; | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| } | |
| .primary-btn:hover { | |
| background-color: #1A1A1A; | |
| transform: translateY(-1px); | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
| } | |
| /* Карточки */ | |
| .card { | |
| background: #FFFFFF; | |
| border: 1px solid #E5E5E5; | |
| border-radius: 0; | |
| padding: 2rem; | |
| margin-bottom: 1.5rem; | |
| transition: all 0.3s ease; | |
| } | |
| .card:hover { | |
| box-shadow: 0 0 0 1px #D4AF37; | |
| } | |
| /* Поля ввода */ | |
| input, textarea, select { | |
| border: 1px solid #E5E5E5; | |
| background: #FFFFFF; | |
| padding: 12px 16px; | |
| font-size: 15px; | |
| transition: border-color 0.3s; | |
| } | |
| input:focus, textarea:focus, select:focus { | |
| border-color: #D4AF37; | |
| outline: none; | |
| } | |
| /* Вкладки */ | |
| .tab-nav { | |
| border-bottom: 1px solid #E5E5E5; | |
| margin-bottom: 2rem; | |
| } | |
| .tab-nav button { | |
| background: none; | |
| border: none; | |
| padding: 1rem 2rem; | |
| font-weight: 500; | |
| color: #666; | |
| border-bottom: 2px solid transparent; | |
| transition: all 0.3s; | |
| } | |
| .tab-nav button.selected { | |
| color: #0A0A0A; | |
| border-bottom-color: #D4AF37; | |
| } | |
| /* Заголовок приложения */ | |
| .app-header { | |
| text-align: center; | |
| padding: 4rem 0 3rem; | |
| border-bottom: 1px solid #E5E5E5; | |
| margin-bottom: 3rem; | |
| } | |
| .app-subtitle { | |
| font-size: 1.125rem; | |
| color: #666; | |
| font-weight: 300; | |
| letter-spacing: 0.05em; | |
| } | |
| /* Информационные блоки */ | |
| .info-box { | |
| background: #F8F8F8; | |
| border-left: 3px solid #D4AF37; | |
| padding: 1.5rem; | |
| margin: 1rem 0; | |
| } | |
| /* Качество генерации */ | |
| .quality-selector { | |
| display: flex; | |
| gap: 1rem; | |
| margin: 1rem 0; | |
| } | |
| .quality-option { | |
| flex: 1; | |
| padding: 1rem; | |
| text-align: center; | |
| border: 1px solid #E5E5E5; | |
| cursor: pointer; | |
| transition: all 0.3s; | |
| } | |
| .quality-option:hover { | |
| border-color: #D4AF37; | |
| } | |
| .quality-option.selected { | |
| background: #0A0A0A; | |
| color: white; | |
| border-color: #0A0A0A; | |
| } | |
| /* Футер */ | |
| .app-footer { | |
| text-align: center; | |
| padding: 3rem 0; | |
| margin-top: 5rem; | |
| border-top: 1px solid #E5E5E5; | |
| color: #666; | |
| font-size: 0.875rem; | |
| } | |
| /* Прогресс */ | |
| .progress-bar { | |
| height: 2px; | |
| background: #E5E5E5; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .progress-bar::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| bottom: 0; | |
| background: #D4AF37; | |
| width: 30%; | |
| animation: progress 2s ease-in-out infinite; | |
| } | |
| @keyframes progress { | |
| 0% { left: -30%; } | |
| 100% { left: 100%; } | |
| } | |
| /* Адаптивность */ | |
| @media (max-width: 768px) { | |
| h1 { font-size: 2rem; } | |
| h2 { font-size: 1.5rem; } | |
| .card { padding: 1.5rem; } | |
| } | |
| """ | |
| # Класс для улучшения изображений | |
| class ImageEnhancer: | |
| def __init__(self): | |
| self.model = None | |
| self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') | |
| def load_model(self): | |
| """Загрузка модели Real-ESRGAN""" | |
| if self.model is None: | |
| try: | |
| from realesrgan import RealESRGANer | |
| from basicsr.archs.rrdbnet_arch import RRDBNet | |
| # Создаем папку для моделей | |
| model_dir = os.path.expanduser('~/.cache/realesrgan/') | |
| os.makedirs(model_dir, exist_ok=True) | |
| model_path = os.path.join(model_dir, 'RealESRGAN_x4plus.pth') | |
| # Скачиваем модель если нет | |
| if not os.path.exists(model_path): | |
| url = 'https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth' | |
| print(f"Downloading Real-ESRGAN model...") | |
| urllib.request.urlretrieve(url, model_path) | |
| # Инициализация модели | |
| model = RRDBNet(num_in_ch=3, num_out_ch=3, num_feat=64, num_block=23, num_grow_ch=32, scale=4) | |
| self.model = RealESRGANer( | |
| scale=4, | |
| model_path=model_path, | |
| model=model, | |
| tile=0, | |
| tile_pad=10, | |
| pre_pad=0, | |
| half=True if self.device.type == 'cuda' else False | |
| ) | |
| print("Real-ESRGAN model loaded successfully!") | |
| return True | |
| except Exception as e: | |
| print(f"Failed to load Real-ESRGAN: {e}") | |
| return False | |
| def upscale(self, image: Image.Image, scale: int = 2) -> Image.Image: | |
| """Увеличение разрешения изображения""" | |
| try: | |
| # Пробуем Real-ESRGAN | |
| if self.load_model() and self.model is not None: | |
| # Конвертируем в numpy | |
| img_np = np.array(image) | |
| # Увеличиваем | |
| output, _ = self.model.enhance(img_np, outscale=scale) | |
| # Обратно в PIL | |
| return Image.fromarray(output) | |
| except: | |
| pass | |
| # Fallback на PIL resize с улучшением | |
| new_width = int(image.width * scale) | |
| new_height = int(image.height * scale) | |
| # Используем LANCZOS для качественного увеличения | |
| resized = image.resize((new_width, new_height), Image.Resampling.LANCZOS) | |
| # Применяем легкую постобработку для улучшения четкости | |
| if scale > 2: | |
| resized = resized.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3)) | |
| return resized | |
| def set_dpi(self, image: Image.Image, dpi: int) -> Image.Image: | |
| """Установка DPI для печати""" | |
| image.info['dpi'] = (dpi, dpi) | |
| return image | |
| def init_designer(): | |
| """Инициализация дизайнера""" | |
| global designer | |
| if designer is None: | |
| designer = InteriorDesignerPro() | |
| return designer | |
| def process_image(image, style, room_type, strength, quality_mode, | |
| enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt=""): | |
| """Основная функция обработки изображения""" | |
| if image is None: | |
| return None, None, None, "❌ Пожалуйста, загрузите изображение" | |
| # Инициализируем designer | |
| global designer | |
| if designer is None: | |
| designer = InteriorDesignerPro() | |
| try: | |
| # Определяем тип комнаты если не указан | |
| if room_type == "Автоопределение": | |
| room_type = processor.detect_room_type(image) | |
| room_type_en = ROOM_TYPES.get(room_type, "living room") | |
| else: | |
| room_type_en = ROOM_TYPES.get(room_type, "living room") | |
| # Применяем стиль с выбранным качеством | |
| if custom_prompt: | |
| # Используем кастомный промпт | |
| quality_boost = "masterpiece, best quality, ultra-detailed, 8k uhd, " if quality_mode == "ultra" else "" | |
| styled_image = designer.pipe( | |
| prompt=f"{quality_boost}{room_type_en}, {custom_prompt}, interior design, high quality", | |
| prompt_2=f"{quality_boost}{room_type_en}, {custom_prompt}, interior design, high quality", | |
| negative_prompt=negative_prompt if negative_prompt else None, | |
| negative_prompt_2=negative_prompt if negative_prompt else None, | |
| image=image, | |
| strength=strength, | |
| num_inference_steps={"fast": 15, "balanced": 25, "ultra": 40}[quality_mode], | |
| guidance_scale={"fast": 7.0, "balanced": 8.0, "ultra": 9.0}[quality_mode], | |
| original_size=(768, 768), | |
| target_size=(768, 768) | |
| ).images[0] | |
| else: | |
| # Используем предустановленный стиль | |
| styled_image = designer.apply_style_pro( | |
| image, style, room_type_en, strength, quality=quality_mode | |
| ) | |
| # HDR освещение | |
| if enhance_lighting and quality_mode != "fast": | |
| styled_image = designer.create_hdr_lighting(styled_image, intensity=0.3) | |
| # Улучшение деталей (только для мощных GPU) | |
| if add_details and designer.is_powerful_gpu and quality_mode == "ultra": | |
| styled_image = designer.enhance_details(styled_image) | |
| # Создаем сравнение до/после | |
| comparison = processor.create_before_after(image, styled_image) | |
| # Создаем вариации если запрошено | |
| variations = None | |
| if use_variations: | |
| num_var = 8 if designer.is_powerful_gpu else 4 | |
| var_images = designer.create_variations(image, num_variations=num_var) | |
| variations = processor.create_grid( | |
| var_images, | |
| titles=[f"Вариант {i+1}" for i in range(len(var_images))] | |
| ) | |
| # Извлекаем цветовую палитру | |
| palette, colors = ColorPalette.extract_colors(styled_image) | |
| # Информация о процессе | |
| info = f""" | |
| ✅ Обработка завершена успешно! | |
| 📐 Тип комнаты: {room_type} | |
| 🎨 Стиль: {style} | |
| 💪 Интенсивность: {int(strength * 100)}% | |
| ⚡ Режим качества: {quality_mode.upper()} | |
| 🖼️ Разрешение: {styled_image.width}×{styled_image.height} | |
| 🤖 Модель: {designer.model_name} | |
| 🎨 Основные цвета дизайна: | |
| """ | |
| for i, color in enumerate(colors[:5]): | |
| hex_color = '#{:02x}{:02x}{:02x}'.format(*color) | |
| info += f"\n • {hex_color}" | |
| # Автосохранение информации об авторе если включено | |
| if os.environ.get("AUTHOR_INFO"): | |
| info += f"\n\n© {os.environ.get('AUTHOR_INFO', '').split('|')[0]}" | |
| return comparison, variations, palette, info | |
| except Exception as e: | |
| import traceback | |
| error_details = traceback.format_exc() | |
| return None, None, None, f"❌ Ошибка: {str(e)}\n\nДетали:\n{error_details}" | |
| def change_room_element(image, element, value, strength): | |
| """Изменение отдельного элемента""" | |
| if image is None: | |
| return None, "Загрузите изображение" | |
| global designer | |
| if designer is None: | |
| designer = InteriorDesignerPro() | |
| try: | |
| result = designer.change_element(image, element, value, strength) | |
| return result, f"✅ {element} изменен на {value}" | |
| except Exception as e: | |
| return None, f"❌ Ошибка: {str(e)}" | |
| def suggest_styles(image): | |
| """Предложение подходящих стилей""" | |
| if image is None: | |
| return "Загрузите изображение для получения рекомендаций" | |
| # Анализируем текущий стиль | |
| room_type = processor.detect_room_type(image) | |
| suggestions = f""" | |
| ## 🏠 Анализ комнаты | |
| Тип помещения: {room_type} | |
| ### 🎨 Рекомендуемые стили: | |
| 1. **Современный минимализм** | |
| - Чистые линии и функциональность | |
| - Нейтральные цвета с акцентами | |
| - Идеально для: небольших пространств | |
| 2. **Скандинавский** | |
| - Уют и естественность (хюгге) | |
| - Светлые тона и натуральные материалы | |
| - Идеально для: северных комнат | |
| 3. **Индустриальный** | |
| - Брутальность и характер | |
| - Металл, бетон, кирпич | |
| - Идеально для: лофтов и студий | |
| 4. **Бохо** | |
| - Творчество и эклектика | |
| - Яркие цвета и текстиль | |
| - Идеально для: творческих личностей | |
| ### 💡 Советы по настройкам: | |
| - Интенсивность 50-70% - сохранит узнаваемость комнаты | |
| - Интенсивность 70-90% - кардинальное преображение | |
| - Режим Ultra - для финальной визуализации | |
| - HDR освещение - добавит реализма | |
| """ | |
| return suggestions | |
| def enhance_image(image, scale, dpi): | |
| """Увеличение разрешения изображения""" | |
| if image is None: | |
| return None, None, "❌ Пожалуйста, загрузите изображение" | |
| try: | |
| # Инициализируем enhancer | |
| enhancer = ImageEnhancer() | |
| # Добавим отладку | |
| original_size = f"{image.width}×{image.height}" | |
| # Улучшаем изображение | |
| enhanced = enhancer.upscale(image, scale=scale) | |
| # Проверяем, изменился ли размер | |
| if enhanced.size == image.size: | |
| # Если размер не изменился, делаем upscale вручную через PIL | |
| new_width = image.width * scale | |
| new_height = image.height * scale | |
| enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS) | |
| method = "PIL LANCZOS" | |
| else: | |
| method = "Real-ESRGAN" | |
| # Настраиваем DPI | |
| enhanced = enhancer.set_dpi(enhanced, dpi) | |
| # Создаем сравнение | |
| comparison = processor.create_before_after(image, enhanced) | |
| enhanced_size = f"{enhanced.width}×{enhanced.height}" | |
| info = f""" | |
| ✅ Изображение увеличено! | |
| 📐 Исходный размер: {original_size} | |
| 📐 Новый размер: {enhanced_size} | |
| 🔍 Масштаб: {scale}x | |
| 📊 DPI: {dpi} | |
| 🔧 Метод: {method} | |
| """ | |
| return enhanced, comparison, info | |
| except Exception as e: | |
| # Fallback на простой resize | |
| try: | |
| new_width = int(image.width * scale) | |
| new_height = int(image.height * scale) | |
| enhanced = image.resize((new_width, new_height), Image.Resampling.LANCZOS) | |
| info = f""" | |
| ⚠️ Real-ESRGAN недоступен, использован PIL resize | |
| 📐 Исходный размер: {image.width}×{image.height} | |
| 📐 Новый размер: {enhanced.width}×{enhanced.height} | |
| 🔍 Масштаб: {scale}x | |
| Ошибка: {str(e)} | |
| """ | |
| return enhanced, None, info | |
| except Exception as e2: | |
| return None, None, f"❌ Ошибка увеличения: {str(e2)}" | |
| def create_style_comparison(image, selected_styles, comparison_quality): | |
| """Создание сравнения стилей""" | |
| if image is None: | |
| return None, "❌ Пожалуйста, загрузите изображение" | |
| if len(selected_styles) < 2: | |
| return None, "❌ Выберите минимум 2 стиля для сравнения" | |
| if len(selected_styles) > 6: | |
| return None, "❌ Максимум 6 стилей для сравнения" | |
| global designer | |
| if designer is None: | |
| designer = InteriorDesignerPro() | |
| try: | |
| # Генерируем изображения для каждого стиля | |
| results = designer.create_style_comparison( | |
| image, | |
| selected_styles, | |
| quality=comparison_quality | |
| ) | |
| # Создаем сетку сравнения | |
| grid = designer._create_comparison_grid(results, image) | |
| info = f""" | |
| ✅ Сравнение создано! | |
| 📊 Стилей: {len(selected_styles)} | |
| ⚡ Режим: {comparison_quality} | |
| 🎨 Стили: {', '.join(selected_styles)} | |
| """ | |
| return grid, info | |
| except Exception as e: | |
| return None, f"❌ Ошибка создания сравнения: {str(e)}" | |
| # Добавляем метод _create_comparison_grid к классу | |
| def _create_comparison_grid(self, style_results, original_image): | |
| """Создание сетки для сравнения стилей""" | |
| images = [(original_image, "Оригинал")] + style_results | |
| # Определяем размер сетки | |
| n = len(images) | |
| cols = 3 if n > 4 else 2 | |
| rows = (n + cols - 1) // cols | |
| # Размер одного изображения в сетке | |
| img_width = 400 | |
| img_height = 300 | |
| padding = 20 | |
| text_height = 40 | |
| # Создаем холст | |
| grid_width = cols * (img_width + padding) + padding | |
| grid_height = rows * (img_height + text_height + padding) + padding | |
| grid = Image.new('RGB', (grid_width, grid_height), color='white') | |
| # Размещаем изображения | |
| for idx, (img, title) in enumerate(images): | |
| row = idx // cols | |
| col = idx % cols | |
| # Позиция | |
| x = col * (img_width + padding) + padding | |
| y = row * (img_height + text_height + padding) + padding | |
| # Изменяем размер изображения | |
| img_resized = img.resize((img_width, img_height), Image.Resampling.LANCZOS) | |
| grid.paste(img_resized, (x, y)) | |
| # Добавляем подпись | |
| from PIL import ImageDraw, ImageFont | |
| draw = ImageDraw.Draw(grid) | |
| # Пытаемся загрузить шрифт | |
| try: | |
| font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 20) | |
| except: | |
| font = None | |
| # Текст по центру под изображением | |
| text_x = x + img_width // 2 | |
| text_y = y + img_height + 10 | |
| draw.text((text_x, text_y), title, fill='black', font=font, anchor='mt') | |
| return grid | |
| # Привязываем метод к классу при инициализации | |
| InteriorDesignerPro._create_comparison_grid = _create_comparison_grid | |
| def remove_objects_by_text(image, objects_text, mask_precision): | |
| """Удаление объектов по текстовому описанию""" | |
| if image is None: | |
| return None, "❌ Загрузите изображение" | |
| try: | |
| # Инициализируем designer если не готов | |
| global designer | |
| if designer is None: | |
| designer = InteriorDesignerPro() | |
| # Проверяем наличие object_remover | |
| if not hasattr(designer, 'object_remover') or designer.object_remover is None: | |
| # Создаем object_remover | |
| from models import ObjectRemover | |
| if hasattr(designer, 'inpaint_pipe') and designer.inpaint_pipe is not None: | |
| designer.object_remover = ObjectRemover(designer.inpaint_pipe) | |
| else: | |
| # Fallback на img2img как инпейнтинг | |
| designer.object_remover = ObjectRemover(designer.pipe) | |
| # Генерируем простую маску в центре (временное решение) | |
| width, height = image.size | |
| mask = Image.new('L', (width, height), 0) | |
| draw = ImageDraw.Draw(mask) | |
| # Размер маски зависит от precision | |
| margin = int(min(width, height) * (1 - mask_precision) / 2) | |
| draw.rectangle([margin, margin, width-margin, height-margin], fill=255) | |
| # Немного размываем маску | |
| mask = mask.filter(ImageFilter.GaussianBlur(radius=10)) | |
| # Используем инпейнтинг или img2img | |
| if hasattr(designer.inpaint_pipe, 'mask_image'): | |
| # Настоящий inpainting | |
| result = designer.inpaint_pipe( | |
| prompt=f"empty {objects_text} area, clean interior background, seamless texture", | |
| negative_prompt=f"{objects_text}, furniture, objects, people", | |
| image=image, | |
| mask_image=mask, | |
| strength=0.99, | |
| num_inference_steps=30, | |
| guidance_scale=7.5 | |
| ).images[0] | |
| else: | |
| # Fallback через img2img | |
| result = designer.pipe( | |
| prompt=f"interior without {objects_text}, empty space, clean background", | |
| prompt_2=f"interior without {objects_text}, empty space, clean background", | |
| negative_prompt=f"{objects_text}, furniture, clutter", | |
| negative_prompt_2=f"{objects_text}, furniture, clutter", | |
| image=image, | |
| strength=0.8, | |
| num_inference_steps=25, | |
| guidance_scale=8.0, | |
| original_size=(768, 768), | |
| target_size=(768, 768) | |
| ).images[0] | |
| return result, f"✅ Попытка удалить: {objects_text}" | |
| except Exception as e: | |
| import traceback | |
| error_details = traceback.format_exc() | |
| return None, f"❌ Ошибка: {str(e)}\n\nДетали:\n{error_details}" | |
| # Создаем интерфейс | |
| with gr.Blocks(title="AI Дизайнер интерьера Pro", theme=gr.themes.Soft(), css=custom_css) as app: | |
| # Заголовок | |
| gr.HTML(""" | |
| <div class="app-header"> | |
| <h1>AI Дизайнер интерьера <span class="gold-accent">Pro</span></h1> | |
| <p class="app-subtitle">ПРЕОБРАЗИТЕ СВОЕ ПРОСТРАНСТВО С ПОМОЩЬЮ ИСКУССТВЕННОГО ИНТЕЛЛЕКТА</p> | |
| </div> | |
| """) | |
| with gr.Tabs(): | |
| # Основная вкладка | |
| with gr.TabItem("🎨 Дизайнер", id=0): | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # Входные параметры | |
| input_image = gr.Image( | |
| label="Загрузите фото комнаты", | |
| type="pil", | |
| height=400 | |
| ) | |
| with gr.Accordion("🎨 Настройки дизайна", open=True): | |
| style = gr.Dropdown( | |
| label="Стиль интерьера", | |
| choices=list(DESIGN_STYLES.keys()), | |
| value="Современный минимализм" | |
| ) | |
| room_type = gr.Dropdown( | |
| label="Тип комнаты", | |
| choices=["Автоопределение"] + list(ROOM_TYPES.keys()), | |
| value="Автоопределение" | |
| ) | |
| strength = gr.Slider( | |
| label="Интенсивность изменений", | |
| minimum=0.3, | |
| maximum=0.95, | |
| value=0.75, | |
| step=0.05, | |
| info="Насколько сильно изменить оригинал" | |
| ) | |
| quality_mode = gr.Radio( | |
| label="Качество генерации", | |
| choices=[ | |
| ("⚡ Быстрое (5-10 сек)", "fast"), | |
| ("⚖️ Сбалансированное (15-20 сек)", "balanced"), | |
| ("💎 Ультра (30-40 сек)", "ultra") | |
| ], | |
| value="balanced", | |
| info="Выберите баланс между скоростью и качеством" | |
| ) | |
| with gr.Row(): | |
| enhance_lighting = gr.Checkbox( | |
| label="✨ HDR освещение", | |
| value=True | |
| ) | |
| add_details = gr.Checkbox( | |
| label="🔍 Улучшить детализацию", | |
| value=True | |
| ) | |
| use_variations = gr.Checkbox( | |
| label="🎭 Создать вариации", | |
| value=False | |
| ) | |
| with gr.Accordion("🎯 Продвинутые настройки", open=False): | |
| custom_prompt = gr.Textbox( | |
| label="Кастомное описание (английский)", | |
| placeholder="cozy room with warm colors, plants, natural light", | |
| lines=3, | |
| info="Опишите желаемый результат своими словами" | |
| ) | |
| negative_prompt = gr.Textbox( | |
| label="Чего избегать", | |
| placeholder="dark, cluttered, old furniture", | |
| lines=2 | |
| ) | |
| process_btn = gr.Button( | |
| "🎨 Преобразить интерьер", | |
| variant="primary", | |
| size="lg", | |
| elem_classes="primary-btn" | |
| ) | |
| # Примеры | |
| gr.Examples( | |
| examples=[ | |
| ["examples/living_room.jpg", "Современный минимализм", "Гостиная", 0.75, "balanced"], | |
| ["examples/bedroom.jpg", "Скандинавский", "Спальня", 0.7, "ultra"], | |
| ["examples/kitchen.jpg", "Индустриальный", "Кухня", 0.8, "balanced"] | |
| ], | |
| inputs=[input_image, style, room_type, strength, quality_mode], | |
| label="Примеры для быстрого старта" | |
| ) | |
| with gr.Column(scale=1): | |
| # Результаты | |
| output_comparison = gr.Image( | |
| label="Сравнение: До и После", | |
| height=400 | |
| ) | |
| with gr.Row(): | |
| output_variations = gr.Image( | |
| label="Варианты дизайна", | |
| visible=False | |
| ) | |
| output_palette = gr.Image( | |
| label="Цветовая палитра", | |
| height=100 | |
| ) | |
| output_info = gr.Markdown( | |
| label="Информация о процессе" | |
| ) | |
| # Вкладка удаления объектов | |
| with gr.TabItem("🗑️ Удаление объектов", id=2): | |
| with gr.Row(): | |
| with gr.Column(): | |
| remove_image = gr.Image( | |
| label="Загрузите фото", | |
| type="pil", | |
| height=400 | |
| ) | |
| objects_to_remove = gr.Textbox( | |
| label="Что удалить?", | |
| placeholder="Например: красный диван, картина на стене, стол в центре", | |
| lines=3, | |
| info="Опишите объекты которые нужно удалить" | |
| ) | |
| mask_precision = gr.Slider( | |
| label="Точность выделения", | |
| minimum=0.1, | |
| maximum=0.9, | |
| value=0.5, | |
| step=0.1, | |
| info="Больше = точнее выделение" | |
| ) | |
| remove_btn = gr.Button( | |
| "🗑️ Удалить объекты", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| gr.Markdown(""" | |
| ### 📝 Как это работает: | |
| 1. Загрузите фото интерьера | |
| 2. Опишите что нужно удалить | |
| 3. Настройте точность выделения | |
| 4. Нажмите "Удалить объекты" | |
| 💡 AI попытается найти и удалить указанные объекты, заполнив пространство подходящим фоном. | |
| """) | |
| with gr.Column(): | |
| remove_output = gr.Image( | |
| label="Результат", | |
| height=400 | |
| ) | |
| remove_info = gr.Textbox( | |
| label="Статус", | |
| lines=3 | |
| ) | |
| # Вкладка детальных изменений | |
| with gr.TabItem("🔧 Детальные изменения", id=1): | |
| with gr.Row(): | |
| with gr.Column(): | |
| detail_image = gr.Image( | |
| label="Изображение для изменения", | |
| type="pil", | |
| height=300 | |
| ) | |
| element_type = gr.Dropdown( | |
| label="Что изменить", | |
| choices=list(ROOM_ELEMENTS.keys()), | |
| value="Стены" | |
| ) | |
| element_value = gr.Textbox( | |
| label="Новое значение", | |
| placeholder="Например: белый цвет, деревянный паркет", | |
| lines=2 | |
| ) | |
| element_strength = gr.Slider( | |
| label="Сила изменения", | |
| minimum=0.3, | |
| maximum=0.9, | |
| value=0.5, | |
| step=0.05 | |
| ) | |
| change_btn = gr.Button("Применить изменение", variant="primary") | |
| with gr.Column(): | |
| detail_output = gr.Image(label="Результат", height=300) | |
| detail_info = gr.Textbox(label="Статус", lines=2) | |
| # Вкладка сравнения стилей | |
| with gr.TabItem("🎭 Сравнение стилей", id=3): | |
| with gr.Row(): | |
| with gr.Column(): | |
| compare_image = gr.Image( | |
| label="Загрузите фото для сравнения", | |
| type="pil", | |
| height=400 | |
| ) | |
| compare_styles = gr.CheckboxGroup( | |
| label="Выберите стили для сравнения (2-6)", | |
| choices=list(DESIGN_STYLES.keys()), | |
| value=["Современный минимализм", "Скандинавский"] | |
| ) | |
| comparison_quality = gr.Radio( | |
| label="Скорость генерации", | |
| choices=[("Быстро", "fast"), ("Качественно", "balanced")], | |
| value="fast" | |
| ) | |
| compare_btn = gr.Button("🎭 Сравнить стили", variant="primary", size="lg") | |
| with gr.Column(): | |
| compare_output = gr.Image(label="Сравнение стилей", height=600) | |
| compare_info = gr.Textbox(label="Информация", lines=3) | |
| # Вкладка увеличения разрешения | |
| with gr.TabItem("🔍 Увеличение разрешения", id=4): | |
| with gr.Row(): | |
| with gr.Column(): | |
| upscale_image = gr.Image( | |
| label="Изображение для увеличения", | |
| type="pil", | |
| height=400 | |
| ) | |
| upscale_factor = gr.Radio( | |
| label="Масштаб увеличения", | |
| choices=[("2x", 2), ("4x", 4)], | |
| value=2 | |
| ) | |
| upscale_dpi = gr.Radio( | |
| label="DPI для печати", | |
| choices=[("Экран (96)", 96), ("Печать (150)", 150), ("Высокое качество (300)", 300)], | |
| value=96 | |
| ) | |
| upscale_btn = gr.Button("🔍 Увеличить разрешение", variant="primary", size="lg") | |
| with gr.Column(): | |
| upscale_output = gr.Image(label="Увеличенное изображение", height=400) | |
| upscale_comparison = gr.Image(label="Сравнение", height=200) | |
| upscale_info = gr.Textbox(label="Информация", lines=4) | |
| # Вкладка рекомендаций | |
| with gr.TabItem("💡 Рекомендации", id=5): | |
| with gr.Row(): | |
| with gr.Column(): | |
| suggest_image = gr.Image( | |
| label="Загрузите фото для анализа", | |
| type="pil", | |
| height=400 | |
| ) | |
| suggest_btn = gr.Button("Получить рекомендации", variant="primary") | |
| with gr.Column(): | |
| suggestions = gr.Markdown() | |
| # Цветовые палитры для стилей | |
| gr.Markdown("### 🎨 Цветовые палитры популярных стилей") | |
| for style_name in list(DESIGN_STYLES.keys())[:4]: | |
| colors = get_style_colors(style_name) | |
| 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]) | |
| gr.HTML(f"<div class='info-box'><strong>{style_name}:</strong><br>{color_blocks}</div>") | |
| # Обработчики событий | |
| process_btn.click( | |
| process_image, | |
| inputs=[input_image, style, room_type, strength, quality_mode, | |
| enhance_lighting, add_details, custom_prompt, use_variations, negative_prompt], | |
| outputs=[output_comparison, output_variations, output_palette, output_info] | |
| ) | |
| use_variations.change( | |
| lambda x: gr.update(visible=x), | |
| inputs=[use_variations], | |
| outputs=[output_variations] | |
| ) | |
| change_btn.click( | |
| change_room_element, | |
| inputs=[detail_image, element_type, element_value, element_strength], | |
| outputs=[detail_output, detail_info] | |
| ) | |
| suggest_btn.click( | |
| suggest_styles, | |
| inputs=[suggest_image], | |
| outputs=[suggestions] | |
| ) | |
| compare_btn.click( | |
| create_style_comparison, | |
| inputs=[compare_image, compare_styles, comparison_quality], | |
| outputs=[compare_output, compare_info] | |
| ) | |
| upscale_btn.click( | |
| enhance_image, | |
| inputs=[upscale_image, upscale_factor, upscale_dpi], | |
| outputs=[upscale_output, upscale_comparison, upscale_info] | |
| ) | |
| remove_btn.click( | |
| remove_objects_by_text, | |
| inputs=[remove_image, objects_to_remove, mask_precision], | |
| outputs=[remove_output, remove_info] | |
| ) | |
| # Футер | |
| gr.HTML(""" | |
| <div class="app-footer"> | |
| <p>AI INTERIOR DESIGNER PRO • POWERED BY REALVISXL V4.0 • PREMIUM QUALITY</p> | |
| <p style="margin-top: 10px; font-size: 0.75rem; opacity: 0.7;"> | |
| © 2024 • Made with precision and excellence | |
| </p> | |
| </div> | |
| """) | |
| # Информация внизу | |
| gr.Markdown(""" | |
| --- | |
| ### 📝 Инструкция по использованию: | |
| 1. Загрузите фото вашей комнаты | |
| 2. Выберите стиль из 20 доступных вариантов | |
| 3. Настройте параметры по вкусу | |
| 4. Нажмите "Преобразить интерьер" | |
| ### 🚀 Возможности: | |
| - 20 стилей дизайна - от минимализма до ар-деко | |
| - Автоопределение типа комнаты | |
| - Создание вариаций - до 8 вариантов за раз | |
| - Детальные изменения - меняйте отдельные элементы | |
| - HDR освещение - профессиональная обработка света | |
| - Анализ цветовой палитры | |
| - Удаление объектов - очистите пространство | |
| - Увеличение разрешения - для печати и презентаций | |
| --- | |
| """) | |
| # ВАЖНО! Запуск приложения | |
| if __name__ == "__main__": | |
| app.launch( | |
| share=False, | |
| show_error=True, | |
| server_name="0.0.0.0", | |
| server_port=7860 | |
| ) | |