5
2012
转载:小波变换和motion信号处理(二)
经原作者Windstorm同意,将他两篇关于小波变换的文章转载在此。这两篇文章是我目前看到的最好的中文小波变换入门文章,因此转载过来希望更多的人能看到。这个系列的第三篇作者还没有写,所以暂时只转两篇。本篇的源地址在这里。我仅可能会对一些错别字等小错误进行修饰性修改,不会试图去修改文章本意。
p.s. 这一篇我还不甚明了,有些地方还是很纠结。不过确定的是,下面的内容只涵盖了离散小波变换。在连续小波变换里面应该不需要分成母小波和父小波,连续变换的母小波有点像下文提到的父小波的意思。不过不管怎样,小波变换都需要构造一个小波集合(有限或无线个元素),这个集合中的小波互不相关,并且以它们为basis足以用于表示当前函数。
这是《小波变换和motion信号处理》系列的第二篇,深入小波。第一篇我进行了基础知识的铺垫,第三篇主要讲解应用。
在上一篇中讲到,每个小波变换都会有一个mother wavelet,我们称之为母小波,同时还有一个father wavelet,就是scaling function。而该小波的basis函数其实就是对这个母小波和父小波缩放和平移形成的。缩放倍数都是2的级数,平移的大小和当前其缩放的程度有关。
还讲到,小波系统有很多种,不同的母小波,衍生的小波基就完全不同。小波展开的近似形式是这样:
其中的就是小波级数,这些级数的组合就形成了小波变换中的基basis。和傅立叶级数有一点不同的是,小波级数通常是orthonormal basis,也就是说,它们不仅两两正交,还归一化了。
我们还讲了一般小波变换的三个特点,就是小波级数是二维的,能定位时域和频域,计算很快。但我们并没有深入讲解,比如,如何理解这个二维?它是如何同时定位频域和时域的?
在这一篇文章里,我们就来讨论一下这些特性背后的原理。
首先,我们一直都在讲小波展开的近似形式。那什么是完整形式呢?之前讲到,小波basis的形成,是基于基本的小波函数,也就是母小波来做缩放和平移的。但是,母小波并非唯一的原始基。在构建小波基函数集合的时候,通常还要用到一个函数叫尺度函数,scaling function,人们通常都称其为父小波。它和母小波一样,也是归一化了,而且它还需要满足一个性质,就是它和对自己本身周期平移的函数两两正交:
另外,为了方便处理,父小波和母小波也需要是正交的。可以说,完整的小波展开就是由母小波和父小波共同定义的。
其中是母小波,
是父小波。需要提醒一点的是,这个正交纯粹是为了小波分析的方便而引入的特性,并不是说小波变换的基就一定必须是正交的。但大部分小波变换的基确实是正交的,所以本文就直接默认正交为小波变换的主要性质之一了。引入这个父小波呢,主要是为了方便做多解析度分析(multiresolution analysis, MRA)。说到这里,你的问题可能会井喷了:好好的为什么出来一个父小波呢?这个scaling function是拿来干嘛的?它背后的物理意义是什么?wavelet function背后的物理意义又是什么?这个多解析度分析又是什么呢?不急,下面,我们围绕一个例子来巩固一下前面的知识,同时再引出新的特性。
假设我们有这样一个信号:
该信号长度为8,是离散的一维信号。我们要考虑的,就是如何用小波将其展开。为了方便讲解,我们考虑最简单的一种小波,哈尔小波。下面是它的一种母小波:
那如何构建基于这个母小波的基呢?刚才提到了,要缩放,要平移。我们先试试缩放,那就是ψ(2n):
但这样的话,它与自己的内积就不是1了,不符合小波基orthonormal的要求,所以我们要在前面加一个系数根号二,这样我们就得到了另一个哈尔小波的basis function:
同理,我们可以一直这样推广下去做scale,得到4n,8n,…….下的basis function。当然在这个例子里,我们信号长度就是8,所以做到4n就够了。但推广来说,就是这种scaling对母小波的作用为,这是归一化后的表示形式。
平移的话也很简单,我们可以对母小波进行平移,也可以对scale之后的basis function进行平移。比如对上一幅图中的basis function进行平移,就成了
看得出来,平移后的basis function和母小波以及仅仅scale过的小波,都是正交的,符合小波basis的特点。如果我们用ψ(n)来表示这个mother wavelet,那么这些orthonormal basis函数可以写成:
这里的k是可以看成时域的参数,因为它控制着小波基时域的转移,而j是频域的参数,因为它决定了小波基的频率特性。看到这里,你应该会感觉很熟悉,因为这里的平移和变换本质和刚才对scaling function的平移变换是一模一样的。
这样,我们就有了针对此信号space的哈尔小波basis组合:
图1
可以看出,我们用到了三层频率尺度的小波函数,每往下一层,小波的数量都是上面一层的两倍。在图中,每一个小波基函数的表达形式都写在了波形的下面。
等等,你可能已经发现了,有问题。这里为什么多了个没有函数表达式的波形呢?这货明显不是wavelet function阿。没错,它是之前提到的scaling function,也就是父小波。然后你可能就会问,为啥这个凭空插了一个scaling function出来呢?明明目标信号已经可以用纯的小波基组合表示了。是,确实是,就算不包括scaling function,这些小波函数本身也组成了正交归一基,但如果仅限于此的话,小波变换也就没那么神奇的功效了。引入这个scaling function,才能引入我们提到的多解析度分析的理论,而小波变换的强大,就体现在这个多解析度上。那在这里,我们怎么用这个多解析度呢?这个哈尔小波basis组合是怎么通过多解析度推导出来的呢?
话说在数学定义中,有一种空间叫Lebesgue空间,对于信号处理非常重要,可以用L^p(R)表示,指的是由p次可积函数所组成的函数空间。我们在小波变换中要研究的信号都是属于L^2(R)空间的,这个空间是R上的所有处处平方可积的可测函数的集合,这样就等于对信号提出了一个限制,就是信号能量必须是有限的,否则它就不可积了。小波变换的定义都是基于但不限于L^2(R)中的信号的。这玩意的特性要具体解释起来太数学了,牵涉到太多泛函知识,我就不在这里详述了。而且老实说我也没能力完全讲清楚,毕竟不是学这个的,有兴趣可以参考wiki。总之你记住,小波变换研究中所使用的信号基本都是平方可积的信号,但其应用不限于这种信号,就行了。
对L^2(R)空间做MRA是在干嘛呢?在L^2(R)空间中,我们可以找出一个嵌套的空间序列,并有下列性质:
(v) 有这样一个方程,
是
的orthonormal basis。
我来简单解释一下这些性质。这个V_j都是L^2(R)空间中的子空间,然后他们是由小到大的,交集是{0},因为这是最小的子空间,并集就是L空间。是不是有点难以理解?没关系,看看下面这个图就清楚了:
这个图是圈圈套圈圈,最里面的圈是V0,之后分别是V1,V2,V3,V4 。那他们有趣的性质就是,假如有一个函数f(t)他属于一个某空间,那你将其在时域上平移,它还是属于这个空间。但如果你对它频域的放大或缩小,它就会相应移到下一个或者上一个空间了。
同时我们还知道,你要形容每一个空间的话,都需要有对应的orthonormal basis,这是必然的,那对于V0来讲,它的orthonormal basis就是
这一系列函数是什么呢?是的时域变换,而且我们刚才也说了,时域上平移,是不会跳出这个空间的。这样,我们就可以说,由这一系列basis所定义的L^2(R)子空间V0被这些basis所span,表示成:
k从负无穷到正无穷。上面的bar表示这是一个闭包空间,也就是说
这样,我们就定义了基本的V0这个子空间。刚才说了,这个子空间的基都是对的整数时域变换,这里我们称
为scaling function,所以换个说法,就是说这里整个子空间V0,由scaling function和其时域变换的兄弟们span。
当然,如果这个scaling function只是用来代表一个子空间的,那它的地位也就不会这么重要了。刚才我们提到,这个嵌套空间序列有一个性质,。这就是这个函数,如果你对它频域的放大或缩小,它就会相应移到下一个或者上一个空间了。这个性质就有意思了,它代表什么呢?对于任何一个包含V0的更上一层的空间来讲,他们的基都可以通过对scaling function做频域的scale后再做时域上的整数变换得到!推广开来就是说,当
我们有
这也就意味着,对于任何属于V_j空间的函数f(t),都可以表示为:
到这里,我们就明白这些个子空间和那个凭空冒出来的scaling function的作用了。scaling的构建这些不同的子空间的基础,当j越大的时候,每一次你对频率变换后的scaling function所做的时域上的整数平移幅度会越小,这样在这个j子空间里面得到的f(t)表示粒度会很细,细节展现很多。反之亦然。通俗点说,就是对scaling function的变换平移给你不同的子空间,而不同的子空间给你不同的分辨率,这样你就可以用不同的分辨率去看目标信号。
下面就是时候看看什么是MRA equation了,这是更加有趣,也是更加核心的地方。通过刚才的讲解,V0属于V1,那scaling function
是在V0中的,自然也在V1中了。我们把他写成V1的基的线性组合,那就是
其中的h(n)是scaling function的系数,也叫做scaling filter或者scaling vector,可以是实数,也可以是虚数。根号2是为了维持norm为1的。看,在这个公式里,我们就把属于V0的函数用V1的基表示出来了。同理,我们可以循环如此,把属于V0的在V2, V3, …, Vn中表示出来。这些方程就是MRA equation,也叫refinement equation,它是scaling function理论的基础,也是小波分析的基础之一。
好,稍微总结一下。到现在,已经讲了关于scaling function的基本理论知识,知道了信号空间可以分为不同精细度的子空间,这些子空间的basis集合就是scaling function或者频率变换之后的scaling function,如下图所示:
上图就是四个子空间的basis集合的展览。通过前面的讨论,我们还知道,一开始的scaling function可以通过更精细的子空间的scaling function(它们都是对应子空间的basis)来构建。比如
对于更加finer的scale:
图2
依此类推。实际上,对于任何scale和translate过的scaling function,都可以用更加精细的scale层面上的scaling function构建出来。
然后,我们有各种scale下的scaling function了,该看看它们分别所对应的嵌套的空间序列了。先看看V0,自然就是以基本的scaling function为基础去span出来的:
这个不新鲜,刚才就讲过了。这个子空间代表什么样的信号?常量信号。道理很简单,这个scaling function在整个信号长度上,没有任何变化。继续往下看:
这个相比V0更加finer的子空间,代表着这样一种信号,它从1-4是常量,从5-8是另一个常量。同理我们有(??):
V2代表的信号,是分别在1,2; 3,4; 5,6; 7,8上有相同值的信号。那么V3呢?则表示任何信号,因为对于V3来讲,任何一个时间刻度上的值都可以不一样。而且现在,我们也可以通过上面的一些scaling functions的波形验证了之前提到的多解析度分析中的一个核心性质,那就是:
我们之前讲了一堆多解析度的理论,但直到现在,通过这些图形化的分析,我们可能才会真正理解它。那好,既然我们有一个现成的信号,那就来看看,对这个信号作多解析度分析是啥样子的:
你看,在不同的子空间,对于同一个信号就有不同的诠释。诠释最好的当然是V3,完全不损失细节。这就是多解析度的意义。我们可以有嵌套的,由scaling function演变的basis function集合,每一个集合都提供对原始信号的某种近似,解析度越高,近似越精确。
说到这里,可能你对scaling function以及多解析度分析已经比较理解了。但是,我们还没有涉及到它们在小波变换中的具体应用,也就是还没有回答刚才那个问题:凭空插了一个scaling function到小波basis组合中干嘛。也就是说,我们希望理解scaling function是怎么和小波函数结合的呢,多解析度能给小波变换带来什么样的好处呢。这其实就是是小波变换中的核心知识。理解了这个,后面的小波变换就是纯数学计算了。
好,我们已经知道,对于子空间V0,basis是scaling function:
对应的小波函数是:
然后子空间V1的basis集合是这俩哥们:
看出什么规律了么?多看几次这三个图,你会惊讶地发现,在V0中的scaling function和wavelet function的组合,其实就是V1中的basis!继续这样推导,V1本来的的basis是:
然后V1中对应的wavelet function是
他们的组合,本质上也就是V2的basis(参考图2)。你继续推导下去,会得到同样的结论:在scale j的wavelet function,可以被用来将Vj的basis扩展到V(j+1)中去!这是一个非常非常关键的性质,因为这代表着,对任何一个子空间Vj,我们现在有两种方法去得到它的orthonormal basis:
2. 第二种就是它上一个子空间的basis,对任意k,以及上一级子空间的wavelet function
,对任意k。
第二种选择能给我们带来额外的好处,那就是我们可以循环不断地用上一级子空间的scaling function以及wavelet function的组合来作为当前子空间的基。换句话说,如果针对V3这个子空间,它实际上就有四种不同的,但是等价的orthonormal basis:
1. 本级(V3)的scaling function basis set
2. 上一级(V2)的scaling function + wavelet function;
3 . 上上一级(V1)的scaling function + 上上一级(V1)的wavelet function + 上一级(V2)的wavelet function;
4. 上上上一级(V0)的scaling function + 上上上一级(V0)的wavelet function + 上上一级(V1)的wavelet function + 上一级(V2)的wavelet function
好,看看最后一种选取方式,有没有感到眼熟?对了,它就是我们之前提到的“针对此信号space的哈尔小波basis组合”,参见图1。现在我们知道了,这个scaling function不是凭空插进去的,而是通过不断的嵌套迭代出来的:)
那为什么我们最后选定的是这种选取方式呢?实际上,刚才介绍的这个性质已经告诉我们,对于任何的scale j0,我们都可以给我们的signal space找到一组orthonormal basis,这个basis是通过组合scale j0上的scaling function以及所有在scale j,j>=j0上的wavelets得到的。这样,基于这个orthonormal basis,所有信号空间中的信号都可以写成组成这个basis的functions的线性组合:
对应的系数的计算和平常一样:
这,就是最终的,也是最核心的,小波变换形式。不管是信号压缩,滤波,还是别的方式处理,只要是用小波变换,都逃不出这个基础流程:
1. 选取合适的wavelet function和scaling function,从已有的信号中,反算出系数c和d。
2. 对系数做对应处理
3. 从处理后的系数中重新构建信号。
这里的系数处理是区别你的应用的重点。比如图像或者视频压缩,就希望选取能将能量聚集到很小一部分系数中的小波,然后抛弃那些能量很小的小波系数,只保留少数的这些大头系数,再反变换回去。这样的话,图像信号的能量并没有怎么丢失,图像体积却大大减小了。
还有一个没有解释的问题是,为什么要强调尺度函数和小波函数组成一个orthonormal basis呢?计算方便是一方面,还有一个原因是,如果他们满足这个性质,就满足瑞利能量定理,也就是说,信号的能量,可以完全用每个频域里面的展开部分的能量,也就是他们的展开系数表示:
到这里,我们对小波变换的形式就讲完了。虽然是用的最简单的哈尔小波为例子,但举一反三即可。我们着重介绍了多解析度分析以及它给小波变换带来的杀手锏:时域频域同时定位。结束之前,再多说几句小波变换的意义。我们拿刚才例子中V3子空间的第二种可选择的orthonormal basis作为例子:
左边这四个basis组成元素,也就是scaling functions,的系数,表征的是信号的local平均(想想它们和信号的内积形式),而右边的这四个basis组成元素,也就是wavelet functions,的系数则表征了在local平均中丢失的信号细节。得益于此,多解析度分析能够对信号在越来越宽的区域上取平均,等同于做低通滤波,而且,它还能保留因为平均而损失的信号细节,等同于做高通滤波!这样,我们终于可以解释了wavelet function和scaling function背后的物理意义了:wavelet function等同于对信号做高通滤波保留变化细节,而scaling function等同于对信号做低通滤波保留平滑的shape!
对小波变换的基础知识,我们就讲到这里。需要注意的是,这只是小波变换最基本最基本的知识,但也是最核心的知识。掌握了这些,代表你对小波变换的物理意义有了一定的了解。但对于小波变换本身的讲解,一本书都不一定能将讲透,还有很多的基础知识我都没有讲,比如如何构建自己的scaling function,选取合适的系数集h[k],并由此构建自己的wavelet functions。所以,如果有深入下去研究的同学,好好买一本书来看吧。而只是希望用小波变换来服务自己的应用的同学,个人觉得这些知识已经足够让你用来起步了。
5
2012
转载:小波变换和motion信号处理(一)
经原作者Windstorm同意,将他两篇关于小波变换的文章转载在此。这两篇文章是我目前看到的最好的中文小波变换入门文章,因此转载过来希望更多的人能看到。这个系列的第三篇作者还没有写,所以暂时只转两篇。本篇的源地址在这里。我对于一些错别字等小错误可能会有修改,除此之外,也可能会高亮下某些文字。
这是《小波变换和motion信号处理》系列的第一篇,基础普及。第二篇我准备写深入小波的东西,第三篇讲解应用。
记得我还在大四的时候,在申请出国和保研中犹豫了好一阵,骨子里的保守最后让我选择了先保研。当然后来也退学了,不过这是后话。当时保研就要找老板,实验室,自己运气还不错,进了一个在本校很牛逼的实验室干活路。我们实验室主要是搞图像的,实力在全国也是很强的,进去后和师兄师姐聊,大家都在搞什么小波变换,H264之类的。当时的我心思都不在这方面,尽搞什么操作系统移植,ARM+FPGA这些东西了。对小波变换的认识也就停留在神秘的“图像视频压缩算法之王”上面。
后来我才发现,在别的很广泛的领域中,小波也逐渐开始流行。比如话说很早以前,我们接触的信号频域处理基本都是傅立叶和拉普拉斯的天下。但这些年,小波在信号分析中的逐渐兴盛和普及。这让人不得不感到好奇,是什么特性让它在图象压缩,信号处理这些关键应用中更得到信赖呢?说实话,我还在国内的时候,就开始好奇这个问题了,于是放狗搜,放毒搜,找遍了中文讲小波变换的科普文章,发现没几个讲得清楚的,当时好奇心没那么重,也不是搞这个研究的,懒得找英文大部头论文了,于是作罢。后来来了这边,有些项目要用信号处理,不得已接触到一些小波变换的东西,才开始硬着头皮看。看了一些材料,听了一些课,才发现,还是那个老生常谈的论调:国外的技术资料和国内真TNND不是一个档次的。同样的事情,别人说得很清楚,连我这种并不聪明的人也看得懂; 国内的材料则绕来绕去讲得一塌糊涂,除了少数天才没几个人能在短时间掌握的。
牢骚就不继续发挥了。在这个系列文章里,我希望能简单介绍一下小波变换,它和傅立叶变换的比较,以及它在移动平台做motion detection的应用。如果不做特殊说明,均以离散小波为例子。考虑到我以前看中文资料的痛苦程度,我会尽量用简单,但是直观的方式去介绍。有些必要的公式是不能少的,但我尽量少用公式,多用图。另外,我不是一个好的翻译者,所以对于某些实在翻译不清楚的术语,我就会直接用英语。我并不claim我会把整个小波变换讲清楚,这是不可能的事,我只能尽力去围绕要点展开,比如小波变换相对傅立叶变换的好处,这些好处的原因是什么,小波变换的几个根本性质是什么,背后的推导是什么。我希望达到的目的就是一个小波变换的初学者在看完这个系列之后,就能用matlab或者别的工具对信号做小波变换的基本分析并且知道这个分析大概是怎么回事。
最后说明,我不是研究信号处理的专业人士,所以文中必有疏漏或者错误,如发现还请不吝赐教。
要讲小波变换,我们必须了解傅立叶变换。要了解傅立叶变换,我们先要弄清楚什么是”变换“。很多处理,不管是压缩也好,滤波也好,图形处理也好,本质都是变换。变换的是什么东西呢?是基,也就是basis。如果你暂时有些遗忘了basis的定义,那么简单说,在线性代数里,basis是指空间里一系列线性独立的向量,而这个空间里的任何其他向量,都可以由这些个向量的线性组合来表示。那basis在变换里面啥用呢?比如说吧,傅立叶展开的本质,就是把一个空间中的信号用该空间的某个basis的线性组合表示出来。小波变换自然也不例外的和basis有关了。再比如你用Photoshop去处理图像,里面的图像拉伸,反转,等等一系列操作,都是和basis的改变有关。
既然这些变换都是在搞基,那我们自然就容易想到,这个basis的选取非常重要,因为basis的特点决定了具体的计算过程。一个空间中可能有很多种形式的basis,什么样的basis比较好,很大程度上取决于这个basis服务于什么应用。比如如果我们希望选取有利于压缩的话,那么就希望这个basis能用其中很少的向量来最大程度地表示信号,这样即使把别的向量给砍了,信号也不会损失很多。而如果是图形处理中常见的线性变换,最省计算量的完美basis就是eigenvector basis了,因为此时变换矩阵T对它们的作用等同于对角矩阵( Tv_n = av_n,a是eigenvalue )。总的来说,抛开具体的应用不谈,所有的basis,我们都希望它们有一个共同的特点,那就是,容易计算,用最简单的方式呈现最多的信号特性。
好,现在我们对变换有了基本的认识,知道他们其实就是在搞基。当然,搞基也是分形式的,不同的变换,搞基的妙处各有不同。接下来先看看,傅立叶变换是在干嘛。
傅立叶级数最早是Joseph Fourier 这个人提出的,他发现,这个basis不仅仅存在与vector space,还存在于function space。这个function space本质上还是一个linear vector space,可以是有限的,可以是无限的,只不过在这个空间里,vector就是function了,而对应的标量就是实数或者复数。在vector space里,你有vector v可以写成vector basis的线性组合,那在function space里,function f(x)也可以写成对应function basis的线性组合,也有norm。你的vector basis可以是正交的,我的function basis也可以是正交的(比如sin(t)和sin(2t))。唯一不同的是,我的function basis是无穷尽的,因为我的function space的维度是无穷的。好,具体来说,那就是现在我们有一个函数,f(x)。我们希望将它写成一些cos函数和一些sin函数的形式,像这样
again,这是一个无限循环的函数。其中的1,cosx, sinx, cos2x …..这些,就是傅立叶级数。傅立叶级数应用如此广泛的主要原因之一,就是它们这帮子function basis是正交的,这就是有趣的地方了。为什么function basis正交如此重要呢?我们说两个vector正交,那就是他俩的内积为0。那对于function basis呢?function basis怎么求内积呢?
现在先复习一下vector正交的定义。我们说两个vector v,w如果正交的话,应符合:
那什么是function正交呢?假设我们有两个函数f(x)和g(x),那是什么?我们遵循vector的思路去想,两个vector求内积,就是把他们相同位置上对应的点的乘积做一个累加。那移过来,就是对每一个x点,对应的f和g做乘积,再累加。不过问题是,f和g都是无限函数阿,x又是一个连续的值。怎么办呢?向量是离散的,所以累加,函数是连续的,那就是…….积分!
我们知道函数内积是这样算的了,自然也就容易证明,按照这个形式去写的傅立叶展开,这些级数确实都是两两正交的。证明过程这里就不展开了。好,下一个问题就是,为什么它们是正交basis如此重要呢?这就牵涉到系数的求解了。我们研究了函数f,研究了级数,一堆三角函数和常数1,那系数呢?a0, a1, a2这些系数该怎么确定呢?好,比如我这里准备求a1了。我现在知道什么?信号f(x)是已知的,傅立叶级数是已知的,我们怎么求a1呢?很简单,把方程两端的所有部分都求和cosx的内积,即:
然后我们发现,因为正交的性质,右边所有非a1项全部消失了,因为他们和cosx的内积都是0!所有就简化为
这样,a1就求解出来了。到这里,你就看出正交的奇妙性了吧:)
好,现在我们知道,傅立叶变换就是用一系列三角波来表示信号方程的展开,这个信号可以是连续的,可以是离散的。傅立叶所用的function basis是专门挑选的,是正交的,是利于计算coefficients的。但千万别误解为展开变换所用的basis都是正交的,这完全取决于具体的使用需求,比如泰勒展开的basis就只是简单的非正交多项式。
有了傅立叶变换的基础,接下来,我们就看看什么是小波变换。首先来说说什么是小波。所谓波,就是在时间域或者空间域的震荡方程,比如正弦波,就是一种波。什么是波分析?针对波的分析拉(囧)。并不是说小波分析才属于波分析,傅立叶分析也是波分析,因为正弦波也是一种波嘛。那什么是小波呢?这个”小“,是针对傅立叶波而言的。傅立叶所用的波是什么?正弦波,这玩意以有着无穷的能量,同样的幅度在整个无穷大区间里面振荡,像下面这样:
那小波是什么呢?是一种能量在时域非常集中的波。它的能量是有限的,而且集中在某一点附近。比如下面这样:
这种小波有什么好处呢?它对于分析瞬时时变信号非常有用。它有效的从信号中提取信息,通过伸缩和平移等运算功能对函数或信号进行多尺度细化分析,解决了傅立叶变换不能解决的许多困难问题。恩,以上就是通常情况下你能在国内网站上搜到的小波变换文章告诉你的。但为什么呢?这是我希望在这个系列文章中讲清楚的。不过在这篇文章里,我先点到为止,把小波变换的重要特性以及优点cover了,在下一篇文章中再具体推导这些特性。
小波变换的本质和傅立叶变换类似,也是用精心挑选的basis来表示信号方程。每个小波变换都会有一个mother wavelet,我们称之为母小波,同时还有一个scaling function,中文是尺度函数,也被成为父小波。任何小波变换的basis函数,其实就是对这个母小波和父小波缩放和平移后的集合。下面这附图就是某种小波的示意图:
从这里看出,这里的缩放倍数都是2的级数,平移的大小和当前其缩放的程度有关。这样的好处是,小波的basis函数既有高频又有低频,同时还覆盖了时域。对于这点,我们会在之后详细阐述。
小波展开的形式通常都是这样(注意,这个只是近似表达,严谨的展开形式请参考第二篇):
其中的就是小波级数,这些级数的组合就形成了小波变换中的基basis。和傅立叶级数有一点不同的是,小波级数通常是orthonormal basis,也就是说,它们不仅两两正交,还归一化了。小波级数通常有很多种,但是都符合下面这些特性:
1. 小波变换对不管是一维还是高维的大部分信号都能cover很好。这个和傅立叶级数有很大区别。后者最擅长的是把一维的,类三角波连续变量函数信号映射到一维系数序列上,但对于突变信号或任何高维的非三角波信号则几乎无能为力。
2. 围绕小波级数的展开能够在时域和频域上同时定位信号,也就是说,信号的大部分能量都能由非常少的展开系数,比如a_{j,k},决定。这个特性是得益于小波变换是二维变换。我们从两者展开的表达式就可以看出来,傅立叶级数是,而小波级数是
。
3. 从信号算出展开系数a需要很方便。普遍情况下,小波变换的复杂度是O(Nlog(N)),和FFT相当。有不少很快的变换甚至可以达到O(N),也就是说,计算复杂度和信号长度是线性的关系。小波变换的等式定义,可以没有积分,没有微分,仅仅是乘法和加法即可以做到,和现代计算机的计算指令完全match。
可能看到这里,你会有点晕了。这些特性是怎么来的?为什么需要有这些特性?具体到实践中,它们到底是怎么给小波变换带来比别人更强的好处的?计算简单这个可能好理解,因为前面我们已经讲过正交特性了。那么二维变换呢?频域和时域定位是如何进行的呢?恩,我完全理解你的感受,因为当初我看别的文章,也是有这些问题,就是看不到答案。要说想完全理解小波变换的这些本质,需要详细的讲解,所以我就把它放到下一篇了。
接下来,上几张图,我们以一些基本的信号处理来呈现小波变换比傅立叶变换好的地方,我保证,你看了这个比较之后,大概能隐约感受到小波变换的强大,并对背后的原理充满期待:)
假设我们现在有这么一个信号:
看到了吧,这个信号就是一个直流信号。我们用傅立叶将其展开,会发现形式非常简单:只有一个级数系数不是0,其他所有级数系数都是0。好,我们再看接下来这个信号:
简单说,就是在前一个直流信号上,增加了一个突变。其实这个突变,在时域中看来很简单,前面还是很平滑的直流,后面也是很平滑的直流,就是中间有一个阶跃嘛。但是,如果我们再次让其傅立叶展开呢?所有的傅立叶级数都为非0了!为什么?因为傅立叶必须用三角波来展开信号,对于这种变换突然而剧烈的信号来讲,即使只有一小段变换,傅立叶也不得不用大量的三角波去拟合,就像这样:
看看上面这个图。学过基本的信号知识的朋友估计都能想到,这不就是Gibbs现象么?Exactly。用比较八股的说法来解释:Gibbs现象是由于展开式在间断点邻域不能均匀收敛所引起的,即使在N趋于无穷大时,这一现象也依然存在。其实通俗一点解释,就是当变化太sharp的时候,三角波fit不过来了,就凑合出Gibbs了:)
接下来我们来看看,如果用刚才举例中的那种小波,展开之后是这样的:
看见了么?只要小波basis不和这个信号变化重叠,它所对应的级数系数都为0!也就是说,假如我们就用这个三级小波对此信号展开,那么只有3个级数系数不为0 。你可以使用更复杂的小波,不管什么小波,大部分级数系数都会是0。原因?由于小波basis的特殊性,任何小波和常量函数的内积都趋近于0。换句话说,选小波的时候,就需要保证母小波在一个周期的积分趋近于0。正是这个有趣的性质,让小波变换的计算以及对信号的诠释比傅立叶变换更胜一筹!原因在于,小波变换允许更加精确的局部描述以及信号特征的分离。一个傅立叶系数通常表示某个贯穿整个时间域的信号分量,因此,即使是临时的信号,其特征也被强扯到了整个时间周期去描述。而小波展开的系数则代表了对应分量它当下的自己,因此非常容易诠释。
小波变换的优势不仅仅在这里。事实上,对于傅立叶变换以及大部分的信号变换系统,他们的函数基都是固定的,那么变换后的结果只能按部就班被分析推导出来,没有任何灵活性,比如你如果决定使用傅立叶变换了,那basis function就是正弦波,你不管怎么scale,它都是正弦波,即使你举出余弦波,它还是移相后的正弦波。总之你就只能用正弦波,没有任何商量的余地。而对于小波变换来讲,基是变的,是可以根据信号来推导或者构建出来的,只要符合小波变换的性质和特点即可。也就是说,如果你有着比较特殊的信号需要处理,你甚至可以构建一个专门针对这种特殊信号的小波basis function集合对其进行分析。这种灵活性是任何别的变换都无法比拟的。总结来说,傅立叶变换适合周期性的,统计特性不随时间变化的信号; 而小波变换则适用于大部分信号,尤其是瞬时信号。它针对绝大部分信号的压缩,去噪,检测效果都特别好。
看到这里,你应该大概了解了小波变换针对傅立叶变换的优点了。你也许对背后的原因还存在一些疑问,并希望深入了解一些小波的构建等知识,请移步本系列第二篇:傅立叶变换,小波变换和motion信号处理(二)
29
2012
Micolog2 更新:2012.2.29版发布
这次更新主要有下面两点:
1 修改了一个删除comment时出现的bug。引起该bug的原因是该评论没有对应entry(相应entry被删除?)。
2 除此之外,增加了三款主题。其中两款是我自己用的(因为每次更新代码后再跟自己主题进行合并很麻烦,就干脆放进去了);还有一款是maple发给我的。本来说那款主题没法用让我看看原因,不过我没发现问题,倒觉得挺漂亮,也就扔进去了。
除此之外,所有的bug请到issue list里面提交一下,否则我可能会忘掉,多谢合作。
下载地址
http://code.google.com/p/micolog2/downloads/list
升级指南
如果您以前安装过Micolog2的beta或者alpha版,请到appengine的dashboard中找到Datastore Admin(没启用的启用),然后在里面把ObjCache删除。
安装过其他版本的,请到博客的后台把cache清空一下。
第一天的效果不会太明显,后面就会很明显了。
如何及时收到更新
请订阅下面Feed之中的任何一个:
30
2012
Micolog2 更新: 2012.1.30版发布
百合的天空前几天提交了一个bug,关于右边栏最新文章没有及时更新的问题。今天抽空修复了这个bug (issue 20)。另外wrenashe的那个bug我暂时无法重现,所以没有办法再处理。如果有人在导入wordpress时遇到相似问题(issue 19),请帮忙提交下必要的步骤和数据,我这里可以重现bug的时候就有办法去修复。
除此之外,所有的bug请到issue list里面提交一下,否则我可能会忘掉,多谢合作。
这个更新我觉得还是有必要安装的(我的博客过两天再换)。
下载地址
http://code.google.com/p/micolog2/downloads/list
升级指南
如果您以前安装过Micolog2的beta或者alpha版,请到appengine的dashboard中找到Datastore Admin(没启用的启用),然后在里面把ObjCache删除。
安装过其他版本的,请到博客的后台把cache清空一下。
第一天的效果不会太明显,后面就会很明显了。
如何及时收到更新
请订阅下面Feed之中的任何一个:
30
2012
今天很幸运
我初中的数学老师,待我很好,母亲般的感觉。但是自高中毕业回母校时便再也没见过她。问其他任课老师也没有得到更多具体消息,只是说那年她突然的就走了,没有和任何人打招呼。
我以为茫茫13亿人,这辈子恐怕再见上一面的机会是没有了。不过私下仍隔几个月便在Google上搜一搜。
今天突然找到了她的线索,顺着线索继续搜,一个小时左右,我找到了几个可能认识她的单位/人——几个电话打过去,我终于找到了!而且就在深圳。
自毕业起有9年不见了,她声音一点都没有变,只是容颜老了许多。想想变很心酸。不管怎样,下星期去探望她。想起来,今天应该算我的幸运日吧。
16
2012
Micolog2 更新: 2012.1.16版发布
wrenashe同学前段时间提交了一个bug:在导入wordpress格式的日志时出现不能全部导入的问题。这个issue (issue 19)现在已解决。没有其他改动,因此没有导入需求的同学可以不必更新代码。
下载地址
http://code.google.com/p/micolog2/downloads/list
升级指南
如果您以前安装过Micolog2的beta或者alpha版,请到appengine的dashboard中找到Datastore Admin(没启用的启用),然后在里面把ObjCache删除。
如何及时收到更新
请订阅下面Feed之中的任何一个:
23
2011
Django+Server Push
聊天室、社交网站中的网页及时聊天一般都是采用了push技术。但是现行的网络架构是标准的pull结构:浏览器发起访问,然后服务器回答,然后一切完毕。
Http 1.1中默认采用了keep alive,但是因为网络中存在各种中转以及各种代理的原因,即使server想通过迭代的方式一点一点的把发生的event传给浏览器,最终的结果也很容易是一开始等半天等不到,最后爆发式的来一坨(另外貌似IE浏览器仅仅在接收完全部结果后js才能access data)。因此,即使server主动push,也要push后马上关闭链接,然后等下一个event发生后再想办法push。
现行的push技术挺多,靠谱的可以分为两类:
1 Long Pulling。这一种是目前浏览器都支持的,因此使用还是很广泛的。其思路是:client向server发一个ajax请求,在没有event的情况下,server不予回答,而是等待——直到有某个事件发生或者已经等待了接近50秒。client收到server的回复后利用收到的数据做更新,然后马上再次向server发出一个相同的请求。前面之所以设置为50秒是因为太久的话浏览器会判断为超时然后就会把连接释放了。
额外的说明一点,因为keep alive默认开启的原因,流行的浏览器一般仅仅允许到某网页的链接同时最多为2个,因此不管event有多少个,实践中一定要让他们聚合走同一个请求。
于是client这边就是不停的ajax调用、更新。那么server那边呢?
server需要可以暂停当前对于request的处理,在未来有event的时候接着做的能力。这对于很多框架而言就成了噩梦——因为要想有这种能力,很多框架要不可避免的使用threading技术,而Long Pulling基本上是一个在线的用户一个thread,这个是很难承受的。(貌似Django这个Python框架就悲剧的在其中,至少两年前是这样)。目前能对此应对能力最好的应该是node.js,V8引擎对于js的thread能力据说是牛逼闪闪带发光。除了thread,还有使用message的,例如RabbitMQ,至于具体原理,我还没有去看,在这里不乱说了。
2 Socket。浏览器的Socket技术来的太晚了——貌似直到html5里面才有(??),而且这个html5的标准化进程又慢的要死。不过还好,flash普及的很广,而且flash支持socket(tcp & udp)。最新的flash甚至开始支持p2p,有着很强大的group功能,但是对开发者而言却有个很不爽的特性:每次开启p2p都要有个吓人的安全提示。Socket比起Long Pulling来说效果要好,不过不是所有浏览器都支持。有个项目是包装socket技术的先锋:http://socket.io/,其试图让各种浏览器都用上socket,不妨一看。
对比这两种办法,虽然Socket技术的效果会好,不过让我选的话我还是会选择Long Pulling,因为Socket技术依赖的东西太多了,会导致变数太多。无论是flash的变化,还是浏览器的变化,甚至是socket.io代码的bug或者其CDN的不给力都能导致自己的网站挂掉。
说完server push,也稍微说了下django要利用multi thread的能力,但是django的测试服务器是single thread的——为了方便调试。如果你就想让它以multi thread来展现,那么安装django-concurrent-server.
22
2011
智障的http协议
大多时候我们都会在某一个时间把一个复杂问题拆成一些基本独立的部分,然后自以为聪明的觉得这是个伟大创举。事实上后面不知道要加多少补丁来去挽回这本该有的契合——同时不可避免的性能也会差很多。
一个简单的应用竟然需要那么多方面的知识和配合,还要整各种trick来去设法还原当初本有的能力,真的是恶心到家了。
——查阅Server Push后有感
20
2011
决定不将Micolog 2进行Django以及Python运行时的升级
今天做过一些调查后我决定将Micolog 2的Django版本维持在v 0.96,python运行时保持2.5.
具体原因如下:
1 Django 0.96版和1.x的模板系统有很多不同。因此以前所有的模板也都需要进行相应的改动,而大家更需要一个经过简单配置就可以使用的博客程序。除此之外,也会有一些其他功能出问题,需要改写。
refs:
http://imxpan.com/2011/10/update-micolog-defult-diango0-96-to-django1-2/
http://stackoverflow.com/questions/4994913/app-engine-default-django-version-change
2 Python 2.7现在还是实验阶段,接口还可能有改动。更重要的,Python 2.7不支持django 0.96.
ref:
http://code.google.com/appengine/docs/python/python27/newin27.html#Django_Version
3 从对datastore ops减少的方面来看,Python 2.7的多线程不如Micolog 2的缓存算法。
4 现在徐明已经在着手重新整理代码,另外还有一个java的GAE博客程序开发比较稳定。因此对于Micolog 2,我将它定位为“保持能用、尽量好用”。因此Micolog 2在未来会更倾向于修复使用中出现的各类bug。(现在已经修复的bug包括:计算相关文章时算法不正确、输入tag的时候和逗号相邻的空格也会被算入tag——这些bug都很影响使用体验。欢迎提交类似的bug)
除此之外,最近时间比较紧张也是一个很重要的原因,只能抽空做一些修补工作,请见谅。
20
2011
Micolog2 更新:2011.12.20版
请阅读以下改动来确定您是否需要更新
1 将recent posts进行自动刷新,这样在发布新文章后能够马上在右边栏看到新文章的出现。如果你的右边栏就没有这一栏,那么不必更新。
2 修改了和主题相关的一个bug [IMPORTANT]。如果你的主题不刷新显示不出留言,请考虑更新并阅读下面对这个bug的描述以及解决方案。
这个bug的原因是因为部分主题在comments.html里面使用了{% entry.comments.count %}来检查是否有留言,这个检查对应一个很耗时的查询。以前的版本因为修改了entry.comments函数,导致这个检查失败,现在已经修复,因此现在主题不需要修改就不会出现上述问题了。不过更好的一个方案是,把{% entry.comments.count %}替换为{% entry.commentcount %},这样可能可以减少很多查询.
3 放进去了一个新主题
下载地址
http://code.google.com/p/micolog2/downloads/list
升级指南
如果您以前安装过Micolog2的beta或者alpha版,请到appengine的dashboard中找到Datastore Admin(没启用的启用),然后在里面把ObjCache删除。
如何及时收到更新
请订阅下面Feed之中的任何一个:
18
2011
Micolog2 正式版发布
下载地址
http://code.google.com/p/micolog2/downloads/list
升级指南:
如果您以前安装过Micolog2的任何一个版本,请到appengine的dashboard中找到Datastore Admin(没启用的启用),然后在里面把ObjCache删除。
如何及时收到更新
请订阅下面Feed之中的任何一个:
14
2011
我的鼠标热点图
看到GrapeOT的《Collecting and visualizing mouse position distribution》后出于好奇,对自己的鼠标热点也进行了测量。
上边栏那几个热点充分说明了我新开启Chrome浏览页时喜欢点+号而不是按快捷键,因此那几个热点都在屏幕右侧。关闭标签时我喜欢用Ctrl+W,因此最上面左边没有热点。左上角那几个热点是因为频繁通过书签进入网页,我常用的书签会放左侧。
右上角应该是关闭,不过我还真不知道我用了这么多次关闭。另外右边边界的那个大点是用鼠标拖拽窗口进行分栏的结果。Win7的这个特性我是相当喜欢。
中间的热点基本上是在阅读或者写代码时的鼠标位置,如果没有分栏,我相信我的热点应该更靠右。除此之外,我发现鼠标的位置我放的位置接近于关注区域的黄金分割点位置。
代码可以在GrapeOT的《有趣的鼠标位置规律及其分析于实现》中找到。不过Start-job那里我始终搞不定,于是就开着Powershell运行。
12
2011
GAE上做域名跳转
这个博客的域名是blog.rexzhao.com,同时www.rexzhao.com不知道用来做什么,但是又不能不做什么,所以以前就让原博客程序占着这两个域名。
上周Sigma提醒我最好让www这个域名做跳转,这样不会让搜索引擎误认为这是两个独立的地址,影响Ranking。
今天重新询问了下,然后在网上找到了我想要的script:
http://blog.dantup.com/2010/01/generic-redirection-script-for-google-app-engine
下面是精简后的代码,传到GAE上然后把www这个域名分配给它就行了。
App.yaml
1: application: redirect-www
2: version: 1
3: runtime: python
4: api_version: 1
5:
6: handlers:
7: - url: /.*
8: script: main.py
main.py
1: from google.appengine.ext import webapp
2: from google.appengine.ext.webapp.util import run_wsgi_app
3: import urlparse, logging
4:
5: REDIRECT_TO = 'blog.rexzhao.com'
6:
7: class MainHandler(webapp.RequestHandler):
8:
9: def get(self):
10: url = get_redirect_url(self.request.url)
11: logging.info('Redirecting ' + self.request.url + ' to ' + url);
12: self.redirect(url, permanent=True)
13:
14: def get_redirect_url(url):
15: scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
16:
17: # Fix empty paths to be just '/' for consistency
18: if path == '':
19: path = '/'
20:
21: return urlparse.urlunsplit(['http', REDIRECT_TO, path, query, fragment])
22:
23: application = webapp.WSGIApplication([("/.*", MainHandler)], debug=True)
24:
25: def main():
26: run_wsgi_app(application)
27:
28: if __name__ == "__main__":
29: main()
11
2011
Micolog2 所有功能完成
今天把flush cache加入到cron job里,每天执行一次。这样功能基本就齐了。现在我的博客也换成了最新代码,自己测试几天。
如果我的博客这几天运行正常,没发现什么问题,那么到时候再更新下载页面。欢迎在我博客测试。有bug请留言。
11
2011
看《史蒂夫·乔布斯传》有感
昨天上飞机前买了这本乔布斯传,路上的时间都在看了。现在一本书基本看完,感触颇多。简要的写一写(第一人称)。
- 商机不是在现有社会中寻找,是在未来和现在的不同中发现。
- 未来就是我们创造的。你说为什么不是他?不仅是他感想不敢做,更因为我们做的快。
- 不是所有的机遇我都要,我只要最大的,这已经足够让我们忙碌了。
- 怕不怕抄袭?不怕,但是气愤。另外,做好保密工作、加快步伐、提高水准,不停的创新,是唯一应对方法。
- 为什么我经常冲你发脾气?因为我希望我们10个人的团队做的是50个人的活,而仅仅需要做3个人的协调。你说你受不了?那么请选择理解或者滚蛋,否则会影响整个团队的效率。
- 苹果以后会不会完蛋?会的,一定会的。能让苹果一体机立于不败之地是因为苹果的创新实在太快,新市场总是苹果的,就如同未来是苹果的。未来苹果创新速度减慢——兼容机一定会让其惨败,这一点盖茨的说法一点都不错。打个简单的比方,如果买时光机,我一定买最新型、最漂亮、最安全的;买磁带机,便宜能用就行了。
- 一人独裁真的好吗?不是的。一人独裁仅仅是因为多人讨论时气氛太温和,太容易顾及别人感受。如果有10个jobs在一起,相信会比一个强——哪怕每天都吵翻天。
- 乔布斯刚开始的第一桶金是电脑刚刚发展的阶段,现在苹果公司所做的,已经不是一个小公司可以完成的了。乔布斯当时的机遇还有吗?有。简直遍地都是。做不了整个iPhone,你可以仅仅做UI设计,只要你的想法真的正确,一定可以被收购,然后等手里有了足够的钱,再做整个产品链。
- 以前一直不知道怎样算是一个好CEO。看完这个后明白,好的CEO不是一个对金融无比了解、每天都用各种数据报表演讲的人,而是一个能为整个公司掌舵,让公司不断前进的人。
11
2011
Google Adsense Pin 码
Adsense账户收入超过10美元后就需要验证账户了,其中包括往家里寄一张包含Pin码的卡用于验证地址。
昨天回家时惊喜的发现终于寄过来了 :)
银行验证今天也收到了,没想到Google的验证金额竟然高达5+HKD,记得以前Paypal验证的时候是0.3几。
6
2011
写Micolog2有感
- Python参数传递的灵活性给程序开发带来很大的简便
静态语言中如果class B用到class S,那么B一般要包装下S的参数,然后在调用S的函数时将相应的参数传进去——于是,如果两个人分别负责B和S,那么A类的接口就需要事先两人商定好,否则S一改B就SB了。在Python开发中有时候却不必如此,因为B用不到的参数可以很简单的通过**wargs或者*args传给S。这样开发时接口的协作上可以少很多,一个人开发自己那块时也可以更加豪放一些。
其他语言当然也可以尝试事先这个,不过一定会看起来很丑,呵呵 - 动态语言的错误检查很需要一个好的工具
一个显而易见但是却很恼人的问题是,很多简单的bug总是不小心就出现——例如类型不对,参数个数不对——这个在静态语言中几乎是不用跑就能解决的。不知道Python有什么工具可以帮助找到这些可能的Bug。实验室有人在做java的自动测试,不知道有没有Python的……
6
2011
Micolog2 RC版释出
今天早晨把cache部分更新完毕,现在对于留言的支持也完成了:基本上一次留言对应的也只是几次查询——哪怕那篇留言的数量有200条。
在数据库这方面我自己想要的效果已经达到,相信这方面也应该可以满足大家的。
只是这几天我要出门,周末回来,因此匆匆释放现在这个简单测试过的版本——暂且就叫它Release Candidate吧,请感兴趣的同学帮我一起测试下,把问题或者补丁发在Google Code的项目页面上,多谢。测试的方法我在README里面有简单写,就是做操作,然后看log。因为cache的生成、失效、更新现在在log里面都有记录(DEBUG级别)。
下载页面:
http://code.google.com/p/micolog2/downloads/list
在RC版的问题修正后,Micolog2以后会用[年.月.日]的格式作为版本号发布,这样也方便大家看到相比于自己的版本,是否有更新。
5
2011
Micolog2 Beta版效果
GAE应该是下午4点或5点开始重新计费,现在是上午11点20. 也就是从昨天的更新点开始,到现在是19个小时20分钟或者18个小时20分钟。
我英文博客流量来于世界各地,访问在各个时间段相对来说比较平均,不会有晚上少,白天多的情况,所以拿英文博客数据来看效果。现在英文博客的日浏览量在300左右:
而现在Micolog2 Beta对于免费配额的使用率为:
可以看到在Micolog2 Beta版中,数据库操作量已经不再是瓶颈。P.S. 以前英文博客撑不过24小时。
下面一个版本更新周期会比较长,不过从数据来看,相信beta版对一般个人博客已经足够。
提示:在admin里面操作是比较费数据库操作的,因此配置Micolog2的当天配额可能占的比较高,后面就会好的……
4
2011
Micolog2 Beta 发布
经过将近一天的紧张编码,Micolog2的缓存自动失效终于编码、测试完成。目前还是一个较为简陋的方案,并没有细致入微的对每一个地方都进行恰当的优化,不过对于个人博客应用,应该已经很有效果了 :)
后续的优化还会继续,不过进度会放缓。
下面是当前优化的手段和思路:
将缓存分为永久型和暂时型两种
对于可以预见变化的地方,采用事件机制,平时一直存着,等变化时更新或者令其失效。对于这种缓存,使用memcache+数据库,保存的时候在数据库和memcache都保存一份,查时先查memcache(memcache自动失效很快的),没找到再查数据库。
对于难以预见变化,或者变化很快的地方,仅仅使用memcache。
缓存失效通知机制
这个是主要用在永久性缓存类型的。举个例子:假如现在已经缓存了主页,但是刚刚新发布了一篇文章,怎么办?没办法,只能发通知让受这个事件影响的缓存失效,例如主页。下次再访问主页的时候,因为关于主页的缓存已经没了,因此会重构主页——不过,因为“好友链接”并不受发布新文章影响,因此在重构主页时对于好友链接扔能直接通过cache拿到。这种通知机制类似于构建一个因果关系网络,能够极大的减少不必要的数据库的查询,尽量在数据变化时再重新查询。
注意事项
缓存失效机制已经覆盖了最重要的地方——例如发布文章、发布评论,一些次要地方是直接使用的全局失效,或者并没有做——还好在您的博客稳定下来后这种情况并不多见。
如果您修改后没有马上看到修改后的结果,并想马上看到效果,请在管理员控制台选择“清空缓存”(这个操作已经被重写,会将永久型和暂时型的缓存都清空);另外尽量少的在控制台进行操作,因为控制台那里是没办法缓存的,并且经常会导致清空现有缓存,所以在那里留恋代价比较高 :)
不过在未来会越来越好的,毕竟我自己的博客在用。
欢迎大家使用、给予反馈,或参与到开发中来!
后面的开发进步会放缓,请谅解
现在这个Beta版对于一般个人博客应该够用了
另外,我的个人博客(http://blog.rexzhao.com)已经换成了Micolog2
2
2011
Micolog2 发布
Google App Engine新政的免费配额比以前少了很多,现在很多使用Micolog的博客会很快把免费配额用完。鉴于Micolog已经停止开发,并且我自己没有独立的主机,所以决定自己去优化一下数据库操作 (data store operations optimization),其他方面的bug或者enhancement暂时不做处理。
我把这份工作放在了Google Code上,今天夜里终于把代码改过了一遍,简单的测试了下,因此算是Alpha版吧。因此请仅仅用于测试,保险起见,暂时最好不要直接应用于自己当前的博客。(当然你博客如果现在一两个小时就超那么不妨试一试,反正在GAE切换回原来版本很简单。)
以后还会有改动,比如现在的缓存会存比较久,有新评论时不能够自动将相关的缓存清除。这个会在未来添加一个自动清除缓存的框架。 (Update: 该框架已实现)
至于效果能有多好,我自己还算乐观,因为以前Micolog的很多操作确实有很大改进空间。而且对于一个更新不频繁、访问量不大的博客,一天50000次左右查询应该够了。
另外下载Micolog后发现其实里面有一些GPL代码的,而原Micolog却是MIT license,因此Micolog2以GPL协议发放。
演示地址 http://micolog2.rexzhao.com
下载请到 http://code.google.com/p/micolog2/downloads/list
bug提交请到 http://code.google.com/p/micolog2/issues/list
Update:
今天才发现在徐明的BBS里面他有提到自己要做改动,不过我决定继续这个项目,毕竟每个人的改动思路是不同的。关注Micolog的同学最近也最好关注下Micolog是否有新版本释出。
29
2011
Google Code在Windows下面设置.netrc
在Push代码的时候每次都输入用户名和密码很麻烦,尤其是自己也记不住密码的时候。Google Code有教怎么设置:
Add the following to your .netrc.
machine code.google.com login [email protected] password XXX
Make sure the clone URL doesn't contain your username:
git clone https://code.google.com/p/micolog2/
但是这个设置在Windows里面没效果,在cygwin里面也不行(这个不确定是为什么)。后来在Stack-Over-Flow上查到了方法,原来Windows里面需要在%HOME%目录下建立_netrc文件,并不是.netrc
打开命令行,执行下面的命令就可以了:
1 设置一个%HOME%环境变量,将其指向某个目录,例如自己的用户目录
setx HOME %HOMEPATH%
2 在该目录下面新建_netrc文件
notepad %HOMEPATH%/_netrc
3 填写相应的内容,然后保存关闭
machine code.google.com
login [email protected]
password XXX
以后打开Git Bash然后再执行pull、push命令就不会再提示登陆了。
29
2011
Windows 命令行设置用户级、系统级环境变量
通常设置环境变量需要通过Windows的“环境变量”对话框来设置,但是这样比较麻烦。在CMD里面直接执行Set命令的话只对当前的session有效,CMD退出那么环境变量自动清除。
在Windows7里面可以通过SetX来设置用户级以及系统级环境变量,其语法是:
SETX Variable Value [-m]
其中-m表示为系统级(Machine environment, HKLM),默认为用户级(User, HKCU)
需要注意的是,这样设置的环境变量并不会在当前的CMD下生效,要想生效,必须重启一个CMD。
将变量值设置为""(空字符串)并不会删除这个变量,要想删除需要执行
REG delete HKCU\Environment /V Variable (用户级)
或者
REG delete "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" /V Variable (系统级)
很明显,删除的时候使用对话框可能更方便些。
29
2011
Google code: Why 'Everything up-to-date' when pushing (git)
第一次在Google Code上弄项目,注册完毕后,尝试增加一个新文件用以测试Git是否好好工作。结果在Push的时候却显示"Every up-to-date",检查文件时却发现实际上一个都没更新上去。因为对Git不够熟悉,因此只好Googling.
进行一番搜索后找到原因如下:
Why does Git refuse to push, saying "everything up to date"?
git push with no additional arguments only pushes branches that exist in the remote already. If the remote repository is empty, nothing will be pushed. In this case, explicitly specify a branch to push, e.g. git push master.
也就是说一开始git服务器仓库是完全空的,不包含任何一个分支(branch),因此刚开始Push时需要指定一个。
执行git remote -v后看到自己的remote端名字为origin:
$ git remote -v
origin https://code.google.com/p/micolog2 (fetch)
origin https://code.google.com/p/micolog2 (push)
执行git branch后看到自己当下用的分支是master:
$ git branch
* master
因此在本地commit后,再执行
git push origin master
即可。
24
2011
分享一个查字典的tip
Google Dictionary死了以后,我就改用了有道。但是有道的速度很慢(至少在香港是如此,无论学校还是家里)。今天发现原来Google Dictionary其实并没有死,只是集成在了搜索框里=_=!!
查的方法是在字的前面加一个"definition:", 例如要查duality就这样:
切记语言使用的是英文,如果是中文,那么看到的是中文解释(鉴于中文文档少,所以很有可能没有匹配,如下图)
23
2011
git使用点滴:如何配置branch
Git虽然是分布式的系统,但是小组工作时Git一般都会设置一个中心服务器,大家都往这个服务器上push/pull代码。
有时候需要离开主线(master)做一些测试或者Debug工作,或者想将代码分为开发版、稳定版等,这时候就需要做分支。
在中心服务器这种结构下,所有人的remote端都是这个中心服务器,因此配置方法是(以建立dev分支为例):
- 登陆服务器
- 执行git branch dev来建立dev分支
- 在本地执行git pull来做更新,然后git checkout dev就可以切换到dev分支
另外,git push origin的时候会push所有origin的分支(在只有一个远端的情况下,remote可以不写,因此一般情况下命令是git push ),如果仅仅想push自己所在的某个分支,那么使用git push –f –v –n origin dev。
8
2011
About the blog failure
Google's App Engine graduated, and as a result, the free quota of every app droped dramatically.
The datastore operations has reached its limits yesterday, so some of you may already see that failure (should displayed as Server-Error). Because the income of my blog is far less than the fee I should pay to Google, I choose to stay with free account, but modified my blog code to reduce the datastore operations.
Also, page cached is reenabled to avoid frequently database fetching. So If you leave a comment but not see it appear immediately, don't worry, it will appear when the cache is invalidated.
Hope this can solve the problem, or I'll choose to move my blog to elsewhere.
7
2011
第10次会议简记
没记错的话这应是第10次了。这次是MileStone2结束前的会议,也是未来Project的一个转折。
MileStone2的完成情况比较糟糕,大概只有50%,主要是平日闲散时间太少吧。所以12月末能否按时推出不好说。我只能尽力把自己那份做好。
另外今天讨论了进度、算法,还有ProjectV——这个想法大家都觉得有意思。鉴于我们人手紧张,是时候扩充队伍了。
热门文章
- Hack EdgeWorld 2(2971)
- Hack Google+上的EdgeWorld游戏(1558)
- Windows7 中记事本和任务管理器的替代程序(570)
- 推荐一个Todo List应用(416)
- 近期话题(353)
- hello world(335)
- 磨舌头,读英语(306)
- Cygwin, SSH, Git:近两日所得(299)
文章作者:Rex