1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
#!/usr/bin/env python
from __future__ import print_function
"""
pipcolours - extract all colours present in source image.
Produces a PNG that has each colour exactly once.
Also performs grey and alpha reduction when possible:
if all colour pixels are grey, output PNG is grey;
if all pixels are opaque, output PNG has no alpha channel.
"""
import argparse
import collections
import itertools
import sys
import png
def png_colours(out, inp):
r = png.Reader(file=inp)
_, _, rows, info = r.asDirect()
rows, info = unique_colours(rows, info)
writer = png.Writer(**info)
writer.write(out, rows)
def jasc_counts(out, inp):
r = png.Reader(file=inp)
_, _, rows, info = r.asDirect()
print("JASC-PAL", file=out)
# A version field? (the internet is unclear)
print("0100", file=out)
colour_dict = count_colours(rows, info)
print(len(colour_dict), file=out)
for colour, count in colour_dict.items():
print(*itertools.chain(colour, ["#", count]), file=out)
def jasc_colours(out, inp):
r = png.Reader(file=inp)
_, _, rows, info = r.read()
print("JASC-PAL", file=out)
# A version field? (the internet is unclear)
print("0100", file=out)
if "palette" in info:
print(len(info['palette']), file=out)
for pixel in info['palette']:
print(*pixel, file=out)
return
rows, info = unique_colours(rows, info)
planes = info['planes']
bitdepth = info['bitdepth']
row, = rows
n = len(row) // planes
print(n, file=out)
for pixel in png.group(row, planes):
print(*pixel, file=out)
def count_colours(rows, info):
planes = info["planes"]
bitdepth = info["bitdepth"]
count = collections.Counter()
for row in rows:
for pixel in png.group(row, planes):
count[pixel] += 1
return count
def unique_colours(rows, info):
planes = info["planes"]
bitdepth = info["bitdepth"]
col = set()
for row in rows:
col = col.union(png.group(row, planes))
col, planes = channel_reduce(col, planes, bitdepth)
col = sorted(col)
width = len(col)
info = dict(
width=width,
height=1,
bitdepth=bitdepth,
greyscale=planes in (1, 2),
alpha=planes in (2, 4),
planes=planes,
)
row = list(itertools.chain(*col))
return [row], info
def channel_reduce(col, planes, bitdepth):
"""Attempt to reduce the number of channels in the set of colours."""
col, planes = reduce_grey(col, planes)
col, planes = reduce_alpha(col, planes, bitdepth)
return col, planes
def reduce_grey(col, planes):
"""
Reduce a colour image to grey if
all intensities match in all pixels.
"""
if planes >= 3:
def isgrey(c):
return c[0] == c[1] == c[2]
if all(isgrey(c) for c in col):
# Every colour is grey, convert to 1- or 2-tuples.
col = set(x[0::3] for x in col)
planes -= 2
return col, planes
def reduce_alpha(col, planes, bitdepth):
"""
Remove alpha channel if all pixels are fully opaque.
"""
maxval = 2 ** bitdepth - 1
if planes in (2, 4):
def isopaque(c):
return c[-1] == maxval
if all(isopaque(c) for c in col):
# Every pixel is opaque, remove alpha channel.
col = set(x[:-1] for x in col)
planes -= 1
return col, planes
def main(argv=None):
if argv is None:
argv = sys.argv
argv = argv[1:]
parser = argparse.ArgumentParser()
version = "%(prog)s " + png.__version__
parser.add_argument("--version", action="version", version=version)
parser.add_argument("--jasc", action="store_true")
parser.add_argument("--count", action="store_true")
parser.add_argument("input", default="-", metavar="PNG")
args = parser.parse_args(argv)
input = png.cli_open(args.input)
if args.count:
return jasc_counts(sys.stdout, input)
if args.jasc:
return jasc_colours(sys.stdout, input)
out = png.binary_stdout()
return png_colours(out, input)
if __name__ == "__main__":
main()
|