压缩图片小工具
🕌

压缩图片小工具

Property
notion image
需要将PNG缩放到特定的尺寸,并且对大小进行压缩,当然这些都很简单(网上太多了),让我自己做一个小工具的最大动力是,他们的要求必须是PNG格式。而网上大多数都是将PNG压缩成JPG,有损且格式不一致(文件头不一样)。
但是现在来看,他们公司并没有要求到那么严格,压缩后的jpg修改后缀为png即可。

Python 压缩

主要用到的库是Pillow,这个库可以对图片进行尺寸调整,并且对PNG进行一定的压缩,但是压缩效果并不太好(通常500x500的PNG图片压缩不到500K或者300K以下)。
 
# 使用pillow压缩图片
def pillow_compress_png(
        file, compress_tag, compress_size, quality=95, inplace=False, is_resize=False, height=None, width=None):

    # 如果图片被处理过
    if compress_tag in file:
        print(f"{file}  图片已被处理 {os.path.getsize(file) // 1024}")
        return

    # 打开图片
    image = Image.open(file)
    out_file = f"{file[:-4]}{compress_tag}.png"

    # 如果图片本身满足大小
    if (os.path.getsize(file) / 1024) <= compress_size:
        print(f"{file}  图片无需处理 {os.path.getsize(file)//1024}")
        return

    # 如果需要进行像素拉伸
    if is_resize:
        assert width is not None
        if height is None:
            height = int(image.height / image.width * width)
        pre_size = os.path.getsize(file)
        image = image.resize((width, height), Image.ANTIALIAS)
        image.save(f"{out_file[:-4]}_resize.png", "PNG")
        print(f"{file}  {pre_size//1024}->{os.path.getsize(file)//1024}  像素: {image.height, image.width}, ")

    # 如果大小不满足要求,进行 RGB 改变
    if os.path.getsize(file) / 1024 > compress_size:
        image = image.convert("RGB")
        image.save(out_file, "PNG", quality=quality)

    # 如果大小还不能满足要求 进一步改变通道
    if (os.path.getsize(out_file) / 1024) > compress_size:
        image.quantize(colors=256, method=0)
        image.save(out_file, "PNG")

    # 依然无法满足需求,返回
    if (os.path.getsize(out_file) / 1024) > compress_size:
        if inplace:
            os.remove(file)
            os.rename(out_file, file)
            if os.path.exists(f"{out_file[:-4]}_resize.png"):
                os.remove(f"{out_file[:-4]}_resize.png")
        return file

    # 打印最终的处理结果
    print(f"{file}  {os.path.getsize(file)//1024}->{os.path.getsize(out_file)//1024}")

    # 直接替换原来的文件
    if inplace:
        os.remove(file)
        os.rename(out_file, file)
        if os.path.exists(f"{out_file[:-4]}_resize.png"):
            os.remove(f"{out_file[:-4]}_resize.png")
于是考虑使用Java的压缩API。

Java 压缩

没有想过Java也可以压缩PNG图片,得益于有人写了Java接口的包,将32位或者24位颜色通道的图片在尽量不损失清晰度的情况下,压缩到最小。
public static ArrayList<String> compressPNGPictures(String rootDir, Integer pictureSize, String compressTag){

        // 无法被压缩到指定大小的图片
        ArrayList<String> pictureException = new ArrayList<>();

        // 获取路径
        ArrayList<String> pictureDirs = new ArrayList<>();
        getPNGPicturesDirectory(rootDir, pictureDirs, compressTag);

        // 压缩 PNG 类型图片
        for (String pictureDir: pictureDirs){
            String fileString = compressPNGPicture(pictureDir, compressTag, pictureSize);
            if (fileString != null) pictureException.add(fileString);
        }

        // 打印输出无法压缩至指定大小以下的图片
        System.out.println("无法被压缩至指定大小的文件:");
        for (String file: pictureException){
            File tempFile = new File(file);
            System.out.println(file + " : " + (tempFile.length()/1024));
        }

        // 返回无法被压缩至指定大小的图片
        return pictureException;
    }

联动压缩

利用Python的胶水特性,将这两部分代码给连接起来,流程为先利用Python的Pillow库压缩,如果不行(保留原图),则利用Java的接口再进行压缩。
# 压缩主函数
def compress_main(args):

    # 是否本地更改
    inplace = args['inplace']

    # 是否需要调整尺寸
    is_resize = args['is_resize']

    # 尺寸要求
    height = args['height']
    width = args['width']

    # 扫描图片类型
    picture_type = args['pictures_type']

    # 图片根目录
    root_dir = args['root_dir']

    # 压缩图片 处理标识
    compress_tag = args['compress_tag']

    # 压缩后的图片大小
    compress_size = args['compress_size']

    # 获取图片地址
    picture_direc = []
    get_pictures_dir(root_dir, picture_direc, compress_tag, picture_type)

    # 处理图片
    compress_pictures(
        picture_direc, compress_tag=compress_tag, compress_size=compress_size,
        inplace=inplace, is_resize=is_resize, height=height, width=width
    )

    # 打印日志
    print("----------------------split line----------------------")
    print("转由Java处理:")

    # Java 压缩
    jar_dir = args['jar_dir']
    picture_exception = java_compress_png(jar_dir, [compress_tag, f"{compress_size}", root_dir])
    if len(picture_exception) < 1:
        picture_exception = None
    return picture_exception
Java是使用Jpype1来调用的,Java首先需要打包成jar包,这部分就不在这里叙述了。

界面&选项定制

为了可以方便自己日后使用,不需要每次对着终端看输出,也为了方便我女朋友使用,我决定写一个UI界面。
代码比较多,就不贴上来了,直接看效果吧,完整的代码参见Github。
notion image
支持定制按“宽边缩放”、“自由缩放(普通模式)”、“仅压缩模式”,可以支持保留原图或者直接完成更名替换,可以输入文件限制,可以一次压缩多个目录下的所有图片。