Saturday, March 3, 2012

xBR vs HQx Interpolation Filter Comparison

UPDATE: the comparisons in this post aren't a true representation of hq2x (more info in this post). The xBR shots are still valid, but don't put much weight on the comparisons without reading the other post for context.

This post compares the popular HQx interpolation algorithm with the newer xBR, which has been covered in some of my other posts. If you just want to download the filters, you can get them from the link at the bottom of the post.

Since Maxim Stepin created the HQx interpolation algorithm more than a decade ago, it has been the favored real-time interpolation filter for the emulation scene. The way it works is it looks at each pixel and then compares its color to that of the 8 surrounding adjacent pixels. If it finds a match, the filter then compares the resulting pattern with a predefined lookup table to guess what the original pattern was trying to represent.

For example, if we take a pattern like this:


and scale it up via nearest neighbor--that is, a straight upscale with no interpolation--you end up with this checkerboard pattern:

But, if you use HQ2x, you end up with this:

The algorithm guesses that the original pattern was trying to represent a diagonal line rather than a checkerboard pattern, so it fills in the gaps to compensate.

Recently, a Brazilian programmer by the name of Hyllian (aka Jararaca) developed a new algorithm that actually improves on HQx, known as xBR (stands for "a filter that scales By Rules").

xBR works much the same way as HQx insofar as it is based on pattern recognition, and it would upscale the above pattern with the same result. However, it goes further than HQx by using a 2-stage set of interpolation rules, which better handle more complex patterns such as antialiased lines and curves. The two main points that can be fine-tuned are the formula used to measure the distance between colors and the way the algorithm treats corners (more on the corners later).

Hyllian began development of xBR in 2011 as a plugin for Steve Snake's Genesis/Mega Drive emulator, Kega Fusion.

You can read more about Hyllian's algorithm here.

Lets take a look at how they compare on a real-world example (HQ2x on the left, 2xBR on the right; click to embiggen):


As you can see, 2xBR does a much better job on smoothing curves without getting chunky (see Yoshi's nose and the dragon coin). It also does a better job on the 'eyes' of the block, which is represented as a diagonal line with 2xBR compared with the series-of-squares look with HQ2x.

Here's another comparison, using Earthworm Jim's title screen:
 


And here's a third comparison, using the title screen to Earthworm Jim 2:


HQ2x actually does a slightly better job at getting a smooth gradient around the highlight in Jim's eye, but the rest of the image is a mess. xBR has smoother, straighter lines at every color transition.

Additionally, the xBR algorithm scales to higher scale factors much more easily than the HQx algorithm, making 3-, 4-, 5- and higher versions faster and more effective.

At very high scale factors (5 and higher), the xBR algorithm can obliterate some small details, such as pupils in eyes, dots, and so on, so Hyllian introduced some additional calculation to compensate (5xBR-v3.5a on the left [uncompensated] vs 5xBR-v3.5b on the right [compensated]):


Of course, this compensation comes with its own drawbacks and false-positives, so which version works better varies on a game-by-game basis.

UPDATE: Hyllian has made a third variant that is even more cautious. Here's an animated GIF comparing the way each variant handles text:
Another interesting aspect of this algorithm is that it works very effectively on images already upscaled with xBR, so you can easily get absurdly large images (scaled 1x, 3x, 9x and 27x) with just a few iterations:
This puts it in direct and favorable competition with this vectorization method that recently made the rounds. xBR manages to maintain detail a bit better than the vectorizer, and it can actually operate in real-time, unlike the vectorizer.

You can download these shaders from my Mediafire repo (most are available in either XML/GLSL or Cg format, both of which are compatible with SSNES). UPDATE: KrossX ported 2xBR-v3.5a to ePSXe format and SimoneT ported 5xBR-v3.5a.

UPDATE: New Cg version of 5x variant. Runs approx. 25% faster. :)
UPDATE: new XML versions of 5xBR-v3.7. This package includes 3 variants (a, b and c), as well as the A-variant+scanlines.

UPDATE: Hyllian has been working on a new version that analyzes an image frame by frame and dynamically decides whether to apply xBR smoothing or Reverse-Anti-aliasing to produce the best picture. It handles text better than any filter/shader I've encountered, and it also excels at digitized, prerendered images and backgrounds. Here's a shot of it with Final Fantasy VI on GBA:

You can download this shader here.

UPDATE (4/19/2013): Another great update from Hyllian, this time adding 3 levels of analysis. This version runs a bit slower in my experience (unsurprisingly), but it now handles jagged edges that earlier versions simply couldn't detect, due to their 2-level analysis pattern. Compare this shot with the ones at the top of the post:
Notice that inclined bit of ground under the dragon/yoshi coin. This new version will also really help with Mega Man X games, which tended to have a lot of these sorts of gently sloping planes. You can download a Cg shader of this new version here, or a 3x scale Kega Fusion plugin here.

