PyPNG to draw a simple filled box
Asked Answered
P

2

6

I am trying to draw a simple 256x256 pixel RGBA square using python's png module.

I'd like to use the png.Writer function and I imagine I'd have to draw it out using the write() method. I have not had any luck however! I don't have faith in my current code so I'm willing to take suggestions from scratch

I prefer not to use the PIL if possible.

Any suggestions?

Pressure answered 25/6, 2012 at 22:8 Comment(0)
S
8

I think the format is what may be affecting you, it seems that png has three formats ...

>>> help(png)
    Boxed row flat pixel::

     list([R,G,B, R,G,B, R,G,B],
       [R,G,B, R,G,B, R,G,B])

    Flat row flat pixel::    

      [R,G,B, R,G,B, R,G,B,
       R,G,B, R,G,B, R,G,B]

    Boxed row boxed pixel::

      list([ (R,G,B), (R,G,B), (R,G,B) ],
           [ (R,G,B), (R,G,B), (R,G,B) ])

The alpha being appended to the end of each RGB sequence.

 write(self, outfile, rows)
 |      Write a PNG image to the output file.  `rows` should be
 |      an iterable that yields each row in boxed row flat pixel format.
 |      The rows should be the rows of the original image, so there
 |      should be ``self.height`` rows of ``self.width * self.planes`` values.
 |      If `interlace` is specified (when creating the instance), then
 |      an interlaced PNG file will be written.  Supply the rows in the
 |      normal image order; the interlacing is carried out internally.

note the each row in boxed row flat pixel format.

heres a quick example that draws a white square.

>>> rows = [[255 for element in xrange(4) for number_of_pixles in xrange(256)] for number_of_rows in xrange(256)]
>>> import numpy # Using numpy is much faster
>>> rows = numpy.zeros((256, 256 * 4), dtype = 'int')
>>> rows[:] = 255
>>> png_writer = png.Writer(width = 256, height = 256, alpha = 'RGBA')
>>> png_writer.write(open('white_panel.png', 'wb'), rows)

note that Writer can also use the other 2 formats which maybe easier to use.

     |  write_array(self, outfile, pixels)
     |      Write an array in flat row flat pixel format as a PNG file on
     |      the output file.  See also :meth:`write` method.
     |  
     |  write_packed(self, outfile, rows)
     |      Write PNG file to `outfile`.  The pixel data comes from `rows`
     |      which should be in boxed row packed format.  Each row should be
     |      a sequence of packed bytes.

Try to use numpy its much faster and easier when dealing with matrix operations, images can be represented as matrices.

good luck.

If you want to print colors then you would need to calculate the RGB values for that color, for example the color red is (255, 0, 0, 255).

import png
import numpy
rows = numpy.zeros((256, 256, 4), dtype = 'int') # eassier format to deal with each individual pixel
rows[:, :] = [255, 0, 0, 255] # Setting the color red for each pixel
rows[10:40, 10:40] = [0, 255, 255, 255] #  filled squared starting at (10,10) to (40,40)
locs = numpy.indices(rows.shape[0:2])
rows[(locs[0] - 80)**2 + (locs[1] - 80)**2 <= 20**2] = [255, 255, 0, 255] # yellow filled circle, with center at (80, 80) and radius 20
png_writer = png.Writer(width = 256, height = 256, alpha = 'RGBA') # create writer
png_writer.write(open('colors_panel.png', 'wb'), rows.reshape(rows.shape[0], rows.shape[1]*rows.shape[2])) # we have to reshape or flatten the most inner arrays so write can properly understand the format
Scrivener answered 25/6, 2012 at 22:46 Comment(4)
This was extremely helpful, and I better understand the format differences. However, I can not seem to get the image to print in color. Do I need a make a palette for the .Writer method do you think. I consulted the manual, but its a bit vague.Pressure
@Pressure you don't have to do anything to write out in color but make sure you are properly setting the RGB values for that color, I've added a couple extra examples.Scrivener
Thanks again! Your explanation really helped me understand this module better :)Pressure
I got the following error when running your last example: png.ProtocolError: ProtocolError: Expected 512 values but got 1024 value, in row 0, I added greyscale=False in the png.Writer definition, no error but I didn't got the expected image.Areca
E
0

Here's a simple example to create an all red png file:

import png

width = 255
height = 255
img = []
for y in range(height):
    row = ()
    for x in range(width):
        row = row + (255, 0, 0, 255)
    img.append(row)
with open('red.png', 'wb') as f:
    w = png.Writer(width, height, greyscale=False, alpha='RGBA')
    w.write(f, img)
Elsi answered 16/5, 2021 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.