129 строки
4.8 KiB
Python
129 строки
4.8 KiB
Python
|
#!/usr/bin/env python
|
||
|
# $URL: http://pypng.googlecode.com/svn/trunk/code/exnumpy.py $
|
||
|
# $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)
|
||
|
'''
|
||
|
|
||
|
# http://www.python.org/doc/2.4.4/lib/module-itertools.html
|
||
|
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('http://www.libpng.org/pub/png/PngSuite/basn2c16.png'))
|
||
|
pngReader=png.Reader(filename='picture.png')
|
||
|
# Tuple unpacking, using multiple assignment, is very useful for the
|
||
|
# result of asDirect (and other methods).
|
||
|
# See
|
||
|
# http://docs.python.org/tutorial/introduction.html#first-steps-towards-programming
|
||
|
row_count, column_count, pngdata, meta = pngReader.asDirect()
|
||
|
bitdepth=meta['bitdepth']
|
||
|
plane_count=meta['planes']
|
||
|
|
||
|
# 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
|
||
|
# http://docs.scipy.org/doc/numpy/user/basics.types.html .
|
||
|
# 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),
|
||
|
dtype=numpy.uint16)
|
||
|
for row_index, one_boxed_row_flat_pixels in enumerate(pngdata):
|
||
|
image_2d[row_index,:]=one_boxed_row_flat_pixels
|
||
|
|
||
|
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,
|
||
|
(row_count,column_count,plane_count))
|
||
|
# --- 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')
|
||
|
try:
|
||
|
# 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,
|
||
|
greyscale=False,
|
||
|
alpha=False,
|
||
|
bitdepth=16)
|
||
|
# 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 http://code.google.com/p/pypng/issues/detail?id=44
|
||
|
# --- extract 003 start
|
||
|
pngWriter.write(pngfile,
|
||
|
numpy.reshape(image_3d, (-1, column_count*plane_count)))
|
||
|
# --- extract 003 end
|
||
|
finally:
|
||
|
pngfile.close()
|
||
|
|