Является ли произведение тензора кубической интерполяции Лагранжа на бикубическую интерполяцию?

11

Я просто реализовал некоторую интерполированную выборку текстур путем выборки ближайших пикселей 4x4, затем выполняя интерполяцию Лагранжа по оси x, чтобы получить четыре значения для использования интерполяции Лагранжа по оси y.

Это то же самое, что и бикубическая интерполяция, или она отличается? Или существуют разные виды бикубической интерполяции, и, возможно, это только одна из них?

Реализация Webgl Shadertoy здесь и соответствующий код GLSL (WebGL) ниже: https://www.shadertoy.com/view/MllSzX

Спасибо!

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
Вы могли бы опубликовать соответствующий код шейдера здесь в случае bitrot, нет?
joojaa
1
у нас должна быть более красивая разметка кода для шейдерного кода, я буду публиковать на мета, если кто-то не побил меня этим!
Алан Вулф
Этот конкретный язык шейдеров недоступен в списке языков, на которые распространяется наша подсветка синтаксиса?
Трихоплакс
Я не уверен. Это просто GLSL (от webgl, если быть точным!). Я просто сделал 4 пробела перед каждой строкой кода, не уверенный, есть ли лучший способ отметить это ...
Алан Вулф

Ответы:

8

Оказывается, что нет, хотя вы можете использовать бикубическую интерполяцию Лагранжа для бикубической выборки текстур, это не самый качественный вариант и, вероятно, вряд ли будет использоваться.

Кубические сплайны Эрмита - лучший инструмент для работы.

Лагранжева интерполяция создаст кривую, которая проходит через точки данных, тем самым сохраняя непрерывность C0, но сплайны Эрмита сохраняют производные по краям, а также проходят через точки данных, сохраняя тем самым непрерывность C1 и выглядя намного лучше.

Этот вопрос содержит полезную информацию о кубических сплайнах Эрмита: /signals/18265/bicubic-interpolation

Вот кодовая версия кода, которую я разместил в вопросе:

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

Вот изображение, показывающее разницу между методами выборки. Слева направо: Ближайший сосед, Билинейный, Бикубический Лагранжа, Бикубический Эрмита

введите описание изображения здесь

Алан Вульф
источник
Хотя все кубические сплайны в некотором смысле эквивалентны, вероятно, концептуально проще использовать сплайны Катмулла-Рома. например, cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf
Саймон Ф
Как вы думаете, параметр тау помогает или мешает в этом случае? Я могу ошибаться, но я чувствую, что Catmull rom слишком «определен пользователем» (и должен быть настроен), тогда как сплайн Эрмита пытается просто использовать информацию из данных, которые там есть. Кажется, что кубический эрмит ближе к "основной правде", которая, я думаю, будет чем-то вроде идеального фильтра sinc. Что вы думаете, хотя?
Алан Вулф
Я не вижу, как Catmull-Rom "определен пользователем". Если у вас есть последовательность из 4 смежных точек, P [i-1], P [i], P [i + 1], P [i + 2] (4x4 для 2D-случая), сегмент кривой определяется между P [i ] и P [i + 1] и C1 непрерывна с соседними сегментами. Фильтр sinc хорош для аудио, но не для видео. См. Mitchell & Netravali: cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/… IIRC Catmull-Rom - это особый случай предлагаемого ими семейства фильтров, но я думаю, что фильтр является аппроксимирующей кривой, так что в отличие от CR, может не пройти исходные пункты.
Саймон Ф
Вот как работает его сплайн-гермет, за исключением того, что у сплайна Catmull rom есть дополнительный параметр тау (натяжение), который определяется пользователем. Кроме того, Sinc действительно относится к видео, DSP это DSP: P
Алан Вулф
Должен признать, что я никогда не видел параметр натяжения, связанный со сплайнами Catmull Rom, но тогда я действительно узнал о них только через Фоли и Ван Дама (и др.) Или, скажем, Уотта и Ватта, которые, AFAICR, делают нет упоминания о таком. На самом деле, сказав, что, учитывая, что есть четыре ограничения - т.е. кривая должна пройти через 2 точки и иметь две определенные касательные ** в этих точках, и это кубический - я немного растерялся относительно того, как больше степеней свободы для поддержки параметра натяжения .... ** Разве вы не имеете в виду, что касательные могут быть масштабированы?
Симон Ф