Skip to content
{{ message }}

Instantly share code, notes, and snippets.

# igv/SSimDownscaler.glsl

Last active Jun 4, 2021
Tuned for use with dscale=mitchell and linear-downscaling=no. Localities are the main parameter to control sharpening strength, lower - sharper.
 // SSimDownscaler by Shiandow // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 3.0 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library. //!HOOK POSTKERNEL //!BIND HOOKED //!BIND PREKERNEL //!SAVE L2 //!WIDTH NATIVE_CROPPED.w //!WHEN NATIVE_CROPPED.h POSTKERNEL.h > //!COMPONENTS 3 //!DESC SSimDownscaler calc L2 pass 1 #define axis 1 #define offset vec2(0,0) #define MN(B,C,x) (x < 1.0 ? ((2.-1.5*B-(C))*x + (-3.+2.*B+C))*x*x + (1.-(B)/3.) : (((-(B)/6.-(C))*x + (B+5.*C))*x + (-2.*B-8.*C))*x+((4./3.)*B+4.*C)) #define Kernel(x) MN(1.0/3.0, 1.0/3.0, abs(x)) #define taps 2.0 vec4 hook() { vec2 base = PREKERNEL_pt * (PREKERNEL_pos * input_size + tex_offset); // Calculate bounds float low = ceil((PREKERNEL_pos - taps*POSTKERNEL_pt) * input_size - offset + tex_offset - 0.5)[axis]; float high = floor((PREKERNEL_pos + taps*POSTKERNEL_pt) * input_size - offset + tex_offset - 0.5)[axis]; float W = 0.0; vec4 avg = vec4(0); vec2 pos = base; for (float k = low; k <= high; k++) { pos[axis] = PREKERNEL_pt[axis] * (k - offset[axis] + 0.5); float rel = (pos[axis] - base[axis])*POSTKERNEL_size[axis]; float w = Kernel(rel); avg += w * pow(clamp(textureLod(PREKERNEL_raw, pos, 0.0) * PREKERNEL_mul, 0.0, 1.0), vec4(2.0)); W += w; } avg /= W; return avg; } //!HOOK POSTKERNEL //!BIND HOOKED //!BIND L2 //!SAVE L2 //!WHEN NATIVE_CROPPED.w POSTKERNEL.w > //!COMPONENTS 3 //!DESC SSimDownscaler calc L2 pass 2 #define axis 0 #define offset vec2(0,0) #define MN(B,C,x) (x < 1.0 ? ((2.-1.5*B-(C))*x + (-3.+2.*B+C))*x*x + (1.-(B)/3.) : (((-(B)/6.-(C))*x + (B+5.*C))*x + (-2.*B-8.*C))*x+((4./3.)*B+4.*C)) #define Kernel(x) MN(1.0/3.0, 1.0/3.0, abs(x)) #define taps 2.0 vec4 hook() { // Calculate bounds float low = ceil((L2_pos - taps*POSTKERNEL_pt) * L2_size - offset - 0.5)[axis]; float high = floor((L2_pos + taps*POSTKERNEL_pt) * L2_size - offset - 0.5)[axis]; float W = 0.0; vec4 avg = vec4(0); vec2 pos = L2_pos; for (float k = low; k <= high; k++) { pos[axis] = L2_pt[axis] * (k - offset[axis] + 0.5); float rel = (pos[axis] - L2_pos[axis])*POSTKERNEL_size[axis]; float w = Kernel(rel); avg += w * textureLod(L2_raw, pos, 0.0) * L2_mul; W += w; } avg /= W; return avg; } //!HOOK POSTKERNEL //!BIND HOOKED //!SAVE M //!WHEN NATIVE_CROPPED.h POSTKERNEL.h > //!COMPONENTS 3 //!DESC SSimDownscaler calc Mean #define locality 8.0 #define offset vec2(0,0) #define Kernel(x) pow(1.0 / locality, abs(x)) #define taps 3.0 #define maxtaps taps vec4 ScaleH(vec2 pos) { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; vec4 avg = vec4(0); for (float k = 0.0; k < maxtaps; k++) { pos = POSTKERNEL_pos + POSTKERNEL_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); avg += w * clamp(POSTKERNEL_tex(pos), 0.0, 1.0); W += w; } avg /= W; return avg; } vec4 hook() { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; vec4 avg = vec4(0); vec2 pos = POSTKERNEL_pos; for (float k = 0.0; k < maxtaps; k++) { pos = POSTKERNEL_pos + POSTKERNEL_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); avg += w * ScaleH(pos); W += w; } avg /= W; return avg; } //!HOOK POSTKERNEL //!BIND HOOKED //!BIND L2 //!BIND M //!SAVE R //!WHEN NATIVE_CROPPED.h POSTKERNEL.h > //!COMPONENTS 3 //!DESC SSimDownscaler calc R #define locality 8.0 #define offset vec2(0,0) #define Kernel(x) pow(1.0 / locality, abs(x)) #define taps 3.0 #define maxtaps taps mat2x4 ScaleH(vec2 pos) { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; mat2x4 avg = mat2x4(0); for (float k = 0.0; k < maxtaps; k++) { pos = L2_pos + L2_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); avg += w * mat2x4(pow(clamp(POSTKERNEL_tex(pos), 0.0, 1.0), vec4(2.0)), L2_tex(pos)); W += w; } avg /= W; return avg; } vec4 hook() { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; mat2x4 avg = mat2x4(0); vec2 pos = L2_pos; for (float k = 0.0; k < maxtaps; k++) { pos = L2_pos + L2_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); avg += w * ScaleH(pos); W += w; } avg /= W; vec3 Sl = abs(avg.rgb - pow(M_texOff(0).rgb, vec3(2.0))); vec3 Sh = abs(avg.rgb - pow(M_texOff(0).rgb, vec3(2.0))); return vec4(mix(vec3(0.5), 1.0 / (1.0 + sqrt(Sh / Sl)), lessThan(vec3(5e-6), Sl)), 0.0); } //!HOOK POSTKERNEL //!BIND HOOKED //!BIND M //!BIND R //!WHEN NATIVE_CROPPED.h POSTKERNEL.h > //!DESC SSimDownscaler final pass #define locality 8.0 #define offset vec2(0,0) #define Kernel(x) pow(1.0 / locality, abs(x)) #define taps 3.0 #define maxtaps taps #define Gamma(x) ( pow(x, vec3(1.0/2.0)) ) #define GammaInv(x) ( pow(clamp(x, 0.0, 1.0), vec3(2.0)) ) mat3x3 ScaleH(vec2 pos) { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; mat3x3 avg = mat3x3(0); for (float k = 0.0; k < maxtaps; k++) { pos = POSTKERNEL_pos + POSTKERNEL_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); vec3 M = Gamma(M_tex(pos).rgb); vec3 R = R_tex(pos).rgb; R = 1.0 / R - 1.0; avg += w * mat3x3(R*M, M, R); W += w; } avg /= W; return avg; } vec4 hook() { // Calculate bounds float low = floor(-0.5*maxtaps - offset); float high = floor(+0.5*maxtaps - offset); float W = 0.0; mat3x3 avg = mat3x3(0); vec2 pos = POSTKERNEL_pos; for (float k = 0.0; k < maxtaps; k++) { pos = POSTKERNEL_pos + POSTKERNEL_pt * (k + low + 1.0); float rel = (k + low + 1.0) + offset; float w = Kernel(rel); avg += w * ScaleH(pos); W += w; } avg /= W; vec4 L = clamp(POSTKERNEL_texOff(0), 0.0, 1.0); return vec4(GammaInv(avg + avg * Gamma(L.rgb) - avg), L.w); }

