Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import numpy as np | |
| from PIL import Image | |
| import io | |
| import tempfile | |
| import os | |
| def resize_images_to_match(images, resize_mode="height"): | |
| if not images or len(images) == 0: | |
| return [] | |
| valid_images = [img for img in images if img is not None] | |
| if not valid_images: | |
| return [] | |
| if resize_mode == "height": | |
| target_height = min(img.size[1] for img in valid_images) | |
| resized_images = [] | |
| for img in valid_images: | |
| aspect_ratio = img.size[0] / img.size[1] | |
| new_width = int(target_height * aspect_ratio) | |
| resized_img = img.resize((new_width, target_height), Image.Resampling.LANCZOS) | |
| resized_images.append(resized_img) | |
| else: | |
| target_width = min(img.size[0] for img in valid_images) | |
| resized_images = [] | |
| for img in valid_images: | |
| aspect_ratio = img.size[1] / img.size[0] | |
| new_height = int(target_width * aspect_ratio) | |
| resized_img = img.resize((target_width, new_height), Image.Resampling.LANCZOS) | |
| resized_images.append(resized_img) | |
| return resized_images | |
| def concatenate_images(images, direction="horizontal", spacing=0, background_color="white"): | |
| if not images or len(images) == 0: | |
| return None | |
| valid_images = [img for img in images if img is not None] | |
| if not valid_images: | |
| return None | |
| rgb_images = [] | |
| for img in valid_images: | |
| if img.mode != 'RGB': | |
| if img.mode in ('RGBA', 'LA'): | |
| background = Image.new('RGB', img.size, 'white') | |
| background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None) | |
| rgb_images.append(background) | |
| else: | |
| rgb_images.append(img.convert('RGB')) | |
| else: | |
| rgb_images.append(img) | |
| if len(rgb_images) == 1: | |
| return rgb_images[0] | |
| if direction == "horizontal": | |
| total_width = sum(img.size[0] for img in rgb_images) + spacing * (len(rgb_images) - 1) | |
| max_height = max(img.size[1] for img in rgb_images) | |
| result = Image.new('RGB', (total_width, max_height), background_color) | |
| x_offset = 0 | |
| for img in rgb_images: | |
| y_offset = (max_height - img.size[1]) // 2 | |
| result.paste(img, (x_offset, y_offset)) | |
| x_offset += img.size[0] + spacing | |
| else: | |
| max_width = max(img.size[0] for img in rgb_images) | |
| total_height = sum(img.size[1] for img in rgb_images) + spacing * (len(rgb_images) - 1) | |
| result = Image.new('RGB', (max_width, total_height), background_color) | |
| y_offset = 0 | |
| for img in rgb_images: | |
| x_offset = (max_width - img.size[0]) // 2 | |
| result.paste(img, (x_offset, y_offset)) | |
| y_offset += img.size[1] + spacing | |
| return result | |
| def save_as_jpg(image, quality=95): | |
| if image is None: | |
| return None | |
| if image.mode != 'RGB': | |
| if image.mode in ('RGBA', 'LA'): | |
| background = Image.new('RGB', image.size, 'white') | |
| background.paste(image, mask=image.split()[-1]) | |
| image = background | |
| else: | |
| image = image.convert('RGB') | |
| buffer = io.BytesIO() | |
| image.save(buffer, format='JPEG', quality=quality, optimize=True) | |
| buffer.seek(0) | |
| return Image.open(buffer) | |
| def process_images( | |
| image1, image2, image3, image4, image5, image6, | |
| resize_mode, concat_direction, spacing, bg_color, jpg_quality | |
| ): | |
| uploaded_images = [image1, image2, image3, image4, image5, image6] | |
| valid_images = [img for img in uploaded_images if img is not None] | |
| if len(valid_images) == 0: | |
| return None, "❌ 請至少上傳一張圖片", None | |
| if len(valid_images) == 1: | |
| return valid_images[0], f"✅ 只有一張圖片,無需拼接", None | |
| try: | |
| if resize_mode == "等高度 (水平拼接)": | |
| resized_images = resize_images_to_match(valid_images, "height") | |
| direction = "horizontal" | |
| else: | |
| resized_images = resize_images_to_match(valid_images, "width") | |
| direction = "vertical" | |
| if concat_direction != "自動 (根據調整模式)": | |
| direction = "horizontal" if concat_direction == "水平拼接" else "vertical" | |
| result = concatenate_images(resized_images, direction, spacing, bg_color) | |
| if result is None: | |
| return None, "❌ 拼接失敗", None | |
| result = save_as_jpg(result, quality=jpg_quality) | |
| temp_path = os.path.join(tempfile.gettempdir(), "result.jpg") | |
| result.save(temp_path, format="JPEG", quality=jpg_quality) | |
| info = f"✅ 成功拼接 {len(valid_images)} 張圖片\n" | |
| info += f"📐 最終尺寸: {result.size[0]} x {result.size[1]} 像素\n" | |
| info += f"🔄 調整模式: {resize_mode}\n" | |
| info += f"➡️ 拼接方向: {'水平' if direction == 'horizontal' else '垂直'}\n" | |
| info += f"📏 間距: {spacing} 像素\n" | |
| info += f"💾 輸出品質: {jpg_quality}%" | |
| return result, info, temp_path | |
| except Exception as e: | |
| return None, f"❌ 處理過程中出現錯誤: {str(e)}", None | |
| def create_interface(): | |
| with gr.Blocks(title="圖片拼接比較工具", theme=gr.themes.Soft()) as iface: | |
| gr.Markdown("# 🖼️ 圖片拼接比較工具") | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 📁 上傳圖片") | |
| with gr.Row(): | |
| image1 = gr.Image(type="pil", label="圖片 1") | |
| image2 = gr.Image(type="pil", label="圖片 2") | |
| image3 = gr.Image(type="pil", label="圖片 3") | |
| with gr.Row(): | |
| image4 = gr.Image(type="pil", label="圖片 4") | |
| image5 = gr.Image(type="pil", label="圖片 5") | |
| image6 = gr.Image(type="pil", label="圖片 6") | |
| gr.Markdown("### ⚙️ 處理設定") | |
| with gr.Row(): | |
| resize_mode = gr.Dropdown( | |
| choices=["等高度 (水平拼接)", "等寬度 (垂直拼接)"], | |
| value="等高度 (水平拼接)", | |
| label="尺寸調整模式" | |
| ) | |
| concat_direction = gr.Dropdown( | |
| choices=["自動 (根據調整模式)", "水平拼接", "垂直拼接"], | |
| value="自動 (根據調整模式)", | |
| label="拼接方向" | |
| ) | |
| with gr.Row(): | |
| spacing = gr.Slider(0, 50, value=2, step=1, label="圖片間距 (像素)") | |
| bg_color = gr.Dropdown( | |
| choices=["white", "black", "gray"], | |
| value="white", | |
| label="背景顏色" | |
| ) | |
| jpg_quality = gr.Slider(60, 100, value=95, step=5, label="JPG 輸出品質 (%)") | |
| process_btn = gr.Button("🚀 開始處理", variant="primary", size="lg") | |
| with gr.Column(scale=2): | |
| gr.Markdown("### 🎯 處理結果") | |
| result_image = gr.Image(type="pil", label="拼接結果") | |
| result_info = gr.Textbox(label="處理信息", lines=6, max_lines=10) | |
| result_file = gr.File(label="下載 JPG 檔案") | |
| process_btn.click( | |
| fn=process_images, | |
| inputs=[image1, image2, image3, image4, image5, image6, | |
| resize_mode, concat_direction, spacing, bg_color, jpg_quality], | |
| outputs=[result_image, result_info, result_file] | |
| ) | |
| return iface | |
| if __name__ == "__main__": | |
| app = create_interface() | |
| app.launch(server_name="0.0.0.0", server_port=7860, share=True) | |