Python PIL实现图片像素矩形区域交换 嵌套循环卡顿问题
故障根因
- 程序卡顿冻结本质是性能问题:
getpixel、putpixel属于Python层逐像素调用接口,单次调用开销很高。两个高100像素、与图片等宽的区域总像素量为2 * 图片宽度 * 100,只要图片宽度超过2000像素,Python嵌套循环就要执行40万次以上的读写操作,直接阻塞主线程,表现就是程序无响应、冻结。 - 交换逻辑失效是读写顺序错误:如果直接在原图上嵌套循环边读边写,第一个区域的原始像素还没完成暂存就被新值覆盖,最终两个区域内容会完全错乱,无法实现交换效果。
- 之前两个独立函数可以正常运行,是因为函数仅给对应区域写入固定颜色值,不需要暂存原始像素,且单次循环的开销还没达到卡死阈值;合并为交换逻辑后,循环计算量翻倍,同时需要处理临时像素存储,性能问题和逻辑问题同时触发就会出问题。
最优实现方案
不要手写Python嵌套循环操作像素,直接调用PIL内置的区域裁剪、粘贴接口即可,这类接口底层由C实现,性能比手写Python循环高2个数量级,完全不会卡顿,逻辑也更简单不易出错。
交换逻辑仅需三步:
- 定义两个待交换区域的坐标范围
- 把两个区域从原图上裁剪下来作为独立备份(该操作不会修改原图)
- 把两个备份区域交叉粘贴到对方的原位置即可
可直接运行的代码如下:
from PIL import Image im = Image.open("Jenny-RedEyes.jpg") width, height = im.size # 配置两个待交换区域的参数:全宽、高度100像素 y1 = 0 # 第一个区域的起始Y坐标 y2 = 166 # 第二个区域的起始Y坐标 region_height = 100 # 裁剪两个区域做临时备份 box1 = (0, y1, width, y1 + region_height) box2 = (0, y2, width, y2 + region_height) region1 = im.crop(box1) region2 = im.crop(box2) # 交叉粘贴完成交换 im.paste(region2, box1) im.paste(region1, box2) im.show()
手动像素操作实现(不推荐)
如果出于原理验证目的非要手写循环实现,必须先把两个区域的所有像素全部读取缓存到内存,再统一写回,绝对不能边读边写;同时用load()方法获取像素访问指针,减少逐点调用接口的开销。该写法性能远差于内置接口,高分辨率图片下依然会有明显卡顿,仅做参考:
from PIL import Image im = Image.open("Jenny-RedEyes.jpg") width, height = im.size pix = im.load() y1, y2 = 0, 166 region_height = 100 # 第一步:缓存两个区域的全部像素值 buf1 = [] buf2 = [] for x in range(width): col1, col2 = [], [] for offset in range(region_height): col1.append(pix[x, y1 + offset]) col2.append(pix[x, y2 + offset]) buf1.append(col1) buf2.append(col2) # 第二步:统一写入交换后的像素值 for x in range(width): for offset in range(region_height): pix[x, y1 + offset] = buf2[x][offset] pix[x, y2 + offset] = buf1[x][offset] im.show()
内容的提问来源于stack exchange,提问作者Oswald
相关产品推荐
相关产品推荐

