提取粗线轮廓

时间:2010-12-13 03:26:53

标签: wolfram-mathematica

如何以矢量形式绘制下面的粗线轮廓?通过矢量形式,我指的是一些不是光栅或图像的图形基元集合。

Graphics[{AbsoluteThickness[100], JoinForm["Round"], CapForm["Round"],
   Line[{{0, 0}, {0, 1}, {1, 1}}]}, ImageSize -> 200]

http://yaroslavvb.com/upload/save/so-outlines.png

文档中提供了以下用于提取文本大纲的示例,但我没有找到修改它以获取Line个对象的大纲的方法

ImportString[ExportString[Style["M8", FontFamily -> "Times", FontSize -> 72],"PDF"], "TextMode" -> "Outlines"]

also tried在线对象上执行Rasterize并从Alpha通道中减去略小的版本。这会产生光栅化伪影,并且对于ImageSize->500

,每个形状在5秒时太慢

也问了数学小组

更新的 我试过通过MorphologicalPerimeter获得的点来拟合样条曲线。 ListCurvePathPlot理论上是这样做的,但它打破了像素“阶梯”模式。为了平滑楼梯,需要找到曲线周围点的排序。 FindCurvePath似乎很有希望,但返回了破碎曲线列表。 FindShortestTour理论上也可以做到这一点,但它在20x20像素图像中花了一秒钟的轮廓。 ConvexHull在圆形部分上做得很好,但是切掉了非凸部分。

解决方案我最终得到的结果是在周边点上构建最近邻图并使用版本8函数FindEulerianCycle来查找形状周围像素的排序,然后使用MovingAverage来平滑楼梯,然后是ListCurvePathPlot来创建样条线对象。它并不完美,因为仍然存在残余的“阶梯”模式,而平均过多则会消除重要的角落。更好的方法可能会将形状分解为多个凸形,使用ConvexHull,然后重新组合。同时,这就是我正在使用的

