Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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)[0];
float high = floor(+0.5*maxtaps - offset)[0];
float W = 0.0;
vec4 avg = vec4(0);
for (float k = 0.0; k < maxtaps; k++) {
pos[0] = POSTKERNEL_pos[0] + POSTKERNEL_pt[0] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[0];
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)[1];
float high = floor(+0.5*maxtaps - offset)[1];
float W = 0.0;
vec4 avg = vec4(0);
vec2 pos = POSTKERNEL_pos;
for (float k = 0.0; k < maxtaps; k++) {
pos[1] = POSTKERNEL_pos[1] + POSTKERNEL_pt[1] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[1];
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)[0];
float high = floor(+0.5*maxtaps - offset)[0];
float W = 0.0;
mat2x4 avg = mat2x4(0);
for (float k = 0.0; k < maxtaps; k++) {
pos[0] = L2_pos[0] + L2_pt[0] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[0];
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)[1];
float high = floor(+0.5*maxtaps - offset)[1];
float W = 0.0;
mat2x4 avg = mat2x4(0);
vec2 pos = L2_pos;
for (float k = 0.0; k < maxtaps; k++) {
pos[1] = L2_pos[1] + L2_pt[1] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[1];
float w = Kernel(rel);
avg += w * ScaleH(pos);
W += w;
}
avg /= W;
vec3 Sl = abs(avg[0].rgb - pow(M_texOff(0).rgb, vec3(2.0)));
vec3 Sh = abs(avg[1].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)[0];
float high = floor(+0.5*maxtaps - offset)[0];
float W = 0.0;
mat3x3 avg = mat3x3(0);
for (float k = 0.0; k < maxtaps; k++) {
pos[0] = POSTKERNEL_pos[0] + POSTKERNEL_pt[0] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[0];
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)[1];
float high = floor(+0.5*maxtaps - offset)[1];
float W = 0.0;
mat3x3 avg = mat3x3(0);
vec2 pos = POSTKERNEL_pos;
for (float k = 0.0; k < maxtaps; k++) {
pos[1] = POSTKERNEL_pos[1] + POSTKERNEL_pt[1] * (k + low + 1.0);
float rel = (k + low + 1.0) + offset[1];
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[1] + avg[2] * Gamma(L.rgb) - avg[0]), L.w);
}
@haasn

This comment has been minimized.

Copy link

@haasn 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

This comment has been minimized.

Copy link

@deus0ww 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

This comment has been minimized.

Copy link
Owner Author

@igv igv commented May 15, 2019

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

This comment has been minimized.

Copy link

@deus0ww 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

This comment has been minimized.

Copy link

@crazysword1 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

@igv

This comment has been minimized.

Copy link
Owner Author

@igv igv commented May 22, 2020

Yes.

@crazysword1

This comment has been minimized.

Copy link

@crazysword1 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

This comment has been minimized.

Copy link
Owner Author

@igv 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

This comment has been minimized.

Copy link

@crazysword1 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

This comment has been minimized.

Copy link

@crazysword1 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

This comment has been minimized.

Copy link
Owner Author

@igv igv commented Oct 30, 2020

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

@crazysword1

This comment has been minimized.

Copy link

@crazysword1 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?

@igv

This comment has been minimized.

Copy link
Owner Author

@igv igv commented Oct 30, 2020

No.

@crazysword1

This comment has been minimized.

Copy link

@crazysword1 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

This comment has been minimized.

Copy link
Owner Author

@igv 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

This comment has been minimized.

Copy link

@crazysword1 crazysword1 commented Nov 26, 2020

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

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Feb 6, 2021

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

This comment has been minimized.

Copy link
Owner Author

@igv 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

This comment has been minimized.

Copy link

@ghost ghost commented Feb 6, 2021

Ok, thanks for the answer

@ghost

This comment has been minimized.

Copy link

@ghost ghost commented Feb 11, 2021

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

@igv

This comment has been minimized.

Copy link
Owner Author

@igv 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

This comment has been minimized.

Copy link

@ghost 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

This comment has been minimized.

Copy link

@crazysword1 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

This comment has been minimized.

Copy link

@deus0ww deus0ww commented Apr 28, 2021

It's in checkpoints_params.7z.

@crazysword1

This comment has been minimized.

Copy link

@crazysword1 crazysword1 commented Apr 28, 2021

thanks :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment