Yet Another PhotoMosaic Generator

发布时间:2016-12-9 10:05:49 编辑:www.fx114.net 分享查询网我要评论
本篇文章主要介绍了"Yet Another PhotoMosaic Generator",主要涉及到Yet Another PhotoMosaic Generator方面的内容,对于Yet Another PhotoMosaic Generator感兴趣的同学可以参考一下。

Yet Another PhotoMosaic Generator Update: Mon 25 Feb 2013 11:38:47 AM CST add classic style. More refer to github Python是面向对象的?没有对象面向毛对象。 ——Anonymous Several weeks ago, I saw a poster of presidential campaign for Obama, in which Obama’s portrait was made up of many voter’s photos. It really attracted me, somedays later, I want to make one myself. The completed code host here. It is much more functional than object-oriented… Search the Internet First of all, I searched the Google to find out how others achieve it, then I found many interesting implement and post on it.Along with them, there are pretty demos around.One of the demo of Foto-Mosaik-Edda striked me.It declaims as follows in their site: The Chaos Mosaic Picture is a new form of photo mosaic which can, at present, only be created by Foto-Mosaik-Edda. Uhm…Foto-Mosaic-Edda is an open-source project that really impressive.But it was an C# project. Linux users don’t like it however.I don’t like mono. I searched other open-source implement on photomosaic. I get some simple programs only use gray photos, and some complex ones can make beautiful classic photomosaic(like metapixel, even chaos style which it calls collage style), But none has as beautiful demos as Foto-Mosaic-Edda.(metapixel really amazing, it is robust and quickly.) However, I saw many enthusiastic people write one themselves, it really looks interesting for me. I’ve used PIL for processing images when I tried to decode captchas several days ago, so I believe with the help of PIL, someone can achieve photomosaic simply. So I just read the documentation of PIL, then start my hack. Write My Own PhotoMosaic Generator It’s not hard, however, what you should do is clear and simple: analyse the image to be made mosaic, get a dict in which position as key and color as value.use a bunch of images to get a dict, in which image name as key and colors as value.thumbnail bunches of images and paste it in the right position, so that the big image looks like it consists of many small one. I’d like to got the chaos style, so some other requirements: frame and shadow for small imagesrandom paste small images onto large one Now, let’s go. Frame, Shadow and Rotate first add frame, shadow to small images def add_frame(image): '''Add frame for image.''' im = ImageOps.expand(image, border=int(0.01 * max(image.size)), fill=0xffffff) return im def drop_shadow(image, offset, border=0, shadow_color=0x444444): """Add shadows for image""" # Caclulate size fullWidth = image.size[0] + abs(offset[0]) + 2 * border fullHeight = image.size[1] + abs(offset[1]) + 2 * border # Create shadow, hardcode color shadow = Image.new('RGBA', (fullWidth, fullHeight), (0, 0, 0)) # Place the shadow, with required offset shadowLeft = border + max(offset[0], 0) # if <0, push the rest of the image right shadowTop = border + max(offset[1], 0) # if <0, push the rest of the image down shadow.paste(shadow_color, [shadowLeft, shadowTop, shadowLeft + image.size[0], shadowTop + image.size[1]]) shadow_mask = shadow.convert("L") # Paste the original image on top of the shadow imgLeft = border - min(offset[0], 0) # if the shadow offset was <0, push right imgTop = border - min(offset[1], 0) # if the shadow offset was <0, push down shadow.putalpha(shadow_mask) shadow.paste(image, (imgLeft, imgTop)) return shadow Then a function to rotate images. def rotate_image(image, degree): '''Rotate images for specific degree. Expand to show all''' if image.mode != 'RGBA': image = image.convert('RGBA') im = image.rotate(degree, expand=1) return im ‘RGBA’ mode is to support transparency. What’s matter here is that jpeg/jpg does not support transparency. So you can’t get transparency shadows and rotate pictures if you just use jpg/jpeg images.So, write a function to process images with jpg/jpeg format, transpose it into png. def process_image(filename, newname): '''convert image to png to support transparency''' if filename.split('.')[-1] != 'png': im = Image.open(filename) im.save(newname + '.png') print "processing image file %s" % filename return 1 def process_directory(path): os.chdir(path) count = 1 for filename in os.listdir(path): ext = filename.split('.')[-1] if ext == 'jpeg' or ext == 'jpg': process_image(filename, str(count)) os.remove(filename) count += 1 return 1 Really poor work… But it works for me: ) We have to thumnail bunches of images, It’s easy to thumbnail with PIL: def thumbnail(im, size): """thumnail the image""" im.thumbnail(size, Image.ANTIALIAS) return im Let’s have a fun with them. To get heaps of images randomly on the desktop, I hardcoded these parameters to get my photos work, you HAVE TO find yours: # Just for fun def chao_image(path, size=(800, 800), thumbnail_size=(50, 50), shadow_offset=(10, 10), backgroud_color=0xffffff): image_all = Image.new('RGB', size, backgroud_color) for image in os.listdir(path): if image.split('.')[-1] == 'png': im = Image.open(image) degree = random.randint(-30, 30) im = thumbnail(rotate_image(drop_shadow(add_frame(im), shadow_offset), degree), thumbnail_size) image_all.paste(im, (random.randint(-thumbnail_size[0], size[0]), random.randint(-thumbnail_size[1], size[1])), im) return image_all Calculate Images And Compare Get average colors of an image def average_image(im): """return average (r,g,b) for image""" color_vector = [int(x) for x in ImageStat.Stat(im).mean] return color_vector to compare images? Compare the (r,g,b) value of them. def compare_vectors(v1, v2): """compare image1 and image2, return relations""" if len(v1) == len(v2): distance = 0 for i in xrange(len(v1)): distance += (v1[i] - v2[i]) ** 2 return distance else: print "vector not match in dimensions" I just use distance in (R, G, B) space to calculate similarity, someone advice compare in other space, you can change it just like the example in PIL’s documentation: # May not useful def rgb2xyz(im): """rgb to xyz""" rgb2xyz = (0.412453, 0.357580, 0.180423, 0, 0.212671, 0.715160, 0.072169, 0, 0.019334, 0.119193, 0.950227, 0) out = im.convert("RGB", rgb2xyz) return out But I find many implements just use R,G,B, and it works well. Next, get a dict of image in current path, in which filename as key, average (R,G,B) colors as value. def tile_dict(path): """Return list of average (R,G,B) for image in this path as dict.""" dic = {} for image in os.listdir(path): if image.split('.')[-1] == 'png': try: im = Image.open(image) except: print "image file %s cannot open" % image continue if im.mode != 'RGB': im = im.convert('RGB') dic[image] = average_image(im) return dic We don’t need to calculate every pixel of the large picture, just thumbnail it to get a nearest color of different regions. def thumbnail_background(im, scale): """thumbnail backgroud image""" newsize = im.size[0] / scale, im.size[1] / scale im.thumbnail(newsize) print 'thumbnail size and the number of tiles %d X %d' % im.size return im.size For every pixel in the thumbnailed large image, find most similar small image filenames.(top ten): def find_similar(lst, dic): """for lst([R, G, B], Calculate which key-value in dic has the most similarity.Return first 10)""" similar = {} for k, v in dic.items(): similar[k] = compare_vectors(v, lst) # if len(v) != len(lst): # print v, len(v), lst, len(lst) similar = [(v, k) for k, v in similar.items()] # Not good, lost the same Score similar.sort() return similar[:10] Poor hack, but it really works… Final Work Now it’s the final magic. Get the small image in order, the order imply where it should be. Then rotate, add shadows and frames for small images, finally paste it onto the large one randomly in the right position: def get_image_list(im, dic): """receive a thumbnail image and a dict of image to be mosaic, return tiles(filename) in order(as a list)""" lst = list(im.getdata()) tiles = [] for i in range(len(lst)): #print find_similar(lst[i], dic)[random.randrange(10)][1] tiles.append(find_similar(lst[i], dic)[random.randrange(10)][1]) return tiles def paste_chaos(image, tiles, size, shadow_off_set=(30, 30)): """size is thumbnail of backgroud size that is how many tiles per line and row""" # image_all = Image.new('RGB', image.size, 0xffffff) image_all = image lst = range(len(tiles)) random.shuffle(lst) fragment_size = (image.size[0] / size[0], image.size[1] / size[1]) print 'tiles size %d X %d' % fragment_size print 'number of tiles one iteration: %d' % len(lst) for i in lst: im = Image.open(tiles[i]) degree = random.randint(-20, 20) im = thumbnail(rotate_image(drop_shadow(add_frame(im), shadow_off_set), degree), (fragment_size[0] * 3 / 2, fragment_size[1] * 3 / 2)) x = i % size[0] * fragment_size[0] + random.randrange(-fragment_size[0] / 2, fragment_size[0] / 2) y = i / size[0] * fragment_size[1] + random.randrange(-fragment_size[1] / 2, fragment_size[1] / 2) # print x, y image_all.paste(im, (x, y), im) return image_all I try it like this, I know parameter n is tricky, it was the scale it thumbnail the large image. Maybe I’ll change it to something more clear later… def main(filename, n, scale, iteration, path='./'): # 0. select an big image for mosaic print "open %s" % filename im = Image.open(filename) # 1. process image as png to support transparency print "process directory %s" % path process_directory(path) # 2. get a dict for path print "get tile dict for path `%s`" % path try: with open('dic.txt', 'r') as f: dic = p.load(f) except: dic = tile_dict(path) with open('dic.txt', 'wb') as f: p.dump(dic, f) # 3. thumbnail the big image for compare print "thumbnail background for compare" # n = 30 # 原始图片缩为多少分之一 # scale = 3 # 原始图片放大倍数 big_size = im.size[0] * scale, im.size[1] * scale im_chao = Image.new('RGB', big_size, 0xffffff) imb_t_size = thumbnail_background(im, n) print "how may tiles: %d X %d" % imb_t_size print 'number of iterations: %d' % iteration for i in range(iteration): print 'iteration: %d' % (i + 1) # 4. get a list of smail image for mosaic print "get pic list" im_tiles = get_image_list(im, dic) # 5. paste in chaos style print "generate final image" im_chao = paste_chaos(im_chao, im_tiles, imb_t_size) return im_chao if __name__ == '__main__': im = main('../mm.jpg', 15, 5, 2) im.save('../final3.png') im.show() Demo Do you like it? Can you see it? Demo Download(45.8M) More examples here(Chinese) Thanks My family, It supports me.Never let me down, never pour cold water, never scold for insignificance.

上一篇:一位资深HR的职场感言
下一篇:求的数组的topN

相关文章

相关评论