| #!/usr/bin/env python |
| |
| # prigreypng |
| |
| """Convert image to grey format (L, or LA), |
| raising an error if input is not grey in colour.""" |
| |
| import argparse |
| import array |
| |
| |
| import png |
| |
| |
| def as_grey(out, inp): |
| """ |
| Convert image to greyscale, but only when no colour change. |
| This works by using the input G channel (green) as |
| the output L channel (luminance) and |
| checking that every pixel is grey as we go. |
| A non-grey pixel will raise an error. |
| """ |
| |
| r = png.Reader(file=inp) |
| _, _, rows, info = r.asDirect() |
| if info["greyscale"]: |
| w = png.Writer(**info) |
| return w.write(out, rows) |
| |
| planes = info["planes"] |
| targetplanes = planes - 2 |
| alpha = info["alpha"] |
| width, height = info["size"] |
| typecode = "BH"[info["bitdepth"] > 8] |
| |
| # Values per target row |
| vpr = width * targetplanes |
| |
| def iterasgrey(): |
| for i, row in enumerate(rows): |
| row = array.array(typecode, row) |
| targetrow = array.array(typecode, [0] * vpr) |
| # Copy G (and possibly A) channel. |
| green = row[0::planes] |
| if alpha: |
| targetrow[0::2] = green |
| targetrow[1::2] = row[3::4] |
| else: |
| targetrow = green |
| # Check R and B channel match. |
| if green != row[0::planes] or green != row[2::planes]: |
| raise ValueError("Row %i contains non-grey pixel." % i) |
| yield targetrow |
| |
| info["greyscale"] = True |
| del info["planes"] |
| w = png.Writer(**info) |
| return w.write(out, iterasgrey()) |
| |
| |
| def main(argv=None): |
| parser = argparse.ArgumentParser() |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument( |
| "input", nargs="?", default="-", type=png.cli_open, metavar="PNG" |
| ) |
| args = parser.parse_args() |
| return as_grey(png.binary_stdout(), args.input) |
| |
| |
| if __name__ == "__main__": |
| import sys |
| |
| sys.exit(main()) |