[HN Gopher] Sharp Bilinear Filters: Big Clean Pixels for Pixel Art
___________________________________________________________________
Sharp Bilinear Filters: Big Clean Pixels for Pixel Art
Author : todsacerdoti
Score : 36 points
Date : 2025-10-12 07:12 UTC (4 days ago)
(HTM) web link (bumbershootsoft.wordpress.com)
(TXT) w3m dump (bumbershootsoft.wordpress.com)
| jan_Inkepa wrote:
| Huh (having scanned but not read in detail the post), interesting
| approach. I'm not that well-versed in this area (as a game
| developer, I tend to jump straight to nearest-neighbour), but
| hadn't come across this before. I love the pathological example
| of a checkerboard pattern - very pleasing worst-case scenario,
| where I suspect it would just be a grey blur. However, the
| developer doesn't show us the equivalent for the suggested filter
| - systemically showing side-by-side comparisons of different
| filters would be useful. I suspect the resulting artefacts would
| be randomly blurry lines, which could also stand out. But nice to
| see people thinking about these things...
|
| Here's a related disucssion on what 'pixelated' should mean from
| the css working group
|
| https://github.com/w3c/csswg-drafts/issues/5837
|
| (every so often browsers break/rename how nearest-neighbouring
| filtering works. I hope at some point it stabilizes lol - I note
| in the discussion linked nobody else cares about backwards
| compatibility ...).
| scheeseman486 wrote:
| For as long as emulators supported shaders I've gotten into the
| habit of configuring them to scale output 4x nearest neighbor and
| then downscaling that to the display resolution using bilinear,
| which has roughly the same results; it gets rid of shimmering
| without blurring everything to a smudge. On any 1080p display
| with lower resolution content it looks great, but the method
| starts to fall apart once you try to scale anything higher than
| 480p.
|
| With a 4K display the pixel density is high enough that virtually
| everything looks good scaled this way, though once you go higher
| than SD content you're usually dealing with 720p and 1080p, both
| of which 2160p divides into evenly anyway.
|
| It's surprising how often I see bad pixel art scaling given how
| easy it is to fix.
| d_tr wrote:
| Sounds like exactly the same thing since bilinear filtering in
| the upscaled image only has an effect near the edges of the fat
| pixels.
| CyberDildonics wrote:
| Downscaling using bilinear interpolation doesn't really make
| sense, since what you want is a weighted average of pixels to
| make one new pixel at the lower resolution.
|
| Single bilinear samples can lose information and leave out
| pixels of the higher res image, it's essentially a worse
| triangle filter.
| TuxSH wrote:
| > Single bilinear samples can lose information and leave out
| pixels of the higher res image, it's essentially a worse
| triangle filter.
|
| Can you do [A B] -> [A 0.5*(A+B) B] 1.5x upscaling with a
| triangle filter? (I think this is not possible, but I might
| be wrong).
|
| Also triangle filter samples too many pixels and makes a
| blurry mess of pixel-art images/sprites/...
|
| Linear downscaling under the assumptions of pixel-center
| mapping and clamp-to-edge always simplifies into a polyphase
| filter with position-independent coefficients using at most
| the current input pixel and the previous one; and integer
| upscaling obviously is too.
|
| Therefore any form of "sharp bilinear" that does not use
| bilinear _upscaling_ reduces into such a polyphase filter. [A
| B] - > [A 0.5*(A+B) B] is equivalent to 2x integer upscale ->
| 0.75 bilinear scale (= 1.5x of input), and works on GPUs
| without fragment shaders too.
| CyberDildonics wrote:
| I think you're confusing a few things.
|
| First, upscaling with a filter kernel (weighted average)
| doesn't make as much sense because you aren't weighting
| multiple pixels to make a single pixel, you are
| interpolating, so "upscaling with a triangle filter" isn't
| something practical.
|
| Second, lots of signal processing things that can be
| technically applied to pixels on a row by row basis don't
| work well visually and don't make a lot of sense when
| trying to get useful results. This is why things like a
| fourier transform is not the backbone of image processing.
|
| Polyphase filtering doesn't make any sense here, you have
| access to all the data verbatim, and you want to use it all
| when you upscale or downsample. There is no compression and
| no analog signal that needs to be sampled.
|
| Third, any filter kernel is going to use the pixels under
| it's width/support. Using 'too many pixels' isn't something
| that makes sense and isn't the problem. How they are
| weighted when scaling an image down is what makes sense. If
| you want a sharper filter you can always use one. What I
| actually said was that linear interpolating samples to
| downsample an image doesn't make sense and is like using a
| triangle filter or half of a triangle filter.
|
| This all seems to be work arounds for what people probably
| actually want if they are trying to get some sharpness,
| which is something like a bilateral filter, that weights
| similiar pixels more. This
| TuxSH wrote:
| You are correct in assuming that I'm not as familiar as
| you seem to be on these topics.
|
| > Polyphase filtering (...) There is no compression and
| no analog signal that needs to be sampled.
|
| The term "polyphase scaling" is used at least by AMD:
| https://docs.amd.com/r/en-US/pg325-v-multi-
| scaler/Polyphase-... , that's why I used the term.
|
| > What I actually said was that linear interpolating
| samples to downsample an image doesn't make sense and is
| like using a triangle filter or half of a triangle
| filter.
|
| In isolation yes it doesn't make sense, but linear
| downsampling is a mere implementation detail here: "4x
| nearest neighbor and then downscaling that to the display
| resolution using bilinear" is an upscaling filter (unless
| the output resolution is lower) that doesn't discard any
| pixel of the initial input.
| TuxSH wrote:
| > that to the display resolution using bilinear
|
| On that topic, Pillow so-called binilnear isn't actually
| bilinear interpolation [1][2], same with Magick IIRC (but
| Magick at least gives you -define filter:blur=<value> to
| counteract this)
|
| [1]
| https://pillow.readthedocs.io/en/stable/releasenotes/2.7.0.h...
|
| [2] https://github.com/python-
| pillow/Pillow/blob/main/src/libIma...
| smallerize wrote:
| Ok but what does that image at the top look like with this new
| filter applied?
| tobr wrote:
| An easy way to do this that I've used when resizing images in
| photoshop is to first scale it to the closest larger integer
| scaling factor of the target output using nearest neighbor, and
| then scale that down to the final result with bilinear or
| bicubic.
___________________________________________________________________
(page generated 2025-10-16 23:02 UTC)