Implementierung
Angenommen, Sie haben bereits eine Strichzeichnungsroutine, müssen Sie diese nur durch eine Art Interpolation ergänzen. Die Kurven werden erstellt, indem genügend interpolierte kurze Linien gezeichnet werden, damit das Ergebnis glatt aussieht. Ein guter Ausgangspunkt wäre die Verwendung einer vorhandenen Interpolationsroutine, wie sie hier von Paul Bourke angegeben wurde .
Ich werde dies anhand der von ihm bereitgestellten kubischen Routinen veranschaulichen, da dies einige der einfachsten sind, die immer noch vernünftige Ergebnisse liefern. Hier ist der erste (übersetzt in Python) als Referenz:
def cubic(mu,y0,y1,y2,y3):
mu2 = mu*mu
a0 = y3 - y2 - y0 + y1
a1 = y0 - y1 - a0
a2 = y2 - y0
a3 = y1
return a0*mu*mu2 + a1*mu2 + a2*mu + a3
Jede Routine verfügt über einen Parameter, mu
der den Bruchteil des Index darstellt, den Sie interpolieren möchten. Abhängig von der Routine sind die anderen Parameter eine Anzahl von Stichproben, die den betreffenden Index umgeben. Im kubischen Fall benötigen Sie vier Proben. Zum Beispiel, wenn Sie Ihre Daten y[n]
, und Sie wollen den Wert an 10.3
, mu
wäre .3
, und man würde passieren in y[9]
, y[10]
, y[11]
, und y[12]
.
Anstatt dann eine einzelne Linie mit Endpunkten zu zeichnen, sagen wir: ( 10 ,y10) → ( 11 ,y11)Mit den interpolierten Werten (z. B. würden Sie eine Reihe kürzerer Werte zeichnen. ). Offensichtlich müssten diese Punkte auf die und Dimensionen des zu rendernden Bildes skaliert werden.( 10 ,y10) → ( 10.1 , kubisch ( .1 ,y9,y10,y11,y12) ) …xy
Theorie
Da die Seite / Routine, auf die ich verwiesen habe, keine Quellen zitiert, lohnt es sich zu erklären, woher diese kubischen Routinen stammen (und wie sie funktionieren). Sowohl der oben reproduzierte als auch der sehr ähnliche Catmull-Rom-Spline, den er direkt darunter erwähnt, sind zwei spezielle Fälle für die Verwendung des folgenden kubischen Faltungskerns:
ψ ( x ) =⎧⎩⎨⎪⎪( α + 2 ) | x|3- ( α + 3 ) | x|2+ 1 ,α | x|3- 5 α | x|2+ 8 α | x | - 4 α ,0 , wenn 0 ≤ | x | < 1 wenn 1 ≤ | x | < 2 wenn 2 ≤ | x |
Die oben aufgeführte Routine entspricht einem Wert von , und der Catmull-Rom-Spline entspricht . Ich werde nicht zu detailliert darauf eingehen, wie die allgemeine Form des Kernels abgeleitet wird, aber es beinhaltet verschiedene Einschränkungen, wie beispielsweise sicherzustellen, dass eins bei Null und null bei allen anderen ganzen Zahlen ist.α = - 1α = - 1 / 2ψ ( x )
So sieht es aus:
Die beiden Auswahlmöglichkeiten für den Wert von sich aus Versuchen, verschiedene Aspekte der sinc-Funktion , dem idealen Rekonstruktionskern, abzugleichen. Wenn Sie stimmt die Ableitung von mit der Ableitung der sinc-Funktion bei überein , und wenn Sie sie gleich machen, erhalten Sie stattdessen die beste niederfrequente Näherung. In jedem Fall hat ein Wert von viel bessere Eigenschaften, daher ist er wahrscheinlich der beste Wert für die Praxis. Eine viel ausführlichere Diskussion finden Sie im folgenden Artikel ab Seite 328:αα = - 1ψx = 1- 1 / 2α = - 1 / 2
Meijering, Erik. "Eine Chronologie der Interpolation: Von der alten Astronomie zur modernen Signal- und Bildverarbeitung." Verfahren des IEEE. vol. 90, nein. 3, S. 319-42. März 2002.
Einblick
Wenn man sich diesen Kernel nur in Bezug auf die tatsächliche Implementierung des Interpolationscodes ansieht, ist möglicherweise nicht klar, wie die beiden zusammenhängen. Grundsätzlich kann der Interpolationsprozess so betrachtet werden, dass verschobene Kopien des Kernels addiert werden, die durch die Stichproben der Daten wie folgt skaliert werden:
Wenn Sie eine Implementierung des Kernels haben, können Sie diese direkt wie folgt für die Interpolation verwenden:
def kernel(x, a=-1.0):
x = abs(x)
if x >= 0.0 and x < 1.0:
return (a + 2.0)*x**3.0 - (a + 3.0)*x**2.0 + 1
elif x >= 1.0 and x < 2.0:
return a*x**3.0 - 5.0*a*x**2.0 + 8.0*a*x - 4.0*a
else:
return 0.0
def cubic(mu,y0,y1,y2,y3):
a = -1.0
result = y0 * kernel(mu + 1, a)
result += y1 * kernel(mu, a)
result += y2 * kernel(mu - 1, a)
result += y3 * kernel(mu - 2, a)
return result
Es ist jedoch viel weniger rechnerisch effizient, dies auf diese Weise zu tun. Betrachten Sie als Brücke vom direkten Kernel-Ansatz zum optimierten oben, dass mit ein wenig algebraischer Manipulation die erste Implementierung in der folgenden Form erfolgen kann:
def cubic(mu,y0,y1,y2,y3):
mu2 = mu*mu
mu3 = mu*mu2
c0 = -mu3 + 2*mu2 - mu
c1 = mu3 - 2*mu2 + 1
c2 = -mu3 + mu2 + mu
c3 = mu3 - mu2
return c0*y0 + c1*y1 + c2*y2 + c3*y3
In dieser Formulierung können die c0...c3
Werte als die Koeffizienten eines FIR-Filters betrachtet werden, das auf die Abtastwerte angewendet wird. Jetzt ist es viel einfacher zu sehen, wie die Routine vom Kernel abgeleitet wird. Betrachten Sie den Kernel mit wie folgt:α = - 1
ψ ( x ) =⎧⎩⎨⎪⎪| x|3- 2 | x|2+1 ,- - | x|3+ 5 | x|2- 8 | x | + 4 ,0 , wenn 0 ≤ | x | < 1 wenn 1 ≤ | x | < 2 wenn 2 ≤ | x |
Bewerten Sie diesen Kernel nun symbolisch bei verschiedenen verschobenen Offsets, wobei Sie berücksichtigen, dass mu
( ) von bis reicht :μ0
1
ψ ( μ + 1 )ψ ( μ )ψ ( μ - 1 )ψ ( μ - 2 )====- ( μ + 1)3+ 5 ( μ + 1)2- 8 ( μ + 1 ) + 4μ3- 2μ2+ 1( 1 - μ)3- 2 ( 1 - μ)2+ 1- ( 2 - μ)3+ 5 ( 2 - μ)2- 8 ( 2 - μ ) + 4====- -μ3+ 2μ2- μμ3- 2μ2+ 1- -μ3+μ2+ μμ3- -μ2(c0)(c1)(c2)(c3)
Beachten Sie, dass aufgrund des Absolutwerts für in der Kerneldefinition auf "gespiegelt" wird . Jetzt haben wir die genauen Polynome, die in der "FIR-Version" der Interpolationsroutine verwendet werden. Die Bewertung dieser Polynome kann dann durch Standardtechniken (z. B. Horners Methode ) effizienter gestaltet werden . Ähnliche Dinge können mit anderen Kerneln durchgeführt werden, und es gibt auch andere Möglichkeiten, effiziente Implementierungen zu erstellen (vgl. Homepage für digitales Audio-Resampling ).μ - 1 , μ - 21 - μ , 2 - μx