使用 PIL 从字典创建图像

Creating an image from a dictionary using PIL

我有一个字典,可以将坐标元组(在 (0,0) 到 (199, 199) 范围内)映射到灰度值(0 到 255 之间的整数)。是否有创建 PIL 图像的好方法在指定的坐标处有指定的值?我更喜欢只使用 PIL 的解决方案而不是使用 scipy.

的解决方案

您可以尝试image.putpixel()更改特定位置像素的颜色。示例代码-

from PIL import Image
from random import randint

d = {(x,y):randint(0,255) for x in range(200) for y in range(200)}
im = Image.new('L',(200,200))

for i in d:
    im.putpixel(i,d[i])

im.save('blah.png')

它给了我这样的结果 -

您可以使用 putpixel() 来完成,但这可能涉及数以万计的调用。这有多重要取决于字典中定义了多少个坐标元组。我已经包含了每个当前答案中显示的方法进行比较(包括在添加任何基准测试之前我自己的方法,但刚才我对它初始化数据缓冲区的方式做了一个小改动,这明显加快了速度)。

为了营造公平的竞争环境,出于测试目的,输入字典仅随机选择图像中 ½ 的可能像素进行定义,并允许将其余像素设置为默认 background 颜色。 Anand S Kumar 的回答目前不支持后者,但下面显示的稍作修改的版本支持。

所有人都从数据中生成相同的图像。

from __future__ import print_function
import sys
from textwrap import dedent
import timeit

N = 100  # number of executions of each algorithm
R = 3  # number of repeations of executions

# common setup for all algorithms - is not included in algorithm timing
setup = dedent("""
    from random import randint, sample, seed
    from PIL import Image

    seed(42)
    background = 0  # default color of pixels not defined in dictionary
    width, height = 200, 200

    # create test dict of input data defining half of the pixel coords in image
    coords = sample([(x,y) for x in xrange(width) for y in xrange(height)],
                    width * height // 2)
    d = {coord: randint(0, 255) for coord in coords}
""")

algorithms = {
    "Anand S Kumar": dedent("""
        im = Image.new('L', (width, height), color=background)  # set bgrd
        for i in d:
            im.putpixel(i, d[i])
    """),

    "martineau": dedent("""
        data = bytearray([background] * width * height)
        for (x, y), v in d.iteritems():
            data[x + y*width] = v
        im = Image.frombytes('L', (width, height), str(data))
    """),

    "PM 2Ring": dedent("""
        data = [background] * width * height
        for i in d:
            x, y = i
            data[x + y * width] = d[i]
        im = Image.new('L', (width, height))
        im.putdata(data)
    """),
}

# execute and time algorithms, collecting results
timings = [
    (label,
     min(timeit.repeat(algorithms[label], setup=setup, repeat=R, number=N)),
    ) for label in algorithms
]

print('fastest to slowest execution speeds (Python {}.{}.{})\n'.format(
        *sys.version_info[:3]),
        '  ({:,d} executions, best of {:d} repetitions)\n'.format(N, R))
longest = max(len(timing[0]) for timing in timings)  # length of longest label
ranked = sorted(timings, key=lambda t: t[1])  # ascending sort by execution time
fastest = ranked[0][1]
for timing in ranked:
    print("{:>{width}} : {:9.6f} secs, rel speed {:4.2f}x, {:6.2f}% slower".
            format(timing[0], timing[1], round(timing[1]/fastest, 2),
                   round((timing[1]/fastest - 1) * 100, 2), width=longest))

输出:

fastest to slowest execution speeds (Python 2.7.10)
   (100 executions, best of 3 repetitions)

    martineau :  0.255203 secs, rel speed 1.00x,   0.00% slower
     PM 2Ring :  0.307024 secs, rel speed 1.20x,  20.31% slower
Anand S Kumar :  1.835997 secs, rel speed 7.19x, 619.43% slower

正如 martineau 所建议的那样,当您修改一些随机像素时,putpixel() 是可以的,但对于构​​建整个图像来说效率不高。我的方法与他的类似,只是我使用了一个整数列表和 .putdata()。这里有一些代码来测试这 3 种不同的方法。

from PIL import Image
from random import seed, randint

width, height = 200, 200
background = 0

seed(42)
d = dict(((x, y), randint(0, 255)) for x in range(width) for y in range(height))

algorithm = 2
print('Algorithm', algorithm)

if algorithm == 0:
    im = Image.new('L', (width, height))
    for i in d:
        im.putpixel(i, d[i])
elif algorithm == 1:
    buff = bytearray((background for _ in xrange(width * height)))
    for (x,y), v in d.items():
        buff[y*width + x] = v
    im = Image.frombytes('L', (width,height), str(buff))
elif algorithm == 2:
    data = [background] * width * height
    for i in d:
        x, y = i
        data[x + y * width] = d[i]
    im = Image.new('L', (width, height))
    im.putdata(data)

#im.show()

fname = 'qrand%d.png' % algorithm
im.save(fname)
print(fname, 'saved')

这是我的 2GHz 机器上的典型时序 运行 Python 2.6.6

$ time ./qtest.py
Algorithm 0
qrand0.png saved

real    0m0.926s
user    0m0.768s
sys     0m0.040s

$ time ./qtest.py
Algorithm 1
qrand1.png saved

real    0m0.733s
user    0m0.548s
sys     0m0.020s

$ time ./qtest.py
Algorithm 2
qrand2.png saved

real    0m0.638s
user    0m0.520s
sys     0m0.032s