吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 94|回复: 6
上一主题 下一主题
收起左侧

[资源求助] 求图片批量加边框软件

[复制链接]
跳转到指定楼层
楼主
wanghao422326 发表于 2026-6-10 10:51 回帖奖励
30吾爱币
求图片批量加边框软件

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
lzy8108 发表于 2026-6-10 13:45
WordPicture V2022.7 (Word批量图片加边框插件)
https://www.52pojie.cn/thread-1668620-1-1.html
(出处: 吾爱破解论坛)
【光影魔术手】可在边框---自定义扩边中任意调节边框大小
https://pan.baidu.com/s/1rBNxfm6TvL2clnuoPnMIGQ?pwd=6969
3#
lzy8108 发表于 2026-6-10 14:04
大更新_摄影老司机_给照片加边框工具
https://www.52pojie.cn/thread-2107541-1-1.html
(出处: 吾爱破解论坛)
这个您也看下,帖子里面的链接已经被楼主删除了,如果感觉是您需要的,我可以分享给您
4#
lzy8108 发表于 2026-6-10 14:07
上面的那些都是电脑端的,下面这个是移动(手机)端的(因为不知道您是在电脑还是手机上用):
光影边框APP v3.1.5
https://www.52pojie.cn/thread-2033894-1-1.html
(出处: 吾爱破解论坛)
5#
my52pojie110 发表于 2026-6-10 14:58
推荐photoshop,只要加一个,做成指令,就可以批处理
6#
shzjz123 发表于 2026-6-10 15:58
[Python] 纯文本查看 复制代码
#!/usr/bin/env python3
"""
图片批量加边框工具 - 图形界面版
支持批量处理图片,可调节边框宽度、颜色、圆角半径、阴影等,美观易用。
"""

import os
import sys
import threading
from pathlib import Path
from tkinter import *
from tkinter import filedialog, messagebox, ttk, colorchooser

try:
    from PIL import Image, ImageDraw, ImageFilter, ImageColor, ImageOps
except ImportError:
    messagebox.showerror("缺少依赖", "请先安装 Pillow 库:pip install Pillow")
    sys.exit(1)


class BorderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("图片批量加边框工具")
        self.root.geometry("620x780")
        self.root.resizable(True, True)
        
        # 变量定义
        self.input_dir = StringVar()
        self.output_dir = StringVar()
        self.border_width = IntVar(value=30)
        self.border_color_hex = StringVar(value="#F0F0F0")
        self.corner_radius = IntVar(value=0)
        self.shadow_enabled = BooleanVar(value=False)
        self.shadow_offset_x = IntVar(value=8)
        self.shadow_offset_y = IntVar(value=8)
        self.shadow_blur = IntVar(value=12)
        self.shadow_opacity = DoubleVar(value=0.4)
        self.quality = IntVar(value=95)
        self.recursive = BooleanVar(value=True)
        self.overwrite = BooleanVar(value=False)
        
        # 颜色预览
        self.color_preview = None
        
        self.create_widgets()
        
    def create_widgets(self):
        # 主框架滚动条
        main_frame = Frame(self.root)
        main_frame.pack(fill=BOTH, expand=True, padx=10, pady=10)
        
        canvas = Canvas(main_frame)
        scrollbar = Scrollbar(main_frame, orient=VERTICAL, command=canvas.yview)
        scrollable_frame = Frame(canvas)
        
        scrollable_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)
        
        canvas.pack(side=LEFT, fill=BOTH, expand=True)
        scrollbar.pack(side=RIGHT, fill=Y)
        
        # 使用 scrollable_frame 作为内容容器
        parent = scrollable_frame
        
        # 1. 文件夹选择
        group_files = LabelFrame(parent, text="文件夹设置", padx=5, pady=5)
        group_files.pack(fill=X, padx=5, pady=5)
        
        Label(group_files, text="输入目录:").grid(row=0, column=0, sticky=W, padx=5, pady=2)
        Entry(group_files, textvariable=self.input_dir, width=45).grid(row=0, column=1, padx=5, pady=2)
        Button(group_files, text="浏览", command=self.select_input_dir).grid(row=0, column=2, padx=5)
        
        Label(group_files, text="输出目录:").grid(row=1, column=0, sticky=W, padx=5, pady=2)
        Entry(group_files, textvariable=self.output_dir, width=45).grid(row=1, column=1, padx=5, pady=2)
        Button(group_files, text="浏览", command=self.select_output_dir).grid(row=1, column=2, padx=5)
        
        Checkbutton(group_files, text="递归处理子文件夹", variable=self.recursive).grid(row=2, column=0, columnspan=3, sticky=W, padx=5, pady=2)
        Checkbutton(group_files, text="覆盖已存在的文件", variable=self.overwrite).grid(row=3, column=0, columnspan=3, sticky=W, padx=5, pady=2)
        
        # 2. 边框设置
        group_border = LabelFrame(parent, text="边框参数", padx=5, pady=5)
        group_border.pack(fill=X, padx=5, pady=5)
        
        Label(group_border, text="边框宽度 (像素):").grid(row=0, column=0, sticky=W, padx=5, pady=2)
        scale_width = Scale(group_border, from_=0, to=200, orient=HORIZONTAL, variable=self.border_width, length=200)
        scale_width.grid(row=0, column=1, sticky=W, padx=5)
        Label(group_border, textvariable=self.border_width).grid(row=0, column=2, sticky=W)
        
        Label(group_border, text="边框颜色:").grid(row=1, column=0, sticky=W, padx=5, pady=2)
        self.color_preview = Label(group_border, text="      ", bg=self.border_color_hex.get(), relief="sunken", width=4)
        self.color_preview.grid(row=1, column=1, sticky=W, padx=5)
        Button(group_border, text="选择颜色", command=self.choose_color).grid(row=1, column=2, sticky=W, padx=5)
        
        Label(group_border, text="圆角半径 (像素,0=直角):").grid(row=2, column=0, sticky=W, padx=5, pady=2)
        scale_radius = Scale(group_border, from_=0, to=100, orient=HORIZONTAL, variable=self.corner_radius, length=200)
        scale_radius.grid(row=2, column=1, sticky=W, padx=5)
        Label(group_border, textvariable=self.corner_radius).grid(row=2, column=2, sticky=W)
        
        # 3. 阴影效果
        group_shadow = LabelFrame(parent, text="阴影效果(可选)", padx=5, pady=5)
        group_shadow.pack(fill=X, padx=5, pady=5)
        
        Checkbutton(group_shadow, text="启用阴影", variable=self.shadow_enabled, command=self.toggle_shadow).grid(row=0, column=0, columnspan=3, sticky=W, padx=5)
        
        # 阴影详细参数(默认禁用状态)
        self.shadow_frame = Frame(group_shadow)
        self.shadow_frame.grid(row=1, column=0, columnspan=3, sticky=W+E, padx=5, pady=2)
        
        Label(self.shadow_frame, text="偏移量 X:").grid(row=0, column=0, sticky=W, padx=5)
        Entry(self.shadow_frame, textvariable=self.shadow_offset_x, width=6).grid(row=0, column=1, padx=5)
        Label(self.shadow_frame, text="Y:").grid(row=0, column=2, sticky=W)
        Entry(self.shadow_frame, textvariable=self.shadow_offset_y, width=6).grid(row=0, column=3, padx=5)
        
        Label(self.shadow_frame, text="模糊半径:").grid(row=1, column=0, sticky=W, padx=5)
        Entry(self.shadow_frame, textvariable=self.shadow_blur, width=6).grid(row=1, column=1, padx=5)
        
        Label(self.shadow_frame, text="不透明度 (0-1):").grid(row=2, column=0, sticky=W, padx=5)
        Entry(self.shadow_frame, textvariable=self.shadow_opacity, width=6).grid(row=2, column=1, padx=5)
        
        self.toggle_shadow()  # 初始状态设置
        
        # 4. 输出质量
        group_quality = LabelFrame(parent, text="输出质量", padx=5, pady=5)
        group_quality.pack(fill=X, padx=5, pady=5)
        
        Label(group_quality, text="JPEG 质量 (1-100):").grid(row=0, column=0, sticky=W, padx=5)
        scale_quality = Scale(group_quality, from_=1, to=100, orient=HORIZONTAL, variable=self.quality, length=200)
        scale_quality.grid(row=0, column=1, sticky=W, padx=5)
        Label(group_quality, textvariable=self.quality).grid(row=0, column=2, sticky=W)
        
        # 5. 开始按钮和进度
        self.btn_start = Button(parent, text="开始批量处理", command=self.start_processing, bg="#4CAF50", fg="white", font=("Arial", 12, "bold"))
        self.btn_start.pack(pady=10, fill=X)
        
        self.progress = ttk.Progressbar(parent, orient=HORIZONTAL, mode='determinate')
        self.progress.pack(fill=X, pady=5)
        
        self.log_text = Text(parent, height=12, wrap=WORD)
        self.log_text.pack(fill=BOTH, expand=True, pady=5)
        scroll_log = Scrollbar(self.log_text)
        scroll_log.pack(side=RIGHT, fill=Y)
        self.log_text.config(yscrollcommand=scroll_log.set)
        scroll_log.config(command=self.log_text.yview)
        
        # 状态栏
        self.status_var = StringVar()
        self.status_var.set("就绪")
        status_bar = Label(self.root, textvariable=self.status_var, bd=1, relief=SUNKEN, anchor=W)
        status_bar.pack(side=BOTTOM, fill=X)
        
    def toggle_shadow(self):
        """根据阴影启用状态启用/禁用阴影参数"""
        state = NORMAL if self.shadow_enabled.get() else DISABLED
        for child in self.shadow_frame.winfo_children():
            if isinstance(child, (Entry, Scale)):
                child.config(state=state)
                
    def choose_color(self):
        color_code = colorchooser.askcolor(title="选择边框颜色", color=self.border_color_hex.get())
        if color_code:
            hex_color = color_code[1]
            self.border_color_hex.set(hex_color)
            self.color_preview.config(bg=hex_color)
    
    def select_input_dir(self):
        dir_path = filedialog.askdirectory(title="选择包含图片的文件夹")
        if dir_path:
            self.input_dir.set(dir_path)
    
    def select_output_dir(self):
        dir_path = filedialog.askdirectory(title="选择输出文件夹")
        if dir_path:
            self.output_dir.set(dir_path)
    
    def log(self, message):
        self.log_text.insert(END, message + "\n")
        self.log_text.see(END)
        self.root.update_idletasks()
    
    def start_processing(self):
        # 验证输入
        if not self.input_dir.get():
            messagebox.showerror("错误", "请选择输入目录")
            return
        if not self.output_dir.get():
            messagebox.showerror("错误", "请选择输出目录")
            return
        
        # 禁用开始按钮,防止重复点击
        self.btn_start.config(state=DISABLED, text="处理中...")
        self.log_text.delete(1.0, END)
        self.progress['value'] = 0
        self.status_var.set("正在扫描图片...")
        self.root.update()
        
        # 在后台线程中处理,避免界面卡死
        thread = threading.Thread(target=self.process_images, daemon=True)
        thread.start()
    
    def process_images(self):
        input_dir = Path(self.input_dir.get())
        output_dir = Path(self.output_dir.get())
        recursive = self.recursive.get()
        overwrite = self.overwrite.get()
        border_width = self.border_width.get()
        border_color_hex = self.border_color_hex.get()
        corner_radius = self.corner_radius.get()
        shadow = self.shadow_enabled.get()
        shadow_offset = (self.shadow_offset_x.get(), self.shadow_offset_y.get())
        shadow_blur = self.shadow_blur.get()
        shadow_opacity = self.shadow_opacity.get()
        quality = self.quality.get()
        
        # 支持的扩展名
        extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp']
        
        # 收集所有图片
        if recursive:
            images = []
            for ext in extensions:
                images.extend(input_dir.rglob(f'*{ext}'))
                images.extend(input_dir.rglob(f'*{ext.upper()}'))
        else:
            images = []
            for ext in extensions:
                images.extend(input_dir.glob(f'*{ext}'))
                images.extend(input_dir.glob(f'*{ext.upper()}'))
        
        images = sorted(set(images))
        total = len(images)
        
        if total == 0:
            self.root.after(0, lambda: messagebox.showinfo("提示", "未找到任何图片文件"))
            self.root.after(0, self.reset_ui)
            return
        
        self.log(f"找到 {total} 张图片,开始处理...")
        self.root.after(0, lambda: self.progress.config(maximum=total))
        
        success = 0
        skip = 0
        fail = 0
        
        for idx, img_path in enumerate(images, 1):
            rel_path = img_path.relative_to(input_dir)
            output_path = output_dir / rel_path
            
            # 检查跳过
            if not overwrite and output_path.exists():
                self.log(f"[{idx}/{total}] 跳过(已存在): {rel_path}")
                skip += 1
                self.root.after(0, lambda v=idx: self.progress.config(value=v))
                continue
            
            self.log(f"[{idx}/{total}] 处理: {rel_path}")
            self.root.after(0, lambda msg=f"正在处理: {rel_path.name}": self.status_var.set(msg))
            
            # 处理图片
            try:
                with Image.open(img_path) as img:
                    # 自动旋转EXIF方向
                    try:
                        img = ImageOps.exif_transpose(img)
                    except:
                        pass
                    
                    # 添加边框
                    result = self.add_border(
                        img,
                        border_width,
                        border_color_hex,
                        corner_radius,
                        shadow,
                        shadow_offset,
                        shadow_blur,
                        shadow_opacity
                    )
                    
                    # 创建输出目录
                    output_path.parent.mkdir(parents=True, exist_ok=True)
                    
                    # 保存
                    if img_path.suffix.lower() in ['.jpg', '.jpeg']:
                        if result.mode in ('RGBA', 'LA', 'P'):
                            result = result.convert('RGB')
                        result.save(output_path, 'JPEG', quality=quality, optimize=True)
                    elif img_path.suffix.lower() == '.png':
                        result.save(output_path, 'PNG', optimize=True)
                    else:
                        result.save(output_path, quality=quality)
                    
                    success += 1
            except Exception as e:
                self.log(f"  &#10060; 失败: {str(e)}")
                fail += 1
            
            # 更新进度
            self.root.after(0, lambda v=idx: self.progress.config(value=v))
        
        # 完成
        self.log("\n" + "="*50)
        self.log(f"处理完成!")
        self.log(f"  成功: {success} 张")
        self.log(f"  跳过: {skip} 张")
        self.log(f"  失败: {fail} 张")
        self.log(f"输出目录: {output_dir.absolute()}")
        
        self.root.after(0, lambda: self.status_var.set(f"完成 - 成功:{success} 失败:{fail}"))
        self.root.after(0, self.reset_ui)
        self.root.after(0, lambda: messagebox.showinfo("完成", f"批量处理完成!\n成功: {success}\n失败: {fail}\n跳过: {skip}"))
    
    def reset_ui(self):
        self.btn_start.config(state=NORMAL, text="开始批量处理")
        self.progress['value'] = 0
    
    def add_border(self, image, border_width, border_color_hex, corner_radius, shadow, shadow_offset, shadow_blur, shadow_opacity):
        """为图像添加边框,返回新图像"""
        # 解析颜色
        border_rgba = ImageColor.getrgb(border_color_hex)
        if len(border_rgba) == 3:
            border_rgba = border_rgba + (255,)
        
        # 转换为RGBA
        if image.mode != 'RGBA':
            image = image.convert('RGBA')
        
        # 圆角处理(原图)
        if corner_radius > 0:
            img_rounded = self.add_rounded_corners(image, corner_radius)
        else:
            img_rounded = image.copy()
        
        # 计算新尺寸
        new_w = img_rounded.width + 2 * border_width
        new_h = img_rounded.height + 2 * border_width
        
        # 创建边框背景
        border_img = Image.new('RGBA', (new_w, new_h), border_rgba)
        
        # 粘贴原图
        border_img.paste(img_rounded, (border_width, border_width), img_rounded)
        
        # 整体圆角(边框外缘)
        if corner_radius > 0:
            total_radius = min(corner_radius + border_width // 2, new_w//2, new_h//2)
            border_img = self.add_rounded_corners(border_img, total_radius)
        
        # 阴影
        if shadow:
            border_img = self.drop_shadow(border_img, shadow_offset, shadow_blur, shadow_opacity)
        
        return border_img
    
    def add_rounded_corners(self, image, radius):
        """添加圆角Alpha通道"""
        mask = Image.new('L', image.size, 0)
        draw = ImageDraw.Draw(mask)
        draw.rounded_rectangle([(0, 0), image.size], radius=radius, fill=255)
        result = image.copy()
        result.putalpha(mask)
        return result
    
    def drop_shadow(self, image, offset, blur_radius, opacity):
        """添加投影"""
        if image.mode != 'RGBA':
            image = image.convert('RGBA')
        
        # 获取alpha通道
        alpha = image.split()[-1]
        shadow_mask = alpha.point(lambda p: int(p * opacity))
        
        # 创建阴影层
        shadow = Image.new('RGBA', image.size, (0,0,0,0))
        shadow.putalpha(shadow_mask)
        
        if blur_radius > 0:
            shadow = shadow.filter(ImageFilter.GaussianBlur(blur_radius))
        
        # 计算画布大小
        total_w = image.width + abs(offset[0]) + blur_radius*2
        total_h = image.height + abs(offset[1]) + blur_radius*2
        canvas = Image.new('RGBA', (total_w, total_h), (0,0,0,0))
        
        shadow_x = max(offset[0], 0) + blur_radius
        shadow_y = max(offset[1], 0) + blur_radius
        canvas.paste(shadow, (shadow_x, shadow_y), shadow)
        
        img_x = max(-offset[0], 0) + blur_radius
        img_y = max(-offset[1], 0) + blur_radius
        canvas.paste(image, (img_x, img_y), image)
        
        return canvas


def main():
    root = Tk()
    app = BorderApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()

用PYTHON开发的程序效果较好
7#
shzjz123 发表于 2026-6-10 16:10
图片加边框软件
https://wwbaw.lanzoue.com/iJWGb3rju4dc
密码:4y5r
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-6-11 08:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表