### haasn commented Jul 5, 2017

 You can add `//!DESC plaintext description` to passes now to get them to show up with friendly names (e.g. in the stats.lua interface)

### deus0ww commented May 15, 2019

 Can the params be modified to be sharper (like SSSR)? If so, would RobidouxSharp params be sharper than Mitchell? Should dscale be changed to match? Would changing the params be better/different than changing the localities?

### igv commented May 15, 2019 • edited

 Params should match params of dscale filter. Thats why it is "Tuned for use with dscale=mitchell". I haven't experimented much with other interpolation filters.

### deus0ww commented May 16, 2019

 Thank you for replying. I asked because I much prefer RobidouxSharp to Mitchell for dscale. Using RobidouxSharp for dscale and params does seem to be a bit sharper. From my limited testing, it seems like this is better than lowering localities, which overly thins lines in anime and cartoons.

### crazysword1 commented May 21, 2020

 Hi igv, Sorry I wasn't able to leave a comment over at FSRCNN-TensorFlow. I noticed that you made some changes to some py files. Are the glsl (x8 , x16) still the same as the old version? Thanks

 Yes.

### crazysword1 commented Sep 26, 2020

 First of all, thanks for making FSRCNNX. I use it everyday and it's imo the best real-time upscaling for a PC right now. Do you have any plans to update FSRCNNX with newer algorithms? RealSR NCNN Vulkan and SRMD NCNN Vulkan are showing incredible results. I understand they are probably too slow to run in real-time but are there alternatives to FSRCNN? Thanks!

