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

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.