You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
4.8 KiB

#!/usr/bin/env python
# $URL: $
# $Rev: 126 $
# Numpy example.
# Original code created by Mel Raab, modified by David Jones.
Example code integrating RGB PNG files, PyPNG and NumPy
(abstracted from Mel Raab's functioning code)
import itertools
import numpy
import png
''' If you have a PNG file for an RGB image,
and want to create a numpy array of data from it.
# Read the file "picture.png" from the current directory. The `Reader`
# class can take a filename, a file-like object, or the byte data
# directly; this suggests alternatives such as using urllib to read
# an image from the internet:
# png.Reader(file=urllib.urlopen(''))
# Tuple unpacking, using multiple assignment, is very useful for the
# result of asDirect (and other methods).
# See
row_count, column_count, pngdata, meta = pngReader.asDirect()
# Make sure we're dealing with RGB files
assert plane_count == 3
''' Boxed row flat pixel:
list([R,G,B, R,G,B, R,G,B],
[R,G,B, R,G,B, R,G,B])
Array dimensions for this example: (2,9)
Create `image_2d` as a two-dimensional NumPy array by stacking a
sequence of 1-dimensional arrays (rows).
The NumPy array mimics PyPNG's (boxed row flat pixel) representation;
it will have dimensions ``(row_count,column_count*plane_count)``.
# The use of ``numpy.uint16``, below, is to convert each row to a NumPy
# array with data type ``numpy.uint16``. This is a feature of NumPy,
# discussed further in
# .
# You can use avoid the explicit conversion with
# ``numpy.vstack(pngdata)``, but then NumPy will pick the array's data
# type; in practice it seems to pick ``numpy.int32``, which is large enough
# to hold any pixel value for any PNG image but uses 4 bytes per value when
# 1 or 2 would be enough.
# --- extract 001 start
image_2d = numpy.vstack(itertools.imap(numpy.uint16, pngdata))
# --- extract 001 end
# Do not be tempted to use ``numpy.asarray``; when passed an iterator
# (`pngdata` is often an iterator) it will attempt to create a size 1
# array with the iterator as its only element.
# An alternative to the above is to create the target array of the right
# shape, then populate it row by row:
if 0:
image_2d = numpy.zeros((row_count,plane_count*column_count),
for row_index, one_boxed_row_flat_pixels in enumerate(pngdata):
del pngReader
del pngdata
''' Reconfigure for easier referencing, similar to
Boxed row boxed pixel:
list([ (R,G,B), (R,G,B), (R,G,B) ],
[ (R,G,B), (R,G,B), (R,G,B) ])
Array dimensions for this example: (2,3,3)
``image_3d`` will contain the image as a three-dimensional numpy
array, having dimensions ``(row_count,column_count,plane_count)``.
# --- extract 002 start
image_3d = numpy.reshape(image_2d,
# --- extract 002 end
''' ============= '''
''' Convert NumPy image_3d array to PNG image file.
If the data is three-dimensional, as it is above, the best thing
to do is reshape it into a two-dimensional array with a shape of
``(row_count, column_count*plane_count)``. Because a
two-dimensional numpy array is an iterator, it can be passed
directly to the ``png.Writer.write`` method.
row_count, column_count, plane_count = image_3d.shape
assert plane_count==3
pngfile = open('picture_out.png', 'wb')
# This example assumes that you have 16-bit pixel values in the data
# array (that's what the ``bitdepth=16`` argument is for).
# If you don't, then the resulting PNG file will likely be
# very dark. Hey, it's only an example.
pngWriter = png.Writer(column_count, row_count,
# As of 2009-04-13 passing a numpy array that has an element type
# that is a numpy integer type (for example, the `image_3d` array has an
# element type of ``numpy.uint16``) generates a deprecation warning.
# This is probably a bug in numpy; it may go away in the future.
# The code still works despite the warning.
# See
# --- extract 003 start
numpy.reshape(image_3d, (-1, column_count*plane_count)))
# --- extract 003 end