Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| from PIL import Image | |
| import requests | |
| from io import BytesIO | |
| import base64 | |
| def download_image(url): | |
| """从URL下载图像""" | |
| response = requests.get(url) | |
| response.raise_for_status() | |
| return Image.open(BytesIO(response.content)).convert("RGB") | |
| def downsample_image(image, target_size=(1024, 576), sharpen=True): | |
| """ | |
| 将高分辨率图像降采样至目标大小 | |
| 参数: | |
| image: PIL图像对象 | |
| target_size: 目标分辨率元组 (宽度, 高度) | |
| sharpen: 是否应用锐化 | |
| 返回: | |
| 降采样后的PIL图像对象 | |
| """ | |
| # 将PIL图像转换为cv2格式 | |
| cv2_image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) | |
| # 获取原始图像尺寸 | |
| original_height, original_width = cv2_image.shape[:2] | |
| print(f"原始图像尺寸: {original_width}x{original_height}") | |
| # 计算目标宽高比 | |
| target_width, target_height = target_size | |
| target_ratio = target_width / target_height | |
| # 计算原始图像尺寸,保持宽高比 | |
| original_ratio = original_width / original_height | |
| if original_ratio > target_ratio: | |
| # 原始图像更宽,按宽度缩放 | |
| new_width = target_width | |
| new_height = int(new_width / original_ratio) | |
| else: | |
| # 原始图像更高,按高度缩放 | |
| new_height = target_height | |
| new_width = int(new_height * original_ratio) | |
| print(f"调整后尺寸: {new_width}x{new_height}") | |
| # 应用高斯模糊减少锯齿 | |
| blurred = cv2.GaussianBlur(cv2_image, (3, 3), 0) | |
| # 使用双三次插值降采样 | |
| downsampled = cv2.resize(blurred, (new_width, new_height), interpolation=cv2.INTER_CUBIC) | |
| # 如果需要,应用锐化增强 | |
| if sharpen: | |
| kernel = np.array([[-1, -1, -1], | |
| [-1, 9, -1], | |
| [-1, -1, -1]]) | |
| downsampled = cv2.filter2D(downsampled, -1, kernel) | |
| # 将cv2图像转换回PIL格式 | |
| result_image = Image.fromarray(cv2.cvtColor(downsampled, cv2.COLOR_BGR2RGB)) | |
| return result_image | |
| def process_image(image, sharpen=True): | |
| """处理上传的图像,返回降采样结果""" | |
| if image is None: | |
| return None, None, "请提供图像" | |
| try: | |
| # 获取原始图像信息 | |
| original_width, original_height = image.size | |
| original_size = f"{original_width}x{original_height}" | |
| # 降采样图像 | |
| downsampled_image = downsample_image(image, sharpen=sharpen) | |
| # 获取降采样后图像信息 | |
| downsampled_width, downsampled_height = downsampled_image.size | |
| downsampled_size = f"{downsampled_width}x{downsampled_height}" | |
| # 计算压缩率 | |
| original_pixels = original_width * original_height | |
| downsampled_pixels = downsampled_width * downsampled_height | |
| compression_ratio = original_pixels / downsampled_pixels | |
| status = f"原始尺寸: {original_size} → 降采样后: {downsampled_size} (压缩率: {compression_ratio:.1f}x)" | |
| return image, downsampled_image, status | |
| except Exception as e: | |
| return None, None, f"处理失败: {str(e)}" | |
| def process_image_url(url, sharpen=True): | |
| """处理图像URL""" | |
| if not url: | |
| return None, None, "请提供图像URL" | |
| try: | |
| # 下载图像 | |
| image = download_image(url) | |
| # 调用处理函数 | |
| return process_image(image, sharpen=sharpen) | |
| except Exception as e: | |
| return None, None, f"处理失败: {str(e)}" | |
| def create_comparison_image(original, downsampled): | |
| """创建对比图像,并排显示原始图像和降采样图像""" | |
| if original is None or downsampled is None: | |
| return None | |
| # 获取图像尺寸 | |
| original_width, original_height = original.size | |
| downsampled_width, downsampled_height = downsampled.size | |
| # 计算目标高度,取较小值 | |
| target_height = min(original_height, downsampled_height) | |
| # 按比例缩放图像 | |
| original_scaled = original.resize((int(original_width * target_height / original_height), target_height)) | |
| downsampled_scaled = downsampled.resize((int(downsampled_width * target_height / downsampled_height), target_height)) | |
| # 创建新图像,并排显示 | |
| comparison_width = original_scaled.width + downsampled_scaled.width | |
| comparison_height = target_height | |
| comparison = Image.new('RGB', (comparison_width, comparison_height)) | |
| comparison.paste(original_scaled, (0, 0)) | |
| comparison.paste(downsampled_scaled, (original_scaled.width, 0)) | |
| return comparison | |
| def main(): | |
| # 示例图像URL | |
| example_images = [ | |
| "https://p26-doubao-search-sign.byteimg.com/labis/37b5a464e50688032736bf0c132848ce~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1764660945&x-signature=e6oLB%2F8eHQeohzwBZRZSsTtJw8Y%3D", | |
| "https://p11-doubao-search-sign.byteimg.com/tos-cn-i-qvj2lq49k0/d0aa7a30b66a4c55a783d440e15cdb52~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1764660945&x-signature=EB1%2F4QQ1nJr1U1CwV7Qw%2FoJDzwY%3D", | |
| "https://p11-doubao-search-sign.byteimg.com/labis/4ff8314a923313d6f18fbf0355993f21~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1764660945&x-signature=a%2FihmjuTBzmmRTMsBZLaTQfhvtA%3D", | |
| "https://p26-doubao-search-sign.byteimg.com/labis/4279cfae8ecb40a269dccce03ae30c48~tplv-be4g95zd3a-image.jpeg?rk3s=542c0f93&x-expires=1764660945&x-signature=kVB4Wif5J7a3eMW8VyQn7dV4arE%3D" | |
| ] | |
| # 创建Gradio应用 | |
| with gr.Blocks(title="Resample Pro - 高质量图像降采样", | |
| theme=gr.themes.Soft( | |
| primary_hue="green", | |
| secondary_hue="blue", | |
| neutral_hue="slate", | |
| font=["Inter", "system-ui", "sans-serif"], | |
| spacing_size="md", | |
| radius_size="md" | |
| )) as demo: | |
| # 自定义CSS | |
| gr.Markdown(""" | |
| <style> | |
| .gradio-container { | |
| background-color: #121212; | |
| color: #ffffff; | |
| } | |
| .gradio-title { | |
| font-weight: 700; | |
| margin-bottom: 0.5rem; | |
| } | |
| .gradio-subtitle { | |
| font-weight: 400; | |
| color: #9ca3af; | |
| margin-bottom: 1rem; | |
| } | |
| .image-container { | |
| position: relative; | |
| overflow: hidden; | |
| border-radius: 0.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| .image-container img { | |
| transition: transform 0.3s ease; | |
| } | |
| .image-container:hover img { | |
| transform: scale(1.02); | |
| } | |
| </style> | |
| """) | |
| # 标题和说明 | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Image("https://p3-flow-imagex-sign.byteimg.com/tos-cn-i-a9rns2rl98/rc/pc/super_tool/60c651b800994851bbdc482bc7745d82~tplv-a9rns2rl98-image.image?rcl=2025100315350927166B93AC1D320B5E7B&rk3s=8e244e95&rrcfp=f06b921b&x-expires=1762068959&x-signature=3nOCcuHdW5SKib3%2FE00MV7voRdc%3D", | |
| width=100, height=100, show_label=False) | |
| with gr.Column(scale=3): | |
| gr.Markdown("<h1 class='gradio-title'>Resample Pro</h1>") | |
| gr.Markdown("<p class='gradio-subtitle'>高质量图像降采样工具 - 将4K图像高效转换为1K,同时保留细节和质量</p>") | |
| gr.Markdown("---") | |
| # 输入区域 | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("<h3>上传图像</h3>") | |
| image_input = gr.Image(type="pil", label="拖放或点击上传", height=300) | |
| gr.Markdown("<h3>或输入图像URL</h3>") | |
| url_input = gr.Textbox(label="图像URL") | |
| with gr.Row(): | |
| sharpen_toggle = gr.Checkbox(label="启用智能锐化", value=True) | |
| process_button = gr.Button("处理图像", variant="primary") | |
| with gr.Column(scale=1): | |
| gr.Markdown("<h3>示例图像</h3>") | |
| example_gallery = gr.Gallery( | |
| value=example_images, | |
| label="点击使用示例图像", | |
| columns=2, | |
| rows=2, | |
| height=300, | |
| object_fit="cover" | |
| ) | |
| gr.Markdown("---") | |
| # 结果区域 | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("<h3>原始图像</h3>") | |
| original_output = gr.Image(label="原始图像", height=300, interactive=False) | |
| with gr.Column(scale=1): | |
| gr.Markdown("<h3>降采样结果</h3>") | |
| result_output = gr.Image(label="降采样后图像", height=300, interactive=False) | |
| # 状态和下载 | |
| with gr.Row(): | |
| status_output = gr.Textbox(label="处理状态", interactive=False) | |
| download_button = gr.Button("下载结果", variant="secondary") | |
| # 细节对比 | |
| gr.Markdown("<h3>细节对比</h3>") | |
| comparison_output = gr.Image(label="对比视图", height=400, interactive=False) | |
| # 事件处理 | |
| def handle_example_click(image_url): | |
| return image_url, "", None, None, "准备处理示例图像..." | |
| example_gallery.select( | |
| fn=handle_example_click, | |
| inputs=[example_gallery], | |
| outputs=[url_input, image_input, original_output, result_output, status_output] | |
| ) | |
| def handle_process(image, url, sharpen): | |
| if image is not None: | |
| return process_image(image, sharpen) | |
| elif url: | |
| return process_image_url(url, sharpen) | |
| else: | |
| return None, None, "请上传图像或提供URL" | |
| process_button.click( | |
| fn=handle_process, | |
| inputs=[image_input, url_input, sharpen_toggle], | |
| outputs=[original_output, result_output, status_output] | |
| ).then( | |
| fn=create_comparison_image, | |
| inputs=[original_output, result_output], | |
| outputs=[comparison_output] | |
| ) | |
| def handle_download(result_image): | |
| if result_image is None: | |
| return None, "没有可下载的图像" | |
| # 将图像转换为BytesIO | |
| buffered = BytesIO() | |
| result_image.save(buffered, format="PNG") | |
| buffered.seek(0) | |
| # 返回图像数据和文件名 | |
| return buffered, "图像已准备好下载" | |
| download_button.click( | |
| fn=handle_download, | |
| inputs=[result_output], | |
| outputs=[gr.File(label="下载图像", type="binary"), status_output] | |
| ) | |
| # 页面加载时的欢迎信息 | |
| gr.Markdown("<p class='gradio-subtitle'>欢迎使用Resample Pro!上传一张高分辨率图像或使用示例图像开始体验。</p>") | |
| # 启动应用 | |
| demo.launch(share=True, server_name="0.0.0.0") | |
| if __name__ == "__main__": | |
| main() | |