Um produto cúbico de tensor de interpolação de Lagrange é o mesmo que interpolação bicúbica?

11

Acabei de implementar algumas amostras de textura interpolada, amostrando os pixels mais próximos 4x4 e, em seguida, fazendo a interpolação de Lagrange no eixo x para obter quatro valores para usar a interpolação de Lagrange no eixo y.

É o mesmo que interpolação bicúbica ou é diferente? Ou existem diferentes tipos de interpolação bicúbica, e talvez seja apenas uma delas?

Implementação do Webgl Shadertoy aqui e código GLSL (WebGL) relevante abaixo: https://www.shadertoy.com/view/MllSzX

Obrigado!

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
fonte
2
Você pode postar o código de sombreador relevante aqui no caso de bitrot, não?
Joojaa
1
devemos ter uma marcação de código mais bonita para o código de sombreador, vou postar na meta se alguém não me derrotar!
Alan Wolfe
Esse idioma de sombreador específico não está disponível na lista de idiomas cobertos pelo destaque de sintaxe?
Trichoplax
Não tenho certeza. É apenas GLSL (de webgl para ser exato!). Eu apenas fiz 4 espaços antes de cada linha de código, não tenho certeza se há uma maneira melhor de marcá-lo ...
Alan Wolfe

Respostas:

8

Acontece que não, embora você possa usar a interpolação bicúbica de Lagrange para amostragem de textura bicúbica, não é a opção de qualidade mais alta e provavelmente provavelmente não será usada.

As splines de hermita cúbica são uma ferramenta melhor para o trabalho.

A interpolação de Lagrange fará uma curva que passa pelos pontos de dados, preservando a continuidade de C0, mas as splines de hermita preservam as derivadas nas bordas enquanto também passam pelos pontos de dados, preservando a continuidade de C1 e parecendo muito melhor.

Esta pergunta tem algumas informações excelentes sobre splines de hermites cúbicos: /signals/18265/bicubic-interpolation

Aqui está a versão hermita cúbica do código que eu postei na pergunta:

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

Aqui está uma figura mostrando a diferença entre os métodos de amostragem. Da esquerda para a direita: Vizinho mais próximo, Bilinear, Lagrange Bicubic, Hermite Bicubic

insira a descrição da imagem aqui

Alan Wolfe
fonte
Embora todos os splines cúbicos sejam, em certo sentido, equivalentes, provavelmente é conceitualmente mais fácil usar os splines de Catmull-Rom. por exemplo, cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
Simon F
Você acha que o parâmetro tau ajuda ou atrapalha nesse caso? Eu posso estar errado, mas sinto que o catmull rom é muito "definido pelo usuário" (e deve ser ajustado), enquanto o hermite spline tenta usar apenas as informações dos dados existentes. Parece que o hermite cúbico está mais próximo de uma "verdade do solo", que eu acho que seria algo como um filtro sinc ideal. O que você acha?
Alan Wolfe
Não vejo como Catmull-Rom é "definido pelo usuário". Depois de ter uma sequência de 4 pontos contíguos, P [i-1], P [i], P [i + 1], P [i + 2] (4x4 para o caso 2D), o segmento da curva é definido entre P [i ] e P [i + 1] e é C1 contínuo com os segmentos vizinhos. Um filtro sinc é adequado para áudio, mas não para vídeo. Veja Mitchell & Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/… IIRC Catmull-Rom é um caso especial da família de filtros que eles propõem, mas acho que esse filtro é uma curva aproximada, ao contrário do CR, pode não passar pelos pontos originais.
Simon F
É assim que ele funciona com o spline de hermite, exceto que o catmull rom spline possui um parâmetro adicional tau (tensão) definido pelo usuário. Além disso, sinc se aplica ao vídeo, DSP é DSP: P
Alan Wolfe
Devo admitir que nunca vi um parâmetro de tensão associado aos splines de Catmull Rom antes, mas só os aprendi através de Foley e van Dam (et al) ou, digamos, Watt e Watt que, AFAICR, produzem. nenhuma menção a isso. Na verdade, tendo dito isso, considerando que há quatro restrições - ou seja, a curva precisa passar por 2 pontos e ter duas tangentes definidas ** nesses pontos e é cúbico - estou um pouco sem saber como existem mais graus de liberdade para suportar um parâmetro de tensão .... ** A menos que você queira dizer que as tangentes podem ser escaladas?
Simon F