如何使用PIL实现多像素块图像像素化?求空间高效代码方案
实现多像素块的高效像素化(Pillow)
嘿,我来帮你搞定多块像素化的问题!你已经成功实现了1x1的整体平均色像素化,现在咱们把这个逻辑扩展成网格状的像素块,同时保证空间效率——不用存储所有像素的RGB值,而是按需计算每个块的平均值。
先梳理下你现有代码的问题
你当前的代码逻辑有点小问题:
- 只遍历了前40x40的像素,却把整个图像都设成最后一个
cow的颜色,完全没用到计算出的平均色y/z/t - 存储所有像素RGB到列表的方式会占用大量内存,尤其是处理大图像时更明显
空间高效的多块像素化实现思路
核心思路是按网格遍历每个像素块,实时计算块内RGB平均值,直接填充块内像素,不用额外存储所有像素数据。具体步骤:
- 定义每个像素块的尺寸(比如10x10像素)
- 按网格遍历每个块的起始坐标
- 对每个块,累加内部所有像素的RGB值,计算平均值
- 将整个块的像素统一设置为这个平均色
- 处理图像边缘的不完整块(避免越界)
完整代码实现
from PIL import Image def pixelate_image(image_path, target_size=(200, 200), block_size=(10, 10)): # 打开图像并缩放到目标尺寸 actual = Image.open(image_path) og = actual.resize(target_size) pixel_data = og.load() img_width, img_height = og.size block_w, block_h = block_size # 遍历每个像素块的起始坐标(按块大小步进) for y_start in range(0, img_height, block_h): # 处理边缘:如果剩余高度不足块高,取到图像底部 y_end = min(y_start + block_h, img_height) for x_start in range(0, img_width, block_w): # 处理边缘:如果剩余宽度不足块宽,取到图像右侧 x_end = min(x_start + block_w, img_width) # 初始化当前块的RGB累加和与像素计数 r_sum = g_sum = b_sum = 0 pixel_count = 0 # 遍历块内所有像素,累加RGB值 for y in range(y_start, y_end): for x in range(x_start, x_end): r, g, b = pixel_data[x, y] r_sum += r g_sum += g b_sum += b pixel_count += 1 # 计算块内平均RGB(整数除法保证颜色值为0-255) r_avg = r_sum // pixel_count g_avg = g_sum // pixel_count b_avg = b_sum // pixel_count # 将整个块的像素设置为平均色 for y in range(y_start, y_end): for x in range(x_start, x_end): pixel_data[x, y] = (r_avg, g_avg, b_avg) return og # 调用示例 pixelated_img = pixelate_image('My image', target_size=(200, 200), block_size=(10, 10)) pixelated_img.show()
关键优化点(空间高效)
- 没有用列表存储所有像素的RGB值,而是每个块单独计算累加和,计算完成后直接填充,内存占用仅为单个块的临时变量大小
- 自动处理图像边缘的不完整块,避免索引越界问题
- 代码模块化,方便调整目标尺寸和块大小
更简洁的写法(利用PIL内置方法)
如果你想让代码更简洁,也可以用crop方法提取块,再计算块的平均色:
from PIL import Image def pixelate_image_simple(image_path, target_size=(200, 200), block_size=(10, 10)): actual = Image.open(image_path) og = actual.resize(target_size) img_width, img_height = og.size block_w, block_h = block_size for y_start in range(0, img_height, block_h): y_end = min(y_start + block_h, img_height) for x_start in range(0, img_width, block_w): x_end = min(x_start + block_w, img_width) # 裁剪出当前块 block = og.crop((x_start, y_start, x_end, y_end)) # 计算块内平均RGB r_avg, g_avg, b_avg = tuple(sum(c) // len(c) for c in zip(*block.getdata())) # 创建纯色块并粘贴回原图 solid_block = Image.new('RGB', (x_end - x_start, y_end - y_start), (r_avg, g_avg, b_avg)) og.paste(solid_block, (x_start, y_start)) return og # 调用示例 pixelated_img = pixelate_image_simple('My image', target_size=(200, 200), block_size=(10, 10)) pixelated_img.show()
这种写法更简洁,可读性更强,内存效率也不错,因为crop和getdata都是按需处理的。
内容的提问来源于stack exchange,提问作者user13237630
相关产品推荐
相关产品推荐

