影刀RPA 图片处理基础:裁剪、缩放、水印
署名:林焱
什么情况用什么
RPA采集商品数据时需要批量下载图片并统一尺寸;截图后需要裁剪指定区域;生成的报表图片需要加水印防盗用。
| 场景 | 推荐库 | 特点 |
|---|---|---|
| 基础裁剪/缩放/旋转 | Pillow(PIL) | 轻量高效,格式支持广 |
| 批量加水印 | Pillow | 文字/图片水印均可 |
| 图片拼接/合并 | Pillow | 多图拼接成一张 |
怎么做
一:图片裁剪
fromPILimportImage img=Image.open(r"D:\图片\截图.png")# 裁剪指定区域 (left, upper, right, lower)cropped=img.crop((100,200,800,600))cropped.save(r"D:\图片\裁剪后.png")# 裁剪居中区域width,height=img.size crop_size=min(width,height)# 取最小边做正方形left=(width-crop_size)//2upper=(height-crop_size)//2cropped_center=img.crop((left,upper,left+crop_size,upper+crop_size))cropped_center.save(r"D:\图片\居中裁剪.png")二:图片缩放
fromPILimportImage img=Image.open(r"D:\图片\原图.jpg")# 按固定尺寸缩放resized=img.resize((800,600))resized.save(r"D:\图片\缩放后.jpg")# 按比例缩放(缩小到50%)width,height=img.size new_size=(int(width*0.5),int(height*0.5))resized=img.resize(new_size,Image.LANCZOS)# LANCZOS高质量缩放resized.save(r"D:\图片\缩小50%.jpg")# 等比缩放(限制最大宽度)max_width=400ifwidth>max_width:ratio=max_width/width new_size=(max_width,int(height*ratio))resized=img.resize(new_size,Image.LANCZOS)resized.save(r"D:\图片\限宽400.jpg")三:添加文字水印
fromPILimportImage,ImageDraw,ImageFont img=Image.open(r"D:\图片\产品图.jpg").convert("RGBA")# 创建透明图层overlay=Image.new("RGBA",img.size,(0,0,0,0))draw=ImageDraw.Draw(overlay)# 加载字体(指定中文字体路径)try:font=ImageFont.truetype("C:/Windows/Fonts/simhei.ttf",40)except:font=ImageFont.load_default()# 绘制水印文字watermark_text="林焱RPA"# 半透明白色draw.text((50,50),watermark_text,fill=(255,255,255,128),font=font)# 合并图层result=Image.alpha_composite(img,overlay)result.convert("RGB").save(r"D:\图片\加水印.jpg",quality=95)四:平铺水印
fromPILimportImage,ImageDraw,ImageFontimportmath img=Image.open(r"D:\图片\产品图.jpg").convert("RGBA")overlay=Image.new("RGBA",img.size,(0,0,0,0))draw=ImageDraw.Draw(overlay)font=ImageFont.truetype("C:/Windows/Fonts/simhei.ttf",36)# 平铺水印text="内部使用 "# 计算文字宽度bbox=draw.textbbox((0,0),text,font=font)text_width=bbox[2]-bbox[0]text_height=bbox[3]-bbox[1]# 旋转45度的水印angle=45foryinrange(-img.height,img.height,150):forxinrange(-img.width,img.width,int(text_width)+80):draw.text((x,y),text,fill=(255,255,255,60),font=font)# 旋转整个水印层overlay=overlay.rotate(angle,expand=False,center=(img.width//2,img.height//2))result=Image.alpha_composite(img,overlay)result.convert("RGB").save(r"D:\图片\平铺水印.jpg",quality=95)五:批量处理
fromPILimportImageimportosimportglob# yd_input: input_dir, output_dir, target_width, qualityinput_dir=yd_input.get("input_dir",r"D:\图片\原始")output_dir=yd_input.get("output_dir",r"D:\图片\处理后")target_width=yd_input.get("target_width",800)quality=yd_input.get("quality",85)os.makedirs(output_dir,exist_ok=True)files=glob.glob(os.path.join(input_dir,"*.jpg"))+\ glob.glob(os.path.join(input_dir,"*.png"))+\ glob.glob(os.path.join(input_dir,"*.jpeg"))success=0errors=[]forfileinfiles:try:img=Image.open(file)# 统一转为RGB(去掉alpha通道)ifimg.mode=="RGBA":img=img.convert("RGB")elifimg.mode!="RGB":img=img.convert("RGB")# 等比缩放width,height=img.sizeifwidth>target_width:ratio=target_width/width new_height=int(height*ratio)img=img.resize((target_width,new_height),Image.LANCZOS)# 保存basename=os.path.splitext(os.path.basename(file))[0]output_path=os.path.join(output_dir,f"{basename}.jpg")img.save(output_path,"JPEG",quality=quality)success+=1exceptExceptionase:errors.append(f"{os.path.basename(file)}:{e}")yd_output={"status":"ok","success":success,"errors":errors,"total":len(files)}有什么坑
坑一:RGBA图片保存为JPEG报错
现象:处理PNG透明图片时,保存为JPEG报错cannot write mode RGBA as JPEG。
原因:JPEG不支持透明通道(Alpha),PNG的RGBA模式直接保存JPEG会报错。
解决:先转成RGB:
img=Image.open("透明图.png")# 转成RGB(去掉透明通道,透明区域变白)ifimg.modein("RGBA","LA"):background=Image.new("RGB",img.size,(255,255,255))background.paste(img,mask=img.split()[-1])# 用alpha通道做maskimg=background img.save("输出.jpg","JPEG",quality=95)# 或者简单转换(透明区域变黑)# img = img.convert("RGB")坑二:中文字体路径找不到
现象:ImageFont.truetype报OSError: cannot open resource。
原因:字体路径不对,或者系统没有该字体。
解决:用系统中存在的字体,加try-catch:
fromPILimportImageFontdefget_font(size=30):"""获取可用中文字体"""font_paths=["C:/Windows/Fonts/simhei.ttf",# 黑体"C:/Windows/Fonts/msyh.ttc",# 微软雅黑"C:/Windows/Fonts/simsun.ttc",# 宋体"C:/Windows/Fonts/Deng.ttf",# 等线]forpathinfont_paths:try:returnImageFont.truetype(path,size)except:continuereturnImageFont.load_default()# 回退默认字体font=get_font(40)坑三:图片旋转后尺寸变化
现象:旋转图片后发现图片变大了,多了黑色边角。
原因:rotate默认expand=False不扩展画布,旋转后部分内容被裁掉。设expand=True会扩展画布但多出黑色区域。
解决:
# 方案1:不扩展画布(保持原尺寸,裁掉超出部分)rotated=img.rotate(45,expand=False)# 方案2:扩展画布 + 填充背景色rotated=img.rotate(45,expand=True,fillcolor=(255,255,255))# 方案3:扩展画布 + 透明背景(需要RGBA模式)img_rgba=img.convert("RGBA")rotated=img_rgba.rotate(45,expand=True)# rotated现在是RGBA,透明背景坑四:批量处理大图片内存溢出
现象:批量处理100张高分辨率照片(每张10MB+),处理到一半内存不足。
原因:Pillow打开图片后不主动释放,循环中累积。
解决:用with语句或手动close:
fromPILimportImageimportgcforfileinfiles:img=Image.open(file)# 处理...img.close()# 显式关闭gc.collect()# 可选:强制垃圾回收# 或者用withforfileinfiles:withImage.open(file)asimg:img=img.copy()# with退出后原文件句柄释放# 处理...