Closely related to xBR are the SABR shader by Joshua Street and Zenju's xBRZ CPU filter. SABR was written by Mr. Street as part of a school project and it differs from xBR in a couple of ways, including using antialiasing on edges. It was forked from Hyllian's 5xBR v3.7c shader. xBRZ is a rewrite of the xBR algorithm in C++, with some changes to the corner detection and color distance measurement. It was forked around xBR v3.5.

There is also a standalone library written in C that incorporates both the xBR algorithm (as it appears in FFmpeg's libavfilter) and Hqx. You can browse this project's source on github.

32 comments:

  1. Hi, HunterK. Nice review! I have some considerations.

    Actually, there are some implementations of HQx that can smooth out diagonal lines on different angles than 45 degrees. The Snes9x CPU filter is one example.

    The major difference between Hqx and xBR is visible in anti-aliased games. Get to the Press Start screen of Earthworm Jim with both filters and you can see how they perform.

    Super Mario World is the easiest game for filters, so most of them do a great jog in it.

    ReplyDelete
  2. Thanks, Hyllian! I made some updates per your recommendations.

    ReplyDelete
  3. Hi, HunterK.

    EJ2 is a good comparison. But, actually I was talking about the first one.

    See these screenshots I made last year in this post: http://board.byuu.org/viewtopic.php?p=50096#p50096

    I know you're comparing the 2x version, though. The press start from EJ1 shows clearly how's the job of those filters on anti-aliased graphics.

    ReplyDelete
  4. Sorry to bother you this much. :p

    ReplyDelete
  5. lol I don't know why I thought you said EWJ2... Anyway, I got the new screens in there and you're right. The left edge of the letter 'M' is a pretty clear win for xBR :D

    It's no trouble, except for Blogspot's terrible interface. They made it super-hard to make side-by-side pictures :/

    ReplyDelete
  6. I will try my best in english.

    Is it possible to create a Dll containing the xBR 3x Filter ?
    I'm working (hobby beside school) on a little Game (towerdefense/gemcraftclone) with only 240x206px size. It would be cool to make the game more flashlike.
    Its written in AutoIt, a scripting language. Because of this I cannot understand C/C++/etc. good enough to create the Dll by my own.

    I can show you some Pictures that I made with an Image Resizer.

    Original: http://i.imgur.com/7OPsu.png
    xBR 3x: http://i.imgur.com/myIHv.png

    It would be great if someone who can help would contact me. marcus@haremhab.com

    Marcus (from Germany)

    ReplyDelete
  7. Who made the xml version of the shader and do they plan to update it ot the latest 3.8 version?

    ReplyDelete
  8. @Darklord42
    I believe Hyllian converted them himself, but he's said he can't really do that every time because he has no way to test them (he writes them all for RetroArch on PS3, so Cg-only). I tried using Themaister's Cg-to-XML script but it gives a black screen, and I had no luck trying to convert them myself :(

    ReplyDelete
  9. How can I get this on Visual Boy Advance and/or SNES9x?

    ReplyDelete
  10. @Anon
    I believe the latest version of snes9x for Windows supports the use of pixel shaders (the XML shaders, I think...). Personally, I use the VBA and snes9x cores in RetroArch, since it has the best shader support.

    ReplyDelete
  11. Where can I download it and can I use it for project64 textures as well under Glide64 Final?

    ReplyDelete
  12. @phly95
    You can download it from any of the links in the post. I'm not sure if it will work with Glide64 as-is, but if not, it probably wouldn't take a lot of work to get it ported to whatever format it uses.

    ReplyDelete
  13. Is it possible to use other scales?
    For example 1.5 scale instead of 2-5?

    Is it possible to downscale?

    ReplyDelete
  14. @Anon
    You can use any of the shaders at any scale factor you want. They will internally scale at their designated factor and then be scaled up or down to fit your screen. For example, 2x can be used at a 5x scale and 5x can be used at a 2x scale.

    ReplyDelete
  15. Hunter K.

    So I'll scale upscale by 2 and than downscale to 0.75 to get 1.5 of the original size.

    Can I use xbr on the server from the command line? Is it implemented as part of a toolkit like imagemagick?

    ReplyDelete
  16. This algorithm is most commonly used in realtime scaling applications, generally in emulators. However, Hyllian has made a few standalone applications, such as this 4x scale version of his lvl 4 algorithm:
    https://anonfiles.com/file/95553996f5223220944d8d258202c13e

    Here is an image explaining the effect of the levels (e.g., lvl 4 vs lvl 1):
    http://i.imagebanana.com/img/b6p3kc4a/xBRlevels.png

    ReplyDelete
  17. @Hunter K.

    Thank you for the link but I need to run it on Linux so the source code will be easier to use.

    Does xBR have advantage over lanczos or bicubic if it's not a real time app?

    ReplyDelete
  18. You can try it in WINE. Dunno if that'll work for you or not.

    It definitely has advantages for certain source material. It's designed for upscaling pixel art, specifically--where the interpolation can smooth out jaggies that would appear with other algos--but it also does well with cartoons/animation. It usually won't be as good as lanczos or bicubic for images with gradients and/or dithering, like photographs.

    ReplyDelete
  19. This comment has been removed by the author.

    ReplyDelete
  20. Hey HunterK, on all your of screens of shaders which include scanlines, the scanlines appear much lighter (i.e. 50% opacity) compared to when I try to run stuff like 5xbr, dot&bright, and the various CRT shaders. On my setup, the scanlines always seem too dark, chopping the image up and losing the bright colorful quality of the original image.

    So are you running some custom settings to make the scanlines more subtle? Is there something potentially wrong on my end?

    ReplyDelete
  21. @Gnalvl
    Hmm. No, nothing special here. However, pretty much any scanline filter/shader should have some way of lightening them (often it's just a matter of changing a number or two), so you can get something that suits your tastes a little better.

    ReplyDelete
  22. "more than a decade ago"! Damn...

    xBR is nice, with current hardware it makes sense to analyze more than just 8 neighbors.

    ReplyDelete
  23. @Maxim Stepin
    Hey man, thanks for stopping by :)

    I've loved using HQ2x over the years. Thanks for making it, and for sharing it with all of us. It was really groundbreaking stuff.

    ReplyDelete
  24. "UPDATE: New Cg version of 5x variant. Runs approx. 25% faster :)"
    How can I get the Kega Fusion plugin version of this? Please answer me. I want to know. :)

    ReplyDelete
  25. @Jaden Salvatus
    I don't think Hyllian has made kega fusion plugins for the most recent xBR revisions. This is the latest one I could find:
    http://www.dcemu.co.uk/vbulletin/threads/548963-KEGA-Fusion-Plugins-(2013-04-27)

    ReplyDelete
  26. Why doesn't Hyllian make the Mega Fusion Plugins anymore? Their so outdated and I want to use the new ones :(

    ReplyDelete
  27. This comment has been removed by the author.

    ReplyDelete
  28. Wonderful information..is there any way I can run this from Linux/Unix terminal?

    ReplyDelete
  29. @Anon
    To what end? Are you wanting to batch-process images? If so, there's a program called ImageResizer but it's Windows-only. The same team makes ImageFlow, which is linux-compatible, but I think it's just a library that you would call with some other program.

    If you don't mind a little manual effort, you can launch RetroArch from a command line (even from an X-less console using KMS) and it can open image files natively. Once it's loaded and the shader is applied, you can take a screenshot and then load up the next image. Rinse/repeat.

    ReplyDelete
  30. Hi people, I try adopt xBR v3.8c glsl shader to my retro game. I found xBR sources here :
    https://forums.libretro.com/t/xbr-v3-8c-on-android-4-4/827

    But I have trouble identify all uniforms, please can someone help me?

    in fragment are defined :

    /*
    Uniforms
    - rubyTexture: texture sampler
    - rubyTextureSize: size of the texture before rendering
    - rubyInputSize: size of the actual game input screen before rendering (input resolution)
    - rubyOutputSize: size of the actual game output screen after rendering (output viewport)
    */

    uniform sampler2D rubyTexture;
    uniform vec2 rubyTextureSize;
    uniform vec2 rubyInputSize;
    uniform vec2 rubyOutputSize;
    uniform vec2 rubyTextureFract;

    rubyTextureSize - clear, texture size
    rubyInputSize - resolution of my game
    rubyOutputSize - viewport size

    right?

    But what I don't know is rubyTextureFract...please what it means?
    Many thanks

    ReplyDelete
  31. @Anon
    Looks like that's just a precalculation of fract(rubyTextureSize.xy). As aliaspider mentioned in that thread, precalculating like that is potentially dangerous as you could lose precision in some cases.

    You're probably better off using one of the newer versions of xBR from https://github.com/libretro/glsl-shaders, though. Hyllian's done quite a bit of work on them since then.

    ReplyDelete
  32. Ah, thank you, I am working on it, but I get bad graphical results, I am doing something wrong, will try again tomorrow after sleep

    ReplyDelete