diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -15,11 +15,14 @@ from models import ( TaskSubmission, ImageToImageSubmission, PhotoStyleSubmission, InteriorDesignRenderingSubmission, WatermarkRemovalSubmission, LineArtConversionSubmission, AnimeToRealSubmission, RealToAnimeSubmission, - ImageOutpaintingSubmission, FiveViewGenerationSubmission + ImageOutpaintingSubmission, FiveViewGenerationSubmission, Figure3DSubmission, + CharacterFigureCollaborationSubmission ) from examples_config import ( TEXT_TO_IMAGE_EXAMPLES_WITH_RESULTS, FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS, + FIGURE_3D_EXAMPLES_WITH_RESULTS, + CHARACTER_FIGURE_COLLABORATION_EXAMPLES_WITH_RESULTS, IMAGE_OUTPAINTING_EXAMPLES_WITH_RESULTS, LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS, ANIME_TO_REAL_EXAMPLES_WITH_RESULTS, @@ -71,6 +74,19 @@ INTERIOR_DESIGN_STYLE_CHOICES = [ for key in INTERIOR_DESIGN_STYLE_MAPPING.keys() ] +# 3D Figure Style Mapping +FIGURE_3D_STYLE_MAPPING = { + "professional_lighting": "💡 Professional Lighting Scene", + "collector_shelf": "📚 Collector's Display Scene", + "desktop_display": "💻 Desktop Display Scene" +} + +FIGURE_3D_STYLE_CHOICES = [ + (display_name, key) for key, display_name in FIGURE_3D_STYLE_MAPPING.items() +] + + + # ============================================================================ # Utility Functions # ============================================================================ @@ -131,23 +147,19 @@ async def submit_task_with_retry(endpoint: str, payload: dict, task_name: str, m ) if response.status_code == 429: - # System is busy, implement exponential backoff if attempt < max_retries: delay = min(base_delay * (1.5 ** attempt), max_delay) logger.info(f"System busy, retrying {task_name} in {delay:.1f}s (attempt {attempt + 1}/{max_retries + 1})") await asyncio.sleep(delay) continue else: - # Max retries exceeded raise Exception("The system is currently busy, please try again later") elif response.status_code >= 500: - # Server error logger.error(f"Server error {response.status_code} for {task_name}") raise Exception("Service temporarily unavailable, please try again later") elif response.status_code >= 400: - # Client error (except 429) logger.error(f"Client error {response.status_code} for {task_name}") raise Exception("Invalid request parameters, please check your input") @@ -162,8 +174,8 @@ async def submit_task_with_retry(endpoint: str, payload: dict, task_name: str, m logger.error(f"Connection error for {task_name}") raise Exception("Unable to connect to the server, please try again later") except Exception as e: - if any(msg in str(e) for msg in ["系统当前繁忙", "服务暂时不可用", "请求参数有误", "网络连接超时", "无法连接到服务器"]): - # Re-raise user-friendly messages (Chinese messages preserved for detection) + if any(msg in str(e) for msg in ["System is currently busy", "Service temporarily unavailable", "Invalid request parameters", "Network connection timeout", "Unable to connect to server"]): + # Re-raise user-friendly messages (English messages for detection) raise else: # Log technical error but show user-friendly message @@ -234,11 +246,10 @@ async def get_task_result(task_id: str) -> dict: raise async def load_image_from_result(result_data: dict) -> Image.Image: - """Load PIL Image from task result data - simplified version matching original logic""" + """Load PIL Image from task result data via URL download only""" if not result_data: raise ValueError("No result data received") - # Get result_url (the field backend actually returns) result_url = result_data.get("result_url") logger.info(f"🔗 Result URL: {result_url}") @@ -328,30 +339,14 @@ def load_example_result(prompt: str, resolution: str, result_path: str = None): return create_placeholder_image_inline(prompt, resolution) def load_line_art_example_result(input_image_path, result_path = None): - """ - Load line art conversion example input and pre-generated result images - - Supports two modes: - 1. Runtime mode: input_image_path is a string path, result_path is a string path - 2. Cache generation mode: input_image_path is a PIL.Image object, result_path is a PIL.Image object - - Args: - input_image_path: input image path (str) or PIL.Image object - result_path: result image path (str) or PIL.Image object - - Returns: - Tuple[PIL.Image, PIL.Image]: (input image, result image) - """ + """Load line art conversion example images""" try: - # 1. 处理输入图片 - 支持字符串路径和PIL.Image对象 input_image = None if isinstance(input_image_path, Image.Image): - # 缓存生成模式:直接使用PIL.Image对象 input_image = input_image_path logger.info(f"Using PIL.Image object for line art input (cache mode): {input_image.size}") elif isinstance(input_image_path, str): - # 运行时模式:从路径加载图片 if os.path.exists(input_image_path): logger.info(f"Loading line art input image: {input_image_path}") input_image = Image.open(input_image_path) @@ -362,21 +357,16 @@ def load_line_art_example_result(input_image_path, result_path = None): logger.warning(f"Unexpected input_image_path type: {type(input_image_path)}") input_image = create_placeholder_image_inline("Input Image", "1024x1024") - # 2. 处理结果图片 - 支持字符串路径和PIL.Image对象 result_image = None if isinstance(result_path, Image.Image): - # 缓存生成模式:直接使用PIL.Image对象 result_image = result_path logger.info(f"Using PIL.Image object for line art result (cache mode): {result_image.size}") return (input_image, result_image) - # 运行时模式:从LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS查找结果图片 from examples_config import LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS - # 对于运行时模式,需要用字符串路径进行匹配 if isinstance(input_image_path, str): - # 运行时模式:直接用路径匹配 search_path = input_image_path for example_input, example_path in LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS: if example_input == search_path: @@ -385,15 +375,13 @@ def load_line_art_example_result(input_image_path, result_path = None): result_image = Image.open(example_path) return (input_image, result_image) else: - # Cache mode: identify example by image size image_size = input_image.size logger.info(f"Cache mode: identifying example by image size: {image_size}") - # 预定义的尺寸映射(需要根据实际图片尺寸调整) size_to_result = { - (474, 845): "examples/results/line_art_example1.jpg", # example1的尺寸 - (720, 1104): "examples/results/line_art_example2.jpg", # example2的尺寸 - (736, 1308): "examples/results/line_art_example3.jpg", # example3的尺寸 + (474, 845): "examples/results/line_art_example1.jpg", + (720, 1104): "examples/results/line_art_example2.jpg", + (736, 1308): "examples/results/line_art_example3.jpg", } result_path = size_to_result.get(image_size) @@ -456,23 +444,14 @@ def load_anime_to_real_example_result(input_image_path, result_path=None): return (input_placeholder, result_placeholder) def load_real_to_anime_example_result(input_image_path, result_path=None): - """ - Load example for Real to Anime: input image and pre-generated result image - - Supports two modes (mirrors Line Art Conversion implementation): - 1. Runtime mode: input_image_path is a string path, result_path is a string path - 2. Cache generation mode: input_image_path is a PIL.Image object, result_path is a PIL.Image object - """ + """Load example for Real to Anime conversion""" try: - # 1. 处理输入图片 - 支持字符串路径和PIL.Image对象 input_image = None if isinstance(input_image_path, Image.Image): - # Cache mode: directly use PIL.Image object input_image = input_image_path logger.info(f"Using PIL.Image object for real to anime input (cache mode): {input_image.size}") elif isinstance(input_image_path, str): - # 运行时模式:从路径加载图片 if os.path.exists(input_image_path): logger.info(f"Loading real to anime input image: {input_image_path}") input_image = Image.open(input_image_path) @@ -483,21 +462,16 @@ def load_real_to_anime_example_result(input_image_path, result_path=None): logger.warning(f"Unexpected input_image_path type: {type(input_image_path)}") input_image = create_placeholder_image_inline("Input Image", "1024x1024") - # 2. Handle result image - supports string path and PIL.Image object result_image = None if isinstance(result_path, Image.Image): - # 缓存生成模式:直接使用PIL.Image对象 result_image = result_path logger.info(f"Using PIL.Image object for real to anime result (cache mode): {result_image.size}") return (input_image, result_image) - # 运行时模式:从REAL_TO_ANIME_EXAMPLES_WITH_RESULTS查找结果图片 from examples_config import REAL_TO_ANIME_EXAMPLES_WITH_RESULTS - # 对于运行时模式,需要用字符串路径进行匹配 if isinstance(input_image_path, str): - # 运行时模式:直接用路径匹配 search_path = input_image_path for example_input, example_result in REAL_TO_ANIME_EXAMPLES_WITH_RESULTS: if example_input == search_path: @@ -506,15 +480,13 @@ def load_real_to_anime_example_result(input_image_path, result_path=None): result_image = Image.open(example_result) return (input_image, result_image) else: - # Cache mode: identify example by image size image_size = input_image.size logger.info(f"Cache mode: identifying real to anime example by image size: {image_size}") - # 预定义的尺寸映射(根据实际图片尺寸) size_to_result = { - (736, 1104): "examples/results/real_to_anime_example1.jpg", # example1的尺寸 - (736, 946): "examples/results/real_to_anime_example2.jpg", # example2的尺寸 - (1206, 796): "examples/results/real_to_anime_example3.jpg", # example3的尺寸 + (736, 1104): "examples/results/real_to_anime_example1.jpg", + (736, 946): "examples/results/real_to_anime_example2.jpg", + (1206, 796): "examples/results/real_to_anime_example3.jpg", } result_path_mapped = size_to_result.get(image_size) @@ -582,7 +554,7 @@ def load_dual_output_example(input_path, param1=None, param2=None): placeholder = create_placeholder_image_inline("Error", "1024x1024") return placeholder, placeholder -def load_five_view_example(input_path, result_path=None): +def load_five_view_example(input_path): """Load five view generation example with input and result images""" try: # Handle input image @@ -593,31 +565,191 @@ def load_five_view_example(input_path, result_path=None): else: input_image = create_placeholder_image_inline("Input", "1024x1024") - # Handle result image + # Try to find result from FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS + from examples_config import FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS + result_image = None + + # When Gradio caches examples, it passes PIL Image objects, not file paths + # We need to match by image size or use the first available result + if isinstance(input_path, Image.Image): + # For PIL Image inputs (cache mode), use the first available result + if len(FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS) > 0: + result_path_found = FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS[0][1] + if os.path.exists(result_path_found): + result_image = Image.open(result_path_found) + logger.info(f"Found five view result: {result_path_found}") + elif isinstance(input_path, str): + for example in FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS: + if example[0] == input_path and len(example) > 1: + result_path_found = example[1] + if os.path.exists(result_path_found): + result_image = Image.open(result_path_found) + logger.info(f"Found five view result: {result_path_found}") + break + + if not result_image: + result_image = create_placeholder_image_inline("Five View Result", "1024x1024") + + return input_image, result_image + except Exception as e: + logger.error(f"Error loading five view example: {e}") + placeholder = create_placeholder_image_inline("Error", "1024x1024") + return placeholder, placeholder + +def load_figure_3d_example(input_image_path, figure_style: str, resolution: str = "square - 1024x1024 (1:1)", result_path=None): + """ + Load 3D figure generation example input image and pre-generated result image + + Supports two modes: + 1. Runtime mode: input_image_path is a string path, result_path is a string path + 2. Cache generation mode: input_image_path is a PIL.Image object, result_path is a PIL.Image object + """ + try: + # 1. 处理输入图片 - 支持字符串路径和PIL.Image对象 + input_image = None + + if isinstance(input_image_path, Image.Image): + # Cache mode: directly use PIL.Image object + input_image = input_image_path + logger.info(f"Using PIL.Image object for input (cache mode): {input_image.size}") + elif isinstance(input_image_path, str): + # 运行时模式:从路径加载图片 + if os.path.exists(input_image_path): + logger.info(f"Loading figure 3D input image: {input_image_path}") + input_image = Image.open(input_image_path) + else: + logger.warning(f"Figure 3D input image not found: {input_image_path}") + input_image = create_placeholder_image("Input Image", "1024x1024") + else: + logger.warning(f"Unexpected input_image_path type: {type(input_image_path)}") + input_image = create_placeholder_image("Input Image", "1024x1024") + + # 2. Handle result image - supports string path and PIL.Image object + result_image = None + if isinstance(result_path, Image.Image): + # Cache mode: directly use PIL.Image object result_image = result_path - elif isinstance(result_path, str) and os.path.exists(result_path): - result_image = Image.open(result_path) + logger.info(f"Using PIL.Image object for result (cache mode): {result_image.size}") + elif isinstance(result_path, str): + # 运行时模式:从路径加载结果图片 + if os.path.exists(result_path): + logger.info(f"Loading figure 3D result image: {result_path}") + result_image = Image.open(result_path) + else: + logger.warning(f"Figure 3D result image not found: {result_path}") + result_image = create_placeholder_image("Result Image", "1024x1024") else: - # Try to find result from FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS - from examples_config import FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS + # 尝试从FIGURE_3D_EXAMPLES_WITH_RESULTS中查找匹配的结果 + from examples_config import FIGURE_3D_EXAMPLES_WITH_RESULTS result_image = None - if isinstance(input_path, str): - for example in FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS: - if example[0] == input_path and len(example) > 1: - result_path_found = example[1] + # 当Gradio缓存examples时,它传递PIL Image对象,不是文件路径 + # 我们需要通过风格匹配或使用第一个可用结果 + if isinstance(input_image_path, Image.Image): + # 对于PIL Image输入(缓存模式),通过风格查找结果 + for example in FIGURE_3D_EXAMPLES_WITH_RESULTS: + if len(example) >= 3 and example[1] == figure_style: + result_path_found = example[2] + if os.path.exists(result_path_found): + result_image = Image.open(result_path_found) + logger.info(f"Found figure 3D result by style: {result_path_found}") + break + elif isinstance(input_image_path, str): + # 对于字符串路径输入,精确匹配 + for example in FIGURE_3D_EXAMPLES_WITH_RESULTS: + if len(example) >= 3 and example[0] == input_image_path and example[1] == figure_style: + result_path_found = example[2] if os.path.exists(result_path_found): result_image = Image.open(result_path_found) - logger.info(f"Found five view result: {result_path_found}") + logger.info(f"Found figure 3D result: {result_path_found}") break if not result_image: - result_image = create_placeholder_image_inline("Five View Result", "1024x1024") + result_image = create_placeholder_image("Figure 3D Result", "1024x1024") + + return input_image, figure_style, resolution, result_image + except Exception as e: + logger.error(f"Error loading figure 3D example: {e}") + placeholder = create_placeholder_image_inline("Error", "1024x1024") + return placeholder, figure_style, resolution, placeholder + +def load_character_figure_collaboration_example(input_image_path, result_path=None): + """ + Load character figure collaboration example input image and pre-generated result image + + Args: + input_image_path: Path to input image or PIL.Image object + result_path: Path to result image or PIL.Image object + + Returns: + tuple: (input_image, result_image) + """ + try: + # 1. 处理输入图片 - 支持字符串路径和PIL.Image对象 + input_image = None + + if isinstance(input_image_path, Image.Image): + # Cache mode: directly use PIL.Image object + input_image = input_image_path + logger.info(f"Using PIL.Image object for input (cache mode): {input_image.size}") + elif isinstance(input_image_path, str): + # 运行时模式:从路径加载图片 + if os.path.exists(input_image_path): + logger.info(f"Loading character figure collaboration input image: {input_image_path}") + input_image = Image.open(input_image_path) + else: + logger.warning(f"Character figure collaboration input image not found: {input_image_path}") + input_image = create_placeholder_image("Input Image", "1024x1024") + else: + logger.warning(f"Unexpected input_image_path type: {type(input_image_path)}") + input_image = create_placeholder_image("Input Image", "1024x1024") + + # 2. Handle result image - 完全按照Line Art的模式 + result_image = None + + if isinstance(result_path, Image.Image): + # Cache mode: directly use PIL.Image object + result_image = result_path + logger.info(f"Using PIL.Image object for result (cache mode): {result_image.size}") + return input_image, result_image + + from examples_config import CHARACTER_FIGURE_COLLABORATION_EXAMPLES_WITH_RESULTS + + if isinstance(input_image_path, str): + # 运行时模式:通过输入路径查找匹配的结果 + search_path = input_image_path + for example_input, example_result in CHARACTER_FIGURE_COLLABORATION_EXAMPLES_WITH_RESULTS: + if example_input == search_path: + if os.path.exists(example_result): + logger.info(f"Loading character figure collaboration example result: {example_result}") + result_image = Image.open(example_result) + return input_image, result_image + else: + # Cache mode: 通过图片尺寸匹配(完全按照Line Art的模式) + image_size = input_image.size + logger.info(f"Cache mode: identifying character figure collaboration example by image size: {image_size}") + # 创建尺寸到结果图片的映射(完全按照Line Art的模式) + size_to_result = { + (1206, 776): "examples/results/character_figure_collaboration_example1.jpg", + (1320, 1920): "examples/results/character_figure_collaboration_example2.jpg", + (1536, 2200): "examples/results/character_figure_collaboration_example3.jpg", + (1206, 1787): "examples/results/character_figure_collaboration_example4.jpg", + } + + result_path = size_to_result.get(image_size) + if result_path and os.path.exists(result_path): + logger.info(f"Loading character figure collaboration result by size mapping: {result_path}") + result_image = Image.open(result_path) + return input_image, result_image + + # If no matching example is found, create a placeholder + logger.warning(f"No matching character figure collaboration example found") + result_image = create_placeholder_image_inline("Character Figure Collaboration Result", "1024x1024") return input_image, result_image except Exception as e: - logger.error(f"Error loading five view example: {e}") + logger.error(f"Error loading character figure collaboration example: {e}") placeholder = create_placeholder_image_inline("Error", "1024x1024") return placeholder, placeholder @@ -824,7 +956,7 @@ async def submit_image_to_image_task(pil_image: Image.Image) -> dict: return await submit_task_with_retry( endpoint="api/v1/tasks/image-to-image", payload=submission.to_api_payload(), - task_name="图像转换" + task_name="Image Conversion" ) async def submit_photo_style_task(pil_image: Image.Image, style_preset: str) -> dict: @@ -836,7 +968,7 @@ async def submit_photo_style_task(pil_image: Image.Image, style_preset: str) -> return await submit_task_with_retry( endpoint="api/v1/tasks/photo-style", payload=submission.to_api_payload(), - task_name="摄影风格转换" + task_name="Photo Style Transfer" ) async def submit_interior_design_task(pil_image: Image.Image, design_style: str) -> dict: @@ -848,7 +980,7 @@ async def submit_interior_design_task(pil_image: Image.Image, design_style: str) return await submit_task_with_retry( endpoint="api/v1/tasks/interior-design-rendering", payload=submission.to_api_payload(), - task_name="室内设计渲染" + task_name="Interior Design Rendering" ) async def submit_watermark_removal_task(pil_image: Image.Image) -> dict: @@ -860,7 +992,7 @@ async def submit_watermark_removal_task(pil_image: Image.Image) -> dict: return await submit_task_with_retry( endpoint="api/v1/tasks/watermark-removal", payload=submission.to_api_payload(), - task_name="水印移除" + task_name="Watermark Removal" ) async def submit_line_art_task(pil_image: Image.Image) -> dict: @@ -872,7 +1004,7 @@ async def submit_line_art_task(pil_image: Image.Image) -> dict: return await submit_task_with_retry( endpoint="api/v1/tasks/line-art-conversion", payload=submission.to_api_payload(), - task_name="线稿转换" + task_name="Line Art Conversion" ) async def submit_image_outpainting_task(pil_image: Image.Image, expand_height: float, expand_width: float) -> dict: @@ -888,7 +1020,7 @@ async def submit_image_outpainting_task(pil_image: Image.Image, expand_height: f return await submit_task_with_retry( endpoint="api/v1/tasks/image-outpainting", payload=submission.to_api_payload(), - task_name="图像扩展" + task_name="Image Outpainting" ) async def submit_anime_to_real_task(pil_image: Image.Image) -> dict: @@ -900,7 +1032,7 @@ async def submit_anime_to_real_task(pil_image: Image.Image) -> dict: return await submit_task_with_retry( endpoint="api/v1/tasks/anime-to-real", payload=submission.to_api_payload(), - task_name="二次元转真人" + task_name="Anime to Real" ) async def submit_real_to_anime_task(pil_image: Image.Image) -> dict: @@ -912,7 +1044,7 @@ async def submit_real_to_anime_task(pil_image: Image.Image) -> dict: return await submit_task_with_retry( endpoint="api/v1/tasks/real-to-anime", payload=submission.to_api_payload(), - task_name="真人转动漫" + task_name="Real to Anime" ) async def submit_five_view_generation_task(input_image: Image.Image) -> dict: @@ -929,6 +1061,42 @@ async def submit_five_view_generation_task(input_image: Image.Image) -> dict: task_name="Five-View Generation" ) +async def submit_figure_3d_generation_task(input_image: Image.Image, figure_style: str, resolution: str) -> dict: + """Submit 2D to 3D figure generation task to backend API""" + # Convert PIL image to base64 + processed_image = input_image.convert("RGB") + base64_data = pil_to_base64(processed_image) + + submission = Figure3DSubmission( + image_data=base64_data, + figure_style=figure_style, + resolution=resolution + ) + + return await submit_task_with_retry( + endpoint="api/v1/tasks/figure-3d-generation", + payload=submission.to_api_payload(), + task_name="2D to 3D Figure" + ) + + +async def submit_character_figure_collaboration_task(input_image: Image.Image) -> dict: + """Submit character figure collaboration task to backend API""" + # Convert PIL image to base64 + processed_image = input_image.convert("RGB") + base64_data = pil_to_base64(processed_image) + + submission = CharacterFigureCollaborationSubmission( + image_data=base64_data + ) + + return await submit_task_with_retry( + endpoint="api/v1/tasks/character-figure-collaboration", + payload=submission.to_api_payload(), + task_name="Character Figure Collaboration" + ) + + # ============================================================================ # UI Configuration and Theme Setup # ============================================================================ @@ -976,16 +1144,13 @@ async def generate_text_to_image(prompt: str, resolution: str, progress=None): logger.info(f"Starting text-to-image generation: {prompt[:50]}...") start_time = datetime.now() - # Update UI to show processing yield ( - None, # result_image - "🚀 Submitting task to AI server...", # status_info - "**Tip**: The AI is processing your description and preparing to create...", # image_info - False, # generate_btn visible - True # cancel_btn visible + None, + "🚀 Submitting task to AI server...", + "**Tip**: The AI is processing your description and preparing to create...", + False, + True ) - - # Submit task try: task_data = await submit_text_to_image_task(prompt, resolution) task_id = task_data.get("task_id") @@ -998,8 +1163,7 @@ async def generate_text_to_image(prompt: str, resolution: str, progress=None): except Exception as e: logger.error(f"Task submission failed: {e}") - # Show user-friendly error message - error_message = str(e) if any(msg in str(e) for msg in ["系统当前繁忙", "服务暂时不可用", "请求参数有误", "网络连接超时", "无法连接到服务器", "提交任务时发生错误"]) else "Task submission failed, please try again later" + error_message = str(e) if any(msg in str(e) for msg in ["System is currently busy", "Service temporarily unavailable", "Invalid request parameters", "Network connection timeout", "Unable to connect to server", "Task submission failed"]) else "Task submission failed, please try again later" yield ( None, f"❌ {error_message}", @@ -1084,7 +1248,7 @@ async def generate_text_to_image(prompt: str, resolution: str, progress=None): logger.error(f"Error polling task status: {e}") yield ( None, - f"❌ Status query failed: {str(e)}", + "❌ Status query failed. Please try again later.", "", True, False @@ -1104,7 +1268,7 @@ async def generate_text_to_image(prompt: str, resolution: str, progress=None): logger.error(f"Unexpected error in text-to-image generation: {e}") yield ( None, - f"❌ An error occurred during generation: {str(e)}", + "❌ An unexpected error occurred during generation. Please try again later.", "", True, False @@ -1130,16 +1294,13 @@ async def generic_image_processing( logger.info(f"Starting {task_name} processing...") start_time = datetime.now() - # Update UI to show processing yield ( - None, # result_image - f"🚀 Submitting {task_name} task to the AI server...", # status_info - f"**Tip**: The AI is analyzing your image and preparing to start {task_name}...", # image_info - False, # generate_btn visible - True # cancel_btn visible + None, + f"🚀 Submitting {task_name} task to the AI server...", + f"**Tip**: The AI is analyzing your image and preparing to start {task_name}...", + False, + True ) - - # Submit task try: task_data = await submit_func(input_image, *submit_args) task_id = task_data.get("task_id") @@ -1153,7 +1314,7 @@ async def generic_image_processing( except Exception as e: logger.error(f"{task_name} task submission failed: {e}") # Show user-friendly error message - error_message = str(e) if any(msg in str(e) for msg in ["系统当前繁忙", "服务暂时不可用", "请求参数有误", "网络连接超时", "无法连接到服务器", "提交任务时发生错误"]) else "Task submission failed, please try again later" + error_message = str(e) if any(msg in str(e) for msg in ["System is currently busy", "Service temporarily unavailable", "Invalid request parameters", "Network connection timeout", "Unable to connect to server", "Task submission failed"]) else "Task submission failed, please try again later" yield ( None, f"❌ {error_message}", @@ -1217,15 +1378,15 @@ async def generic_image_processing( # Update status with task-specific tips task_tips = { - "图像转换": ["🔄 Processing: analyzing image features", "🎯 Optimizing: applying advanced image processing"], - "五视角生成": ["👁️ Analyzing: understanding facial and pose features", "🔄 Generating: creating multiple viewpoints"], - "摄影风格转换": ["📸 Stylizing: applying professional photography techniques", "✨ Enhancing: optimizing lighting and colors"], - "室内设计渲染": ["🏠 Designing: composing interior layout", "🎨 Rendering: adding furniture and decor"], - "水印移除": ["🚫 Detecting: locating watermark areas", "🔧 Repairing: intelligently inpainting background"], - "线稿转换": ["✏️ Outlining: extracting contours", "🎨 Refining: improving line details"], - "图像扩展": ["📐 Extending: adding coherent border content", "🔄 Blending: ensuring seamless continuity"], - "二次元转真人": ["👤 Converting: mapping anime features to realistic ones", "🎭 Refining: adjusting facial details"], - "真人转动漫": ["🎌 Stylizing: applying anime art style", "✨ Enhancing: optimizing anime effects"] + "Image Conversion": ["🔄 Processing: analyzing image features", "🎯 Optimizing: applying advanced image processing"], + "Five-View Generation": ["👁️ Analyzing: understanding facial and pose features", "🔄 Generating: creating multiple viewpoints"], + "Photo Style Transfer": ["📸 Stylizing: applying professional photography techniques", "✨ Enhancing: optimizing lighting and colors"], + "Interior Design Rendering": ["🏠 Designing: composing interior layout", "🎨 Rendering: adding furniture and decor"], + "Watermark Removal": ["🚫 Detecting: locating watermark areas", "🔧 Repairing: intelligently inpainting background"], + "Line Art Conversion": ["✏️ Outlining: extracting contours", "🎨 Refining: improving line details"], + "Image Outpainting": ["📐 Extending: adding coherent border content", "🔄 Blending: ensuring seamless continuity"], + "Anime to Real": ["👤 Converting: mapping anime features to realistic ones", "🎭 Refining: adjusting facial details"], + "Real to Anime": ["🎌 Stylizing: applying anime art style", "✨ Enhancing: optimizing anime effects"] } tips = task_tips.get(task_name, ["🔄 Processing: AI is working hard", "⏱️ Please wait: almost done"]) @@ -1245,7 +1406,7 @@ async def generic_image_processing( logger.error(f"Error polling {task_name} task status: {e}") yield ( None, - f"❌ Status query failed: {str(e)}", + "❌ Status query failed. Please try again later.", "", True, False @@ -1265,7 +1426,7 @@ async def generic_image_processing( logger.error(f"Unexpected error in {task_name}: {e}") yield ( None, - f"❌ An error occurred during {task_name}: {str(e)}", + f"❌ An unexpected error occurred during {task_name}. Please try again later.", "", True, False @@ -1339,6 +1500,22 @@ async def generate_five_view(input_image: Image.Image, progress=None): ): yield result +async def generate_figure_3d(input_image: Image.Image, figure_style: str, resolution: str, progress=None): + """Generate 3D figure from 2D character image""" + async for result in generic_image_processing( + input_image, "2D to 3D Figure", submit_figure_3d_generation_task, (figure_style, resolution), progress + ): + yield result + +# Character figure collaboration generation +async def generate_character_figure_collaboration(input_image: Image.Image, progress=None): + """Generate character figure collaboration image""" + async for result in generic_image_processing( + input_image, "Character Figure Collaboration", submit_character_figure_collaboration_task, (), progress + ): + yield result + + def placeholder_handler(*args): """Placeholder function for AI processing""" # Suppress unused arguments warning @@ -1363,9 +1540,9 @@ def create_text_to_image_interface(): resolution_input = gr.Dropdown( label="Resolution", choices=[ + "portrait - 896x1152 (3:4)", "square - 1024x1024 (1:1)", - "landscape - 1152x896 (4:3)", - "portrait - 896x1152 (3:4)" + "landscape - 1152x896 (4:3)" ], value="square - 1024x1024 (1:1)", interactive=True @@ -1465,583 +1642,737 @@ def create_main_interface(): fill_width=True ) as interface: - # Main layout with sidebar - with gr.Sidebar(): - gr.Markdown("# AI Toolbox") - gr.Markdown("Choose a feature below to get started") - - # Creation Tools group - gr.Markdown("## Creation Tools") - with gr.Group(): - text_to_image_btn = gr.Button("Text to Image", size="sm", variant="secondary") - image_convert_btn = gr.Button("Image to Image", size="sm", variant="secondary") - five_view_btn_sidebar = gr.Button("Five-View Generation", size="sm", variant="secondary") - - gr.Markdown("---") - - # Style Transfer group - gr.Markdown("## Style Transfer") - with gr.Group(): - photo_style_btn_sidebar = gr.Button("Photo Style", size="sm", variant="secondary") - interior_btn_sidebar = gr.Button("Interior Design", size="sm", variant="secondary") - - gr.Markdown("---") - - # Image Processing group - gr.Markdown("## Image Processing") - with gr.Group(): - watermark_btn_sidebar = gr.Button("Watermark Removal", size="sm", variant="secondary") - line_art_btn_sidebar = gr.Button("Line Art Conversion", size="sm", variant="secondary") - expand_btn_sidebar = gr.Button("Image Outpainting", size="sm", variant="secondary") - - gr.Markdown("---") - - # Anime Conversion group - gr.Markdown("## Anime Conversion") - with gr.Group(): - anime_to_real_btn_sidebar = gr.Button("Anime to Real", size="sm", variant="secondary") - real_to_anime_btn_sidebar = gr.Button("Real to Anime", size="sm", variant="secondary") - - # 主内容区域 - with gr.Column(scale=4): - # 欢迎页面 - 使用推荐的elem_id方法 - welcome_content = gr.HTML(""" -
-

AI Image Generator

-

Select a feature on the left to start your AI creation journey

-
-
-

Creation Tools

-

Text to Image, Image to Image, Multi-view Generation

-
-
-

Style Transfer

-

Photo Style, Interior Design

-
-
-

Image Processing

-

Watermark Removal, Line Art, Outpainting

-
-
-

Anime Conversion

-

Anime to Real, Real to Anime

+ # Main layout with sidebar using Row and Column + with gr.Row(): + # Sidebar column + with gr.Column(scale=1, min_width=250): + gr.Markdown("# AI Toolbox") + gr.Markdown("Choose a feature below to get started") + + # Creation Tools group + gr.Markdown("## Creation Tools") + with gr.Group(): + text_to_image_btn = gr.Button("Text to Image", size="sm", variant="secondary") + image_convert_btn = gr.Button("Image to Image", size="sm", variant="secondary") + five_view_btn_sidebar = gr.Button("Five-View Generation", size="sm", variant="secondary") + figure_3d_btn_sidebar = gr.Button("2D to 3D Figure", size="sm", variant="secondary") + character_figure_btn_sidebar = gr.Button("Character Figure Collaboration", size="sm", variant="secondary") + + + gr.Markdown("---") + + # Style Transfer group + gr.Markdown("## Style Transfer") + with gr.Group(): + photo_style_btn_sidebar = gr.Button("Photo Style", size="sm", variant="secondary") + interior_btn_sidebar = gr.Button("Interior Design", size="sm", variant="secondary") + + gr.Markdown("---") + + # Image Processing group + gr.Markdown("## Image Processing") + with gr.Group(): + watermark_btn_sidebar = gr.Button("Watermark Removal", size="sm", variant="secondary") + line_art_btn_sidebar = gr.Button("Line Art Conversion", size="sm", variant="secondary") + expand_btn_sidebar = gr.Button("Image Outpainting", size="sm", variant="secondary") + + gr.Markdown("---") + + # Anime Conversion group + gr.Markdown("## Anime Conversion") + with gr.Group(): + anime_to_real_btn_sidebar = gr.Button("Anime to Real", size="sm", variant="secondary") + real_to_anime_btn_sidebar = gr.Button("Real to Anime", size="sm", variant="secondary") + + # Main content area + with gr.Column(scale=4): + # 欢迎页面 - 使用推荐的elem_id方法 + welcome_content = gr.HTML(""" +
+

AI Image Generator

+

Select a feature on the left to start your AI creation journey

+
+
+

Creation Tools

+

Text to Image, Image to Image, Multi-view Generation

+
+
+

Style Transfer

+

Photo Style, Interior Design

+
+
+

Image Processing

+

Watermark Removal, Line Art, Outpainting

+
+
+

Anime Conversion

+

Anime to Real, Real to Anime

+
-
- """, elem_id="welcome-container") - - # 动态内容区域(初始隐藏) - dynamic_content = gr.HTML(visible=False) - - # 文本生成图像功能区域(初始隐藏) - with gr.Column(visible=False) as text_to_image_interface: - gr.Markdown("## 📝 Text to Image") - gr.Markdown("Enter a textual description and let AI generate an image for you") - - with gr.Row(): - with gr.Column(scale=2): - prompt_input = gr.Textbox( - label="Image Description", - placeholder="Describe the image you want in detail, e.g., a cute cat playing in a garden, anime style, high-quality details", - lines=4, - max_lines=6 - ) - - resolution_input = gr.Dropdown( - label="Resolution", - choices=[ - "square - 1024x1024 (1:1)", - "landscape - 1152x896 (4:3)", - "portrait - 896x1152 (3:4)", - "landscape - 1344x768 (16:9)", - "portrait - 768x1344 (9:16)", - "landscape - 1216x832 (3:2)", - "landscape - 1536x640 (21:9)" - ], - value="square - 1024x1024 (1:1)", - interactive=True - ) - - with gr.Row(): - generate_btn = gr.Button("Generate Image", variant="primary", scale=2) - cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - status_info = gr.Markdown("") - - with gr.Column(scale=3): - result_image = gr.Image( - label="Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - image_info = gr.Markdown(""" - ### 💡 Tips - - Be descriptive: the more detailed the description, the better the results - - Style keywords: e.g., \"high-definition photography\", \"anime style\", \"oil painting style\" - - Composition: describe subject pose, scene layout, and lighting - - Quality hints: e.g., \"4K quality\", \"high-detail\", \"professional photography\" - - Processing time: typically 3–10 minutes. Please wait patiently - """, visible=True) - - # 添加文本生成图像的examples - text_to_image_examples_input_only = [[example[0], example[1]] for example in TEXT_TO_IMAGE_EXAMPLES_WITH_RESULTS] - - gr.Examples( - examples=text_to_image_examples_input_only, - inputs=[prompt_input, resolution_input], - outputs=result_image, - fn=load_example_result, - label="Example Prompts - Click to preview", - examples_per_page=6, - cache_examples=True - ) - - # 图像转换功能区域(初始隐藏) - with gr.Column(visible=False) as image_convert_interface: - gr.Markdown("## Image to Image") - gr.Markdown("Upload an image and let AI intelligently transform it") - - with gr.Row(): - with gr.Column(scale=2): - convert_input = gr.Image( - label="Upload Image", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - with gr.Row(): - convert_btn = gr.Button("Transform Image", variant="primary", scale=2) - convert_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - convert_status = gr.Markdown("") - - with gr.Column(scale=3): - convert_result = gr.Image( - label="Transformed Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - convert_info = gr.Markdown(""" - ### 💡 Tips - - Supported formats: PNG, JPEG, JPG, WEBP - - Recommended size: 512x512 to 1536x1536 pixels - - File size: recommended under 10MB - - Image quality: higher clarity yields better results - - Processing time: typically 1–3 minutes - """, visible=True) - - # 五视角生成功能区域(初始隐藏) - with gr.Column(visible=False) as five_view_interface: - gr.Markdown("## Five-View Generation") - gr.Markdown("Upload a portrait to generate 5 different viewpoints") - - with gr.Row(): - with gr.Column(scale=2): - five_view_input = gr.Image( - label="Upload Portrait", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - with gr.Row(): - five_view_btn = gr.Button("Generate Five Views", variant="primary", scale=2) - five_view_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - five_view_status = gr.Markdown("") - - with gr.Column(scale=3): - five_view_result = gr.Image( - label="Five-View Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - five_view_info = gr.Markdown(""" - ### 💡 Tips - - Portrait: upload a clear portrait (front-facing preferred) - - Supported types: real persons, anime/game characters - - Recommended size: 512x512 to 1024x1024 pixels - - Background: simple backgrounds work better - - Processing time: typically 1–3 minutes. Please wait patiently - """, visible=True) - - # 添加五视角生成的examples - gr.Examples( - examples=FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS, - inputs=[five_view_input], - outputs=[five_view_input, five_view_result], - fn=load_five_view_example, - label="💡 Five-View Examples - Click to preview", - examples_per_page=3, - cache_examples=True - ) - - # 摄影风格转换功能区域(初始隐藏) - with gr.Column(visible=False) as photo_style_interface: - gr.Markdown("## Photo Style Transfer") - gr.Markdown("Apply professional photography styles to your photos") - - with gr.Row(): - with gr.Column(scale=2): - photo_style_input = gr.Image( - label="Upload Photo", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - photo_style_dropdown = gr.Dropdown( - label="Select Photo Style", - choices=PHOTO_STYLE_CHOICES, - value="camera_movement", - info="Choose the photography style to apply" - ) - - with gr.Row(): - photo_style_btn = gr.Button("Apply Style", variant="primary", scale=2) - photo_style_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - photo_style_status = gr.Markdown("") - - with gr.Column(scale=3): - photo_style_result = gr.Image( - label="Style Transfer Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - photo_style_info = gr.Markdown(""" - ### 💡 Tips - - Photo types: portraits, landscapes, product photos, etc. - - Recommended quality: higher resolution photos yield better results - - Style selection: choose a style that matches the photo type - - Lighting: well-lit photos convert better - - Processing time: typically 1–2 minutes - """, visible=True) - - # 室内设计渲染功能区域(初始隐藏) - with gr.Column(visible=False) as interior_interface: - gr.Markdown("## Interior Design Rendering") - gr.Markdown("Upload a white model interior image to generate design renders") - - with gr.Row(): - with gr.Column(scale=2): - interior_input = gr.Image( - label="Upload Interior White Model", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - interior_style = gr.Dropdown( - label="Select Interior Style", - choices=INTERIOR_DESIGN_STYLE_CHOICES, - value="japanese_wabi_sabi", # keep default key - info="Choose the interior design style to apply" - ) - - with gr.Row(): - interior_btn = gr.Button("Render Design", variant="primary", scale=2) - interior_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - interior_status = gr.Markdown("") - - with gr.Column(scale=3): - interior_result = gr.Image( - label="Rendered Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - interior_info = gr.Markdown(""" - ### 💡 Tips - - Input: upload a white model or line art of the interior - - Style description: describe the desired design style in detail - - Space type: living room, bedroom, kitchen, office, etc. - - Style keywords: modern minimalism, Nordic, classic Chinese, etc. - - Processing time: typically 2–5 minutes - """, visible=True) - - # 添加室内设计的examples - - interior_design_examples_input_only = [[example[0], example[1]] for example in INTERIOR_DESIGN_EXAMPLES_WITH_RESULTS] - - gr.Examples( - examples=interior_design_examples_input_only, - inputs=[interior_input, interior_style], - outputs=[interior_input, interior_result], - fn=load_interior_design_example_result, - label="💡 Interior Design Examples - Click to preview", - examples_per_page=4, - cache_examples=True - ) - - # 水印移除功能区域(初始隐藏) - with gr.Column(visible=False) as watermark_interface: - gr.Markdown("## Watermark Removal") - gr.Markdown("Intelligently detect and remove watermarks from images") - - with gr.Row(): - with gr.Column(scale=2): - watermark_input = gr.Image( - label="Upload Image with Watermark", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - with gr.Row(): - watermark_btn = gr.Button("Remove Watermark", variant="primary", scale=2) - watermark_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - watermark_status = gr.Markdown("") - - with gr.Column(scale=3): - watermark_result = gr.Image( - label="Watermark Removal Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - watermark_info = gr.Markdown(""" - ### 💡 Tips - - Watermark types: supports text and icon watermarks - - Image quality: higher quality images yield better results - - Watermark location: AI will automatically detect and remove - - Background complexity: simpler backgrounds remove better - - Processing time: typically 1–3 minutes - """, visible=True) - - # 线稿转换功能区域(初始隐藏) - with gr.Column(visible=False) as line_art_interface: - gr.Markdown("## Line Art Conversion") - gr.Markdown("Convert your photo into clean line art") - - with gr.Row(): - with gr.Column(scale=2): - line_art_input = gr.Image( - label="Upload Photo", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - with gr.Row(): - line_art_btn = gr.Button("Convert to Line Art", variant="primary", scale=2) - line_art_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - line_art_status = gr.Markdown("") - - with gr.Column(scale=3): - line_art_result = gr.Image( - label="Line Art Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - line_art_info = gr.Markdown(""" - ### 💡 Tips - - Photo types: portraits, landscapes, architecture, objects - - Image clarity: higher clarity yields better line art - - Contrast: higher contrast improves line extraction - - Complexity: rich details produce better line art - - Processing time: typically 1–2 minutes - """, visible=True) - - # 添加线稿转换的examples - gr.Examples( - examples=LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS, - inputs=[line_art_input], - outputs=[line_art_input, line_art_result], - fn=load_line_art_example_result, - label="💡 Line Art Examples - Click to preview", - examples_per_page=3, - cache_examples=True - ) - - # 图像扩展功能区域(初始隐藏) - with gr.Column(visible=False) as expand_interface: - gr.Markdown("## Image Outpainting") - gr.Markdown("Intelligently extend image boundaries while keeping content coherent") - - with gr.Row(): - with gr.Column(scale=2): - expand_input = gr.Image( - label="Upload Image to Outpaint", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) - - expand_height = gr.Slider( - label="Outpaint Height (%)", - minimum=0.0, - maximum=1.0, - value=0.2, - step=0.1, - interactive=True, - info="Percentage to extend vertically" - ) - - expand_width = gr.Slider( - label="Outpaint Width (%)", - minimum=0.0, - maximum=1.0, - value=0.3, - step=0.1, - interactive=True, - info="Percentage to extend horizontally" - ) - - with gr.Row(): - expand_btn = gr.Button("Outpaint Image", variant="primary", scale=2) - expand_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) - - expand_status = gr.Markdown("") - - with gr.Column(scale=3): - expand_result = gr.Image( - label="Outpainting Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) - - expand_info = gr.Markdown(""" - ### 💡 Tips - - Direction: choose which sides to extend (top/bottom/left/right) - - Pixel amount: 64–512 pixels recommended; too large may impact quality - - Edges: richer edge content yields more natural results - - Coherence: AI will fill in content consistent with the original - - Processing time: typically 2–4 minutes - """, visible=True) - - # 图像扩展Examples - 修复inputs/outputs匹配问题 - gr.Examples( - examples=IMAGE_OUTPAINTING_EXAMPLES_WITH_RESULTS, - inputs=[expand_input, expand_height, expand_width, expand_result], - outputs=[expand_input, expand_height, expand_width, expand_result], - fn=load_outpainting_example_for_gradio, - label="💡 Outpainting Examples - Click to preview", - examples_per_page=3, - cache_examples=False # Disable caching to avoid serialization issues - ) - - # 二次元转真人功能区域(初始隐藏) - with gr.Column(visible=False) as anime_to_real_interface: - gr.Markdown("## Anime to Real") - gr.Markdown("Convert anime characters to realistic humans") - - with gr.Row(): - with gr.Column(scale=2): - anime_to_real_input = gr.Image( - label="Upload Anime Character", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) + """, elem_id="welcome-container") + + # Dynamic content area (initially hidden) + dynamic_content = gr.HTML(visible=False) + + # 文本生成图像功能区域(初始隐藏) + with gr.Column(visible=False) as text_to_image_interface: + gr.Markdown("## 📝 Text to Image") + gr.Markdown("Enter a textual description and let AI generate an image for you") + + with gr.Row(): + with gr.Column(scale=2): + prompt_input = gr.Textbox( + label="Image Description", + placeholder="Describe the image you want in detail, e.g., a cute cat playing in a garden, anime style, high-quality details", + lines=4, + max_lines=6 + ) + + resolution_input = gr.Dropdown( + label="Resolution", + choices=[ + "portrait - 768x1344 (9:16)", + "portrait - 896x1152 (3:4)", + "square - 1024x1024 (1:1)", + "landscape - 1152x896 (4:3)", + "landscape - 1216x832 (3:2)", + "landscape - 1344x768 (16:9)", + "landscape - 1536x640 (21:9)" + ], + value="square - 1024x1024 (1:1)", + interactive=True + ) + + with gr.Row(): + generate_btn = gr.Button("Generate Image", variant="primary", scale=2) + cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + status_info = gr.Markdown("") + + with gr.Column(scale=3): + result_image = gr.Image( + label="Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + image_info = gr.Markdown(""" + ### 💡 Tips + - Be descriptive: the more detailed the description, the better the results + - Style keywords: e.g., \"high-definition photography\", \"anime style\", \"oil painting style\" + - Composition: describe subject pose, scene layout, and lighting + - Quality hints: e.g., \"4K quality\", \"high-detail\", \"professional photography\" + - Processing time: typically 3–10 minutes. Please wait patiently + """, visible=True) + + # 添加文本生成图像的examples + text_to_image_examples_input_only = [[example[0], example[1]] for example in TEXT_TO_IMAGE_EXAMPLES_WITH_RESULTS] + + gr.Examples( + examples=text_to_image_examples_input_only, + inputs=[prompt_input, resolution_input], + outputs=result_image, + fn=load_example_result, + label="Example Prompts - Click to preview", + examples_per_page=6, + cache_examples=True + ) - with gr.Row(): - anime_to_real_btn = gr.Button("Convert to Real", variant="primary", scale=2) - anime_to_real_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + # 图像转换功能区域(初始隐藏) + with gr.Column(visible=False) as image_convert_interface: + gr.Markdown("## Image to Image") + gr.Markdown("Upload an image and let AI intelligently transform it") + + with gr.Row(): + with gr.Column(scale=2): + convert_input = gr.Image( + label="Upload Image", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + convert_btn = gr.Button("Transform Image", variant="primary", scale=2) + convert_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + convert_status = gr.Markdown("") + + with gr.Column(scale=3): + convert_result = gr.Image( + label="Transformed Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + convert_info = gr.Markdown(""" + ### 💡 Tips + - Supported formats: PNG, JPEG, JPG, WEBP + - Recommended size: 512x512 to 1536x1536 pixels + - File size: recommended under 10MB + - Image quality: higher clarity yields better results + - Processing time: typically 1–3 minutes + """, visible=True) + + # 五视角生成功能区域(初始隐藏) + with gr.Column(visible=False) as five_view_interface: + gr.Markdown("## Five-View Generation") + gr.Markdown("Upload a portrait to generate 5 different viewpoints") + + with gr.Row(): + with gr.Column(scale=2): + five_view_input = gr.Image( + label="Upload Portrait", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + five_view_btn = gr.Button("Generate Five Views", variant="primary", scale=2) + five_view_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + five_view_status = gr.Markdown("") + + with gr.Column(scale=3): + five_view_result = gr.Image( + label="Five-View Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + five_view_info = gr.Markdown(""" + ### 💡 Tips + - Portrait: upload a clear portrait (front-facing preferred) + - Supported types: real persons, anime/game characters + - Recommended size: 512x512 to 1024x1024 pixels + - Background: simple backgrounds work better + - Processing time: typically 1–3 minutes. Please wait patiently + """, visible=True) + + # 添加五视角生成的examples + five_view_examples_input_only = [[example[0]] for example in FIVE_VIEW_GENERATION_EXAMPLES_WITH_RESULTS] + + gr.Examples( + examples=five_view_examples_input_only, + inputs=[five_view_input], + outputs=[five_view_input, five_view_result], + fn=load_five_view_example, + label="💡 Five-View Examples - Click to preview", + examples_per_page=3, + cache_examples=True + ) - anime_to_real_status = gr.Markdown("") + # 2D转3D手办功能区域(初始隐藏) + with gr.Column(visible=False) as figure_3d_interface: + gr.Markdown("## 2D to 3D Figure Generation") + gr.Markdown("Convert 2D character images into 3D figure renders with various scene styles") + + with gr.Row(): + with gr.Column(scale=2): + figure_3d_input = gr.Image( + label="Upload 2D Character Image", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + figure_3d_style = gr.Dropdown( + label="Select Figure Style", + choices=FIGURE_3D_STYLE_CHOICES, + value="professional_lighting", + info="Choose the 3D figure scene style" + ) + + figure_3d_resolution = gr.Dropdown( + label="Resolution", + choices=[ + "portrait - 768x1344 (9:16)", + "portrait - 896x1152 (3:4)", + "square - 1024x1024 (1:1)", + "landscape - 1152x896 (4:3)", + "landscape - 1216x832 (3:2)", + "landscape - 1344x768 (16:9)", + "landscape - 1536x640 (21:9)" + ], + value="square - 1024x1024 (1:1)", + info="Choose the output image resolution" + ) + + with gr.Row(): + figure_3d_btn = gr.Button("Generate 3D Figure", variant="primary", scale=2) + figure_3d_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + figure_3d_status = gr.Markdown("") + + with gr.Column(scale=3): + figure_3d_result = gr.Image( + label="3D Figure Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + figure_3d_info = gr.Markdown(""" + ### 💡 Tips + - Character: upload clear 2D character images (anime, game characters, illustrations) + - Supported formats: PNG, JPEG, JPG, WEBP + - Recommended size: 512x512 to 1536x1536 pixels + - File size: under 10MB recommended + - Processing time: typically 1–3 minutes + - Scene styles: professional lighting, collector shelf, desktop display, miniature adventure, Alice's tea party + """, visible=True) + + # 添加3D手办生成的examples - 使用不同分辨率展示多样性 + figure_3d_examples_input_only = [ + [FIGURE_3D_EXAMPLES_WITH_RESULTS[0][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[0][1], "square - 1024x1024 (1:1)"], + [FIGURE_3D_EXAMPLES_WITH_RESULTS[1][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[1][1], "landscape - 1152x896 (4:3)"] if len(FIGURE_3D_EXAMPLES_WITH_RESULTS) > 1 else [FIGURE_3D_EXAMPLES_WITH_RESULTS[0][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[0][1], "landscape - 1152x896 (4:3)"], + [FIGURE_3D_EXAMPLES_WITH_RESULTS[2][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[2][1], "portrait - 896x1152 (3:4)"] if len(FIGURE_3D_EXAMPLES_WITH_RESULTS) > 2 else [FIGURE_3D_EXAMPLES_WITH_RESULTS[0][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[0][1], "portrait - 896x1152 (3:4)"], + [FIGURE_3D_EXAMPLES_WITH_RESULTS[3][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[3][1], "landscape - 1344x768 (16:9)"] if len(FIGURE_3D_EXAMPLES_WITH_RESULTS) > 3 else [FIGURE_3D_EXAMPLES_WITH_RESULTS[0][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[0][1], "landscape - 1344x768 (16:9)"], + [FIGURE_3D_EXAMPLES_WITH_RESULTS[4][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[4][1], "portrait - 768x1344 (9:16)"] if len(FIGURE_3D_EXAMPLES_WITH_RESULTS) > 4 else [FIGURE_3D_EXAMPLES_WITH_RESULTS[0][0], FIGURE_3D_EXAMPLES_WITH_RESULTS[0][1], "portrait - 768x1344 (9:16)"] + ][:len(FIGURE_3D_EXAMPLES_WITH_RESULTS)] + + gr.Examples( + examples=figure_3d_examples_input_only, + inputs=[figure_3d_input, figure_3d_style, figure_3d_resolution], + outputs=[figure_3d_input, figure_3d_style, figure_3d_resolution, figure_3d_result], + fn=load_figure_3d_example, + label="🎨 3D Figure Examples - Click to preview different styles", + examples_per_page=5, + cache_examples=True + ) - with gr.Column(scale=3): - anime_to_real_result = gr.Image( - label="Anime to Real Result", - show_label=True, - show_download_button=True, - show_share_button=True + # 人物手办合影功能区域(初始隐藏) + with gr.Column(visible=False) as character_figure_collaboration_interface: + gr.Markdown("## Character Figure Collaboration") + gr.Markdown("Generate collaboration photos between characters and figures") + + with gr.Row(): + with gr.Column(scale=2): + character_figure_input = gr.Image( + label="Upload Character Full-Body Photo", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + character_figure_btn = gr.Button("Generate Collaboration Photo", variant="primary", scale=2) + character_figure_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + character_figure_status = gr.Markdown("") + + with gr.Column(scale=3): + character_figure_result = gr.Image( + label="Collaboration Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + character_figure_info = gr.Markdown(""" + ### 💡 Tips + - Upload clear full-body character photos (real person or virtual character) + - Supported formats: PNG, JPEG, JPG, WEBP + - Recommended size: 512x512 to 1536x1536 pixels + - File size: under 10MB recommended + - Processing time: typically 1–3 minutes + - The AI will generate a collaboration photo with the character and a figure + """, visible=True) + + # 添加人物手办合影的examples + if CHARACTER_FIGURE_COLLABORATION_EXAMPLES_WITH_RESULTS: # 只有当有examples时才显示 + gr.Examples( + examples=CHARACTER_FIGURE_COLLABORATION_EXAMPLES_WITH_RESULTS, + inputs=[character_figure_input], + outputs=[character_figure_input, character_figure_result], + fn=load_character_figure_collaboration_example, + label="🎨 Character Figure Collaboration Examples - Click to preview", + examples_per_page=5, + cache_examples=True ) - anime_to_real_info = gr.Markdown(""" - ### 💡 Tips - - Anime character: supports various anime/game characters - - Facial features: clearer facial features yield better conversion - - Image quality: higher-quality anime images perform better - - Character type: humanoid characters convert best - - Processing time: typically 1–3 minutes - """, visible=True) - - # 添加二次元转真人的examples - gr.Examples( - examples=ANIME_TO_REAL_EXAMPLES_WITH_RESULTS, - inputs=[anime_to_real_input], - outputs=[anime_to_real_input, anime_to_real_result], - fn=load_anime_to_real_example_result, - label="💡 Anime to Real Examples - Click to preview", - examples_per_page=1, - cache_examples=True - ) - - # 真人转动漫功能区域(初始隐藏) - with gr.Column(visible=False) as real_to_anime_interface: - gr.Markdown("## Real to Anime") - gr.Markdown("Convert real photos into anime style") - - with gr.Row(): - with gr.Column(scale=2): - real_to_anime_input = gr.Image( - label="Upload Real Photo", - type="pil", - sources=["upload", "clipboard"], - height=250 - ) + # 摄影风格转换功能区域(初始隐藏) + with gr.Column(visible=False) as photo_style_interface: + gr.Markdown("## Photo Style Transfer") + gr.Markdown("Apply professional photography styles to your photos") + + with gr.Row(): + with gr.Column(scale=2): + photo_style_input = gr.Image( + label="Upload Photo", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + photo_style_dropdown = gr.Dropdown( + label="Select Photo Style", + choices=PHOTO_STYLE_CHOICES, + value="camera_movement", + info="Choose the photography style to apply" + ) + + with gr.Row(): + photo_style_btn = gr.Button("Apply Style", variant="primary", scale=2) + photo_style_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + photo_style_status = gr.Markdown("") + + with gr.Column(scale=3): + photo_style_result = gr.Image( + label="Style Transfer Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + photo_style_info = gr.Markdown(""" + ### 💡 Tips + - Photo types: portraits, landscapes, product photos, etc. + - Recommended quality: higher resolution photos yield better results + - Style selection: choose a style that matches the photo type + - Lighting: well-lit photos convert better + - Processing time: typically 1–2 minutes + """, visible=True) + + # 室内设计渲染功能区域(初始隐藏) + with gr.Column(visible=False) as interior_interface: + gr.Markdown("## Interior Design Rendering") + gr.Markdown("Upload a white model interior image to generate design renders") + + with gr.Row(): + with gr.Column(scale=2): + interior_input = gr.Image( + label="Upload Interior White Model", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + interior_style = gr.Dropdown( + label="Select Interior Style", + choices=INTERIOR_DESIGN_STYLE_CHOICES, + value="japanese_wabi_sabi", # keep default key + info="Choose the interior design style to apply" + ) + + with gr.Row(): + interior_btn = gr.Button("Render Design", variant="primary", scale=2) + interior_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + interior_status = gr.Markdown("") + + with gr.Column(scale=3): + interior_result = gr.Image( + label="Rendered Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + interior_info = gr.Markdown(""" + ### 💡 Tips + - Input: upload a white model or line art of the interior + - Style description: describe the desired design style in detail + - Space type: living room, bedroom, kitchen, office, etc. + - Style keywords: modern minimalism, Nordic, classic Chinese, etc. + - Processing time: typically 2–5 minutes + """, visible=True) + + # 添加室内设计的examples + + interior_design_examples_input_only = [[example[0], example[1]] for example in INTERIOR_DESIGN_EXAMPLES_WITH_RESULTS] + + gr.Examples( + examples=interior_design_examples_input_only, + inputs=[interior_input, interior_style], + outputs=[interior_input, interior_result], + fn=load_interior_design_example_result, + label="💡 Interior Design Examples - Click to preview", + examples_per_page=4, + cache_examples=True + ) - with gr.Row(): - real_to_anime_btn = gr.Button("Convert to Anime", variant="primary", scale=2) - real_to_anime_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + # 水印移除功能区域(初始隐藏) + with gr.Column(visible=False) as watermark_interface: + gr.Markdown("## Watermark Removal") + gr.Markdown("Intelligently detect and remove watermarks from images") + + with gr.Row(): + with gr.Column(scale=2): + watermark_input = gr.Image( + label="Upload Image with Watermark", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + watermark_btn = gr.Button("Remove Watermark", variant="primary", scale=2) + watermark_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + watermark_status = gr.Markdown("") + + with gr.Column(scale=3): + watermark_result = gr.Image( + label="Watermark Removal Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + watermark_info = gr.Markdown(""" + ### 💡 Tips + - Watermark types: supports text and icon watermarks + - Image quality: higher quality images yield better results + - Watermark location: AI will automatically detect and remove + - Background complexity: simpler backgrounds remove better + - Processing time: typically 1–3 minutes + """, visible=True) + + # 线稿转换功能区域(初始隐藏) + with gr.Column(visible=False) as line_art_interface: + gr.Markdown("## Line Art Conversion") + gr.Markdown("Convert your photo into clean line art") + + with gr.Row(): + with gr.Column(scale=2): + line_art_input = gr.Image( + label="Upload Photo", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + line_art_btn = gr.Button("Convert to Line Art", variant="primary", scale=2) + line_art_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + line_art_status = gr.Markdown("") + + with gr.Column(scale=3): + line_art_result = gr.Image( + label="Line Art Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + line_art_info = gr.Markdown(""" + ### 💡 Tips + - Photo types: portraits, landscapes, architecture, objects + - Image clarity: higher clarity yields better line art + - Contrast: higher contrast improves line extraction + - Complexity: rich details produce better line art + - Processing time: typically 1–2 minutes + """, visible=True) + + # 添加线稿转换的examples + gr.Examples( + examples=LINE_ART_CONVERSION_EXAMPLES_WITH_RESULTS, + inputs=[line_art_input], + outputs=[line_art_input, line_art_result], + fn=load_line_art_example_result, + label="💡 Line Art Examples - Click to preview", + examples_per_page=3, + cache_examples=True + ) - real_to_anime_status = gr.Markdown("") + # 图像扩展功能区域(初始隐藏) + with gr.Column(visible=False) as expand_interface: + gr.Markdown("## Image Outpainting") + gr.Markdown("Intelligently extend image boundaries while keeping content coherent") + + with gr.Row(): + with gr.Column(scale=2): + expand_input = gr.Image( + label="Upload Image to Outpaint", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + expand_height = gr.Slider( + label="Outpaint Height (%)", + minimum=0.0, + maximum=1.0, + value=0.2, + step=0.1, + interactive=True, + info="Percentage to extend vertically" + ) + + expand_width = gr.Slider( + label="Outpaint Width (%)", + minimum=0.0, + maximum=1.0, + value=0.3, + step=0.1, + interactive=True, + info="Percentage to extend horizontally" + ) + + with gr.Row(): + expand_btn = gr.Button("Outpaint Image", variant="primary", scale=2) + expand_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + expand_status = gr.Markdown("") + + with gr.Column(scale=3): + expand_result = gr.Image( + label="Outpainting Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + expand_info = gr.Markdown(""" + ### 💡 Tips + - Direction: choose which sides to extend (top/bottom/left/right) + - Pixel amount: 64–512 pixels recommended; too large may impact quality + - Edges: richer edge content yields more natural results + - Coherence: AI will fill in content consistent with the original + - Processing time: typically 2–4 minutes + """, visible=True) + + # 图像扩展Examples - 修复inputs/outputs匹配问题 + gr.Examples( + examples=IMAGE_OUTPAINTING_EXAMPLES_WITH_RESULTS, + inputs=[expand_input, expand_height, expand_width, expand_result], + outputs=[expand_input, expand_height, expand_width, expand_result], + fn=load_outpainting_example_for_gradio, + label="💡 Outpainting Examples - Click to preview", + examples_per_page=3, + cache_examples=False # Disable caching to avoid serialization issues + ) - with gr.Column(scale=3): - real_to_anime_result = gr.Image( - label="Real to Anime Result", - show_label=True, - show_download_button=True, - show_share_button=True - ) + # 二次元转真人功能区域(初始隐藏) + with gr.Column(visible=False) as anime_to_real_interface: + gr.Markdown("## Anime to Real") + gr.Markdown("Convert anime characters to realistic humans") + + with gr.Row(): + with gr.Column(scale=2): + anime_to_real_input = gr.Image( + label="Upload Anime Character", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + anime_to_real_btn = gr.Button("Convert to Real", variant="primary", scale=2) + anime_to_real_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + anime_to_real_status = gr.Markdown("") + + with gr.Column(scale=3): + anime_to_real_result = gr.Image( + label="Anime to Real Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + anime_to_real_info = gr.Markdown(""" + ### 💡 Tips + - Anime character: supports various anime/game characters + - Facial features: clearer facial features yield better conversion + - Image quality: higher-quality anime images perform better + - Character type: humanoid characters convert best + - Processing time: typically 1–3 minutes + """, visible=True) + + # 添加二次元转真人的examples + gr.Examples( + examples=ANIME_TO_REAL_EXAMPLES_WITH_RESULTS, + inputs=[anime_to_real_input], + outputs=[anime_to_real_input, anime_to_real_result], + fn=load_anime_to_real_example_result, + label="💡 Anime to Real Examples - Click to preview", + examples_per_page=1, + cache_examples=True + ) - real_to_anime_info = gr.Markdown(""" - ### 💡 Tips - - Real photo: upload a clear portrait - - Lighting: evenly lit photos work better - - Pose: front or side portraits convert best - - Background: simple backgrounds help the subject stand out - - Processing time: typically 1–3 minutes - """, visible=True) - - # 添加真人转动漫的examples - gr.Examples( - examples=REAL_TO_ANIME_EXAMPLES_WITH_RESULTS, - inputs=[real_to_anime_input], - outputs=[real_to_anime_input, real_to_anime_result], - fn=load_real_to_anime_example_result, - label="💡 Real to Anime Examples - Click to preview", - examples_per_page=3, - cache_examples=True - ) + # 真人转动漫功能区域(初始隐藏) + with gr.Column(visible=False) as real_to_anime_interface: + gr.Markdown("## Real to Anime") + gr.Markdown("Convert real photos into anime style") + + with gr.Row(): + with gr.Column(scale=2): + real_to_anime_input = gr.Image( + label="Upload Real Photo", + type="pil", + sources=["upload", "clipboard"], + height=250 + ) + + with gr.Row(): + real_to_anime_btn = gr.Button("Convert to Anime", variant="primary", scale=2) + real_to_anime_cancel_btn = gr.Button("Cancel", variant="secondary", scale=1, visible=False) + + real_to_anime_status = gr.Markdown("") + + with gr.Column(scale=3): + real_to_anime_result = gr.Image( + label="Real to Anime Result", + show_label=True, + show_download_button=True, + show_share_button=True + ) + + real_to_anime_info = gr.Markdown(""" + ### 💡 Tips + - Real photo: upload a clear portrait + - Lighting: evenly lit photos work better + - Pose: front or side portraits convert best + - Background: simple backgrounds help the subject stand out + - Processing time: typically 1–3 minutes + """, visible=True) + + # 添加真人转动漫的examples + gr.Examples( + examples=REAL_TO_ANIME_EXAMPLES_WITH_RESULTS, + inputs=[real_to_anime_input], + outputs=[real_to_anime_input, real_to_anime_result], + fn=load_real_to_anime_example_result, + label="💡 Real to Anime Examples - Click to preview", + examples_per_page=3, + cache_examples=True + ) # Bind events to sidebar buttons within the Blocks context # pylint: disable=no-member + # Define all interfaces in consistent order for interface switching + all_interface_outputs = [ + welcome_content, # 0 + dynamic_content, # 1 + text_to_image_interface, # 2 + image_convert_interface, # 3 + five_view_interface, # 4 + figure_3d_interface, # 5 + character_figure_collaboration_interface, # 6 + photo_style_interface, # 7 + interior_interface, # 8 + watermark_interface, # 9 + line_art_interface, # 10 + expand_interface, # 11 + anime_to_real_interface, # 12 + real_to_anime_interface # 13 + ] + # Helper function to show specific interface def show_interface(interface_index): """Show specific interface by index, hide all others""" updates = [] - for i in range(12): # Total 12 interfaces (matching outputs count) + for i in range(len(all_interface_outputs)): if i == interface_index: updates.append(gr.update(visible=True)) else: @@ -2051,9 +2382,7 @@ def create_main_interface(): # Text-to-image events text_to_image_btn.click( fn=lambda: show_interface(2), # text_to_image_interface is at index 2 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + outputs=all_interface_outputs ) generate_btn.click( @@ -2072,9 +2401,7 @@ def create_main_interface(): # Image convert events image_convert_btn.click( fn=lambda: show_interface(3), # image_convert_interface is at index 3 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + outputs=all_interface_outputs ) convert_btn.click( @@ -2093,11 +2420,11 @@ def create_main_interface(): # Five view events five_view_btn_sidebar.click( fn=lambda: show_interface(4), # five_view_interface is at index 4 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + outputs=all_interface_outputs ) + + five_view_btn.click( fn=generate_five_view, inputs=[five_view_input], @@ -2111,12 +2438,48 @@ def create_main_interface(): queue=False ) + # Figure 3D events + figure_3d_btn_sidebar.click( + fn=lambda: show_interface(5), # figure_3d_interface is at index 5 + outputs=all_interface_outputs + ) + + figure_3d_btn.click( + fn=generate_figure_3d, + inputs=[figure_3d_input, figure_3d_style, figure_3d_resolution], + outputs=[figure_3d_result, figure_3d_status, figure_3d_info, figure_3d_btn, figure_3d_cancel_btn], + show_progress=True + ) + + figure_3d_cancel_btn.click( + fn=cancel_current_task, + outputs=[figure_3d_btn, figure_3d_cancel_btn, figure_3d_status], + queue=False + ) + + # Character Figure Collaboration events + character_figure_btn_sidebar.click( + fn=lambda: show_interface(6), # character_figure_collaboration_interface is at index 6 + outputs=all_interface_outputs + ) + + character_figure_btn.click( + fn=generate_character_figure_collaboration, + inputs=[character_figure_input], + outputs=[character_figure_result, character_figure_status, character_figure_info, character_figure_btn, character_figure_cancel_btn], + show_progress=True + ) + + character_figure_cancel_btn.click( + fn=cancel_current_task, + outputs=[character_figure_btn, character_figure_cancel_btn, character_figure_status], + queue=False + ) + # Photo style events photo_style_btn_sidebar.click( - fn=lambda: show_interface(5), # photo_style_interface is at index 5 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(7), # photo_style_interface is at index 7 + outputs=all_interface_outputs ) photo_style_btn.click( @@ -2133,10 +2496,8 @@ def create_main_interface(): ) # Interior design events interior_btn_sidebar.click( - fn=lambda: show_interface(6), # interior_interface is at index 6 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(8), # interior_interface is at index 8 + outputs=all_interface_outputs ) interior_btn.click( @@ -2154,10 +2515,8 @@ def create_main_interface(): # Watermark removal events watermark_btn_sidebar.click( - fn=lambda: show_interface(7), # watermark_interface is at index 7 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(9), # watermark_interface is at index 9 + outputs=all_interface_outputs ) watermark_btn.click( @@ -2175,10 +2534,8 @@ def create_main_interface(): # Line art events line_art_btn_sidebar.click( - fn=lambda: show_interface(8), # line_art_interface is at index 8 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(10), # line_art_interface is at index 10 + outputs=all_interface_outputs ) line_art_btn.click( @@ -2196,10 +2553,8 @@ def create_main_interface(): # Expand events expand_btn_sidebar.click( - fn=lambda: show_interface(9), # expand_interface is at index 9 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(11), # expand_interface is at index 11 + outputs=all_interface_outputs ) expand_btn.click( @@ -2217,10 +2572,8 @@ def create_main_interface(): # Anime to real events anime_to_real_btn_sidebar.click( - fn=lambda: show_interface(10), # anime_to_real_interface is at index 10 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(12), # anime_to_real_interface is at index 12 + outputs=all_interface_outputs ) anime_to_real_btn.click( @@ -2241,10 +2594,8 @@ def create_main_interface(): # Real to anime events real_to_anime_btn_sidebar.click( - fn=lambda: show_interface(11), # real_to_anime_interface is at index 11 - outputs=[welcome_content, dynamic_content, text_to_image_interface, image_convert_interface, - five_view_interface, photo_style_interface, interior_interface, watermark_interface, - line_art_interface, expand_interface, anime_to_real_interface, real_to_anime_interface] + fn=lambda: show_interface(13), # real_to_anime_interface is at index 13 + outputs=all_interface_outputs ) real_to_anime_btn.click( @@ -2271,6 +2622,9 @@ def create_main_interface(): 'text_to_image_btn': text_to_image_btn, 'image_convert_btn': image_convert_btn, 'five_view_btn_sidebar': five_view_btn_sidebar, + 'figure_3d_btn_sidebar': figure_3d_btn_sidebar, + 'character_figure_btn_sidebar': character_figure_btn_sidebar, + 'photo_style_btn_sidebar': photo_style_btn_sidebar, 'interior_btn_sidebar': interior_btn_sidebar, 'watermark_btn_sidebar': watermark_btn_sidebar, @@ -2287,6 +2641,9 @@ def create_main_interface(): 'text_to_image_interface': text_to_image_interface, 'image_convert_interface': image_convert_interface, 'five_view_interface': five_view_interface, + 'figure_3d_interface': figure_3d_interface, + 'character_figure_collaboration_interface': character_figure_collaboration_interface, + 'photo_style_interface': photo_style_interface, 'interior_interface': interior_interface, 'watermark_interface': watermark_interface, @@ -2386,7 +2743,7 @@ demo, components = create_main_interface() # ========================================================================= -# Security hardening: block internal settings routes at the server level +# Security Configuration # ========================================================================= try: from fastapi import Request