prigreypng 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. #!D:\TestPythonProject\TEST\venv\Scripts\python.exe
  2. # prigreypng
  3. # Convert image to grey (L, or LA), but only if that involves no colour change.
  4. import argparse
  5. import array
  6. import png
  7. def as_grey(out, inp):
  8. """
  9. Convert image to greyscale, but only when no colour change.
  10. This works by using the input G channel (green) as
  11. the output L channel (luminance) and
  12. checking that every pixel is grey as we go.
  13. A non-grey pixel will raise an error.
  14. """
  15. r = png.Reader(file=inp)
  16. _, _, rows, info = r.asDirect()
  17. if info["greyscale"]:
  18. w = png.Writer(**info)
  19. return w.write(out, rows)
  20. planes = info["planes"]
  21. targetplanes = planes - 2
  22. alpha = info["alpha"]
  23. width, height = info["size"]
  24. typecode = "BH"[info["bitdepth"] > 8]
  25. # Values per target row
  26. vpr = width * targetplanes
  27. def iterasgrey():
  28. for i, row in enumerate(rows):
  29. row = array.array(typecode, row)
  30. targetrow = array.array(typecode, [0] * vpr)
  31. # Copy G (and possibly A) channel.
  32. green = row[0::planes]
  33. if alpha:
  34. targetrow[0::2] = green
  35. targetrow[1::2] = row[3::4]
  36. else:
  37. targetrow = green
  38. # Check R and B channel match.
  39. if green != row[0::planes] or green != row[2::planes]:
  40. raise ValueError("Row %i contains non-grey pixel." % i)
  41. yield targetrow
  42. info["greyscale"] = True
  43. del info["planes"]
  44. w = png.Writer(**info)
  45. return w.write(out, iterasgrey())
  46. def main(argv=None):
  47. parser = argparse.ArgumentParser()
  48. parser.add_argument(
  49. "input", nargs="?", default="-", type=png.cli_open, metavar="PNG"
  50. )
  51. args = parser.parse_args()
  52. return as_grey(png.binary_stdout(), args.input)
  53. if __name__ == "__main__":
  54. import sys
  55. sys.exit(main())