### igv commented Sep 26, 2020

 No such plans. All "cool" networks (like RealSR) are GAN based. They are all huge and take too much time to train. And SRMD is just a pre/post-processing layers for deblurring and denoising. Not interested.

### crazysword1 commented Sep 27, 2020

 Wish I could contribute to the training but I don't have the knowledge and expertise to do it. RealSR is probably going to be way to slow to run in real-time anyways though.

### crazysword1 commented Oct 28, 2020

 Hi IGV, I am using FSRCNNX plus all the other shaders here. I noticed that FSRCNNX will only run if the content I am watching is < 2x my display resolution. For example, A 1920 x 1080P would not run FSRCNNX . My display is 3440 x 1440p. My question is, is there a way to force FSRCNNX to run so that it will upscale the content 2x regardless of resolution? The reason I want to do this is that I imagine this will be sort of like supersampling. The rendered resolution will be higher than the display but will be scaled down using the other shaders, resulting in a better image. Is my understanding correct? Can I force FSRCNNX to run? Thanks

### igv commented Oct 30, 2020

 Open the shader in a text editor -> Find and Replace -> 1.400 -> 1.300

### crazysword1 commented Oct 30, 2020

 Thank you, it worked. In your opinion, does doing this (upscale 2x then downscale to fit screen) improve visual fidelity?

 No.

### crazysword1 commented Nov 21, 2020

 Hi igv I would like to report an issue. Imagine a video. Rotate right by 90 Rotate right again by 90 (the video will be 180 from the original) Rotate right again by 90 (270) Rotate right again by 90 (359) Here is where the problem starts. If you rotate the video by 90 degrees four times, it will end at 359. Furthermore, the video will be slanted. See screenshot below. https://postimg.cc/rdZ5YnL4 At this point, if you rotate left by 90, the images will still be slanted until you rotate left 4 times so that it's back in the original position (0 degrees). At that point, the video will straighten again. Is there any fix for this? Thanks a lot.

### igv commented Nov 22, 2020

 That's an mpv issue, maximal rotation degree is 359, but should be 360. And in the manual it says "Currently supports 90° steps only", however with `n cycle video-rotate` rotation step is 1°.

### crazysword1 commented Nov 26, 2020

 Thanks, I will need to create an issue for this over at mpv.

### ghost commented Feb 6, 2021 • edited by ghost

 Why is it better to set linear-downscaling to no here? Can this shader be rewritten to support linear downscaling and would it have any advantage?

### igv commented Feb 6, 2021

 Less ringing artifacts. `linear-downscaling` should be used only with soft scalers (and only when it doesn't cause more aliasing artifacts). But you can use it with `linear-downscaling` if you like it and don't notice any artifacts.

### ghost commented Feb 6, 2021

 Ok, thanks for the answer

### ghost commented Feb 11, 2021

 And another question, is it possible to turn sharpening completely off or just minimize it?

### igv commented Feb 11, 2021

 is it possible to turn sharpening completely off This shader is a sharpener. or just minimize it? In the description.

### ghost commented Feb 11, 2021

 Ok, thank you. Asked in case there could be some special values, like if 0 did nothing, for example

### crazysword1 commented Apr 28, 2021

 How do I access the new 16-04-1 (thanks for updating that). You mentioned that it is in the archive but I can't find it anywhere on the page. Thanks

### deus0ww commented Apr 28, 2021

 It's in checkpoints_params.7z.

### crazysword1 commented Apr 28, 2021

 thanks :)
to join this conversation on GitHub. Already have an account? Sign in to comment