最近朋友结婚,帮忙排了些照片,结果在导出时只能保存为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)