Python图片处理:基于Gradio构建启动后在浏览器打开交互界面,支持上传图片、自由拖拽4个顶点实现任意角度拉伸压缩、并添加文字
完整代码(保存为 image_warp.py 并运行)
```python
import gradio as gr
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
# 核心处理函数
def warp_image(image,
tl_x, tl_y, # 左上角偏移
tr_x, tr_y, # 右上角偏移
br_x, br_y, # 右下角偏移
bl_x, bl_y, # 左下角偏移
text, # 要添加的文字
text_x, text_y, # 文字位置
font_size,
font_color,
bg_color):
h, w = image.shape[:2]
# 原始四个角点
pts_src = np.float32([[0, 0], [w, 0], [w, h], [0, h]])
# 目标四个角点(用户通过滑块控制偏移)
pts_dst = np.float32([
[0 + tl_x, 0 + tl_y],
[w + tr_x, 0 + tr_y],
[w + br_x, h + br_y],
[0 + bl_x, h + bl_y]
])
# 计算透视变换矩阵
M = cv2.getPerspectiveTransform(pts_src, pts_dst)
# 计算变换后完整图像的边界框,避免裁剪
all_pts = pts_dst.copy()
min_x = min(all_pts[:, 0])
max_x = max(all_pts[:, 0])
min_y = min(all_pts[:, 1])
max_y = max(all_pts[:, 1])
# 保证最小尺寸,防止矩阵报错
out_w = int(max_x - min_x) + 20
out_h = int(max_y - min_y) + 20
if out_w < 10 or out_h < 10:
out_w, out_h = w, h
# 平移矩阵,让整个变形后的画面显示在画布中央
T = np.array([[1, 0, -min_x + 10], [0, 1, -min_y + 10], [0, 0, 1]], dtype=np.float32)
M = T @ M
# 执行透视变形
warped = cv2.warpPerspective(image, M, (out_w, out_h))
# ----- 添加文字(PIL支持中文,若系统无中文字体则回退默认)-----
pil_img = Image.fromarray(cv2.cvtColor(warped, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_img)
try:
# 尝试加载系统字体(Windows用simhei,Mac用PingFang,Linux用arial)
font = ImageFont.truetype("simhei.ttf", int(font_size))
except:
try:
font = ImageFont.truetype("PingFang.ttc", int(font_size))
except:
font = ImageFont.load_default()
# 解析颜色 (#RRGGBB格式)
def hex_to_rgb(hex_str):
hex_str = hex_str.lstrip('#')
return tuple(int(hex_str[i:i+2], 16) for i in (0, 2, 4))
f_color = hex_to_rgb(font_color)
b_color = hex_to_rgb(bg_color) if bg_color else None
# 若设置了背景色,先绘制文字背景框
if b_color:
bbox = draw.textbbox((text_x, text_y), text, font=font)
draw.rectangle(bbox, fill=b_color)
# 绘制文字
draw.text((text_x, text_y), text, font=font, fill=f_color)
# 转回OpenCV格式返回
result = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
return result
# ----- 构建 Gradio 界面 -----
with gr.Blocks(title="图片任意角度拉伸压缩+添加文字", theme=gr.themes.Soft()) as demo:
gr.Markdown("## 上传图片,拖拽滑块控制四个角,实现任意角度拉伸/压缩,并添加文字")
with gr.Row():
with gr.Column(scale=1):
input_img = gr.Image(label=" 上传图片", type="numpy", height=300)
gr.Markdown("### 顶点偏移控制(正负值均可)")
with gr.Row():
tl_x = gr.Slider(-300, 300, value=0, label="左上 X")
tl_y = gr.Slider(-300, 300, value=0, label="左上 Y")
with gr.Row():
tr_x = gr.Slider(-300, 300, value=0, label="右上 X")
tr_y = gr.Slider(-300, 300, value=0, label="右上 Y")
with gr.Row():
br_x = gr.Slider(-300, 300, value=0, label="右下 X")
br_y = gr.Slider(-300, 300, value=0, label="右下 Y")
with gr.Row():
bl_x = gr.Slider(-300, 300, value=0, label="左下 X")
bl_y = gr.Slider(-300, 300, value=0, label="左下 Y")
gr.Markdown("### 文字设置")
text_input = gr.Textbox(value="你好,世界!", label="文字内容")
with gr.Row():
text_x = gr.Slider(0, 800, value=50, label="文字 X 位置")
text_y = gr.Slider(0, 600, value=50, label="文字 Y 位置")
with gr.Row():
font_size = gr.Slider(10, 100, value=40, label="字号")
font_color = gr.Textbox(value="#FF0000", label="字体颜色 (如 #FF0000)")
bg_color = gr.Textbox(value="#000000", label="背景颜色 (留空则透明)")
btn = gr.Button("🚀 执行变形并添加文字", variant="primary")
with gr.Column(scale=1):
output_img = gr.Image(label="📥 处理结果", type="numpy", height=500)
# 绑定按钮事件
btn.click(
fn=warp_image,
inputs=[input_img, tl_x, tl_y, tr_x, tr_y, br_x, br_y, bl_x, bl_y,
text_input, text_x, text_y, font_size, font_color, bg_color],
outputs=output_img
)
# 示例提示
gr.Markdown("""
### 💡 使用技巧
- 拖动四个角的 **X/Y 偏移滑块**,画面会像纸张一样被拉伸或压缩,实现透视变形。
- 支持 **负值**(向内收缩)和 **正值**(向外扩张)。
- 文字颜色和背景色请使用 **#RRGGBB** 格式(例如 `#FF0000` 为红色)。
""")
if __name__ == "__main__":
demo.launch()
```
---
运行方法
1. 安装依赖(打开终端或命令提示符):
```bash
pip install gradio opencv-python pillow numpy
```
2. 运行程序:
```bash
python image_warp.py
```
3. 使用:
· 终端会显示本地访问地址(通常是 http://127.0.0.1:7860),用浏览器打开。
· 上传图片 → 拖动8个滑块控制4个顶点 → 填写文字 → 点击按钮,即可实时看到变形+文字效果。
功能说明
· 任意角度拉伸/压缩:通过独立控制四个顶点的偏移量,可实现透视、扭曲、缩放等所有变形效果。
· 添加文字:支持中英文,可调字号、颜色,并可设置文字背景色(或透明)。
· 画面自适应:变形后画面自动扩展画布,不会裁剪超出部分。
如果调整滑块的取值范围(当前为 -300 ~ 300),修改代码中 Slider 的 minimum 和 maximum 参数即可(如改为 -500 ~ 500)。
