Python实现图片格式批量转换(支持HEIC)

最近朋友结婚,帮忙排了些照片,结果在导出时只能保存为HEIC格式的图片,这种格式在有的电脑上并不支持显示,因此需要将其转换为jpg/png等常见的格式。为了不引入更多的库,本文主要基于pillow进行转换。

一般图像格式批量转换

import os
from PIL import Image
import argparse

def convert_images(input_dir, output_dir, target_format):
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    supported_formats = ('.jpg', '.jpeg', '.png', '.webp', '.bmp')
    count = 0

    for root, _, files in os.walk(input_dir):
        for file in files:
            if file.lower().endswith(supported_formats):
                try:
                    # 构建路径
                    rel_path = os.path.relpath(root, input_dir)
                    output_subdir = os.path.join(output_dir, rel_path)
                    if not os.path.exists(output_subdir):
                        os.makedirs(output_subdir)

                    # 转换处理
                    img_path = os.path.join(root, file)
                    img = Image.open(img_path)
                    output_name = os.path.splitext(file)[0] + f'.{target_format}'
                    output_path = os.path.join(output_subdir, output_name)

                    # 保存时优化质量
                    if target_format.lower() in ('jpg', 'jpeg'):
                        img.save(output_path, quality=95, optimize=True)
                    elif target_format.lower() == 'webp':
                        img.save(output_path, quality=90, method=6)
                    else:
                        img.save(output_path)

                    count += 1
                    print(f'Converted: {img_path} -> {output_path}')
                except Exception as e:
                    print(f'Error processing {file}: {str(e)}')

    print(f'\nConversion complete! {count} images converted to {target_format.upper()}')

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Batch Image Format Converter')
    # parser.add_argument('-i', '--input', required=True, help=r'输入路径')
    # parser.add_argument('-o', '--output', required=True, help=r'输出路径')
    # parser.add_argument('-f', '--format', required=True,
    #                     choices=['jpg', 'png', 'webp', 'bmp'], help='Target format')
    args = parser.parse_args()
    args.input = r'E:\输入照片路径'
    args.output = r'E:\保存照片路径'
    args.format = 'png'
    convert_images(args.input, args.output, args.format)

使用说明:上述为了方便测试手动设置了照片路径
安装依赖:pip install pillow
执行命令:python image_converter.py -i 输入文件夹 -o 输出文件夹 -f 目标格式
示例:将photos文件夹转PNG格式:python image_converter.py -i ./photos -o ./converted -f png
特性说明:自动保留子目录结构、支持透明通道转换、智能压缩优化,处理异常时会跳过错误文件继续执行。

HEIC图像格式批量转换为png

上面的代码中不能读取HEIC的图像进行转换,会报如下错误:python Failed IMG_20250608_095936.HEIC: EXIF data is too long(EXIF数据特别长)。

解决方案:删除或截断EXIF数据,虽然这不是最佳实践,因为这样做会丢失元数据,但在某些情况下可以作为临时解决方案。可以在保存图片前清空或修改EXIF数据。

if exif_data:
    # 创建一个不带EXIF的字节流
    image_without_exif = Image.frombytes(image.mode, image.size, image.tobytes())
    image_without_exif.save("output_no_exif.jpg", "JPEG")
else:
    image.save("output_no_exif.jpg", "JPEG")
import pillow_heif
import os
from PIL import Image

def batch_convert(input_dir, output_dir):
    pillow_heif.register_heif_opener()  #这句很关键
    os.makedirs(output_dir, exist_ok=True)

    for root, _, files in os.walk(input_dir):
        for file in files:
            print(file)
            if file.lower().endswith(('.heic', '.heif','.jpg', '.jpeg', '.png', '.webp', '.bmp')):
                input_path = os.path.join(root, file)
                output_path = os.path.join(output_dir,
                                           f"{os.path.splitext(file)[0]}.png")

                try:
                    image = Image.open(input_path)
                    exif_data = image.info.get("exif", b"")
                    if exif_data:
                        # 创建一个不带EXIF的字节流
                        image_without_exif = Image.frombytes(image.mode, image.size, image.tobytes()) #此处也是关键点
                        image_without_exif.save(output_path, "JPEG")
                    else:
                        image.save(output_path, "PNG")
                    # with Image.open(input_path) as img:
                    #     img.convert("RGB").save(output_path,
                    #                             "JPEG", quality=90, exif=img.info.get('exif'))
                    print(f"Converted: {input_path}")
                except Exception as e:
                    print(f"Failed {file}: {str(e)}")


if __name__ == "__main__":
    batch_convert( r'E:\输入照片路径', r'E:\输出照片路径')

HEIC图像格式批量转换为png(多线程加速)

from concurrent.futures import ThreadPoolExecutor
import pillow_heif
from PIL import Image
import os


def convert_single(args):
    input_path, output_path = args

    try:
        image = Image.open(input_path)
        exif_data = image.info.get("exif", b"")
        if exif_data:
            # 创建一个不带EXIF的字节流
            image_without_exif = Image.frombytes(image.mode, image.size, image.tobytes())  # 此处也是关键点
            image_without_exif.save(output_path, "JPEG")
        else:
            image.save(output_path, "PNG")
        # with Image.open(input_path) as img:
        #     img.convert("RGB").save(output_path,
        #                             "JPEG", quality=90, optimize=True)
        return True
    except Exception as e:
        print(f"Error converting {input_path}: {str(e)}")
        return False


def batch_convert(input_dir, output_dir, workers=4):
    pillow_heif.register_heif_opener()
    os.makedirs(output_dir, exist_ok=True)

    tasks = []
    for root, _, files in os.walk(input_dir):
        for file in files:
            if file.lower().endswith(('.heic', '.heif', '.jpg')):
                tasks.append((
                    os.path.join(root, file),
                    os.path.join(output_dir,
                                 f"{os.path.splitext(file)[0]}.png")
                ))

    with ThreadPoolExecutor(max_workers=workers) as executor:
        results = list(executor.map(convert_single, tasks))

    print(f"Success: {sum(results)}/{len(tasks)}")


if __name__ == "__main__":
    batch_convert(r"E:\输入照片路径", r"E:\输出照片路径", workers=8)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值