Potrace算法通过几个步骤将位图转换为矢量轮廓。
第一步,将位图分解为若干条路径,在黑白区域间形成边界。
在第二步中,每条路径由一个最优多边形逼近。
在第三步中,每个多边形被转换成光滑的轮廓。
在可选的第四步中,通过在可能的情况下将连续的Bezier曲线段
连接在一起来优化生成的曲线。
最后,以所需的格式生成输出。
一、路径:
1、路径分解
定位边缘为像素间的有向边,使黑色像素在它的左边,白色像素在它的右边。这条边定义了一条长度为1的路径。然后,我们继续扩展这条路径,使每条新边缘相对于路径的方向,在其左侧有一个黑色像素,在其右侧有一个白色像素。
换句话说,我们沿着像素之间的边缘移动,每次遇到拐角时,我们要么直行,要么向左或向右转,这取决于周围像素的颜色。我们继续,直到我们回到我们开始的顶点,得到了一条闭合路径。
2、转向策略
在每次转向时,可以选择左转还是右转。这种选择对路径分解算法的成败没有影响,因为无论哪种方式,我们最终都会得到一组封闭的路径。然而,这种选择确实会影响所选择的封闭路径的形状。
二、多边形:
输入封闭路径p = {v0,…,vn},输出是一个近似于这条路径的最优多边形{i0,…,in}。黑色轮廓为封闭路径(黑色点是像素边缘坐标,不是像素中心,正方形是路径点的邻域,不是像素),蓝色轮廓为可能的多边形。
1、直路径
满足两个条件:
(1)a在v0邻域内,b在vn邻域内,对于v0vn上的任一点vi,ab上存在点c,c在vi的邻域内
(2)四个方向的转向没有全都出现
2、多边形
想要从闭合路径p构造一个多边形。我们说,如果i到j的长度小于n−3并且子路径(i-1,j+1)在之前的定义上是直的,那么从i到j可能存在一条线段。
换句话说,如果一条子路径可以在两个方向上都延伸一点,并且仍然是直线,那么它就对应于一条可能的线段。
3、惩罚
最优性的主要标准是片段的数量:具有较少片段的多边形被认为比具有更多片段的多边形更优。
在相同段数的多边形中,仍然有一些比其他的更可取。我们将每个可能的片段关联到一个惩罚。给定从i到j的可能线段,将其关联为直线线段(多边形的边ikik+1)。与该线段关联的惩罚等于ikik+1的欧氏长度乘以路径点到ikik+1的欧氏距离的标准差。
4、最优多边形
可以把给定的闭合路径p = {v0,…,vn}为顶点为0,…n-1的有向图,如果存在从i到j的可能段,则存在从i到j的箭头。对于每个序列i0→i1→…→ik的箭头,我们可以关联一个惩罚,它是一个有序对(k,P),其中k是序列中箭头的数量,P是它们的数值惩罚的总和,如第2.2.3节所讨论的。惩罚按字典顺序进行比较,即如果k < k ',或k = k '和P < P ',则(k,P)优于(k ',P ')。
这样,寻找最优多边形的问题就简化为在有向图中寻找最优循环的问题。
三、从多边形到矢量轮廓
1、顶点调整
为了计算惩罚,我们将多边形的顶点i精确地放置在相应的路径点vi上,这是一个在坐标平面上具有整数坐标的点(即位于原始位图中四个像素的交汇点)。虽然这种顶点的放置使我们能够有效地计算惩罚,但它不一定是最优的安排。我们现在将每个顶点ik关联到坐标平面上的一个点ak,不一定是整数坐标,使得ak靠近vik,并且对于多边形的任意两个连续顶点ik和ik+1,得到的线段ak,ak+1与原始子路径vik相当接近。
2、贝塞尔曲线
z1z2两点关于对称和在其他位置两种情况下的曲线一致,所以默认两点对称位置,简化工作。
3、平滑和角点分析
假设这个多边形的顶点是a0,…,ak−1。让b0,…,bk−1为多边形各边的中点,即bi = (ai + ai+1)/2。对于每个i,我们现在考虑角bi−1,ai。我们决定是用平滑曲线(如图(a) - ©所示)来近似它,还是用锐角(如图(d)所示)来近似它。
检测完转角以后,a的值被调整为0.55 和 1 之间。a > 0.55 是为了阻止曲线太"平"。让 a < 0.55常导致奇怪的图像。a < 1是为了保证结果贝塞尔曲线是凸的。
四、曲线的优化
曲线优化阶段,即尝试通过连接临近的贝塞尔曲线来优化。曲线优化仅仅对最终曲线产生非常小的改变;小到一般看不出区别。但是,结果曲线会由更少的片段构成,所以程序输出会以更紧密的方式显示。
五、生成输出
1、缩放和旋转
经过上述步骤已经产生一组曲线,每个都由贝塞尔曲线和直线片段构成。现在执行一个线性的变化(缩放图像至需要的尺寸,并可以旋转它)。
2、冗余编码
3、量化
最终坐标(实数)是量化的,这意味着它们被四舍五入到最接近的1/10像素。因此,通过将所有控制点有效地放置在一个非常精细的网格上,可以减少表示每个坐标所需的十进制数。然后,这些点的坐标可以作为整数输出。默认的量化常数为1/10通常会得到很好的结果。