PIL - 对每个像素应用相同的操作

PIL - apply the same operation to every pixel

我创建一个图像并填充像素:

img = Image.new( 'RGB', (2000,2000), "black") # create a new black image
pixels = img.load() # create the pixel map

for i in range(img.size[0]):    # for every pixel:
    for j in range(img.size[1]):
      #do some stuff that requires i and j as parameter

这是否可以做得更优雅(并且可能更快,因为理论上循环是可并行化的)?

注意:我会先回答问题,然后提出一个我认为更好的选择

回答问题

在不知道您打算应用哪些更改以及将图像作为 PIL 图像加载是问题的一部分还是给定的情况下,很难提供建议。

  • 在 Python 中更优雅-说话通常意味着使用列表理解
  • 对于并行化,您可以查看类似 multiprocessing module or joblib
  • 的内容

根据您创建/加载图像的方法,您可能会对 list_of_pixels = list(img.getdata())img.putdata(new_list_of_pixels) 函数感兴趣。

示例:

from PIL import Image
from multiprocessing import Pool

img = Image.new( 'RGB', (2000,2000), "black")

# a function that fixes the green component of a pixel to the value 50
def update_pixel(p):
    return (p[0], 50, p[2])

list_of_pixels = list(img.getdata())
pool = Pool(4)
new_list_of_pixels = pool.map(update_pixel, list_of_pixels)
pool.close()
pool.join()
img.putdata(new_list_of_pixels)

但是,我认为这不是一个好主意...当您看到 Python 中数千个元素的循环(和列表理解)并且您想到了性能时,您可以确保有一个库可以使它更快。

更好的选择

首先,快速指向 Channel Operations module, 由于您没有指定您打算执行的像素操作类型,并且您显然已经了解 PIL 库,因此我假设您知道它并且它不会执行您想要的操作。

然后,Python 中任何中等复杂的矩阵操作都将受益于引入 Pandas, Numpy or Scipy...

纯 numpy 示例:

import numpy as np
import matplotlib.pyplot as plt
#black image
img = np.zeros([100,100,3],dtype=np.uint8)
#show
plt.imshow(img)
#make it green
img[:,:, 1] = 50
#show
plt.imshow(img)

由于您只是在使用标准 numpy.ndarray,因此您可以使用任何可用的功能,例如 np.vectorize、应用、地图等。要显示与上述类似的解决方案,请使用update_pixel 函数:

import numpy as np
import matplotlib.pyplot as plt
#black image
img = np.zeros([100,100,3],dtype=np.uint8)
#show
plt.imshow(img)
#make it green
def update_pixel(p):
    return (p[0], 50, p[2])
green_img = np.apply_along_axis(update_pixel, 2, img)
#show
plt.imshow(green_img)

再举个例子,这次直接从索引中计算图像内容,而不是从现有的图像像素内容中计算(不需要先创建一个空图像):

import numpy as np
import matplotlib.pyplot as plt

def calc_pixel(x,y):
    return np.array([100-x, x+y, 100-y])

img = np.frompyfunc(calc_pixel, 2, 1).outer(np.arange(100), np.arange(100))    
plt.imshow(np.array(img.tolist()))
#note: I don't know any other way to convert a 2D array of arrays to a 3D array...

而且,瞧瞧,scipy 有读取和写入图像的方法,您可以使用 numpy 将它们作为 "classic" 多维数组进行操作。 (scipy.misc.imread 取决于 PIL,顺便说一句)

More example code.