Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
# -*- coding: utf-8 -*- import sys import os import Image from numpy import * def save(a, name): Image.fromarray(bit2rgb(a)).save(name) def bit2rgb(b): b = b[:,:,newaxis]*255 return concatenate((b, b, b), 2) def pixels_of(img): for y, x in combinations_of(range(img.shape[0]), range(img.shape[1])): yield y,x def combinations_of(A,B): for a in A: for b in B: yield a,b def area_of(yx_1, yx_2, yx_3): y1, x1 = yx_1 y2, x2 = yx_2 y3, x3 = yx_3 return 1/2. * abs(-x2*y1 + x3*y1 + x1*y2 - x3*y2 - x1*y3 + x2*y3) def dist(yx1, yx2): y1, x1 = yx1 y2, x2 = yx2 return sqrt(float((x2-x1)**2 + (y2-y1)**2)) def fill(pixel, (y,x), val): pixel = pixel.copy() edge = [(y,x)] while edge: newedge = [] for (y,x) in edge: for (s, t) in ((y, x+1), (y, x-1), (y+1, x), (y-1, x)): try: p = pixel[s, t] except IndexError: pass else: if p != val: pixel[s, t] = val newedge.append((s, t)) edge = newedge return pixel def count_neighbors((y,x), val, img): "return the number of pixels adjacent to (Y,X) with value `val`." c = 0 for t,s in [(y-1, x), ( y, x+1), (y+1, x), ( y, x-1),]: if (t < 0 or t >= img.shape[0]) or (s < 0 or s >= img.shape[1]): c+=1 continue if img[t,s] == val: c+=1 return c def neighbors_of(yx): y,x = yx for yx in [(y-1, x-1), (y-1, x), (y-1, x+1), ( y, x-1), ( y, x+1), (y+1, x-1), (y+1, x), (y+1, x+1)]: yield yx def shapes_from(a): # return the seperate contiguous shapes of a. a = a.copy() while True: Y, X = a.nonzero() YX = zip(Y,X) y,x = min(YX) b = fill(a, (y,x), 0) s = logical_xor(b, a) yield s a = logical_xor(s,a) if not a.any(): break def poly_from(shp): if not shp.any(): return [] Y, X = shp.nonzero() YX = zip(Y,X) y,x = min(YX) wfirst = (y, x) bfirst = (y-1, x) hist = [(wfirst, bfirst)] wcurs, bcurs = wfirst, bfirst # Traverse the shape in a clockwise manner, recording all # pixels as we go. while True: wn = [yx for yx in list(neighbors_of(wcurs))+[wcurs] if yx in YX] bn = [yx for yx in list(neighbors_of(bcurs)) + [bcurs] if not yx in YX] distances = [dist(w, b) for w, b in combinations_of(wn, bn)] temp = sorted(zip(distances, list(combinations_of(wn, bn)))) next_curs = [wb for d, wb in temp if d < 2.0 and wb not in hist] if not next_curs: break for wb in next_curs: wcurs, bcurs = wb hist.append(wb) break poly = [w for w,b in hist] return poly def simplified(poly, min_area = 0.25): # simplify polygon i = 0 while True: if i+2 >= len(poly): break j,k,m = i, i+1, i+2 if area_of(poly[j], poly[k], poly[m]) < min_area: del poly[k] i = 0 continue else: i+=1 return poly def areatag_from(img_name=None, **kwds): """ Return an html area tag """ behind = False img = asarray(Image.open(img_name)) img = img[:,:,0].astype(bool) if not img.any(): return "", 0 # get the smallest box containing the shape in a. Y, X = img.nonzero() miny, maxy = min(Y), max(Y)+1 minx, maxx = min(X), max(X)+1 box = zeros((maxy-miny+6, maxx-minx+6), bool) box[3:-3, 3:-3] = img[miny:maxy, minx:maxx] # fill in any lonely pixels (a pixel is lonely # if it has less than 2 identical neighbors.) print " Flipping lonely pixels…" for Y, X in pixels_of(box): if count_neighbors((Y,X), box[Y,X], box) < 2: box[Y,X] = not box[Y,X] for Y, X in pixels_of(box): if count_neighbors((Y,X), box[Y,X], box) < 2: box[Y,X] = not box[Y,X] # fill in holes. print " Filling holes…" e = box.copy() for yx in [(0,0), (-1,0), (0,-1), (-1,-1)]: d = fill(box, yx, True) e = logical_and(e, d) if not e.any(): # the shape has a hole. and is behind something behind = True box = logical_or(box, logical_not(d)) # find polys print " Finding shapes…" print " Found:", shapes = [] for i, shp in enumerate(shapes_from(box)): print i+1, shapes.append(shp) print print " Computing bounding polygons from shapes…" polys = [] for i, shp in enumerate(shapes): polys.append(poly_from(shp)) # simplify polys print " Simplifying polys…" polys = [simplified(poly, min_area=1.5) for poly in polys] polys = [[(miny + y - 2, minx + x - 2) for y,x in poly] for poly in polys] # format html tag = [] for poly in polys: tag.append("<area shape='poly' ") if 'name' in kwds: tag.append("name='%(name)s' " % kwds) if 'title' in kwds: tag.append("title='%(title)s' alt='%(title)s' " % kwds) if 'id' in kwds: tag.append("id='%(id)s' " % kwds) if 'href' in kwds: tag.append("href='%(href)s' " % kwds) tag.append("coords='") tag.append(", ".join("%i,%i" % (x,y) for y,x in poly)) tag.append("'></area>\n") return "".join(tag), behind def rgbint(color): """ Converts an rgb integer into an rgb tuple. """ color = int(color) - 2**25 return (color % 2**8, color % 2**16 / 2**8, color / 2**16) def rgbtup(rgb): r,g,b = rgb return 2**25 + b * 2**16 + g * 2**8 + r if __name__ == '__main__': masks = [] if not os.path.exists("./masks"): os.mkdir("masks") base_img = sys.argv[1] f = open(sys.argv[2]) for line in f: ix, zone, color = line.split() color = rgb(color) new_mask = "masks/%s.png" % zone if not os.path.exists(new_mask): ex = 'convert %s -transparent "rgb%r" '\ '-fx "a" +matte -negate %s' % (base_img, color, new_mask) print ex os.system(ex) masks.append([zone, new_mask]) area_tags = [] for z, m in masks: print "Processing area tag for %s." % z tag, behind = areatag_from(m, href=z, name=z) if not tag: continue if not behind: area_tags.insert(0, tag) continue area_tags.append(tag) html = file("test.html", "w") html.write("<html><body>\n<map name='#zonemap'>\n") for tag in reversed(area_tags): html.write(tag) html.write("</map>\n") html.write("<img src='%s' usemap='#zonemap'>\n" % base_img) html.write("</body></html>\n")
This paste will be private.
From the Design Piracy series on my blog: