用 PIL 制作 ASCII Art
2012年2月26日 13:22
把每一个字符绘制到一个小的区域中, 然后计算整个区域中的灰白比例, 得到对应的一个参数, 按参数排列得到一个字符按疏密排序后的列表.
import Image, ImageDraw, ImageFont from itertools import product def get_data_list(): fontpath = '/usr/share/fonts/TTF/DejaVuSansMono.ttf' fontsize = 20 h = 24 w = int(h*0.618) charlist = r'''0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~!@#$%^&*()-_=+[{]}\|;:'",<.>/?''' def get_char_data(c): im = Image.new('L',(w, h),255) draw = ImageDraw.Draw(im) font = ImageFont.truetype(fontpath, fontsize) draw.text((0,0), c, font = font) #im.show() ans = 0.0 for i, j in product(range(w), range(h)): ans += im.getpixel((i,j)) return ans / (255.0 * w * h) lst = map(lambda c: [c, get_char_data(c)], charlist) lst.sort(key=lambda i: i[1]) return lst
然后把要转换的图片转成灰度, 因为每个字符的宽长比大约是1/2, 所以图片的宽度要先伸展2倍, 然后缩小的一个需要的大小, 用每个像素的灰度值索引到之前算出的列表中的相应字符, 输出即可.
import Image from pre import get_data_list from itertools import product clst = get_data_list() im = Image.open('stevejobs.png') size = list(im.size) size[0]=size[0]/0.5 width = 70 size = (width,int(size[1]/size[0]*width)) im = im.resize(size) im = im.convert('L') #im.show() fo = file('asciiart.txt','w') for j in range(size[1]): for i in range(size[0]): idx = int( im.getpixel((i,j)) / 255.0 * (len(clst)-1) ) fo.write(clst[idx][0]) fo.write('\n') fo.close()
效果图:
离远点看, 还是有点意思的~
python的闭包和装饰器
2011年12月06日 21:02
- 闭包(closure)
>>> def F(name): def f(): print name return f >>> f1=F('f1') >>> f2=F('f2') >>> f1() f1 >>> f2() f2
F 是一个返回函数的函数。对于 F 产生的函数 f1、f2,其依赖于F的本地变量 name,但是在F执行结束后,函数 f1、f2 依然可以访问 name,而且相互独立。就像在返回内层函数时,把产生它的环境一块儿打包返回了,这种语法现象叫闭包(closure)。
在《Programming in Lua 2nd》里展示了闭包的各种绚丽的用法,感觉 lua 把闭包和元编程用到极致了。即使不打算用 lua,也强烈推荐读一读,对了解 python 和其他动态语言的本质和实现或有帮助。
在 lua 中有这样一种写法:
>> function gen_ins(init) .. local i=init-1 .. return function() i=i+1; return i end .. end >> ins5=gen_ins(5) >> ins2=gen_ins(2) >> ins5() => 5 >> ins2() => 2
但是这在 python 中却行不通:
>>> def gen_ins(init): i=init-1 def ins(): i=i+1 return i return ins >>> ins0=gen_ins(0) >>> ins0() Traceback (most recent call last): File "<pyshell#111>", line 1, in <module> ins0() File "<pyshell#108>", line 4, in ins i=i+1 UnboundLocalError: local variable 'i' referenced before assignment
这就是所谓的 python 不支持真闭包,python 中对外层变量不能赋值。一般在 python 的函数中,变量的查找顺序是局部符号表,全局符号表,内置符号表。我们知道,全局变量可以被引用,但是要赋值的话,必须用 global 声明一下,否则其实是新建了一个本地变量。从python解释器的角度看,“i=i+1”中 i 既然被赋值,就应该当做本地变量,所以就会发生上述 UnboundLocalError 错误。i 也并不是全局变量,所以在 ins 中用 global 声明了 i 则会找不到全局变量。按照 PEP3104 来看,这是一个由来已久的问题。
PEP3104 中提到一种解决办法如下:
>>> def gen_ins(init): class T: pass t=T() t.i=init-1 def ins(): t.i=t.i+1 return t.i return ins >>> ins0=gen_ins(0) >>> ins0() 0
称为"wrapping it in a mutable object",看起来很诡异。因为Guido觉得不能改动关键字 global 的含义,所以在 python 3 中引入了 nonlocal 关键字,总算解决了这个问题。
- 装饰器(decorators)
前面的闭包算是装饰器的引子了。装饰器有两种形式:
@A def foo(): pass
相当于:
def foo(): pass foo = A(foo)
而:
@A(arg) def foo(): pass
则相当于:
def foo(): pass foo = A(arg)(foo)
可以看出第一种的装饰器是个返回函数的函数,第二种的装饰器是个返回 返回函数的函数 的函数。两种装饰器的简单示例:
def A(func): def newfunc(*args, **argkw): print 'A' return func(*args, **argkw) return newfunc def A(arg): def _A(func): def newfunc(*args, **argkw): print arg return func(*args, **argkw) return newfunc return _A
装饰器中的嵌套定义的函数就涉及到 python 中闭包的问题。
装饰器可以做很多事,比如在原函数调用前检查参数,或者检查登陆状态,调用后记录日志什么的。python 中默认有 staticmethod 和 classmethod 两个装饰器。
staticmethod用来声明类的静态方法,这样调用时就不会传入实例对象(self):
>>> class T: name = 'T' @staticmethod def getname(): print T.name >>> T.getname() T
classmethod 修饰那些直接以类对象作为参数的方法:
>>> class T: name = 'T' @classmethod def getname(a_class): print a_class.name >>> T.getname() T
其实更”正常“的是不用装饰器:
>>> def getname(a_class): print a_class.name >>> class T: name = 'T' getname = classmethod(getname) >>> T.getname() T
pygame实现文本转图片
2011年11月12日 13:35
渣浪微博上随处可见把大段的文字转成图片发布的。一直以为需要PIL才能实现,今天看到pygame也有这种功能,而且还很简单,看例子:
# coding: utf-8 import pygame pygame.init() # 所有可用系统字体 # print pygame.font.get_fonts() # 字体名, 大小 font = pygame.font.SysFont('microsoftyahei', 16) # 字儿们, 是否开反锯齿, 前景色, 背景色 text1 = font.render(u'根据相关法律', True, (0,0,0), (255,255,255)) pygame.image.save(text1, 'text1.jpg') # 带透明的话不设背景色 text2 = font.render(u'此页无法显示', True, (0,0,0)) pygame.image.save(text2, 'text2.png') # 组合 w, h = text1.get_size() surface = pygame.Surface((w, 2*h)) surface.fill((255, 255, 255)) # 因为默认背景黑 surface.blit(text1, (0, 0)) surface.blit(text2, (0, h)) pygame.image.save(surface, 'all.png')
学习自: 用Python和Pygame写游戏-从入门到精通(4)
还可参考这个使用PIL的例子: Python: 纯文本转PNG
Google Reader 导出数据搜索预览小工具
2011年11月02日 15:06
updated at 2013/3/14: R.I.P Google Reader.
Google Reader改版了,是好是坏众说纷纭,总之原来那些分享的文章变成冷冰冰的数据包了。在Google悔过之前,我们总不能对着这一个个数据包发呆吧,特别是突然想找什么老文章的时候。所以在大牛 @isnowfy 的启示下有了这个小工具^_^
需要说明的是针对的是“阅读器 JSON”这种数据包,比如 shared-items.json 和 starred-items.json。搜索框里回车可以对标题进行搜索,列表框里单击可以在右侧看到详细信息,比如url地址和去html标签后的网页内容。去html标签使用了BeautifulSoup,所以请自备 BeautifulSoup.py在同一目录下 BeautifulSoup4。由于对Tk(Tkinter)这个古老而恶心的GUI不熟悉,界面上请不要苛责了^_^
附上使用图片和脚本:
# coding: utf-8 import json from Tkinter import * import tkFileDialog from bs4 import BeautifulSoup root = Tk() e = StringVar() li, te, data, items = [None]*4 sanitize_all_html = lambda value: BeautifulSoup(value).get_text() def selectFile(): global data, items filename = tkFileDialog.askopenfilename(parent=root, initialdir='.', filetypes=[('GR data', '.json')]) data = json.load(open(filename)) items = data['items'] if 'items' in data else data items = filter(lambda i: 'title' in i, items) filterItems() def filterItems(event=None): global data, items li.delete(0, END) s = e.get() for i in items: title = i['title'] try: content = ('summary' in i and i['summary'] or i['content'])['content'] except: content = '' if s in title or s in content: li.insert(END, title) def showItemInfo(event=None): te.delete(1.0, END) title = li.get(li.curselection()) item = filter(lambda i: i['title'] == title, items)[0] te.insert(1.0, item['title']+'\n') te.insert(2.0, ('author' in item and item['author'] or 'NO AUTHOR')+'\n') te.insert(3.0, item['origin']['title']+'\n') te.insert(4.0, item['origin']['htmlUrl']+'\n') te.insert(5.0, ('alternate' in item and item['alternate'][0]['href'] or 'NO URL')+'\n\n') if 'summary' in item: te.insert(7.0, sanitize_all_html(item['summary']['content'])+'\n') else: te.insert(7.0, sanitize_all_html(item['content']['content'])+'\n') f = Frame(root) lf = Frame(f) rf = Frame(f) f.pack(fill='both', expand=1) lf.pack(side='left', fill='both') rf.pack(side='left', fill='both', expand=1) fbt = Button(lf, text="Select file", height=1, command=selectFile) fbt.pack(side='top', fill=X) en = Entry(lf, textvariable=e, width=30) en.bind('<Return>', filterItems) en.pack(side='top', fill=X) liy = Scrollbar(lf, orient=VERTICAL) li = Listbox(lf, yscrollcommand=liy.set) li.bind('<<ListboxSelect>>', showItemInfo) liy['command'] = li.yview li.pack(side='left', fill='both', expand=1) liy.pack(side='left', fill=Y) tey = Scrollbar(rf, orient=VERTICAL) te = Text(rf, yscrollcommand=tey.set, height=30, font=('Microsoft Yahei', '12')) tey['command'] = te.yview te.pack(side='left', fill='both', expand=1) tey.pack(side='left', fill=Y) root.title('GR Data Reader') root.mainloop()