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
|
#!/usr/bin/env python
# PNG Histogram.
# Only really works on grayscale images.
"""pnghist [<] grey.png > hist.png"""
from array import array
import getopt
import sys
import png
def decidemax(level):
"""Given an array of levels, decide the maximum value to use for the
histogram. This is normally chosen to be a bit bigger than the 99th
percentile, but if the 100th percentile is not much more (within a
factor of 2) then the 100th percentile is chosen.
"""
truemax = max(level)
sl = level[:]
sl.sort(reverse=True)
i99 = int(round(len(level)*0.01))
if truemax <= 2*sl[i99]:
return truemax
return 1.05*sl[i99]
def hist(out, inp, verbose=None):
"""Open the PNG file `inp` and generate a histogram."""
r = png.Reader(file=inp)
x,y,pixels,info = r.asDirect()
bitdepth = info['bitdepth']
level = [0]*2**bitdepth
for row in pixels:
for v in row:
level[v] += 1
maxlevel = decidemax(level)
h = 100
outbitdepth = 8
outmaxval = 2**outbitdepth - 1
def genrow():
for y in range(h):
y = h-y-1
# :todo: vary typecode according to outbitdepth
row = array('B', [0]*len(level))
fl = y*maxlevel/float(h)
ce = (y+1)*maxlevel/float(h)
for x in range(len(row)):
if level[x] <= fl:
# Relies on row being initialised to all 0
continue
if level[x] >= ce:
row[x] = outmaxval
continue
frac = (level[x] - fl)/(ce - fl)
row[x] = int(round(outmaxval*frac))
yield row
w = png.Writer(len(level), h, gamma=1.0,
greyscale=True, alpha=False, bitdepth=outbitdepth)
w.write(out, genrow())
if verbose: print >>verbose, level
def usage(f):
f.write(__doc__ + "\n")
def main(argv=None):
import sys
if argv is None:
argv = sys.argv
argv = argv[1:]
opt,arg = getopt.getopt(argv, '', ['help'])
for o,v in opt:
if o == '--help':
usage(sys.stdout)
return 0
if len(arg) < 1:
f = sys.stdin
else:
f = open(arg[0])
hist(sys.stdout, f)
if __name__ == '__main__':
sys.exit(main())
|