如何以矢量形式绘制下面的粗线轮廓?通过矢量形式,我指的是一些不是光栅或图像的图形基元集合。
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
也问了数学小组
更新的
我试过通过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]]
答案 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]
答案 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}]}]
修改
更新:
答案 2 :(得分:2)
当然,这个应该能够使用'笛卡尔几何'来击败。唯一的问题是需要计算很多弧和交叉点。
我做了一个方法。限制是它不处理“分支”线(例如树)。
一些例子:
计算是即时的,但代码很乱。
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
BTW我正在尝试&#34;出口&#34;使用所有矢量图像格式,令人惊讶的是,大多数圆形表格都丢失了,但PDF格式除外,因为它在导入时恢复相同的线条定义,所以没用。