From 9b345841e4633ccf8891aae5d051af07e388e32a Mon Sep 17 00:00:00 2001 From: Austin Blanton Date: Wed, 30 Sep 2015 16:43:42 -0400 Subject: [PATCH] put some HTML visualization functions in brpy --- scripts/brpy/face_cluster_viz.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/brpy/html_viz.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ scripts/face_cluster_viz.py | 63 --------------------------------------------------------------- 3 files changed, 111 insertions(+), 63 deletions(-) create mode 100755 scripts/brpy/face_cluster_viz.py create mode 100644 scripts/brpy/html_viz.py delete mode 100755 scripts/face_cluster_viz.py diff --git a/scripts/brpy/face_cluster_viz.py b/scripts/brpy/face_cluster_viz.py new file mode 100755 index 0000000..09b23de --- /dev/null +++ b/scripts/brpy/face_cluster_viz.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +import csv, sys, json, argparse +from brpy.html_viz import crop_to_bb + +parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') +parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') +parser.add_argument('img_loc', type=str, help='Location of images on disk (this gets prepended to image path)') +parser.add_argument('--cluster_key', '-c', type=str, default='Cluster', help='The name of the cluster ID header in input_file') +parser.add_argument('--height', '-mh', type=int, default=400, help='Height of the cluster rows in pixels') +parser.add_argument('--output_file', '-o', type=str, default='clustahs.html', help='Where to save the output HTML file.') +args = parser.parse_args() + +maxheight = args.height +clustmap = dict() +with open(args.input_file) as f: + for line in csv.DictReader(f): + c = int(line[args.cluster_key]) + if c not in clustmap: + clustmap[c] = [] + x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] + imname = '%s/%s' % (args.img_loc, line['File']) + try: + html = crop_to_bb(x,y,width,height,imname,maxheight=400) + except IOError: + print('problem with %s' % imname) + continue + clustmap[c].append(html) + +# browsers crash for a DOM with this many img tags, +# so instead this makes links for each cluster that dynamically populate the DOM using the HTML strings from +# the clusters javascript object below (which is a direct translation of clustmap above). +# hacky, but it works. +html = ['', '', '', 'Face clusters', '', ''] +script = ''' + +''' +html.append(script % json.dumps(clustmap)) +for c, imgs in clustmap.items(): + html.append('
' % c) + reveal = 'show cluster %i (count=%i)' + html.append(reveal % (c,c,len(imgs))) + html.append('
') +html.extend(['','']) + +with open(args.output_file, 'w') as out: + out.write("\n".join(html)) diff --git a/scripts/brpy/html_viz.py b/scripts/brpy/html_viz.py new file mode 100644 index 0000000..665afcd --- /dev/null +++ b/scripts/brpy/html_viz.py @@ -0,0 +1,57 @@ +''' +Some funcs to generate HTML visualizations. +Run from the folder you intend to save the HTML page so +the relative paths in the HTML file are correct and PIL can find the images on disk. +Requires local images, but should be pretty easy to set up an apache server (or whatev) +and host them as long as the relative paths remain the same on ya serva. +''' + +from PIL import Image + +def crop_to_bb(x, y, width, height, imname, maxheight=None): + ''' + Generates an HTML string that crops to a given bounding box and resizes to maxheight pixels. + A maxheight of None will keep the original size (default). + When two crops are put next to each other, they will be inline. To make each crop its own line, wrap it in a div. + ''' + img = Image.open(imname) + imwidth, imheight = img.size + if not maxheight: + maxheight = height + ratio = maxheight / height + # note for future me: + # image is cropped with div width/height + overflow:hidden, + # resized with img height, + # and positioned with img margin + html = '
' % (width*ratio, maxheight) + html += '' % (imname, imheight*ratio, y*ratio, x*ratio) + html += '
' + return html + +def bbs_for_image(imname, bbs, maxheight=None, colors=None): + ''' + Generates an HTML string for an image with bounding boxes. + bbs: iterable of (x,y,width,height) bounding box tuples + ''' + img = Image.open(imname) + imwidth, imheight = img.size + if not maxheight: + maxheight = imheight + ratio = maxheight/imheight + html = [ + '
', + '' % (imname, maxheight) + ] + if not colors: + colors = ['green']*len(bbs) + html.extend([ bb(*box, ratio=ratio, color=color) for color,box in zip(colors,bbs) ]) + html.append('
') + return '\n'.join(html) + + +def bb(x, y, width, height, ratio=1.0, color='green'): + ''' + Generates an HTML string bounding box. + ''' + html = '
' + return html % (color, color, x*ratio, y*ratio, width*ratio, height*ratio) diff --git a/scripts/face_cluster_viz.py b/scripts/face_cluster_viz.py deleted file mode 100755 index 7752c34..0000000 --- a/scripts/face_cluster_viz.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python - -from PIL import Image -import csv, sys, json, argparse - -parser = argparse.ArgumentParser(description='Visualize face cluster results in an HTML page.') -parser.add_argument('input_file', type=str, help='Results from clustering (in csv format)') -parser.add_argument('img_loc', type=str, help='Location of images on disk (this gets prepended to image path)') -parser.add_argument('--cluster_key', '-c', type=str, default='Cluster', help='The name of the cluster ID header in input_file') -parser.add_argument('--height', '-mh', type=int, default=400, help='Height of the cluster rows in pixels') -parser.add_argument('--output_file', '-o', type=str, default='clustahs.html', help='Where to save the output HTML file.') -args = parser.parse_args() - -maxheight = args.height -clustmap = dict() -with open(args.input_file) as f: - for line in csv.DictReader(f): - c = int(line[args.cluster_key]) - x,y,width,height = [ float(line[k]) for k in ('Face_X','Face_Y','Face_Width','Face_Height') ] - imname = '%s/%s' % (args.img_loc, line['File']) - try: - img = Image.open(imname) - imwidth, imheight = img.size - except IOError: - print('problem with %s' % imname) - continue - ratio = maxheight / height - # note for future me: - # image is cropped with div width/height + overflow:hidden, - # resized with img height, - # and positioned with img margin - html = '
' % (width*ratio, maxheight) - html += '' % (imname, imheight*ratio, y*ratio, x*ratio) - html += '
' - if c not in clustmap: - clustmap[c] = [] - clustmap[c].append(html) - -# browsers crash for a DOM with this many img tags, -# so instead this makes links for each cluster that dynamically populate the DOM using the HTML strings from -# the clusters javascript object below (which is a direct translation of clustmap above). -# hacky, but it works. -html = ['', '', '', 'Face clusters', '', ''] -script = ''' - -''' -html.append(script % json.dumps(clustmap)) -for c, imgs in clustmap.items(): - html.append('
' % c) - reveal = 'show cluster %i (count=%i)' - html.append(reveal % (c,c,len(imgs))) - html.append('
') -html.extend(['','']) - -with open(args.output_file, 'w') as out: - out.write("\n".join(html)) -- libgit2 0.21.4