123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- #!/usr/bin/env python
- # $URL: http://pypng.googlecode.com/svn/trunk/code/pipstack $
- # $Rev: 190 $
- # pipstack
- # Combine input PNG files into a multi-channel output PNG.
- """
- pipstack file1.png [file2.png ...]
- pipstack can be used to combine 3 greyscale PNG files into a colour, RGB,
- PNG file. In fact it is slightly more general than that. The number of
- channels in the output PNG is equal to the sum of the numbers of
- channels in the input images. It is an error if this sum exceeds 4 (the
- maximum number of channels in a PNG image is 4, for an RGBA image). The
- output colour model corresponds to the number of channels: 1 -
- greyscale; 2 - greyscale+alpha; 3 - RGB; 4 - RGB+alpha.
- In this way it is possible to combine 3 greyscale PNG files into an RGB
- PNG (a common expected use) as well as more esoteric options: rgb.png +
- grey.png = rgba.png; grey.png + grey.png = greyalpha.png.
- Color Profile, Gamma, and so on.
- [This is not implemented yet]
- If an input has an ICC Profile (``iCCP`` chunk) then the output will
- have an ICC Profile, but only if it is possible to combine all the input
- ICC Profiles. It is possible to combine all the input ICC Profiles
- only when: they all use the same Profile Connection Space; the PCS white
- point is the same (specified in the header; should always be D50);
- possibly some other things I haven't thought of yet.
- If some of the inputs have a ``gAMA`` chunk (specifying gamma) and
- an output ICC Profile is being generated, then the gamma information
- will be incorporated into the ICC Profile.
- When the output is an RGB colour type and the output ICC Profile is
- synthesized, it is necessary to supply colorant tags (``rXYZ`` and so
- on). These are taken from ``sRGB``.
- If the input images have ``gAMA`` chunks and no input image has an ICC
- Profile then the output image will have a ``gAMA`` chunk, but only if
- all the ``gAMA`` chunks specify the same value. Otherwise a warning
- will be emitted and no ``gAMA`` chunk. It is possible to add or replace
- a ``gAMA`` chunk using the ``pipchunk`` tool.
- gAMA, pHYs, iCCP, sRGB, tIME, any other chunks.
- """
- class Error(Exception):
- pass
- def stack(out, inp):
- """Stack the input PNG files into a single output PNG."""
- from array import array
- import itertools
- # Local module
- import png
- if len(inp) < 1:
- raise Error("Required input is missing.")
- l = map(png.Reader, inp)
- # Let data be a list of (pixel,info) pairs.
- data = map(lambda p: p.asDirect()[2:], l)
- totalchannels = sum(map(lambda x: x[1]['planes'], data))
- if not (0 < totalchannels <= 4):
- raise Error("Too many channels in input.")
- alpha = totalchannels in (2,4)
- greyscale = totalchannels in (1,2)
- bitdepth = []
- for b in map(lambda x: x[1]['bitdepth'], data):
- try:
- if b == int(b):
- bitdepth.append(b)
- continue
- except (TypeError, ValueError):
- pass
- # Assume a tuple.
- bitdepth += b
- # Currently, fail unless all bitdepths equal.
- if len(set(bitdepth)) > 1:
- raise NotImplemented("Cannot cope when bitdepths differ - sorry!")
- bitdepth = bitdepth[0]
- arraytype = 'BH'[bitdepth > 8]
- size = map(lambda x: x[1]['size'], data)
- # Currently, fail unless all images same size.
- if len(set(size)) > 1:
- raise NotImplemented("Cannot cope when sizes differ - sorry!")
- size = size[0]
- # Values per row
- vpr = totalchannels * size[0]
- def iterstack():
- # the izip call creates an iterator that yields the next row
- # from all the input images combined into a tuple.
- for irow in itertools.izip(*map(lambda x: x[0], data)):
- row = array(arraytype, [0]*vpr)
- # output channel
- och = 0
- for i,arow in enumerate(irow):
- # ensure incoming row is an array
- arow = array(arraytype, arow)
- n = data[i][1]['planes']
- for j in range(n):
- row[och::totalchannels] = arow[j::n]
- och += 1
- yield row
- w = png.Writer(size[0], size[1],
- greyscale=greyscale, alpha=alpha, bitdepth=bitdepth)
- w.write(out, iterstack())
- def main(argv=None):
- import sys
- if argv is None:
- argv = sys.argv
- argv = argv[1:]
- arg = argv[:]
- return stack(sys.stdout, arg)
- if __name__ == '__main__':
- main()
|