#!/usr/bin/env python # $URL: http://pypng.googlecode.com/svn/trunk/code/pnghist $ # $Rev: 153 $ # PNG Histogram # Only really works on grayscale images. from array import array import getopt 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 main(argv=None): import sys if argv is None: argv = sys.argv argv = argv[1:] opt,arg = getopt.getopt(argv, '') if len(arg) < 1: f = sys.stdin else: f = open(arg[0]) hist(sys.stdout, f) if __name__ == '__main__': main()