Ist ein kubisches Lagrange-Interpolationstensorprodukt dasselbe wie eine bikubische Interpolation?

11

Ich habe gerade eine interpolierte Texturabtastung implementiert, indem ich die nächstgelegenen 4x4-Pixel abgetastet und dann eine Lagrange-Interpolation über die x-Achse durchgeführt habe, um vier Werte für die Verwendung der Lagrange-Interpolation über die y-Achse zu erhalten.

Ist das dasselbe wie bikubische Interpolation oder ist es anders? Oder gibt es verschiedene Arten der bikubischen Interpolation, und dies ist vielleicht nur eine davon?

Webgl Shadertoy-Implementierung hier und relevanter GLSL (WebGL) -Code unten: https://www.shadertoy.com/view/MllSzX

Vielen Dank!

float c_textureSize = 64.0;

float c_onePixel = 1.0 / c_textureSize;
float c_twoPixels = 2.0 / c_textureSize;

float c_x0 = -1.0;
float c_x1 =  0.0;
float c_x2 =  1.0;
float c_x3 =  2.0;

//=======================================================================================
vec3 CubicLagrange (vec3 A, vec3 B, vec3 C, vec3 D, float t)
{
    return
        A * 
        (
            (t - c_x1) / (c_x0 - c_x1) * 
            (t - c_x2) / (c_x0 - c_x2) *
            (t - c_x3) / (c_x0 - c_x3)
        ) +
        B * 
        (
            (t - c_x0) / (c_x1 - c_x0) * 
            (t - c_x2) / (c_x1 - c_x2) *
            (t - c_x3) / (c_x1 - c_x3)
        ) +
        C * 
        (
            (t - c_x0) / (c_x2 - c_x0) * 
            (t - c_x1) / (c_x2 - c_x1) *
            (t - c_x3) / (c_x2 - c_x3)
        ) +       
        D * 
        (
            (t - c_x0) / (c_x3 - c_x0) * 
            (t - c_x1) / (c_x3 - c_x1) *
            (t - c_x2) / (c_x3 - c_x2)
        );
}

//=======================================================================================
vec3 BicubicTextureSample (vec2 P)
{
    vec2 pixel = P * c_textureSize + 0.5;

    vec2 frac = fract(pixel);
    pixel = floor(pixel) / c_textureSize - vec2(c_onePixel/2.0);

    vec3 C00 = texture2D(iChannel0, pixel + vec2(-c_onePixel ,-c_onePixel)).rgb;
    vec3 C10 = texture2D(iChannel0, pixel + vec2( 0.0        ,-c_onePixel)).rgb;
    vec3 C20 = texture2D(iChannel0, pixel + vec2( c_onePixel ,-c_onePixel)).rgb;
    vec3 C30 = texture2D(iChannel0, pixel + vec2( c_twoPixels,-c_onePixel)).rgb;

    vec3 C01 = texture2D(iChannel0, pixel + vec2(-c_onePixel , 0.0)).rgb;
    vec3 C11 = texture2D(iChannel0, pixel + vec2( 0.0        , 0.0)).rgb;
    vec3 C21 = texture2D(iChannel0, pixel + vec2( c_onePixel , 0.0)).rgb;
    vec3 C31 = texture2D(iChannel0, pixel + vec2( c_twoPixels, 0.0)).rgb;    

    vec3 C02 = texture2D(iChannel0, pixel + vec2(-c_onePixel , c_onePixel)).rgb;
    vec3 C12 = texture2D(iChannel0, pixel + vec2( 0.0        , c_onePixel)).rgb;
    vec3 C22 = texture2D(iChannel0, pixel + vec2( c_onePixel , c_onePixel)).rgb;
    vec3 C32 = texture2D(iChannel0, pixel + vec2( c_twoPixels, c_onePixel)).rgb;    

    vec3 C03 = texture2D(iChannel0, pixel + vec2(-c_onePixel , c_twoPixels)).rgb;
    vec3 C13 = texture2D(iChannel0, pixel + vec2( 0.0        , c_twoPixels)).rgb;
    vec3 C23 = texture2D(iChannel0, pixel + vec2( c_onePixel , c_twoPixels)).rgb;
    vec3 C33 = texture2D(iChannel0, pixel + vec2( c_twoPixels, c_twoPixels)).rgb;    

    vec3 CP0X = CubicLagrange(C00, C10, C20, C30, frac.x);
    vec3 CP1X = CubicLagrange(C01, C11, C21, C31, frac.x);
    vec3 CP2X = CubicLagrange(C02, C12, C22, C32, frac.x);
    vec3 CP3X = CubicLagrange(C03, C13, C23, C33, frac.x);

    return CubicLagrange(CP0X, CP1X, CP2X, CP3X, frac.y);
}
Alan Wolfe
quelle
2
Sie könnten den entsprechenden Shader-Code hier im Falle von Bitrot posten, nein?
Joojaa
1
Wir sollten ein hübscheres Code-Markup für Shader-Code haben. Ich werde auf Meta posten, wenn mich jemand nicht geschlagen hat!
Alan Wolfe
Ist das eine bestimmte Shader-Sprache, die in der Liste der Sprachen, die von unserer Syntaxhervorhebung abgedeckt werden, nicht verfügbar ist?
Trichoplax
Ich bin mir nicht sicher. Es ist nur GLSL (von Webgl um genau zu sein!). Ich habe gerade 4 Leerzeichen vor jeder Codezeile gesetzt, nicht sicher, ob es einen besseren Weg gibt, sie zu markieren ...
Alan Wolfe

Antworten:

8

Es stellt sich heraus, dass Sie zwar die bikubische Lagrange-Interpolation für die Abtastung von bikubischen Texturen verwenden können, diese jedoch nicht die Option mit der höchsten Qualität ist und wahrscheinlich auch nicht verwendet wird.

Kubische Einsiedlerkeile sind ein besseres Werkzeug für den Job.

Durch die Lagrange-Interpolation wird eine Kurve erstellt, die durch die Datenpunkte verläuft, wodurch die C0-Kontinuität erhalten bleibt. Hermite-Splines bewahren jedoch die Ableitungen an den Rändern, während sie gleichzeitig durch die Datenpunkte verlaufen, wodurch die C1-Kontinuität erhalten bleibt und viel besser aussieht.

Diese Frage enthält einige nützliche Informationen zu kubischen Einsiedler-Splines: /signals/18265/bicubic-interpolation

Hier ist die kubische Einsiedlerversion des Codes, den ich in der Frage gepostet habe:

//=======================================================================================
vec3 CubicHermite (vec3 A, vec3 B, vec3 C, vec3 D, float t)
{
    float t2 = t*t;
    float t3 = t*t*t;
    vec3 a = -A/2.0 + (3.0*B)/2.0 - (3.0*C)/2.0 + D/2.0;
    vec3 b = A - (5.0*B)/2.0 + 2.0*C - D / 2.0;
    vec3 c = -A/2.0 + C/2.0;
    vec3 d = B;

    return a*t3 + b*t2 + c*t + d;
}

//=======================================================================================
vec3 BicubicHermiteTextureSample (vec2 P)
{
    vec2 pixel = P * c_textureSize + 0.5;

    vec2 frac = fract(pixel);
    pixel = floor(pixel) / c_textureSize - vec2(c_onePixel/2.0);

    vec3 C00 = texture2D(iChannel0, pixel + vec2(-c_onePixel ,-c_onePixel)).rgb;
    vec3 C10 = texture2D(iChannel0, pixel + vec2( 0.0        ,-c_onePixel)).rgb;
    vec3 C20 = texture2D(iChannel0, pixel + vec2( c_onePixel ,-c_onePixel)).rgb;
    vec3 C30 = texture2D(iChannel0, pixel + vec2( c_twoPixels,-c_onePixel)).rgb;

    vec3 C01 = texture2D(iChannel0, pixel + vec2(-c_onePixel , 0.0)).rgb;
    vec3 C11 = texture2D(iChannel0, pixel + vec2( 0.0        , 0.0)).rgb;
    vec3 C21 = texture2D(iChannel0, pixel + vec2( c_onePixel , 0.0)).rgb;
    vec3 C31 = texture2D(iChannel0, pixel + vec2( c_twoPixels, 0.0)).rgb;    

    vec3 C02 = texture2D(iChannel0, pixel + vec2(-c_onePixel , c_onePixel)).rgb;
    vec3 C12 = texture2D(iChannel0, pixel + vec2( 0.0        , c_onePixel)).rgb;
    vec3 C22 = texture2D(iChannel0, pixel + vec2( c_onePixel , c_onePixel)).rgb;
    vec3 C32 = texture2D(iChannel0, pixel + vec2( c_twoPixels, c_onePixel)).rgb;    

    vec3 C03 = texture2D(iChannel0, pixel + vec2(-c_onePixel , c_twoPixels)).rgb;
    vec3 C13 = texture2D(iChannel0, pixel + vec2( 0.0        , c_twoPixels)).rgb;
    vec3 C23 = texture2D(iChannel0, pixel + vec2( c_onePixel , c_twoPixels)).rgb;
    vec3 C33 = texture2D(iChannel0, pixel + vec2( c_twoPixels, c_twoPixels)).rgb;    

    vec3 CP0X = CubicHermite(C00, C10, C20, C30, frac.x);
    vec3 CP1X = CubicHermite(C01, C11, C21, C31, frac.x);
    vec3 CP2X = CubicHermite(C02, C12, C22, C32, frac.x);
    vec3 CP3X = CubicHermite(C03, C13, C23, C33, frac.x);

    return CubicHermite(CP0X, CP1X, CP2X, CP3X, frac.y);
}

Hier ist ein Bild, das den Unterschied zwischen den Probenahmemethoden zeigt. Von links nach rechts: Nächster Nachbar, Bilinear, Lagrange Bicubic, Hermite Bicubic

Geben Sie hier die Bildbeschreibung ein

Alan Wolfe
quelle
Obwohl alle kubischen Splines in gewissem Sinne gleichwertig sind, ist es wahrscheinlich konzeptionell einfacher, Catmull-Rom-Splines zu verwenden. zB cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
Simon F
Denken Sie, dass der Tau-Parameter in diesem Fall hilft oder behindert? Ich könnte mich irren, aber ich denke, Catmull Rom ist zu "benutzerdefiniert" (und muss angepasst werden), während der Hermite Spline versucht, nur Informationen aus den Daten zu verwenden, die dort sind. Es scheint, als ob kubischer Einsiedler einer "Grundwahrheit" näher kommt, was meiner Meinung nach so etwas wie ein idealer Sinc-Filter wäre. Was denkst du aber?
Alan Wolfe
Ich sehe nicht, wie Catmull-Rom "benutzerdefiniert" ist. Sobald Sie eine Folge von 4 zusammenhängenden Punkten P [i-1], P [i], P [i + 1], P [i + 2] (4x4 für 2D-Fall) haben, wird das Kurvensegment zwischen P [i definiert ] und P [i + 1] und ist C1 stetig mit den benachbarten Segmenten. Ein Sinc-Filter ist gut für Audio, aber nicht für Video. Siehe Mitchell & Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/… IIRC Catmull-Rom ist ein Sonderfall der von ihnen vorgeschlagenen Filterfamilie, aber ich denke, dass Filter eine ungefähre Kurve ist. Im Gegensatz zu CR werden die ursprünglichen Punkte möglicherweise nicht durchlaufen.
Simon F
So funktioniert der Hermite Spline, außer dass der Catmull Rom Spline einen zusätzlichen Parameter Tau (Spannung) hat, der benutzerdefiniert ist. Auch sinc gilt für Video, DSP ist DSP: P
Alan Wolfe
Ich muss zugeben, ich habe noch nie einen Spannungsparameter gesehen, der mit Catmull Rom-Splines in Verbindung gebracht wird, aber dann habe ich wirklich nur über Foley & van Dam (et al.) Oder beispielsweise Watt & Watt, die AFAICR herstellen, davon erfahren keine Erwähnung von solchen. Angesichts der Tatsache, dass es vier Einschränkungen gibt - dh die Kurve muss durch 2 Punkte verlaufen und an diesen Punkten zwei definierte Tangenten ** haben und es ist eine Kubik -, bin ich ein bisschen ratlos, wie es welche gibt mehr Freiheitsgrade zur Unterstützung eines Spannungsparameters .... ** Es sei denn, Sie meinen, die Tangenten können skaliert werden?
Simon F