<p>I was curious so I tried it. I used the <a href="https://en.wikipedia.org/wiki/Kaiser_window" rel="nofollow noreferrer">Kaiser Window</a> from Wikipedia: $\frac{I_0\left(\alpha\sqrt{1-x^2}\right)}{I_0\left(\alpha\right)}$</p>

<p><a href="https://en.wikipedia.org/wiki/Window_function#Kaiser_window" rel="nofollow noreferrer">This page</a> has a slightly different formulation using $\pi\alpha$ instead of $\alpha$. Given the comments in the page you linked about the Kaiser window being very similar to Lanczos and how they even overlap in the graph, I suspect they also used the former formula. You can see how close they are if you <a href="http://www.wolframalpha.com/input/?i=plot+I_0(4*sqrt(1-x%5E2))%2FI_0(4),+sin(PI*x)%2F(PI*x),+x%3D0+to+1" rel="nofollow noreferrer">plot both on Wolfram</a>.</p>

<p>This left me wondering if that's what Mitchell originally meant as it's not "noticably better". I could not see the difference at all with my test texture (it's there, just not noticeable). However, using the second formula, the difference in the <a href="http://www.wolframalpha.com/input/?i=plot%20I_0(PI*4*sqrt(1-x%5E2))%2FI_0(PI*4),%20sin(PI*x)%2F(PI*x),%20x%3D0%20to%201" rel="nofollow noreferrer">plots</a> is much more striking. And there is a noticeable difference in the resulting images. Whether it's better is a matter of interpretation and depends on the filter size.</p>

<p>With $x \in \left[-1,1\right]$ , I used this C++ windowing function:</p>

<pre><code>#include &lt;boost/math/special_functions/bessel.hpp&gt;
float bessel_i0( float x )
{
    return boost::math::cyl_bessel_i( 0, x );
}

float window_kaiser( float x )
{
    const float alpha = 4.0f * M_PI;
    float k =
        bessel_i0( alpha * sqrtf( 1.0f - x * x ) ) /
        bessel_i0( alpha );
    return k;
}
</code></pre>

<p>This is the second version. Remove the <code>M_PI</code> to get the first version which is more like Lanczos.</p>

<p>The modified Bessel function of the first kind seems to be a C++17 / boost thing. If you have neither, this quick hack will do:</p>

<pre><code>float bessel_i0( float x )
{
    float r = 1.0f;
    float term = 1.0f;
    for( int k = 1; true; ++k )
    {
        float f = x / float(k);
        term *= 0.25f * f * f;
        float new_r = r + term;
        if( new_r == r )
            break;
        r = new_r;
    }
    return r;
}
</code></pre>

<p>Just don't consider that a general replacement for a proper bessel function. However, it's fine for computing the window as far as I could tell.</p>

<p>As the second wikipedia page I linked shows, that <code>window_kaiser</code> can be used on a symmetric filter with $N$ weights by using $x = \frac{2n}{N-1}-1$, where $n$ is $0, 1, 2 \dots N-1$.</p>

<p>Computing the weights of the sinc filter itself is another thing entirely as it depends on filter size, how much you're resizing, etc. I suggest posting a separate question if you don't know how do to that.</p>

CC BY-SA 3.0
2018-03-12T13:09:41.907Z
2018-03-12T13:14:47.820Z
2018-03-12T13:14:47.820Z
Olivier
<p>These older but informative article talks about how a Kaiser windowed sinc filter is a good way to generate mips.</p>

<p><a href="http://number-none.com/product/Mipmapping,%20Part%201/index.html" rel="nofollow noreferrer">The Inner Product - Mipmapping - Part 1 | Jonathan Blow</a></p>

<p><a href="http://number-none.com/product/Mipmapping,%20Part%202/index.html" rel="nofollow noreferrer">The Inner Product - Mipmapping - Part 2 | Jonathan Blow</a></p>

<p>Unfortunately, the source code it mentions isn't anywhere to be found.</p>

<p>Can anyone explain how a kaiser windowed sinc filter is calculated and/or give the kernel weights to use for convolution?</p>

CC BY-SA 3.0
2018-03-12T12:04:01.887Z
I've not used a Kaiser windowed sinc, but I did find with (some) other windowed sincs that you can get some very strange behaviour once you'd normalised all the non-zero sample weights. In particular, if you plot the weight applied to the "centre" sample as the centre of the window moved from being located on the centre weight to being just off it, the magnitude would increase (rather than decrease as you'd expect with a sinc).
Simon F
CC BY-SA 4.0
2019-05-19T05:27:19.447Z
One issue with using a sinc filter for mip-mapping has to do with the hardware texture units. Mipmaps are usually built in a way that the next level sample falls 'between' the previous level samples. That is as if the samples are located at half-integer coordinates and the origins of all the levels coincide. Sinc filter downsampling, on the other hand, filters the data and then throws away every other pixel. This results in a visible half pixel shift. If you try to average those pixels instead of subsampling, then you arrive at a 2x2 box filter essentially.
Yakov Galka
CC BY-SA 4.0
2024-12-03T02:27:47.267Z
The source code it mentions is archived here: [part 1](https://github.com/dwilliamson/GDMagArchive/tree/master/dec01/blow/gdmag_mipmap_1) and here: [part 2](https://github.com/dwilliamson/GDMagArchive/tree/master/jan02/blow/gdmag_mipmap_2)
drewcassidy
<texture><filtering><mipmap>
Kaiser windowed sinc filter for mip mapping
Alan Wolfe
# Kaiser windowed sinc filter for mip mapping

(Asked by: username_0 on 2018-03-10)

These older but informative article talks about how a Kaiser windowed sinc filter is a good way to generate mips.

[The Inner Product - Mipmapping - Part 1 | Jonathan Blow](http://number-none.com/product/Mipmapping,%20Part%201/index.html)

[The Inner Product - Mipmapping - Part 2 | Jonathan Blow](http://number-none.com/product/Mipmapping,%20Part%202/index.html)

Unfortunately, the source code it mentions isn't anywhere to be found.

Can anyone explain how a kaiser windowed sinc filter is calculated and/or give the kernel weights to use for convolution?

**Tags:** texture, filtering, mipmap

> **Comments:**
> **username_1**: I've not used a Kaiser windowed sinc, but I did find with (some) other windowed sincs that you can get some very strange behaviour once you'd normalised all the non-zero sample weights. In particular, if you plot the weight applied to the "centre" sample as the centre of the window moved from being located on the centre weight to being just off it, the magnitude would increase (rather than decrease as you'd expect with a sinc).  
> **username_2**: One issue with using a sinc filter for mip-mapping has to do with the hardware texture units. Mipmaps are usually built in a way that the next level sample falls 'between' the previous level samples. That is as if the samples are located at half-integer coordinates and the origins of all the levels coincide. Sinc filter downsampling, on the other hand, filters the data and then throws away every other pixel. This results in a visible half pixel shift. If you try to average those pixels instead of subsampling, then you arrive at a 2x2 box filter essentially.  
> **username_3**: The source code it mentions is archived here: [part 1](https://github.com/dwilliamson/GDMagArchive/tree/master/dec01/blow/gdmag_mipmap_1) and here: [part 2](https://github.com/dwilliamson/GDMagArchive/tree/master/jan02/blow/gdmag_mipmap_2)  

---

## 1 answers

### Answer by username_4 on 2018-03-12. Score: 4 | Accepted answer

I was curious so I tried it. I used the [Kaiser Window](https://en.wikipedia.org/wiki/Kaiser_window) from Wikipedia: $\frac{I\_0\left(\alpha\sqrt{1-x^2}\right)}{I\_0\left(\alpha\right)}$

[This page](https://en.wikipedia.org/wiki/Window_function#Kaiser_window) has a slightly different formulation using $\pi\alpha$ instead of $\alpha$. Given the comments in the page you linked about the Kaiser window being very similar to Lanczos and how they even overlap in the graph, I suspect they also used the former formula. You can see how close they are if you [plot both on Wolfram](http://www.wolframalpha.com/input/?i=plot+I_0(4*sqrt(1-x%5E2))%2FI_0(4),+sin(PI*x)%2F(PI*x),+x%3D0+to+1).

This left me wondering if that's what Mitchell originally meant as it's not "noticably better". I could not see the difference at all with my test texture (it's there, just not noticeable). However, using the second formula, the difference in the [plots](http://www.wolframalpha.com/input/?i=plot%20I_0(PI*4*sqrt(1-x%5E2))%2FI_0(PI*4),%20sin(PI*x)%2F(PI*x),%20x%3D0%20to%201) is much more striking. And there is a noticeable difference in the resulting images. Whether it's better is a matter of interpretation and depends on the filter size.

With $x \in \left[-1,1\right]$ , I used this C++ windowing function:

```
#include <boost/math/special_functions/bessel.hpp>
float bessel_i0( float x )
{
    return boost::math::cyl_bessel_i( 0, x );
}

float window_kaiser( float x )
{
    const float alpha = 4.0f * M_PI;
    float k =
        bessel_i0( alpha * sqrtf( 1.0f - x * x ) ) /
        bessel_i0( alpha );
    return k;
}

```

This is the second version. Remove the `M_PI` to get the first version which is more like Lanczos.

The modified Bessel function of the first kind seems to be a C++17 / boost thing. If you have neither, this quick hack will do:

```
float bessel_i0( float x )
{
    float r = 1.0f;
    float term = 1.0f;
    for( int k = 1; true; ++k )
    {
        float f = x / float(k);
        term *= 0.25f * f * f;
        float new_r = r + term;
        if( new_r == r )
            break;
        r = new_r;
    }
    return r;
}

```

Just don't consider that a general replacement for a proper bessel function. However, it's fine for computing the window as far as I could tell.

As the second wikipedia page I linked shows, that `window_kaiser` can be used on a symmetric filter with $N$ weights by using $x = \frac{2n}{N-1}-1$, where $n$ is $0, 1, 2 \dots N-1$.

Computing the weights of the sinc filter itself is another thing entirely as it depends on filter size, how much you're resizing, etc. I suggest posting a separate question if you don't know how do to that.


---

