Python按照指定大小和比例居中裁剪和缩放图片

Python按照指定大小和比例居中裁剪和缩放图片,第1张

2022.5.10 更新:

要求手动输入图片源文件夹(支持~符号作为用户主目录)转换后的文件名增加了新尺寸修复了代码中的一处算法错误自动删除源文件夹和目标文件夹中讨厌的.DS_Store文件(如果有的话)移除了不必要的time库

To Do:

增加不同方位剪裁的功能(不按比例缩放时)做成GUI界面的(等我学会了QT再说吧…)

这个脚本的需求,源于某些场景下机器学习需要预先处理数据,例如端侧目标检测如果将训练数据集处理成与端侧图像输入相同的尺寸,会明显提高准确率。
上代码,使用了pillowpathlib。最后显示源文件夹、目标文件夹,转换的文件数量,以及文件列表。代码会自动遍历子文件夹,但对文件类型不进行检测,对不同子文件夹存在的重名文件也不进行检测,如果子文件夹有重名文件,会默认覆盖同名的输出文件。
版本要求:
Python版本 >=3.6(我用了f表达式)
先看效果:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 将文件夹下的图片居中剪裁为指定比例并缩放到指定大小
###

from PIL import Image
from pathlib import Path

# 目标尺寸
dst_w = 400
dst_h = 300

src_dir = input('请输入图片文件夹位置:') 
src_path = Path.expanduser(Path(src_dir))
if(not src_path.exists()):
    print(f'文件夹{dir}不存在,请检查输入的文件夹名称是否正确。')

dst_dir = f'{src_path}_{dst_w}x{dst_h}'
dst_path = Path(dst_dir)
filecount = 0
# 此参数规定了最后文件列表每行显示的文件名数量。
display_each_line = 10

def remove_ds_store(path):
    # print('检查 %s 内是否存在 .DS_Store 文件'%str(path))
    target = path/'.DS_Store'
    if (target.exists()):
        Path.unlink(target)
        print(f'{target} 文件已自动删除。')
    else:
        # print(f'.DS_Store 文件不存在,继续 *** 作。')
        pass
    
def resize_dir_images(src_path):
    global filecount
    remove_ds_store(src_path)  
    for f in src_path.glob('**/*'):  # 也可以用src_path.rglob('*')
        if f.is_file():
            filecount += 1
            print('正在转换第 %d 个文件:%s       \r'%(filecount,f.name),end='')
            crop_and_resize(f)
            
            
def crop_and_resize(fp):
    ''' 图片按照目标比例裁剪并缩放 '''
    global dst_w,dst_h,dst_path,option
    im = Image.open(str(fp))
    src_w,src_h = im.size
    dst_scale = float(dst_h / dst_w) #目标高宽比
    src_scale = float(src_h / src_w) #原高宽比

    if src_scale >= dst_scale:
        #过高
        # print("原图过高")
        width = src_w
        height = int(width*dst_scale)
        x = 0
        y = (src_h - height) / 2

    else:
        #过宽
        # print("原图过宽\n")
        height = src_h
        width = int(height/dst_scale)
        x = (src_w - width) / 2
        y = 0
        
    #裁剪
    box = (x,y,width+x,height+y)
    
    #这里的参数可以这么认为:从某图的(x,y)坐标开始截,截到(width+x,height+y)坐标
    newIm = im.crop(box)
    im = None
    #压缩
    ratio = float(dst_w) / width
    newWidth = int(width * ratio)
    newHeight = int(height * ratio)
    # dst_file = dst_path/fp.name  # 保持原文件名
    dst_file = dst_path/f'{fp.stem}_{dst_w}x{dst_h}{fp.suffix}' # 文件名添加新尺寸
    
    newIm.resize((newWidth,newHeight),Image.Resampling.LANCZOS).save(dst_file,quality=100)

def main():
    global src_path,dst_path,filecount
    cnt = 0
    print('原始图片文件夹:',src_path)
    print('目标文件夹:',dst_path)
    if dst_path.exists():
        print(f'目标文件夹 {dst_path} 已经存在...')
    else:
        print(f'创建目标文件夹: {dst_path} ')
        dst_path.mkdir(exist_ok=True, parents=True)
    print(f'目标分辨率:{dst_w}x{dst_h}')
    resize_dir_images(src_path)
    remove_ds_store(dst_path)
    # dst_file_list = dst_path.iterdir() # 
    # 直接遍历目标文件夹下的jpg/jpeg文件(使用*.jp*g来匹配两种后缀名)。
    dst_file_list = dst_path.glob('*.jp*g')
    print(f'\n一共转换了{filecount}个文件\n已转换的文件列表:',end='')
    line_count = 0
    for f in sorted(dst_file_list, key = lambda f : f.stem):
        cnt += 1
        if cnt%display_each_line == 1:
            line_count += 1
            print('\n%d | %10s'%(line_count,f.name),end='')
        else:
            print('%10s'%f.name,end='')

if __name__ == '__main__':
    main()

参考文献:
python使用pil进行图像处理(等比例压缩、裁剪)实例代码
注意上述参考文献页面的代码算法有误

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1295098.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存