Un tenseur d'interpolation de Lagrange cubique est-il le même que l'interpolation bicubique?


11

Je viens d'implémenter un échantillonnage de texture interpolé en échantillonnant les 4x4 pixels les plus proches, puis en effectuant une interpolation Lagrange sur l'axe x pour obtenir quatre valeurs pour utiliser l'interpolation Lagrange sur l'axe y.

Est-ce la même chose que l'interpolation bicubique ou est-ce différent? Ou existe-t-il différents types d'interpolation bicubique, et ce n'est peut-être que l'un d'entre eux?

Implémentation de Webgl Shadertoy ici et code GLSL (WebGL) ci-dessous: https://www.shadertoy.com/view/MllSzX

Merci!

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
Vous pouvez poster le code shader pertinent ici en cas de bitrot, non?
joojaa

1
nous devrions avoir un balisage de code plus joli pour le code shader, je posterai sur méta si quelqu'un ne m'a pas battu!
Alan Wolfe

N'est-ce pas un langage de shader spécifique qui n'est pas disponible dans la liste des langues couvertes par notre coloration syntaxique?
trichoplax

Je ne suis pas sûr. C'est juste GLSL (de webgl pour être exact!). Je viens de faire 4 espaces avant chaque ligne de code, je ne sais pas s'il y a une meilleure façon de le baliser ...
Alan Wolfe

Réponses:


8

Il s'avère que non, bien que vous puissiez utiliser l'interpolation bicubique de Lagrange pour l'échantillonnage de texture bicubique, ce n'est pas l'option de la plus haute qualité, et probablement peu susceptible d'être utilisée.

Les cannelures hermite cubiques sont un meilleur outil pour le travail.

L'interpolation de Lagrange fera une courbe qui passe à travers les points de données, préservant ainsi la continuité C0, mais les splines hermites préservent les dérivées sur les bords tout en passant par les points de données, préservant ainsi la continuité C1 et semblant beaucoup mieux.

Cette question contient d'excellentes informations sur les splines cubiques d'hermite: /signals/18265/bicubic-interpolation

Voici la version hermite cubique du code que j'ai posté dans la question:

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

Voici une image montrant la différence entre les méthodes d'échantillonnage. De gauche à droite: le plus proche voisin, bilinéaire, Lagrange Bicubic, Hermite Bicubic

entrez la description de l'image ici


Bien que toutes les splines cubiques soient, dans un sens, équivalentes, il est probablement conceptuellement plus facile d'utiliser des splines Catmull-Rom. par exemple cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
Simon F

Pensez-vous que le paramètre tau aide ou gêne dans ce cas? Je peux me tromper, mais j'ai l'impression que la rom catmull est trop "définie par l'utilisateur" (et doit être réglée), tandis que la spline hermite tente d'utiliser uniquement les informations des données qui s'y trouvent. Il semble que l'hermite cubique soit plus proche d'une "vérité fondamentale", ce qui, je suppose, serait quelque chose comme un filtre sinc idéal. Mais qu'en pensez-vous?
Alan Wolfe

Je ne vois pas comment Catmull-Rom est "défini par l'utilisateur". Une fois que vous avez une séquence de 4 points contigus, P [i-1], P [i], P [i + 1], P [i + 2] (4x4 pour le cas 2D), le segment de courbe est défini entre P [i ] et P [i + 1] et est C1 continu avec les segments voisins. Un filtre sinc est bien pour l'audio mais pas pour la vidéo. Voir Mitchell & Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/… IIRC Catmull-Rom est un cas particulier de la famille de filtres qu'ils proposent, mais je pense que ce filtre est une courbe approximative donc, contrairement à CR, peut ne pas passer par les points d'origine.
Simon F

C'est ainsi que fonctionne la spline hermite, sauf que la spline catmull rom a un paramètre supplémentaire tau (tension) défini par l'utilisateur. Aussi, sinc s'applique à la vidéo, DSP est DSP: P
Alan Wolfe

Je dois admettre que je n'ai jamais vu de paramètre de tension associé aux splines Catmull Rom auparavant, mais je ne l'ai vraiment appris que via Foley & van Dam (et al) ou, disons, Watt & Watt qui, AFAICR, font aucune mention de tels. En fait, cela étant dit, étant donné qu'il y a quatre contraintes - c'est-à-dire que la courbe doit passer par 2 points et avoir deux tangentes définies ** à ces points et c'est un cube - je suis un peu perdu quant à la façon dont il y a plus de degrés de liberté pour supporter un paramètre de tension .... ** Sauf si vous voulez dire que les tangentes peuvent être mises à l'échelle?
Simon F
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.