Czy sześcienny tensor interpolacji Lagrange'a jest taki sam jak interpolacja dwuububowa?


11

Właśnie zaimplementowałem niektóre interpolowane próbkowanie tekstur, próbkując najbliższe piksele 4x4, a następnie wykonując interpolację Lagrange'a na osi x, aby uzyskać cztery wartości do użycia interpolacji Lagrange'a na osi y.

Czy to jest to samo, co interpolacja dwuububowa, czy też jest inne? A może istnieją różne rodzaje interpolacji dwuububowych, a może to tylko jeden z nich?

Implementacja Webgl Shadertoy tutaj i odpowiedni kod GLSL (WebGL) poniżej: https://www.shadertoy.com/view/MllSzX

Dzięki!

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
Możesz zamieścić tutaj odpowiedni kod modułu cieniującego w przypadku bitrotu, nie?
— joojaa,

1
powinniśmy mieć ładniejszy kod do kodu modułu cieniującego, opublikuję na meta, jeśli ktoś mnie nie pobił!
— Alan Wolfe

Czy to konkretny język modułu cieniującego nie jest dostępny na liście języków objętych naszym wyróżnianiem składni?
— trichoplax,

Nie jestem pewny. To tylko GLSL (a dokładniej webgl!). Zrobiłem tylko 4 spacje przed każdą linią kodu, nie jestem pewien, czy jest lepszy sposób na oznaczenie go ...
— Alan Wolfe

Odpowiedzi:


8

Okazuje się, że nie, chociaż można użyć dwuububowej interpolacji Lagrange'a do dwububowego próbkowania tekstur, nie jest to opcja najwyższej jakości i prawdopodobnie raczej nie będzie używana.

Sześcienne splajny pustelnicze są lepszym narzędziem do pracy.

Interpolacja Lagrange'a stworzy krzywą, która przechodzi przez punkty danych, zachowując w ten sposób ciągłość C0, ale splajny pustelnika zachowują pochodne na krawędziach, jednocześnie przechodząc przez punkty danych, zachowując w ten sposób ciągłość C1 i wyglądając znacznie lepiej.

To pytanie ma kilka świetnych informacji na temat splajnów sześciennych pustelników: /signals/18265/bicubic-interpolation

Oto sześcienna wersja pustelnika kodu, który opublikowałem w pytaniu:

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

Oto zdjęcie pokazujące różnicę między metodami próbkowania. Od lewej do prawej: najbliższy sąsiad, dwuliniowy, Lagrange Bicubic, Hermite Bicubic

wprowadź opis zdjęcia tutaj


Chociaż wszystkie splajny sześcienne są w pewnym sensie równoważne, prawdopodobnie jest to koncepcyjnie łatwiejsze użycie splajnów Catmull-Rom. np. cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
— Simon F

Czy uważasz, że parametr tau pomaga lub przeszkadza w tym przypadku? Mogę się mylić, ale wydaje mi się, że catmull rom jest zbyt „zdefiniowany przez użytkownika” (i musi zostać dostrojony), podczas gdy splajn pustelniczy próbuje po prostu wykorzystać informacje z dostępnych danych. Wydaje się, że pustelnik sześcienny jest bliższy „gruntowej prawdzie”, która, jak sądzę, byłaby czymś w rodzaju idealnego filtra cynkowego. Co myślisz?
— Alan Wolfe,

Nie rozumiem, w jaki sposób Catmull-Rom jest „zdefiniowany przez użytkownika”. Po utworzeniu sekwencji 4 ciągłych punktów, P [i-1], P [i], P [i + 1], P [i + 2] (4x4 w przypadku 2D) segment krzywej jest definiowany między P [i ] i P [i + 1] i jest ciągła C1 z sąsiednimi segmentami. Filtr cynkowy nadaje się do audio, ale nie do wideo. Zobacz Mitchell i Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/... IIRC Catmull-Rom to szczególny przypadek rodziny proponowanych filtrów, ale myślę, że filtr jest krzywą aproksymacyjną, więc, w przeciwieństwie do CR, może nie przejść przez pierwotne punkty.
— Simon F

Tak działa splajn pustelniczy, z tym że splajn rom catmull ma dodatkowy parametr tau (napięcie) zdefiniowany przez użytkownika. Również sinc ma zastosowanie do wideo, DSP to DSP: P
— Alan Wolfe

Muszę przyznać, że nigdy wcześniej nie widziałem parametru napięcia związanego z splajnami Catmull Rom, ale tak naprawdę dowiedziałem się o nich tylko za pośrednictwem Foley & van Dam (i in.) Lub, powiedzmy, Watt & Watt, które, AFAICR, tworzą brak wzmianki o takich. Właściwie to powiedziawszy, biorąc pod uwagę, że istnieją cztery ograniczenia - tj. Krzywa musi przejść przez 2 punkty i mieć dwie zdefiniowane styczne ** w tych punktach i jest sześcienna - nie mam pewności, czy są jakieś więcej stopni swobody do obsługi parametru napięcia .... ** Chyba że masz na myśli, że styczne można skalować?
— Simon F
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.