Generating Image from Dictionary using PIL

I have a dictionary that maps coordinate tuples (ranging from (0,0) to (199, 199) to grayscale (integers from 0 to 255.) Is there a good way to create a PIL image that has given values ​​in the specified I would prefer a solution that only uses PIL for what scipy uses.

+3


source to share


3 answers


As Martino says, it's putpixel()

okay for you to change a few random pixels, but it's not as efficient for plotting entire images. My approach is similar to his, except that I use a list of ints and .putdata()

. Here's some code to test these three different approaches.

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')

      



Here are the typical timings on my 2GHz machine running 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

      

+2


source


You can try image.putpixel()

to change the color of the pixel at a specific position. Sample code -

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')

      



This gave me the output as -

enter image description here

+3


source


You can do this with putpixel()

, but this could potentially involve tens of thousands of calls. How important this is depends on how many coordinate tuples are defined in the dictionary. I've included the method shown in each of the current answers for comparison (including my own, before any benchmarking was added, but for now I've made a small change in how it initializes the data buffer which made it noticeably faster).

To create a level playing field, for testing purposes, the input dictionary randomly selects only 1/2 of the possible pixels in the image to define and allows the rest to be set to the default color background

. Anand S Kumar's answer does not currently do the latter, but the slightly modified version shown below does.

Everyone produces the same image from the data.

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))

      

Output:

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

      

+3


source







All Articles