getSplineOutline[pp_, smoothLen_: 2, interOrder_: 3] := (
   (* need to negate before finding perimeter to avoid border *)

   perim = MorphologicalPerimeter@ColorNegate@pp;
   points = 
    Cases[ArrayRules@SparseArray@ImageData[perim], 
     HoldPattern[{a_Integer, b_Integer} -> _] :> {a, b}];
   (* raster coordinate system is upside down, flip the points *)

   points = {1, -1} (# - {0, m}) & /@ points;
   (* make nearest neighbor graph *)

   makeEdges[point_] := {Sort[{point, #}]} & /@ 
     Nearest[DeleteCases[points, point], point];
   edges = Union[Flatten[makeEdges /@ points, 2]];
   graph = Graph[UndirectedEdge @@@ edges];
   tour = FindEulerianCycle[graph] // First;
   smoothed = MovingAverage[tour[[All, 1]], smoothLen];
   g = ListCurvePathPlot[smoothed, InterpolationOrder -> interOrder];
   Cases[g, BSplineCurve[___], Infinity] // First
   );

scale = 200;
pp = Graphics[{AbsoluteThickness[scale/2], JoinForm["Round"], 
    CapForm["Round"], Line[{{0, 0}, {0, 1}, {1, 1}}]}, 
   ImageSize -> scale];
Graphics[getSplineOutline[pp, 3, 3]]

http://yaroslavvb.com/upload/save/so-outlines2.png

4 个答案:

答案 0 :(得分:2)

遗憾的是EdgeForm[](如文档中所述)不适用于Line个对象。所以我们能做的最好的事情就是不使用Line[]或者使用某种黑客。我能想到的最简单的是

Graphics[{AbsoluteThickness[100], JoinForm["Round"], CapForm["Round"],
   Line[{{0, 0}, {0, 1}, {1, 1}}], AbsoluteThickness[99], White, 
  Line[{{0, 0}, {0, 1}, {1, 1}}]}, ImageSize -> 200]

alt text

答案 1 :(得分:2)

好的,我不确定这是否值得,但我们继续:使用图像变换,最小二乘和数据聚类的方法。

Clear["Global`*"];
(*Functions for Least Square Circle \
from  http://www.dtcenter.org/met/users/docs/write_ups/circle_fit.pdf*)


t[x_] := Plus[#, -Mean[x]] & /@ x;
Suu[x_] := Sum[i[[1]]^2, {i, t[x]}];
Svv[x_] := Sum[i[[2]]^2, {i, t[x]}];
Suv[x_] := Sum[i[[1]] i[[2]], {i, t[x]}];
Suvv[x_] := Sum[i[[1]] i[[2]]^2, {i, t[x]}];
Svuu[x_] := Sum[i[[2]] i[[1]]^2, {i, t[x]}];
Suuu[x_] := Sum[i[[1]]^3, {i, t[x]}];
Svvv[x_] := Sum[i[[2]]^3, {i, t[x]}];
s[x_] := Solve[{uc Suu[x] + vc Suv[x] == 1/2 (Suuu[x] + Suvv[x]), 
    uc Suv[x] + vc Svv[x] == 1/2 (Svvv[x] + Svuu[x])}, {uc, vc}];
(*Utility fun*)
ppfilterCoords[x_, k_] := Module[{ppflat},
   ppflat = 
    Flatten[Table[{i, j, ImageData[x][[i, j]]}, {i, k[[1]]}, {j, 
       k[[2]]}], 1];
   Take[#, 2] & /@ Select[ppflat, #[[3]] == 0 &]
   ];
(*Start*)
thk = 100;
pp = Graphics[{AbsoluteThickness[100], JoinForm["Round"], 
   CapForm["Round"], Line[{{0, 0}, {0, 1}, {2, 1}, {2, 2}}]}, 
  ImageSize -> 300]
(*
pp=Graphics[{AbsoluteThickness[thk],JoinForm["Round"],CapForm["Round"]\
,Line[{{0,0},{0,3},{1,3},{1,0}}]},ImageSize->300];
*)
pp1 = ColorNegate@MorphologicalPerimeter@pp;
(* Get vertex in pp3*)
pp3 = Binarize[ColorNegate@HitMissTransform[pp1,
     { {{1, -1}, {-1, -1}}, {{-1, 1}, {-1, -1}},
      {{-1, -1}, {1, -1}}, {{-1, -1}, {-1, 1}}}], 0];
k = Dimensions@ImageData@pp3;

clus = FindClusters[ppfilterCoords[pp3, k],(*get circles appart*)
   Method -> {"Agglomerate", "Linkage" -> "Complete"}, 
   DistanceFunction -> (If [EuclideanDistance[#1, #2] <= thk/2, 0, 
       EuclideanDistance[#1, #2]] &)]; 
(*Drop Spurious clusters*)
clus = Select[clus, Dimensions[#][[1]] > 10 &];
(*Calculate centers*)
centerOffset = Flatten[{uc, vc} /. s[#] & /@ clus, 1];
(*coordinates correction*)
center = {-1, 1} Plus[#, {0, k[[2]]}] & /@ -N[
     centerOffset + Mean /@ clus, 2];
Print["Circles Centers ", center];
(*get radius from coordinates. All radius are equal*)
radius = Max[Table[
     {Max[First /@ clus[[i]]] - Min[First /@ clus[[i]]],
      Max[Last /@ clus[[i]] - Min[Last /@ clus[[i]]]]}
     , {i, Length[clus]}]]/2;
Print["Circles Radius ", radius];

(*Now get the straight lines*)
(*horizontal lines*)
const = 30;(*a number of aligned pixels for line detection*)
ph = ColorNegate@
  HitMissTransform[ColorNegate@pp1, {Table[1, {const}]}];
(*vertical lines *)
pv = ColorNegate@
   HitMissTransform[ColorNegate@pp1, {Table[{1}, {const}]}];
(*if there are diagonal lines add patterns accordingy*)
(*coordinates correction function*)
corr[x_, k_] := {-1, 1} Plus[-x, {0, k[[2]]}];
dfunH[x_, y_] := Abs[x[[1]] - y[[1]]];
dfunV[x_, y_] := Abs[x[[2]] - y[[2]]];
(*Get clusters for horiz*)
clusH = FindClusters[ppfilterCoords[ph, k],(*get lines appart*)
   Method -> {"Agglomerate", "Linkage" -> "Complete"}, 
   DistanceFunction -> dfunH];
hlines = Table[{Line[{corr[First[i], k] + {1, const/2 - 1}, 
      corr[Last[i], k] + {1, -const/2 - 1}}]}, {i, clusH}];

clusV = FindClusters[ppfilterCoords[pv, k],(*get lines appart*)
   Method -> {"Agglomerate", "Linkage" -> "Complete"}, 
   DistanceFunction -> dfunV];
vlines = Table[{Line[{corr[First[i], k] - {const/2 - 1, 1}, 
      corr[Last[i], k] + {const/2 - 1, -1}}]}, {i, clusV}];
Graphics[{vlines, hlines, 
  Table[Circle[center[[i]], radius], {i, Length@clus}]}]

alt text

alt text

修改

更新:

alt text

答案 2 :(得分:2)

仅使用几何


当然,这个应该能够使用'笛卡尔几何'来击败。唯一的问题是需要计算很多弧和交叉点。

我做了一个方法。限制是它不处理“分支”线(例如树)。

一些例子:

alt text

计算是即时的,但代码很乱。

k[pp_] := Module[{ED(*TODO: make all symbols local*)}, (
    (*follows some analytic geometry *)
    (*Functions to calcu|late borderlines*)
    linesIncrUpDown[{x0_, y0_}, {x1_, y1_}] := 
     thk/2 {-(y1 - y0), (x1 - x0)}/ED[{x0, y0}, {x1, y1}];
    lineUp[{{x0_, y0_}, {x1_, y1_}}] := 
     Plus[linesIncrUpDown[{x0, y0}, {x1, y1}], #] & /@ {{x0, y0}, {x1,y1}};
    lineDown[{{x0_, y0_}, {x1_, y1_}}] := 
     Plus[-linesIncrUpDown[{x0, y0}, {x1, y1}], #] & /@ {{x0,y0}, {x1, y1}};
    (*Distance from line to point*)
    distanceLinePt[{{x1_, y1_}, {x2_, y2_}}, {x0_, y0_}] := 
     Abs[(x2 - x1) (y1 - y0) - (x1 - x0) (y2 - y1)]/ED[{x1, y1}, {x2, y2}];
    (*intersect between two lines without overflows for verticals*)
    intersect[{{{x1_, y1_}, {x2_, y2_}}, {{x3_, y3_}, {x4_, 
         y4_}}}] := {((x3 - x4) (-x2 y1 + x1 y2) + (x1 - x2) (x4 y3 - 
          x3 y4))/(-(x3 - x4) (y1 - y2) + (x1 - x2) (y3 - 
          y4)), (-(x2 y1 - x1 y2) (y3 - y4) + (y1 - y2) (x4 y3 - 
          x3 y4))/(-(x3 - x4) (y1 - y2) + (x1 - x2) (y3 - y4))};
    l2C := #[[1]] + I #[[2]] & ; (*list to complex for using Arg[]*);
    ED = EuclideanDistance; (*shorthand*)


    thk = Cases[pp, AbsoluteThickness[x_] -> x, Infinity][[1]];
    lines = Cases[pp, Line[x_] -> x, Infinity][[1]];
    isz = Cases[pp, Rule[ImageSize, x_] -> x, Infinity][[1]];
    (*now get the scale *)
    {minX, maxX} = {Min[#], Max[#]} &@Transpose[lines][[1]];
    (*scale graphDiam +thk= isz *)
    scale = (isz - thk)/(maxX - minX);
    (*calculate absolute positions for lines*)
    absL = (lines) scale + thk/2;
    (*now we already got the centers for the circles*)
    (*Calculate both lines Top Down*)
    luT = Table[Line[lineUp[absL[[i ;; i + 1]]]], {i, Length[absL] - 1}];
    luD = Table[Line[lineDown[absL[[i ;; i + 1]]]], {i, Length[absL] - 1}];
    (*Calculate intersection points for Top and Down lines*)
    iPuT =Table[intersect[{luT[[i, 1]], luT[[i + 1, 1]]}], {i,Length@luT - 1}];
    iPuD =Table[intersect[{luD[[i, 1]], luD[[i + 1, 1]]}], {i,Length@luD - 1}];

    (*beware drawArc has side effects as modifies luT and luD*)
    drawArc[i_] := Module[{s},
      Circle[absL[[i]], thk/2,
       Switch[i,

        1 , (*first point*)
        If[ ED[absL[[i + 1]],absL[[i]] + {Cos[s = ((#[[2]] + #[[1]])/2)], Sin[s]}] <
            ED[absL[[i + 1]],absL[[i]] + {Cos[s + Pi], Sin[s + Pi]}], # + Pi, #]
            &@{Min@#, Max@#} &@
         Mod[ {Arg[l2C @((luD[[i]])[[1, 1]] - absL[[i]])],
               Arg[l2C @((luT[[i]])[[1, 1]] - absL[[i]])]}, 2 Pi],

        Length@absL,(*last point*)
        If[ED[absL[[i - 1]], absL[[i]] + {Cos[s = ((#[[2]] + #[[1]])/2)], Sin[s]}] <
           ED[absL[[i - 1]], absL[[i]] + {Cos[s + Pi], Sin[s + Pi]}], # + Pi, #] 
           &@{Min@#, Max@#} &@
         Mod[{Arg[l2C @((luD[[i - 1]])[[1, 2]] - absL[[i]])], 
              Arg[l2C@((luT[[i - 1]])[[1, 2]] - absL[[i]])]}, 2 Pi],

        _,(*all middle points*)
        (* here I must chose which lines to intersect luD or luT.
        the correct answer is the line farthest to the previous point*)


        If[
         distanceLinePt[luD[[i, 1]], absL[[i - 1]]] > 
         distanceLinePt[luT[[i, 1]], absL[[i - 1]]],
         (*shorten the other lines*)
         luT[[i - 1, 1, 2]] = luT[[i, 1, 1]] = iPuT[[i - 1]]; lu = luD;
         ,
         (*shorten the other lines*)
         luD[[i - 1, 1, 2]] = luD[[i, 1, 1]] = iPuD[[i - 1]]; 
         lu = luT;];
        (If[ED[absL[[i - 1]], absL[[i]] + {Cos[s = ((#[[2]] + #[[1]])/2)], Sin[s]}] <
            ED[absL[[i - 1]], absL[[i]] + {Cos[s + Pi], Sin[s + Pi]}], {#[[2]]-2 Pi, #[[1]]}, #]) 
          &@{Min@#, Max@#} &@
         {Arg[l2C @((lu[[i - 1]])[[1, 2]] - absL[[i]])], 
          Arg[l2C@((lu[[i]])[[1, 1]] - absL[[i]])]}
        ] ] ];
    );
   Graphics[{Black, Table[drawArc[i], {i, Length@absL}], Red, luT, Blue, luD},
     ImageSize -> isz] ];

试驾

isz = 250;
pp[1] = Graphics[{AbsoluteThickness[50], JoinForm["Round"], 
    CapForm["Round"], Line[{{0, 0}, {1, 0}, {0, 1}, {1, 1}}]}, 
   ImageSize -> isz];
pp[2] = Graphics[{AbsoluteThickness[50], JoinForm["Round"], 
    CapForm["Round"], 
    Line[{{0, 0}, {1, 0}, {0, -1}, {0.7, -1}, {0, -4}, {2, -3}}]}, 
   ImageSize -> isz];
pp[3] = Graphics[{AbsoluteThickness[50], JoinForm["Round"], 
    CapForm["Round"], 
    Line[{{0, 0}, {0, 1}, {1, 1}, {2, 0}, {2, 3}, {5, 5}, {5, 1}, {4, 
       1}}]}, ImageSize -> isz];
pp[4] = Graphics[{AbsoluteThickness[50], JoinForm["Round"], 
    CapForm["Round"], 
    Line[{{0, 0}, {0, 1}, {1, 1}, {1, 0}, {1/2, 0}}]}, 
   ImageSize -> isz];
GraphicsGrid[Table[{pp[i], k@pp[i]}, {i, 4}]]

答案 3 :(得分:1)

不是答案,只是解决你的光栅化评论。

我认为这可能会更快(我的机器中500张图像尺度为0.1秒)

pp = Graphics[{AbsoluteThickness[100], JoinForm["Round"], 
    CapForm["Round"], Line[{{0, 0}, {0, 1}}]}, ImageSize -> 200];

ColorNegate@MorphologicalPerimeter@pp 

alt text

BTW我正在尝试&#34;出口&#34;使用所有矢量图像格式,令人惊讶的是,大多数圆形表格都丢失了,但PDF格式除外,因为它在导入时恢复相同的线条定义,所以没用。