前几天因为某些原因,我把饭否上所有的好友和关注者全部清空了。当然,如果没有程序的帮忙,估计还不等我删完我也就后悔了。
我没有那么狠心的把饭否的消息给清空,因为消息是不可恢复的(而且也太多了),但好友和关注者是可以的。做事情都给自己留后路显然是我一贯的风格,不然的话,我大概早从我家阳台跳下去了……
Read the rest of this entry »
前几天因为某些原因,我把饭否上所有的好友和关注者全部清空了。当然,如果没有程序的帮忙,估计还不等我删完我也就后悔了。
我没有那么狠心的把饭否的消息给清空,因为消息是不可恢复的(而且也太多了),但好友和关注者是可以的。做事情都给自己留后路显然是我一贯的风格,不然的话,我大概早从我家阳台跳下去了……
Read the rest of this entry »
Comments
上一篇日志里面我做了一个从 VeryCD 为 Rhythmbox 提取专辑封面的程序,后来想到豆瓣其实也有这个东西,于是又做了一个豆瓣的。然后想,既然这样我干脆把原来的封面艺人插件 fork 出来,自己开一个新项目,专门做封面艺人插件的 China Mod~
现在已经开好了,就在 Google Code 上:http://code.google.com/p/rhythmbox-artdisplay-cn/
其实说 Mod 也不算,就是加了几个扩展进去……主要是考虑到如果不 fork 似乎很单独安装,所以就 fork 出来了……那个插件看过去也很长时间没有更新了呢?
第一个版本0.1可以点击这里下载:rhythmbox-artdisplay-cn-0.1.tar.bz2,暂时还没有做任何安装包,deb 包和 arch 的包应该会随后奉上~
安装很简单,常规的 make 和 make install 就可以了~
如果各位有什么改进意见,欢迎提出~
Comments
继上一篇文章之后,我又下大力气对这个程序做了许多修改,在精确度和速度方面似乎都有些许提高。在此推出第二季~
在上一次的程序中使用的 PIL 似乎是因为不支持宋体 ttc 文件中对于小字体下优化的点阵形式,才在选择小于 19px 的字号时不能正确渲染汉字。考虑到这一点,我就想到把 ttc 文件里面 12px 的点阵字体单独提取出来使用,毕竟贴吧上面显示 ID 都是用这个字号显示的。
使用 FontForge 提取出来了 simsun-12.bdf 文件,就是宋体 12px 下的点阵。参考 PIL 的手册,发现 PIL 不能直接使用 .bdf 文件,需要使用一个叫做 pilfont 的脚本转换成专有的 .pil 文件才行。我想转换就转换呗。simsun-12.bdf 一个 2.4MB 的文件,转换完就剩不到 100KB,我就觉得肯定有问题,用 PIL 导入,发现还是不能渲染中文。后来知道,这个 .pil 文件根本不支持非拉丁字母的字符,它的储存空间限定了 256 个字符……
无奈了,这意味着 PIL 完全无法支持中文点阵了……
当然,办法总归是有的,那就是——抛弃 PIL!为什么我能有这样的想法呢,因为看到 .bdf 文件是 UNIX 标准的。UNIX 标准意味着什么呢?记不记得 UNIX 有一个非常好的传统叫做,尽量使用纯文本。是的,这虽然让有些文件会变得太大,不过同时也让这些东西更容易被其他程序读取,而 .bdf 恰好即使这么一种文件。
这样读取 .bdf 点阵字体文件的程序自己写不就好了,什么额外的库都不需要……当然,纠错性极弱就是了~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | line_count = 0 def read_split(f): global line_count line_count += 1 line = f.readline() if not line: return False return line[:-1].split() with open('simsun-12.bdf', 'r') as f: chars = {} line = [''] try: # 获得字符总数 while line[0] != 'CHARS': line = read_split(f) # 读取所有字符 for i in range(int(line[1])): assert read_split(f)[0] == 'STARTCHAR' line = read_split(f) # 编码 assert line[0] == 'ENCODING' char = unichr(int(line[1])) line = read_split(f) # 绘制参数 assert line[0] == 'BBX' width, height, x1, y1 = [int(x) for x in line[1:]] x0, y0 = (1 + x1, 12 - y1 - height) # 准备绘制文字 base_image = [[0 for x in range(14)] for y in range(14)] # 读取并绘制文字 line = read_split(f) assert line[0] == 'BITMAP' for y in range(y0, y0 + height): line = read_split(f)[0] bits = int(line, 16) bits >>= len(line) * 4 - width for x in range(x0 + width - 1, x0 - 1, -1): base_image[y][x] = bits & 1 bits >>= 1 chars[char] = base_image # 结束这个文字 line = read_split(f) assert line[0] == 'ENDCHAR' line = read_split(f) assert line[0] == 'ENDFONT' except AssertionError: print 'line', line_count raise |
这样所有的字符就被读入,并变成一个单色像素二位数组了。
当然,这个性能很低,在我的机器上转换读取文件的2W+字符大概需要 18s,这可能也是为什么 PIL 要选择进行转换。事实上,使用纯文本储存一直以来都给 UNIX 风格的这一类软件带来一定性能缺陷。不过其实,这很值得,因为方便。
不过这个时间确实是太长了,更何况我们到目前为止还什么都没处理。怎么办呢?两个想法:一、优化代码;二、保存处理的数据。
第一种,基本上是没什么希望了,而且即使能优化,估计效果也不会太好,可能省个几秒封顶了。第二个显然不是个坏想法。
Python 在数据的持续化方面还是有很多现成的东西的,比如 pickle 什么的。不过那个速度太慢,而且是纯文本!好吧,偶尔我也会不喜欢纯文本,因为在这里意义不大……因此选择了 marshal。marshal 也是一个用于数据持续化的库,不过仅能对 Python 的内部类型进行。我会看中它最重要的原因就是它的应用范围极其有限,只能持续化内部类型。如果一个 Python 标准库,它有很明显的限制,却没有标明不推荐或在新版中被剔除,说明它必然有一个其他库不可及的优势。对于 marshal,我猜它的优势就是效率。
使用 marshal 就很简单了……
62 63 | import marshal marshal.dump(chars, open('base_image', 'wb')) |
这样后面的处理不需要不断重复这个低效的步骤了~
原来是用 PIL 处理文字图像,现在抛弃 PIL 了,就得自己写了……不过这样也很好,自由发挥的空间很大了~
我猜用的是 Matrix67 大牛的那种在附近留阴影的方法,不过似乎我写的不够好就是了,怎么测试效果都不大理想。除此之外,纯 Python 实现的算法效率和 PIL 这种包装还是没得比,很简单的算法却慢的不得了……
下面是目前的处理代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | #!/usr/bin/python # - * - coding: utf8 - * - import marshal import copy base_chars = marshal.load(open('base_image', 'rb')) chars = {} for char, image in base_chars.iteritems(): new_image = [] for y in range(14): new_row = [] for x in range(14): if image[y][x]: value = 81 else: value = 0 if y > 0 and image[y-1][x]: value += 4 if y < 13 and image[y+1][x]: value += 4 if x > 0 and image[y][x-1]: value += 4 if x < 13 and image[y][x+1]: value += 4 new_image.append(value) chars[char] = new_image marshal.dump(chars, open('advanced_data', 'wb')) |
处理效果不是很理想就是了,耗时大概也是 30s+。
其实这个部分就是一样的了……直接贴代码好了……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | #!/usr/bin/python # - * - coding: utf8 - * - import marshal chars = marshal.load(open('advanced_data', 'rb')) def image_diff(image1, image2): ret = 0 for v1, v2 in zip(image1, image2): ret += (v1 - v2) ** 2 return ret remember_number = 5 try: searched = marshal.load(open('searched_chars', 'rb')) except IOError: searched = {} while True: input_string = unicode(raw_input('ID: '), 'utf8') if not input_string: break for char in input_string: if char not in chars: continue if char not in searched: diff_data = [] image = chars[char] for c, v in chars.iteritems(): if c == char: continue diff_data.append((image_diff(v, image), c)) diff_data.sort() searched[char] = diff_data[:remember_number] print char, for item in searched[char]: print u'({0}, {1})'.format(*item), print print marshal.dump(searched, open('searched_chars', 'wb')) |
和上次不同的是查找过的字会被保存下来,效率可以高一些……
现在的主要问题就是如何提高相似度的识别精度了……目前的想法是通过逐像素比对测试两个字的相似度,最多加一些模糊化什么的处理。doggy 提出一个想法是计算连通区域面积的比例,我个人认为不大可行……我的想法是识别文字的笔画,把文字的骨架弄出来,然后对比什么的,可能效果更好吧?
不知道各位还有没有其他什么想法?
最后是完整版的目前的程序以及已经生成的数据,各位有兴趣可以直接拿来试用了……find-similar-character.tar.bz2 (1.7MB)
Comments
今天大学军训完了,不想做什么正经事,就想到前一段时间想做的寻找相似汉字的程序,用以寻找更高仿的贴吧 ID。用程序来寻找相似汉字,从另一个角度,也是从 Matrix67 大牛的一篇日志里得到的启发。不过 Matrix67 大牛使用的是 Mathematica 来寻找,我不大会 Mathematica,就想用我熟悉的 Python 来解决,毕竟 Python 是一个很强大的东西~
Read the rest of this entry »
Comments
今天突然有兴致来对比一下我在过去的一年中主要学习的两种语言,Python 和 Erlang,他们都是很神奇的语言。不过,在我看来,更神奇的是,这两种语言几乎是完全对立的!
下面我弄了张表格来对比他们:
| 名称 | Python | Erlang |
|---|---|---|
| 主要范型 | 面向对象 | 函数式、面向并发 |
| 开发者 | Python 基金会 | 商业公司 爱立信 |
| 文件编码 | 在 Python 2 中默认为 ASCII,Python 3 中默认为 UTF-8,但都可以制定任意编码。 | 默认为 Latin-1,不允许指定文件编码。不过事实上编译器并不会在意文件中是否存在“不正确”的字符。 |
| 变量 | 几乎什么都是变量,变量都可以不断变化,无论是值还是类型。甚至你可以改变 True 和 False,让他们颠倒。(当然,也有例外,如 None,我至今没找到改变他的方法) | 且不说原子是显然不可变的,任何一个变量被赋值之后,他的值也是不可变的,这是所谓变量绑定。(单次赋值) |
| 字符串 | 拥有完善的字符串支持,在 Python 3 中这更是发挥到了极致。 | 根本不存在字符串类型,一切字符串都是字符列表,因此就更没有什么强大的字符串处理函数了。 |
| 列表 | 可以根据下标索引快速获取列表任意位置的内容。 | 考虑效率的话,只能从列表的头部摘取内容。 |
| 模块 | 依据文件系统的文件分布构建层级式的模块体系,使用模块前必须声明将模块引入当前名字空间。 | 所有模块是平行的,在同一层中,模块名称强制与文件名相同。使用模块时模块必须已经被编译,Erlang 将会自动寻找模块并读取。 |
| 并行 | 通过 GIL 这一全局锁将所有多线程操作退化为单线程操作,不存在任何并发性。(不过 Python 正在尝试加入新的模块来改变这一问题) | 天生就是面向并发的……用他仅做顺序编程是一种莫大的浪费…… |
| C 接口 | 使用 Python.h 编写接口,编译为动态链接库,运行时调入 Python 内使用。 | 编写外部程序,通过标准输入输出与 Erlang 主进程交换数据,在 Erlang 进程空间外单独运行。(也有一种在进程空间内运行的接口,不过不推荐使用) |
| 函数重载 | 不支持任何形式的函数重载,但支持可选参数。 | 支持参数数量不同的同名函数,但不支持可选参数。而且基于模式匹配,支持同一函数的不同分支的定义。 |
| 数据库 | 内置了通用的 SQLite 数据库支持,可以通过 SQL 语句进行数据库操作,储存的信息限制在文本、数字等几种。 | 内置了专用的 Mnesia,可以与 Erlang 无缝衔接地储存任何内容,通过 Erlang 的 qlc 模块不使用 SQL 语句进行数据库操作。(NoSQL) |
| 代码热替换 | 支持使用 reload 函数重新载入一个模块。 | 模块的新版本被编译后任何调用即采用新的代码。 |
可以看到这两种语言真的是非常非常不同,我想这和他们的设计初衷和适用范围是很有关系的:Python 大多数时候被用于快速的原型开发、用作胶水语言、创建小型桌面级应用程序;而 Erlang 则在一开始就被用在电信,这一对容错、并发要求较苛刻的领域。
不过我还是觉得,这两种语言都是非常优美的,以他们自己的专长而言。不过 Python 已经非常不错了,Erlang 在我看来则还有很大的发展空间~
PS:以上仅仅是就我自己的了解给出的一些对比,疏漏和错误在所难免,也希望看到了及时告诉我~
Comments
17号的时候虫子向我求助,想要我帮忙做一个支持海地的小网站。大体的思路就是,在上面的放一个大地图,然后大家可以留言、上传照片什么的,然后就在地图上出现亮点,然后亮点点进去可以看到留言和照片~这个网站已经进入公测了,大家可以在这里查看:Haiti <3(话说我曾经问虫子“<3”是什么意思,她告诉我,是爱心的意思~)
我猜从来没做过网站的人一定会觉得这个很简单,但我觉得一点都不简单……不过似乎可以实现~这种无疑又是对我技术的一次挑战!
Read the rest of this entry »
Comments
昨晚花了几个小时写了个计算人人网(其实我还是更喜欢叫他校内)当中,任意两个用户的好友之间交集的在线小工具,可以到我的实验室里看看这个小工具:人人网好友交集。
话说能写出这样的工具,主要有赖于我的空间提供商将系统换为 FreeBSD 后可以解析 Python 了,而且也没有限制 CGI,所以就成功了~
Read the rest of this entry »
Comments
看这个标题一定很奇怪:难道我以前控制音乐播放不用鼠标么?这个文章的标题看起来像鼠标刚刚发明的推广期的文章……不过,当然不是这样的!
其实只是想:如何把我的小本合上放书架上,当作一个音乐播放器+功放,并用我的无线6键鼠当遥控器遥控控制之。
想想其实还是蛮有意义的功能,这样我做作业的时候可以不用戴耳机,不用用MP3,直接把本当播放器;同时,我不会看到屏幕上的东西,可以安心做作业~再看看我的6键无限鼠,那额外的功能键平时根本不用,也想不出能有什么用……这么好的东西就这样被我浪费了……(话说,拿本当音乐播放器是不是更浪费?)
Comments
非常无语的,同济大一什么物理理论都没学,需要的高数基础也都没有学过,竟然要我们先学物理实验,而且要处理数据,计算平均值和标准误差什么的,十分麻烦……
上次处理数据弄了个零碎的小程序来,很麻烦,这次就把他统一编为一个模块,方便以后使用……这个模块里面的说有公式依据是同济大学出版社《物理实验教程》第1版第二章的内容。
如何使用这个模块呢?首先,作为一个 Python 模块,肯定要用 Python……
直接输入数据,标准格式输出~
最后是代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | #!/usr/bin/env python # -*- coding: utf-8 -*- import math sqrt = math.sqrt def gen_imet(func): def retfunc(self, other): ret = func(self, other) self.ave = ret.ave self.u = ret.u self.digs = ret.digs return self return retfunc def get_digs(a, b): if a == 0: return b elif b == 0: return a else: return min(a, b) class PhyStat: """ 物理实验数据 PhyStat(ave, u = 0.0, digs = 0) ave 表示平均值,如果传入一列数则自动计算平均值及其标准误差 u 表示标准误差,如果 ave 传入数据列表则此处表示仪器精度 digs 表示有效数字个数,0 表示不限制 """ def __init__(self, ave, u = 0.0, digs = 0): """ 对于 ave 为数据列表的情况,使用下面公式计算平均值: ave = Σx[i] / n 根据下面公式计算标准误差: S = Sqrt(Σ(x[i]-ave)^2 / (n - 1)) u = Δinst / Sqrt(3) U = Sqrt(S^2 + u^2) """ self.digs = digs if isinstance(ave, tuple) or isinstance(ave, list): data = ave n = len(data) ave = math.fsum(ave) / n self.ave = ave s = 0.0 for i in data: s += (i - ave) ** 2 s = sqrt(s / (n - 1)) u /= sqrt(3) self.u = sqrt(s ** 2 + u ** 2) else: self.ave = float(ave) self.u = abs(float(u)) def __repr__(self): ave = self.ave exp = int(math.floor(math.log10(ave))) exp10 = 10 ** exp ave = ave / exp10 u = self.u / exp10 digs = self.digs if digs == 0: ret = '%f ±%f' else: ret = '%%.%df ±%%.%df' % (digs, digs) if exp: ret = '(%s) E %d' % (ret, exp) return ret % (ave, u) __neg__ = lambda self: PhyStat(-self.ave, self.u, self.digs) __pos__ = lambda self: self __abs__ = lambda self: PhyStat(abs(self.ave), self.u, self.digs) def __add__(self, other): if isinstance(other, PhyStat): ave = self.ave + other.ave u = sqrt(self.u ** 2 + other.u ** 2) digs = get_digs(self.digs, other.digs) return PhyStat(ave, u, digs) else: ave = self.ave + other return PhyStat(ave, self.u, self.digs) __radd__ = __add__ __sub__ = lambda self, other: self + (-other) __rsub__ = lambda self, other: other + (-self) def __mul__(self, other): if isinstance(other, PhyStat): a = self.ave b = other.ave ave = a * b u = ave * sqrt((self.u / a) ** 2 + (other.u / b) ** 2) digs = get_digs(self.digs, other.digs) return PhyStat(ave, u, digs) else: ave = self.ave * other u = self.u * other return PhyStat(ave, u, self.digs) __rmul__ = __mul__ def __div__(self, other): if isinstance(other, PhyStat): return self * other ** -1 else: return (1.0 / other) * self __rdiv__ = lambda self, other: other * self ** -1 def __pow__(self, other): a = self.ave ave = a ** other u = abs(other * self.u / a * ave) return PhyStat(ave, u, self.digs) __iadd__ = gen_imet(lambda self, other: self + other) __isub__ = gen_imet(lambda self, other: self - other) __imul__ = gen_imet(lambda self, other: self * other) __idiv__ = gen_imet(lambda self, other: self / other) __ipow__ = gen_imet(lambda self, other: self ** other) __int__ = lambda self: int(self.ave) __long__ = lambda self: long(self.ave) __float__ = lambda self: self.ave |
如果大家发现什么 bug,或者有什么改进建议,欢迎提哈~ Read the rest of this entry »
Comments
曾经的曾经,很早很早以前,我发布了一个叫 ujudger-base 0.1 的东西,是用 C++ 写成的简单测评程序,仅能测试程序运行时间和内存耗用(还不准确)。
这次发布的是一个完整的测评机,支持传统题型、交互题和提交答案题,可导出测评结果。整个程序使用 Python 编写,直接把 ujudger.py 复制到待测目录下并编写 config 文件即可测评。
不过现在这个程序只有一个文字界面,非常简陋,而且安全性不佳。但现在正在撰写的 ujudger 1.0 (版本跨越大,意味着变化之巨大……) 将就这些问题有重大改进,敬请期待。
另外,有人询问是否准备让这个测评机与另一个 Windows 下的开源测评机 Cena 兼容。我的回答是,由于设计差异,可能比较困难。对比 Cena 的自定义校验器,ujudger 的自定义校验器更强大,同时 Cena 的校验器可以通过一点简单的修改或加一个脚本外壳就可以在 ujudger 中使用。在 1.0 中计划兼容 Cena 的校验器。至于配置文件,1.0 中配置文件的格式会有较大变动,不过可能可以通过转换程序将 Cena 的配置文件转为 ujudger 的。
我在 Google Code 上开了一个项目,感兴趣的同学可以关注关注~地址:http://ujudger.googlecode.com/
最后,ujudger 0.1 的下载地址也在 Google Code 上:ujudger.py
Comments