jiangcaiyang / app.py
wkplhc's picture
Update app.py
cdc9b81 verified
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()