首页 > 解决方案 > 在 PIL 图像的每个像素上应用函数的快速方法

问题描述

我需要对 PIL图像中的每个像素应用一个函数。我在这里发现了类似的问题,但不知何故,这些答案对我来说从来没有用过(主要是因为它们是特定于函数的)。

用两个 for 循环遍历每个像素是可行的,但速度非常慢。所以我想,可能有一种更快的方法,比如numpy.apply_along_axis矢量化。但是,第一个也很慢,我无法让第二个工作。我很感激任何建议!

from PIL import Image
import numpy as np

# example functions to be applied on each pixel RGB value
# (lets just imagine for now, that l and r cannot be 0)
def blueness(rgb):
    r, g, b = int(rgb[0]), int(rgb[1]), int(rgb[2])
    l = (r+g+b)/3
    return (b/r)*(1/l)*100

input_path = 'input.png'
img = Image.open(input_path)
img_np = np.asarray(orig_img)
h, w, z = img_np.shape
converted_img = np.zeros((h, w))

# apply blueness on img_np
for x in range(w):
    for y in range(h):
        converted_img[y, x] = blueness(img_np[y, x])

标签: python-3.xpython-imaging-librarynumpy-ndarray

解决方案


一般来说,如果您考虑将图像转换为列表并for在 Python 中使用循环,您可能已经出错了。你真的需要用 Numpy 或 Numba 或类似的东西进行矢量化numexpr

这是对您的功能执行此操作的一种方法:

#!/usr/bin/env python3

import numpy as np

def loopy(na):
    # Create output image in greyscale
    res = np.zeros_like(na[..., 0])

    # apply blueness on na
    for x in range(w):
        for y in range(h):
            res[y, x] = blueness(na[y, x])

    return res

def blueness(rgb):
    r, g, b = int(rgb[0]), int(rgb[1]), int(rgb[2])
    l = (r+g+b)/3
    return (b/r)*(1/l)*100

def me(na):
    # Take mean of RGB values
    l = np.mean(na, axis=2)
    res = (na[..., 2] / na[..., 0]) * 100/l
    return res.astype(np.uint8)

# Guess the height and width
h, w = 1080, 1920

# Create a random image in Numpy array
na = np.random.randint(1,256, (h,w,3), np.uint8)

# Double for loop method
res1 = loopy(na)

# Numpy method
res2 = me(na)

这是结果。Numpy 大约快 65 倍:

%timeit me(na)
37 ms ± 523 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit loopy(na)
2.36 s ± 11.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

推荐阅读