pythonpng

Drawing rectangle in PNG file


TLDR: My goal is simple. I have a PNG file. I want to draw a rectangle in it using Python and save it to a new file.

I have a PNG file (attached to this post). All I want to do is draw a rectangle in the image using Python and save the image to a new file. Here's code that doesn't work:

import png

org_path = './arragon.png'
altered_path = './altered.png'

f = open(org_path, 'rb')
image = png.Reader(file=f)
width, height, rows, metadata = image.read()

for row in rows:
    for i in range(len(row)):
        row[i] = 255

writer = png.Writer(
    width=width,
    height=height,
    bitdepth=metadata['bitdepth'],
    greyscale=metadata['greyscale'],
    alpha=metadata['alpha']
)

writer.write(open(altered_path, 'wb'), rows)

That last line produces the following error:

Traceback (most recent call last):
File "/home/miko/tmp/alter-image/./edit.py", line 23, in <module>
    writer.write(open(altered_path, 'wb'), rows)
File "/home/miko/.local/lib/python3.10/site-packages/png.py", line 670, in write
    raise ProtocolError(
png.ProtocolError: ProtocolError: rows supplied (0) does not match height (450)

Now, to break it down, I tried just copying the image object to a Writer, without any changes:

import png

f = open(org_path, 'rb')
image = png.Reader(file=f)
width, height, rows, metadata = image.read()

writer = png.Writer(
    width=width,
    height=height,
    bitdepth=metadata['bitdepth'],
    greyscale=metadata['greyscale'],
    alpha=metadata['alpha']
)

writer.write(open(altered_path, 'wb'), rows)

Then I get this message:

Traceback (most recent call last):
File "/home/miko/tmp/alter-image/./edit.py", line 23, in <module>
    writer.write(open(altered_path, 'wb'), rows)
File "/home/miko/.local/lib/python3.10/site-packages/png.py", line 668, in write
    nrows = self.write_passes(outfile, check_rows(rows))
File "/home/miko/.local/lib/python3.10/site-packages/png.py", line 703, in write_passes
    return self.write_packed(outfile, rows)
File "/home/miko/.local/lib/python3.10/site-packages/png.py", line 738, in write_packed
    for i, row in enumerate(rows):
File "/home/miko/.local/lib/python3.10/site-packages/png.py", line 658, in check_rows
    raise ProtocolError(
png.ProtocolError: ProtocolError: Expected 633 values but got 211 values, in row 0

I can't make heads or tails of what's wrong. Can anybody please tell me how to do this?

arragon.png


Solution

  • You are reading a paletted image which is 8 bits per pixel. The Writer object doesn't use that format unless you give it a palette argument. It's trying to write 24 bits per pixel so a row comes up way short.

    Make this subtle change:

    writer = png.Writer(
        width=width,
        height=height,
        bitdepth=metadata['bitdepth'],
        greyscale=metadata['greyscale'],
        alpha=metadata['alpha'],
        palette=metadata['palette']
    )