| #!/usr/bin/env python |
| |
| # PNG Histogram. |
| # Only really works on grayscale images. |
| |
| """pnghist [<] grey.png > hist.png""" |
| |
| 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.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): |
| 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 = png.binary_stdin() |
| else: |
| f = open(arg[0], 'rb') |
| hist(png.binary_stdout(), f) |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |