Un tensore di interpolazione cubica di Lagrange è uguale all'interpolazione bicubica?


11

Ho appena implementato un campionamento di trama interpolato campionando i pixel 4x4 più vicini, quindi eseguendo l'interpolazione di Lagrange sull'asse x per ottenere quattro valori per utilizzare l'interpolazione di Lagrange sull'asse y.

È uguale all'interpolazione bicubica o è diversa? O ci sono diversi tipi di interpolazione bicubica, e forse questa è solo una di queste?

Implementazione di Webgl Shadertoy qui e codice GLSL (WebGL) pertinente di seguito: https://www.shadertoy.com/view/MllSzX

Grazie!

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);
}

2
Potresti pubblicare qui il codice shader pertinente in caso di bitrot, no?
joojaa,

1
dovremmo avere un markup di codice più carino per il codice shader, posterò su meta se qualcuno non mi ha battuto!
Alan Wolfe,

È una lingua shader specifica non disponibile nell'elenco delle lingue coperte dall'evidenziazione della sintassi?
trichoplax,

Non ne sono sicuro. È solo GLSL (dal webgl per l'esattezza!). Ho appena fatto 4 spazi prima di ogni riga di codice, non sono sicuro che esista un modo migliore per contrassegnarlo ...
Alan Wolfe,

Risposte:


8

Si scopre che no, sebbene sia possibile utilizzare l'interpolazione bicubica di Lagrange per il campionamento bicubico delle trame, non è l'opzione della più alta qualità e probabilmente probabilmente non verrà effettivamente utilizzata.

Le spline cubiche di eremita sono uno strumento migliore per il lavoro.

L'interpolazione di Lagrange creerà una curva che attraversa i punti dati, preservando così la continuità C0, ma le spline dell'ermite preservano le derivate ai bordi mentre passano anche attraverso i punti dati, preservando così la continuità C1 e sembrano molto migliori.

Questa domanda ha alcune informazioni fantastiche sulle spline cubiche dell'eremita: /signals/18265/bicubic-interpolation

Ecco la versione cubica eremita del codice che ho pubblicato nella domanda:

//=======================================================================================
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);
}

Ecco un'immagine che mostra la differenza tra i metodi di campionamento. Da sinistra a destra: vicino più vicino, bilineare, bicagrico di Lagrange, bicubico di eremita

inserisci qui la descrizione dell'immagine


Sebbene tutte le spline cubiche siano, in un certo senso, equivalenti, è probabilmente concettualmente più facile utilizzare le spline Catmull-Rom. ad es. cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
Simon F

Pensi che il parametro tau aiuti o ostacoli in questo caso? Potrei sbagliarmi, ma mi sento come se la rom di catmull è troppo "definita dall'utente" (e deve essere sintonizzata), mentre la spline dell'eremita tenta di usare solo le informazioni dai dati che sono lì. Sembra che l'eremita cubico sia più vicino a una "verità fondamentale", che immagino sarebbe qualcosa di simile a un filtro sinc ideale. Cosa ne pensi però?
Alan Wolfe,

Non vedo come Catmull-Rom sia "definito dall'utente". Una volta che hai una sequenza di 4 punti contigui, P [i-1], P [i], P [i + 1], P [i + 2] (4x4 per caso 2D) il segmento di curva viene definito tra P [i ] e P [i + 1] ed è C1 continuo con i segmenti vicini. Un filtro sinc va bene per l'audio ma non per il video. Vedi Mitchell & Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/… IIRC Catmull-Rom è un caso speciale della famiglia di filtri che propongono, ma penso che il filtro sia una curva approssimativa quindi, a differenza di CR, potrebbe non passare attraverso i punti originali.
Simon F,

Ecco come funziona la spline eremita, tranne per il fatto che la spline rom di catmull ha un parametro aggiuntivo tau (tensione) definito dall'utente. Inoltre, sinc si applica al video, DSP è DSP: P
Alan Wolfe il

Devo ammettere che non ho mai visto un parametro di tensione associato alle spline Catmull Rom prima, ma poi ho davvero imparato a conoscerli solo tramite Foley & van Dam (et al) o, diciamo, Watt & Watt che, AFAICR, fanno nessuna menzione di questo. In realtà, detto questo, dato che ci sono quattro vincoli - cioè la curva deve passare attraverso 2 punti e avere due tangenti definite ** in quei punti ed è un cubo - Sono un po 'in perdita su come ci sia più gradi di libertà per supportare un parametro di tensione .... ** A meno che tu non voglia dire che le tangenti possono essere ridimensionate?
Simon F,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.