zoukankan      html  css  js  c++  java
  • Potrace:一个基于多边形的位图轮廓矢量化算法(translation)

    原文地址:http://linxinboy.blogbus.com
    这是一个非常古老的算法 源于2003年,作者 Peter Selinger。
    它的作用是输入一张二值图(不是黑就是白),输出此图的矢量化结果,也就是将位图轮廓矢量化。
    下面是译文部分, 所有注释会放在文章最下方。 

    预览

    csdn看不了在线预览,http://linxinboy.blogbus.com里面有。
    由于算法只能输入纯黑白图像 所以我把输入图像二值化了一下。

    代码:这里


    1 介绍

     

     

     


    二值图可以被以位图形式或者矢量的形式进行呈现。位图是将图像当做一个个不是黑就是白的像素格子。矢量图则将一张图像通过代数描述其轮廓来呈现,比如贝塞尔曲线。将一张图像以矢量方式进行呈现的好处是他可以被缩放至任意大小而没有品质上的降低。图像轮廓对于任何特殊的输出设备都是独立的。他们在字体这种必须在不同尺寸可重复使用的东西里非常流行。矢量轮廓的字体有PostScript Type 1, TrueType,和Metafont。另外一方面,大多数输入输出设备,比如扫描仪,显示器,打印机最终都产生或者使用位图。将矢量轮廓转为位图的过程叫做呈现(rendering).将位图转为矢量轮廓的过程叫做描绘(tracing).


    很明显没有矢量化算法能在任意场合表现完美,因为同一张位图一般可以引起多种可能的矢量轮廓。轮廓矢量化不能用于产生还没有呈现的信息。另一方面,由于多种可能的轮廓可以产生同一张位图,但明显有一些比其他的更加合理和美观。比如,一个在高分辨率下显示位图的一般方法,是去绘制每个黑色像素在精确的方形格子里,这样会导致锯齿。很明显,锯齿既不好看也不合理。形成好的矢量化算法可能没有绝对的标准,但明显有些算法的结果要好于其他。


    在这片论文里,我们描述了一种简单,高效并产生极好结果的轮廓矢量化算法。这个算法被称作Potrace,表示多边形绘图。然而这个算法的输出并不是一个多边形,而是一个由贝塞尔曲线构成的光滑轮廓。算法的名字来源于他使用多边形来当做中间层呈现图像的事实。


    Potrace算法被设计为在高分辨率图像上工作良好。所以,一个典型的用途是产生某公司或者大学的大分辨率logo矢量化结果。另一个可能的用途是将位图字体转为矢量字体在源位图有高分辨率的情况下。没有矢量化算法可以在非常小的图像上工作良好(注1),比如把10像素的字体以75像素显示。然而,算法在相对还好的分辨率下的非精确图形上比如扫描的手写或卡通绘画上工作良好。


    每一个矢量化算法都得有几个步骤。其中两个是找到最合理地近似轮廓的曲线,并检测转角。在这两个目标里有一个权衡。如果检测出太多转角,结果会看起来像多边形而不再光滑。如果检测出太少转角,结果会看起来光滑但太圆滑。图1显示了一个例子。

    矢量化算法的另一个重要功能是决定位图在扫描或呈现阶段哪些特征是有关的,那些特征是无关的。那些可以被看做无关的特征必须完全过滤掉,因为即使留下一点点这样的特征在输出上都会导致可见的缺陷。比如一个非常短的正向的斜的直线。当以位图进行呈现,这样一条线将导致锯齿,个别像素会偏离得很远。不论像素离得有多远,结果应该是一条直线,否则结果会看起来让人厌。这个例子也显示了矢量化算法一般不是局部运算,即他不能仅仅只看某点的周围固定范围的像素。


    Potrace算法非常高效,它比其他算法产生更好的输出。比如,图2比较了标准设置下的Potrace 1.0和AutoTrace 0.31.1,另一个矢量化算法。(参见http://autotrace.sourceforge.net/).除了它优秀的输出结果,Potrace也在速度和文件大小方面超出AutoTrace:图2花费了Potrace0.27秒,Autotrace花费了1.69秒。Potrace产生了一个15790字节的EPS文件,AutoTrace是39788 字节。


    2 Potrace算法描述


    Protrace算法将位图转为矢量轮廓有几个步骤。第一,位图被分解为一些路径,他们构成了黑白区域之间的边界。第二,每条路径都被近似为一个最优多边形。第三,每个多边形都转化为光滑的轮廓。一个可选的步骤,结果曲线通过链接连续的贝塞尔曲线片段来进行优化。最后产生需要的格式输出。下面的章节将对每个步骤进行详细描述。

    2.1 路径


    2.1.1 路径分解


    我们假定我们的位图放置于一个坐标系每个像素的转角都有整型坐标。让我们进一步假定背景为白色,前景为黑色。按照惯例,超出位图边界的区域被假定为白色填充。


    我们现在构造一个有向图如下。p是一个整型坐标的点,这样的一个点与4个像素相邻(注2)。如果4个像素都不是同一种颜色这个点被称作顶点。如果v和w是顶点,如果v和w的欧几里得距离为1我们说这里有一条从v到w的边,并且如果这条直线片段将v和w分为一个黑像素和一个白像素,当从v移向w使黑像素在它的左边白像素在它的右边。让我们将这个结果有向图称为G。


    一个路径是一系列顶点{v0, . . . ,vn},对所有i = 0, . . . ,n−1都有一条边从vi到vi+1,这些边都非常清晰。一条路径被称为封闭如果vn=v0。路径的长度为边的个数即路径分解的目标是将图G分解为封闭路径。即找到一个封闭路径的集合使G的每条边都只出现一次。


    Potrace使用如下的简单方法去分解一个位图到路径。从一对有不同颜色的相邻像素开始。可以这样完成,比如通过选择某一行最左边的黑像素。两个被选择的像素在一条边上相遇,我们改变这条边的朝向来使黑色像素在边的左边白色像素在右边。边被定义为长度为1的路径。我们继续扩张这条路径使新的边都有一个黑色像素在它的左边一个白色像素在右边,相对于路径的方向。换句话,我们在像素间沿着边移动,每次遇到一个转角的时候,我们要直走或转左转右,根据像素周围的颜色来决定如图3所示。我们继续下去直到我们返回到我们开始的那个点,即那个我们定义了一个封闭图的点。


    每次我们找到一个封闭图,我们通过反转它所有像素的颜色来从图中移除。这定义了一个新的位图,我们在这张图继续递归应用这个算法直到没有黑色像素余留。最终结果是将用于Potrace算法下个阶段的封闭路径的集合。后面Potrace的阶段将单独着眼于每个路径。


    2.1.2 转向方针


    在图3(d)的情况下,我们要选择是往左还是右转。这个选择对路径分解算法的成功失败没有影响,不论以哪种方式我们都是以封闭路径的集合结束。然而,这个选择会对封闭路径的形状有影响。


    在Potrace算法里,往左还是往右由一个转向方针决定,它可以通过 turnpolicy在命令行选项定义。可能的转向方针有:左,一直会往左转。右,一直会往右转。黑,更倾向于链接黑色单元。白,更倾向于链接白色单元。少数派,更倾向于链接在当前点给定范围内出现的最少的颜色。多数派,更倾向于链接出现的更多的颜色。随机,随机转向。默认规则是少数派。

    黑色和白色从右和左的转向方针有区别的原因是某些像素可能在路径分解的过程中颜色被翻转。黑白方针查看源像素颜色来决定转向的方向。(此段话不是很理解)


    2.1.3 去杂点

    去杂点可以通过去除所有路径内部像素少于t个像素的路径。t为一个给定的值,可以通过turdsize命令行选项来设置。路径内部的面积可以通过下面的公式高效计算:


    2.2 多边形

    Potrace算法的第二个阶段以章节2.1定义的封闭路径为输入。输出时一个近似这条路径的最优多边形。我们由精确定义什么是"最优"和"近似"来开始。


    2.2.1 直线路径

    给定两个点z0(x0, y0) 和 z1(x1, y1)在坐标系平面,不用必须整型坐标,我们定义他们的最大距离为d(z0, z1) = max{|x1 − x0|, |y1 −y0|}。因此,离点(1/2, 1/2)最大距离为最多 1/2 的点的集合为那些以(1/2, 1/2)为中心的点(注3)。

    对坐标平面的任两点a,b,令ab表示链接a和b直线的直线片段。这里a和b并不必须在整型坐标。

    给定一组非封闭的路径p = {v0, . . . ,vn}如在章节2.1,我们说线片段ab近似这条路径如果d(v0,a) ≤ 1/2,d(vn,b) ≤ 1/2,并且对于每一个i = 1, . . . ,n−1,存在点ci使得d(vn,b) ≤ 1/2。对于一条路径p = {v0, . . . ,vn},我们说在索引i上的方向为vi+1 - vi,i = 0, . . . ,n−1。这里有4中可能的方向:(0,1), (1,0), (0,−1), 和(−1,0)。一条路径被称作直的如果它可以被一

    些线片段近似而且4种方向没有都出现。


    图4显示了一些直和非直的路径。注意图中,那些点表示源位图坐落于转角而非中心的顶点。显示的正方形不是像素,而是路径点周围像素。(注2)


    图4(e)显示了一个非直路径的例子,虽然它被一些线片段近似,但是4种方向都在路径中出现了。


    非常明显如果一条路径是直的,那么它所有的子路径也是直的。为了计算一条路径是否是直的,我们使用一个更强的事实,在随后的部分平直度是一个3元组属性。假设路径p = {v0, . . . ,vn} 没有出现所有4中方向。那么p是直的当且仅当对于所有索引的3元组(i, j, k)使0≤ i < i < j < k ≤ n,在这条直线上通过vi 和 vk存在一个点w使d(vj, w) ≤ 1。这产生了一个简单的平直测试算法,最坏的情况下为3次方复杂度。它简单对所有3元组(i, j, k)进行测试。


    在Potrace的实现中,我们使用一种优化来允许我们最坏情况下在时间O(n^2)内找到一个给定长度n的封闭路径所有直的子路径。简单说,诀窍是计算,对每对(i, j),约束所有将来的vk的位置。如果i是固定的而j是增长的,只要检查一次每个j的约束就可以。而且,一个约束由最多两个不等式构成病切可以在固定的时间内被更新和检查。(此段不懂)


    2.2.2 多边形

    现在考虑一个封闭路径p = {v0, . . . ,vn}。回想vn = v0,所以路径的长度为n。任一对索引 i, j∈ {0, . . . ,n−1} 定义了一个子路径Pi,j ,其中路径为vi, . . . ,vj如果 i ≤ j 或者为vi, . . . ,vn−1,v0, . . . ,vj如果j < i。让我们记做j Θ i (Θ原符号不会打)对i和j之间的环形差异,j Θ i  = j - i如果 i ≤ j 而 j Θ i = j - i + n如果 j < i。因此,子路径Pi,j的长度被精确为j Θ i 。在下面的讨论中,我们假定加法和减法都对n取模(注4)。


    我们现在想从封闭路径p中构建一个多变形。我们说这里存在一个可能的从i到j的片段如果j Θ i ≤ n - 3并且子路径Pi-1,j+1是如前面的定义那样是直的。换句话说,一个子路径对应于一个可能的片段如果它可以在两边的方向上都扩张1像素并且保持直的。这个某条直的路径两边端点特殊的"剪裁"对于整个Potrace算法的输出非常重要;没有它,会在转角处导致奇怪的现象。

    注意根据章节2.2.1的定义任何长度3的路径为直的,因此它被保证总有一个可能的片段从i 到 i+1。



    为了这个算法的此阶段的目的,一个多边形是一序列索引i0 ≤ i1 ≤ . . . ≤ im−1 如此这里有一个可能的片段从ik 到 ik+1对于每个k = 0, . . . ,m−2,和从im-1到i0。(注5)图5显示了一个路径两个可能的多边形。

    注意图5中的多边形片段没有真正的必须近似他们对应的子路径从图4的红线片段来说。他们简单呈现了一个近似线片段存在的事实。


    2.2.3 惩罚(注6)


    已找到所有可能的多边形,现在我们想要找到一个最优的。我们对最佳性的主要标准是片段的数量:一个拥有更少片段的多边形被认为是比更多片段多边形更优的。在图5中,左边多边形有14个片段,而右边的有17个片段。因此左边的比右边的更优

    在片段数相同的多边形当中,有一些仍然比其它的更好。我们赋予每个片段一个惩罚。给定一个从 i 到 j 的片段,记直线片段ViVj(如图5蓝线)。赋予这个片段的惩罚等于ViVj的欧几里得长度乘以路径上每个点到ViVj欧几里得距离的标准偏差。即公式如下:


    dist(a,bc)表示某点距离一条直线的欧几里得距离,且易理解的从 i 到 j 的累计数量在一个循环方式中。简而言之,这些点偏离片段越远,受到的惩罚越大

    Pi,j的公式这样是因为他可以高效计算;即令(x,y) = vj −vi , (x,y) = (vi+vj)/2(注)。然后我们有

    其中


    是 Xk的平方的期望值,k =i, . . . , j,其他的E(x)类似。

    注意这些累加可以提前计算,通过制定一个的累加值的表,为每一个数量总计qk的值。建表后,这使花费的时间和空间呈线性,以上Pi,j的公式可以在一个固定时间内计算对每个给定的i,j。


    2.2.4 优化多边形


    我们可以将给定的封闭路径p = {v0, . . . ,vn}视为一个有着顶点0, . . . ,n−1的有向图,其中从 i 到 j 的箭头如果从 i  至 j其中有可能的片段。对于序列 i0 → i1 → . . . → ik ,我们可以赋予一个惩罚,(k, P), 其中k是箭头的数量,P是他们的惩罚的和如章节2.2.3中讨论的那样。(k, P)比(k′,P′)更好如果 k < k′, 或者 k = k′且P < P′。


    这样,找到一个最优多边形被降为在一个有向图中找到一个最优环形。我们使用一个标准的图论算法的变量来寻找有向图中的最优环形来高效解决这个问题。一旦图被计算了,一个最优环形可以在时间O(nm)内找到,其中n是输入路径的大小,m是最长的可能的片段长度。

    我们注意到是这个优化让我们的算法非局部,因为一次必须考虑整个路径;优化多边形的每部分可能取决于其他部分。在前面计算一个位图路径的阶段,和接下来把多边形转为矢量轮廓的阶段,都是局部的,即他们每次只查看很少的相邻的点。


    2.3 从多边形到矢量轮廓


    算法最后的阶段是将前面获得的多边形转为光滑的矢量轮廓。在准备阶段,我们调整多边形顶点的位置来使它尽量符合源位图。在主要步骤,我们计算转角和曲线根据临近的线片段的长度和他们间的夹角。


    2.3.1 顶点调整


    算法前一阶段的输出时一个多边形{i0, . . . , im−1}关联一个封闭路径{v0, . . . ,vn}。我们指的是索引i0, . . . , im−1,以及他们关联的作为多边形顶点的点vi0 , . . . ,vim−1。因为我们的多边形为环形,所以按照惯例把索引取模m。


    为了计算惩罚,我们将多边形的顶点 i 精确地放置于对应路径的点vi,即在坐标系有整型坐标的点。(即坐落于源位图4个像素的交叉点)。这样放置顶点允许我们高效地计算惩罚,这在优化范围内并不是必要的。我们现在关联每个顶点ik 一个点 ak在坐标系,不需要一定是整型,这样ak离Vik很近, 而且对于多边形任意两个连续顶点 ik 和 ik+1,结果线片段akak+1是合理接近于源子路径vik , . . . ,vik+1 。

    我们使用下述算法来放置点Ak:对每个连续的顶点Ik 和 Ik+1,计算最佳近似点vik , . . . ,vik+1 的直线Lk,k+1,就这而言它最小化了它们离直线的欧几里得距离。如果Ik-1,Ik,和Ik+1是连续的顶点,那么我们最想把Ak放置在Lk-1,k 和 Lk,k+1的交叉处。然而,我们并不想让Ak太过远离原顶点Vik。因此,我们让Ak成为单位平方形里的最大距离d(Ak, Vik) ≤ 1/2的点,这样从Ak 到 Lk-1,k 和 Lk,k+1的欧几里得距离的平方和为最小。尤其如果Lk-1,k和Lk,k+1的交点在这个单位平方形里;否则,我们将它放置在离Vik很近的点即离交叉点"很近"。


    计算ak 很容易,它只是简单总计去在一个正方形内最小化一个二次函数。并且,通过使用一个标准方法"best fit"很容易计算从点vik , . . . , vik+1 到直线Lk,k+1 :这条线通过重心(E(xk),E(yk))即 k = ik,...,ik+1,并且它的斜率由下面矩阵更大特征值的特征向量给出。
    矩阵
    [a b
    b c] ,
    其中


    2.3.2 一种贝塞尔曲线


    此章节的目的是做一个简单,有用的贝塞尔曲线观测。回想贝塞尔曲线由4个控制点z0, z1, z2, z3, 和 参数方程z= (1−t)3z0+3t(1−t)2z1+3t2(1−t)z2+t3z3 给定。为了分析,我们限制通过z0z1和z3z2的直线交叉于点o(即他们不是平行的)。然后,我们限制曲线是凸的且改变方向小于180度;这意味着z1位于z0和o之间,z2位于z3和o之间。这种情况如图6所示。通过一个坐标系的线性转化,我们假定z0 =(−1,0), z3 =(1,0), o=(0,1)。任何这种特殊的贝塞尔曲线都由两个参数决定,a,b ∈ [0,1],z1 = (−1+a,a),z2 =(1−b,b)。图7给出了在这种情况下的a和b的值每次乘以0.2的贝塞尔曲线预览。在图中,控制点z1和z2显示为红点。我们可以马上从图中发现贝塞尔曲线在任何特殊水平行上视觉上都几乎无法区分,除了大概a或者b非常接近于0的情况。我们将看到我们的算方法从不产生a,b很小的贝塞尔曲线,这样我们可以避免后面的可能性。于是我们如果我们限制a = b则不会漏掉任何有趣的曲线。这消除了一个角度从我们需要考虑的可能的贝塞尔曲线中,而且这样简化了我们找最优曲线的工作。

    我们没有强调我们要求所有的贝塞尔曲线都想图7中显示的那样。不如说这是在线性变化的情况。因此,如果给定z0和z3,这里有两个在o位置的自由度,且一个额外的自由度是a的选择。通过设置a = b,第四个自由度被消除了。

    一个有趣的事实是被贝塞尔曲线和x轴包围的面积等于3/10*(2a+2b−ab) 或 3/10 * (4−(2−a)(2−b))。由图7我们得出两个曲线看起来很相似如果他们包围的面积相同。因此,我们可以用相等参数近似任何参数a和b的曲线。


    另一个贝塞尔曲线有趣的测量是他的最高点。当a = b,最高点会在t = 1/2的时候达到,而他的y坐标为 3a/4;(这里的a b 是上

    图里面的阿尔法 贝塔)


    2.3.3 平滑化和转角分析


    算法的最后阶段的输入是从章节2.3.1调整后的多边形。假定这个多边形的顶点为a0, . . . ,ak−1。令b0, . . . ,bk−1为多边形边的中点,即bi = (ai+ai+1)/2。对于每个i,我们现在考虑转角bi−1..ai..bi,且我们通过一个光滑曲线来决定是否近似它,如图8(d)所示。

    我们如下继续。首先,我们在点ai上画一个单位方形。然后,我们找到平行于bi−1bi的线Li,并在ai的周围接触到方形,且它尽可能地接近直线bi−1bi。令c为Li和 bi−1ai的交点,并令g为bi−1c的长度和bi−1ai的商。令 a = 4g/3 并 考虑贝塞尔曲线(章节

    2.3.2所考虑的那种)链接bi−1 和bi 用参数a。这条曲线切线于bi−1ai, Li, aibi 这3条线。


    我们使用刚刚计算的参数a来进行转角检测盒决定最终从bi−1 到 bi的曲线。这里有两种情况。 如果 a ≤ 1,然后我们绘制一条光滑的贝斯阿尔曲线在这个顶点,如图8(a)-(c),如果 a > 1,这里没有凸的贝塞尔曲线来链接bi−1 和 b 并切线于Li。在这种情况下,我们检测了一个转角且我们通过两条相交于点ai的直线片段链接bi−1 和 bi ,如图8(d)所示。


    转角检测可以通过转角阀值参数amax来定义,通过--alphamax命令行选项来设置。如果这个参数有被设置,那么顶点将为圆角 如果 a< amax, 如果a > amax则是一个转角 。因此,更小的amax会导致更多的转角,如图1(b)所示,更大的值则会导致更多圆角,如图1(c)所示。默认的amax = 1。 如果amax = 0,没有平滑化会执行Potrace的输出为一个多边形。如果amax > 4/3,将会完全没有转角,输出全都是光滑曲线。


    检测完转角以后,a的值被调整为0.55 和 1 之间。a > 0.55 是为了阻止曲线太"平"。让 a < 0.55常导致奇怪的图像。a < 1是为了保证结果贝塞尔曲线是凸的。

    a = 0.55被选择是因为它会导致圆形物的良好近似在输出时一个正常多边形的情况下。它被选择是因为理论值
    a0 =4/3(√2−1) ≈ 0.552285


    它通过一条贝塞尔曲线对1/4圆有着最好的近似。更精确地讲,这条贝塞尔曲线有着坐落于半径为1.00027253单位圆中的控制点z0 = (1,0), z1 = (1,a0), z2 =(a0,1), 和 z3 = (0,1)。因此,这条曲线偏离一个真正的圆(1/4圆)不到0.01363%。虽然贝塞尔曲线片段对1/4圆的近似众人皆知,但准确地范围很难在文献中找到;比如,Faux 和 Pratt 错误地给出了0.13%的值,这应该

    是显然的印刷错误,反之Knuth给出它只"比0.06%要小"。


    注意我们的转角检测算法有如下属性:尖锐的角度和长片段更有利转角。因此,我们检测一个转角如果两个短片段相交于一个很小的角度,或者两个很长的片段相交于一个轻微的角度。


    2.4 曲线优化

    前一个阶段在转角检测和光滑化之后,输出地是由贝塞尔曲线片段和直线片段构成的。结果曲线非常接近Potrace的最终结果。但是,这里算法仍然有一个最后的优化,曲线优化阶段,即尝试通过连接临近的贝塞尔曲线来优化。曲线优化仅仅对最终曲线产生非常小的改变;小到一般看不出区别。但是,结果曲线会由更少的片段构成,所以程序输出会以更紧密的方式显示。如果曲线优化是不需要的,可以通过--longcurve命令行选项来设置。


    曲线优化基于几个简单的想法。首先,我们仅仅尝试去连接临近的曲线片段,绝不连接直线片段或者转角。第二步,我们只连接角度凸的曲线片段,即他们都向右或者向左弯曲。三,我们连接总的方向改变少于179度的曲线片段。(我们没有允许180度是为了避免在下面计算不受控制)。这使我们要考虑如图9蓝色显示的片段序列。

    问题是我们是否能找到一条单个贝塞尔曲线来近似给定的一组更短的贝塞尔曲线。假定这里有这样的一条曲线C。明显,C将切线于b0a1和anbn。我们可以找到b0a1和anbn的交点O。根据我们在章节2.3.2中讨论的那样,这只留下一个自由度在需要考虑的曲线中,即参数α。如果我们强行让被曲线C包围的面积和原曲线片段包围的面积相等,然后这决定了参数α。回想章节2.3.2这个面积是很容易计算的。这留给我们一条特殊的近似那些给定的片段的曲线C.如图9红色所示。


    接下来我们需要检查C是否真正地可以去近似给定的曲线片段,且如果是的,给它一个数值的惩罚。我们用一个简单的相切来检查。对于每个i = 1,...,n−1,我们找到点zi在C上的切线并平行于aiai+1.我们另di为zi到线片段aiai+1的欧几里得距离。再,对于每个i = 1,...,n,我们找到在C上的点z′i 切线于C并平行于bi-1bi。我们另di′ 为z′i 到章节2.3.3定义的线片段Li的欧几里得距离,记为正的如果z′i 和Li和ai一样的一边,否则负的。(as是何意)


    我们说这个近似是可接受的如果di≤ε,di′ ≥ −ε,且zi在线aiai+1上的正交投影位于ai和ai+1之间。这里值ε 是一个常量值叫做曲线优化算法的容忍;它预先定义为0.2,他可以用--opttolerance命令来更改。

    为了一个可接受的曲线,我们定义它的惩罚为所有di和di’的平方的和。最后,我们使用一个标准的图形理论算法用最短路径来分解给定的曲线片段至可接受的近似,优化片段数量,然后总的惩罚。

    2.5 结果

    2.5.1 缩放和旋转


    Potrace算法已产生了一组曲线,每个都由贝塞尔曲线和直线片段构成。这些片段的结束点和控制点是坐标平面内的任意点。依据选择的后端和参数,Potrace现在执行一个线性的变化(缩放图像至需要的尺寸,并可以旋转它).


    2.5.2 冗余编码


    当使用PostScript后端,Potrace使用一个非常紧密的数值格式在输出中去呈现贝塞尔曲线。这样做利用了曲线参数里的冗余。原则上,需要6个参数去描述每个贝塞尔曲线片段(1个结束点2个控制点)。然而,通过消除多余的参数,Potrace可以将每个片段仅编码为3至4个真正的数字。一个自由度可以被消除是因为我们只使用 α = β的曲线,见章节2.3.2。另一个自由度可以被消除是因为点bi常常位于线片段aiai+1上,见章节2.3.3。第3个自由度可以经常被消除是因为bi实际上在ai和ai+1的半途中对那些没有被章节2.4的曲线优化步骤影响的曲线片段。(注释7)


    这个贝塞尔曲线的冗余编码仅仅在PostScript后端执行,因为它利用了PostScript语言的巨量的性能。冗余编码可以用--longcoding选项关闭,用于更长但更易读的输出。


    2.5.3 数字化


    对于大多数后端,最终的坐标,即真正的数字,是量子化的,它意味着他们被四舍五入至最接近1/10像素(注4)。因此,数字的小数部分需要呈现的每个坐标是通过在一个很好的网格中高效放置所有控制点来简化的。这些点的坐标可以待会输出为整形。默认的量子化常量为1/10可得到很好的记过。然而,它可用-urable -unit命令行选项来设置。

    3 一个完整的例子

    图10显示了Potrace的一个完整的例子。a部分显示了源位图,在b部分,记录了默认的“少数派”转向方针是怎样保持黑色轮廓沿着连着的图形,同时保持白色轮廓在图形内也相连。同样记录了图形内大小为1斑点被移除。b部分显示了优化过得多边形。c部分显示了调整过后的多边形顶点,相对于源位图,即用灰色显示的。每个顶点都围绕在单元方形周围。同样,章节2.3.3的线片段Li也显示了,参数α被写在每个顶点的单元方形内;这可以在Acrobat Reader中以一个很大的缩放来观看。(....这些小方块里面真的有数字,诸位同学可以到原版英文pdf里面观看)。转角分析也再这一步执行,注意这张特殊的位图,仅仅很少的转角被检测到。通常,转角分析在更高分辨率上工作得更好。光滑化下一个被执行;结果贝塞尔曲线片段和线片段在图中以蓝色显示。d部分显示了曲线优化的结果;原曲线以蓝色显示,优化过得曲线以红色显示。红点表示新的片段边界。注意片段的数量由112减少到68,接近40%。最终的结果如部分e所示。

    调试结果以图10(b)-(d)那样显示可以用命令行选项-d1 -d3来设置。

    参考文献 
    [1]  I. D. Faux and M. J. Pratt. Computational Geometry for Design and Manufacture. Ellis Horwood Series in Mathematics and its Applications, Editor: G. M. Bell. Ellis Horwood, New York, NY, USA, 1979.
    [2]  D. E. Knuth. The METAFONTbook, volume C of Computers and Typesetting. Addison-Wesley, Reading, MA, USA, 1986.


    注释(1):实际上已经有专门的用于非常小的位图矢量化算法,07年的Depixelizing Pixel Art

    注释(2):这里的四个点应该是包括自己的4个点

    注释(3):比如以(0,0)点为中心,最大距离为0.5的点的集合就以(0,0)为中心,边长为1的正方形区域点的集合

    注释(4):实际上不是Θ这个字符 原字符打不出来

    注释(5):i0之类的符号 0是下标 im-1 之类也是im-1 具体可看原文

    注释(6):惩罚Penalties,我们在图形算法里经常可以看到这个单词,它指的是当数值偏离最优解付出的代价,我们需要求的是惩罚总和最小的情况即为最优解,比如惩罚的公式是x的4次方,当x取0的时候值最小,当x>1的时候将会付出巨大的惩罚来限制x尽量<1。一般一个值的改变也会影响其他的值改变,所以是要求总的惩罚最小。

    注释(7):自由度:自由度(degree of freedom, df)在数学中能够自由取值的变量个数,如有3个变量x、y、z,但x+y+z=18,因此其自由度等于2。

    英文原版地址: ftp://94.198.20.42/pub/Graph/potrace.pdf

    代码以及预览(as的)大约2-3天以后放上来。

    _________________________________________________________________________________________________________________________________________________
    每一个不曾起舞的日子,都是对生命的辜负。
    But it is the same with man as with the tree. The more he seeks to rise into the height and light, the more vigorously do his roots struggle earthward, downward, into the dark, the deep - into evil.
    其实人跟树是一样的,越是向往高处的阳光,它的根就越要伸向黑暗的地底。----尼采
  • 相关阅读:
    动态规划最大利润的问题
    【转】mysql基础汇总
    mac使用frida
    Mac 下python3 [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed 解决方法
    mac使用jadx逆向app
    python桶排序代码
    requests_html使用asyncio
    async for的使用
    [转载]微信企业号:企业客户的移动应用入口
    微信服务号、订阅号、企业号差别
  • 原文地址:https://www.cnblogs.com/leoking01/p/14323603.html
Copyright © 2011-2022 走看看