有沒有方法可以將圖片轉為函數?
看到wolfram強大的畫圖能力,我突然想到有沒有給出任意函數的圖像,通過軟體之類還原一個最擬合的函數式。(do it inverse)
以前自己做的一個Mathematica的小項目,現在把代碼發上來,核心代碼來自「Making Formulas... for Everything—From Pi to the Pink Panther to Sir Isaac Newton」,中文注釋由我加入,只能保證應用,不會解釋背後的數學原理。。。
首先初始化一個Module:
f[image_, method_: 1, range_: 2, threshold_: 0.2, random_: 2,
linenumber_: 10, terms_: 10, order_: 3, detail_: 64*Pi,
points_: 100] := Module[{},
(*開始初始化*)
fourierComponentData[pointList_, nMax_, op_] :=
Module[{[CurlyEpsilon] = 10^-3, [Mu] = 2^14, M = 10000, s,
scale, [CapitalDelta], L , nds, sMax,
if, [ScriptX][ScriptY]Function, X, Y, XFT, YFT, type},
(* prepare curve *)
scale =
1. Mean[Table[
Max[ fl /@ pointList] -
Min[fl /@ pointList], {fl, {First, Last}}]];
[CapitalDelta] =
EuclideanDistance[First[pointList], Last[pointList]];
L = Which[op === "Closed", type = "Closed";
If[First[pointList] === Last[pointList],
pointList, Append[pointList, First[pointList]]],
op === "Open", type = "Open"; pointList,
[CapitalDelta] == 0., type = "Closed";
pointList,
[CapitalDelta]/scale &< op, type = "Closed";
Append[pointList, First[pointList]],
True, type = "Open";
Join[pointList, Rest[Reverse[pointList]]]];
(* re-parametrize curve by arclength *)
[ScriptX][ScriptY]Function =
BSplineFunction[L, SplineDegree -&> 4];
nds =
NDSolve[{s"[t] ==
Sqrt[[ScriptX][ScriptY]Function"[
t].[ScriptX][ScriptY]Function"[t]], s[0] == 0}, s,
{t, 0, 1}, MaxSteps -&> 10^5,
PrecisionGoal -&> 4];
(* total curve length *)
sMax = s[1] /. nds[[1]];
if =
Interpolation[
Table[{s[[Sigma]] /. nds[[1]], [Sigma]}, {[Sigma], 0, 1,
1/M}]];
X[t_Real] :=
BSplineFunction[L][Max[Min[1, if[(t + Pi)/(2 Pi) sMax]] , 0]][[
1]];
Y[t_Real] :=
BSplineFunction[L][Max[Min[1, if[(t + Pi)/(2 Pi) sMax]] , 0]][[
2]];
(* extract Fourier coefficients *)
{XFT, YFT} =
Fourier[Table[#[N @ t], {t, -Pi + [CurlyEpsilon],
Pi - [CurlyEpsilon], (2 Pi -
2 [CurlyEpsilon])/[Mu]}]] /@ {X, Y};
{type, 2 Pi/Sqrt[[Mu]] *
((Transpose[
Table[{Re[#], Im[#]} [Exp[I k Pi] #[[k + 1]]], {k, 0,
nMax}]] /@ {XFT, YFT}))} ];
pointListToLines[pointList_, neighborhoodSize_: 6] :=
Module[{L = DeleteDuplicates[pointList], NF, [Lambda], lineBag,
counter, seenQ, sLB, nearest,
nearest1, nextPoint,
couldReverseQ, [ScriptD], [ScriptN], [ScriptS]},
NF = Nearest[L] ;
[Lambda] = Length[L];
Monitor[
(* list of segments *)
lineBag = {};
counter = 0;
While[counter &< [Lambda],
(* new segment *)
sLB = {RandomChoice[DeleteCases[L, _?seenQ]]};
seenQ[sLB[[1]]] = True;
counter++;
couldReverseQ = True;
(* complete segment *)
While[(nearest = NF[Last[sLB], {Infinity, neighborhoodSize}];
nearest1 =
SortBy[DeleteCases[nearest, _?seenQ],
1. EuclideanDistance[Last[sLB], #] ];
nearest1 =!= {} || couldReverseQ),
If[nearest1 === {},
(* extend the other end;
penalize sharp edges *)
sLB = Reverse[sLB]; couldReverseQ = False,
(* prefer straight continuation *)
nextPoint = If[Length[sLB] &<= 3, nearest1[[1]],
[ScriptD] =
1. Normalize[(sLB[[-1]] - sLB[[-2]]) +
1/2 (sLB[[-2]] - sLB[[-3]])];
[ScriptN] = {-1,
1} Reverse[[ScriptD]];
[ScriptS] =
Sort[{Sqrt[([ScriptD].(# - sLB[[-1]]))^2 +
(* perpendicular *) 2 ([ScriptN].(# - sLB[[-1]]))^2], # } /@
nearest1];
[ScriptS][[1, 2]]];
AppendTo[sLB, nextPoint];
seenQ[nextPoint] = True;
counter++ ]];
AppendTo[lineBag, sLB]];
(* return segments sorted by length *)
Reverse[SortBy[Select[lineBag , Length[#] &> 12 ], Length]],
(* monitor progress *)
Grid[{{Text[
Style["progress point joining", Darker[Green, 0.66]]],
ProgressIndicator[counter/[Lambda]]},
{Text[
Style["number of segments", Darker[Green, 0.66]]],
Length[lineBag] + 1}},
Alignment -&> Left, Dividers -&> Center]]];
Options[fourierComponents] = {"MaxOrder" -&> 500,
"OpenClose" -&> 0.025};(*MaxOrder是最多有多少個Sin項的意思,似乎越大越好。*)
fourierComponents[pointLists_, OptionsPattern[]] :=
Monitor[
Table[fourierComponentData[
pointLists[[k]],
If[Head[#] === List, #[[k]], #] [ OptionValue["MaxOrder"]],
If[Head[#] === List, #[[k]], #] [ OptionValue["OpenClose"]]],
{k, Length[pointLists]}],
Grid[{{Text[
Style["progress calculating Fourier coefficients",
Darker[Green, 0.66]]],
ProgressIndicator[k/Length[pointLists]]} },
Alignment -&> Left, Dividers -&> Center]] /;
Depth[pointLists] === 4;
makeFourierSeries[{"Closed" |
"Open", {{cax_, sax_}, {cay_, say_}}}, t_, n_] :=
{Sum[If[k == 0, 1/2, 1] cax[[k + 1]] Cos[k t] +
sax[[k + 1]] Sin[k t], {k, 0, Min[n, Length[cax]]}],
Sum[If[k == 0, 1/2, 1] cay[[k + 1]] Cos[k t] +
say[[k + 1]] Sin[k t], {k, 0, Min[n, Length[cay]]}]};
makeFourierSeriesApproximationManipulate[fCs_, maxOrder_: 60] :=
Manipulate[
With[{opts =
Sequence[PlotStyle -&> Black, Frame -&> True, Axes -&> False,
FrameTicks -&> None,
PlotRange -&> All, ImagePadding -&> 12]},
Show[{
ParametricPlot[
Evaluate[
makeFourierSeries[#, t, n] /@
Cases[fCs, {"Closed", _}]], {t, -Pi, Pi}, opts],
ParametricPlot[
Evaluate[
makeFourierSeries[#, t, n] /@
Cases[fCs, {"Open", _}]], {t, -Pi, 0}, opts]}] // Quiet],
{{n, 1, "max series order"}, 1, maxOrder, 1,
Appearance -&> "Labeled"},
TrackedSymbols :&> True, SaveDefinitions -&> True];
sinAmplitudeForm[kt_, {cF_, sF_}] :=
With[{[CurlyPhi] = phase[cF, sF]},
Sqrt[cF^2 + sF^2] Sin[kt + [CurlyPhi]]];
phase[cF_, sF_] := With[{T = Sqrt[cF^2 + sF^2]},
With[{g =
Total[
Abs[Table[
cF Cos[x] + sF Sin[x] -
T Sin[x + #1 ArcSin[cF/T] + #2], {x, 0, 1, 0.1}]]] },
If[g[1, 0] &< g[-1, Pi], ArcSin[cF/T], Pi - ArcSin[cF/T]]]];
singleParametrization[fCs_, t_, n_] :=
UnitStep[Sign[ Sqrt[Sin[t/2]]]] *
Sum[UnitStep[
t - ((m - 1) 4 Pi - Pi)] UnitStep[(m - 1) 4 Pi + 3 Pi - t]*
({+fCs[[m, 2, 1, 1, 1]]/2 +
Sum[sinAmplitudeForm[
k t, {fCs[[m, 2, 1, 1, k + 1]], fCs[[m, 2, 1, 2, k + 1]]}],
{k,
Min[If[Head[n] === List, n[[m]], n],
Length[fCs[[1 , 2, 1, 1]]]]}],
+fCs[[m, 2, 2, 1, 1]]/2 +
Sum[sinAmplitudeForm[
k t, {fCs[[m, 2, 2, 1, k + 1]], fCs[[m, 2, 2, 2, k + 1]]}],
{k,
Min[If[Head[n] === List, n[[m]], n],
Length[fCs[[1 , 2, 1, 1]]]]}]} ),
{m, Length[fCs]}] ;
importedimage = Import[image];
(*初始化完畢*)
(pinkPantherImage = importedimage);
(pinkPantherEdgeImage = Thinning[EdgeDetect[ColorConvert[
ImagePad[Image[Map[Most, ImageData[pinkPantherImage], {2}]],
20, White], "Grayscale"]]]);
(*Show[pinkPantherImage,pinkPantherEdgeImage, ImageSize [Rule]
360]*)
(pinkPantherEdgeImage2 =
Thinning[
EdgeDetect[Image[importedimage, "Real"], range, threshold], 1])
(*EdgeDetect里,第二三個參數分別是像素範圍和閾值*)
Print[GraphicsGrid[{{pinkPantherImage,
ColorNegate[pinkPantherEdgeImage],
ColorNegate[pinkPantherEdgeImage2]}}, ImageSize -&> 720]];
If[method == 1,
edgePoints = {#2, -#1} @@@
Position[ImageData[pinkPantherEdgeImage], 1, {2}],
edgePoints = {#2, -#1} @@@
Position[ImageData[pinkPantherEdgeImage2], 1, {2}]];
SeedRandom[random];
hLines =
pointListToLines[edgePoints,
linenumber];
(*第二個參數控制著有多遠不在同一條線上的兩個點可以判定為在同一條線上,以便開始一筆畫,數越大,線條數就越少,速度一般越慢。*)
Print["線條數:", Length[hLines]];
fCs = fourierComponents[hLines];
Print[makeFourierSeriesApproximationManipulate[fCs, 100]];
Print["最終表達式:"];
finalCurve =
Rationalize[singleParametrization[fCs, t, terms] , 10^-order];
Print[finalCurve // TraditionalForm];
Print[ParametricPlot[Evaluate[N[finalCurve]], {t, 0, detail},
PlotPoints -&> points]];
]
再整合起來進行計算:
f["http://i.stack.imgur.com/F6VO6.png", 1, 2, 0.2, 2, 5, 10, 3,
64 Pi, 140]
(*第1個參數是圖片地址;
第2個參數是方法類別,方法1:默認方法,自動方法,不需要第3,4個參數,效果是第二個圖,方法2:可以手動調整識別的輪廓,效果是第三個圖;
第3個參數是方法2中EdgeDetect的像素範圍,默認是2;
第4個參數是閾值,默認是0.2;
第5個參數是隨機數默認是2,好像跟起筆點有關;
第6個參數控制著有多遠不在同一條線上的兩個點可以判定為在同一條線上,以便開始一筆畫,數越大,線條數就越少,速度一般越慢,
有些情況線條越多越好,有些需要盡量少。
第7個參數控制著Sin的數量,數字越大,項越多,表達式越噁心但是越精確。
第8個參數是一個階數,關係著把類似的Sin項合併,數越大,Sin項越多,圖越精細,默認為3。
第9個參數是畫圖的詳細程度,函數參數的取值範圍,默認64Pi。
第10個參數是最後結果用Mathematica的描點數,默認100,點越多越精確也越慢,效果是最後一張圖。*)
本來是想放上運行結果的圖的,但是排版什麼的太麻煩了。。。總之他最後會給出輪廓圖和函數表達式
使用方法:新建一個Mathematica的筆記本,把上面兩段代碼複製到Mathematica裡面,改變圖片地址(第一個參數),然後按小鍵盤的回車鍵(計算單元)就可以了。
就像這樣:
這樣滿意否? http://www.zhihu.com/question/24325442/answer/27428348
額 AI的確可以把點陣圖轉為矢量圖,效果太差罷了。
原圖
AI臨摹圖片
簡單的效果還好,稍複雜點,會差很多。
原圖
臨摹圖
。。。它本來就是函數
推薦閱讀: