-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Possible RAVU improvement ideas #10
Comments
If naive anti-ringing is not done in-place, it can always be applied independent from prescaler. Save the texture before prescaling, filtering after prescaling with offset considered. So I'm not in favor of integrating naive anti-ringing unless it can be proven to have reasonably small quality loss (in PSNR/SSIM, for example). But I will at least try some in-place anti-ringing method, probably with edge direction considered. I'm currently working on
It's not how RAVU works. No math functions like I mentioned combination of different existing kernel function, just to demonstrate an idea that tries to make RAVU work with arbitrary scaling factor. But I later realized the model will only be trained to "reverse" certain downscaling kernel. So please ignore it. |
It doesn't make sense to use a linear regression for a convolution, surely? Why not fit a cubic function or something instead? |
You mean cubic function as model (weighted sum of I assume you mean that we need to use alternative cost function like cubic function. Then we have to use algorithm like gradient descent to solve it. Doable but requires more effects, and much much longer training time. |
I think I'm completely lost. What exactly is linear? What do the weights look like? My point is that I'd expect the trained weights themselves to look like (appropriately shaped) jinc functions, but your description makes it seem like they're linear functions or something. |
Okay, I will try to explain. Suppose we are interpolate from four (known) points The final answer |
Oh, I see now. Hmm; I still wonder if you could find a way to visualize the kernels for different keys. Would almost surely help with optimizing RAVU; if we could get a visual understanding of what's going on. |
Here is the visualization of all red is negative weights, blue is positive weights. Both Are normalized and sigmoidized. There are 24 columns, from There are 21 rows, divided into three row groups, from Each row group is divided into 9 rows, from //!DESC RAVU r3 visualizer
//!HOOK MAIN
//!BIND HOOKED
//!BIND ravu_lut3
//!WIDTH 144
//!HEIGHT 162
const int radius = 3;
const int quant_angle = 24;
const int quant_strength = 9;
const int quant_coherence = 3;
const int n = radius * 2;
const int width = quant_angle * n; // 144
const int height = quant_strength * quant_coherence * n; // 162
const vec4 red = vec4(1.0, 0.0, 0.0, 0.0);
const vec4 blue = vec4(0.0, 0.0, 1.0, 0.0);
const vec4 white = vec4(1.0, 1.0, 1.0, 0.0);
vec4 hook() {
ivec2 pos = ivec2(floor(HOOKED_pos * vec2(float(width), float(height))));
int angle = pos.x / n;
int coherence = pos.y / n / quant_strength;
int strength = pos.y / n % quant_strength;
int id = (pos.x % n) * n + (pos.y % n);
float w = texelFetch(ravu_lut3, ivec2(id / 4, (angle * quant_strength + strength) * quant_coherence + coherence), 0)[id % 4];
w *= n * n;
w = w / (1 + abs(w));
if (w < 0) {
w = -w;
return mix(white, red, vec4(w));
}
return mix(white, blue, vec4(w));
} //!DESC RAVU r4 visualizer
//!HOOK MAIN
//!BIND HOOKED
//!BIND ravu_lut4
//!WIDTH 192
//!HEIGHT 216
const int radius = 4;
const int quant_angle = 24;
const int quant_strength = 9;
const int quant_coherence = 3;
const int n = radius * 2;
const int width = quant_angle * n; // 192
const int height = quant_strength * quant_coherence * n; // 216
const vec4 red = vec4(1.0, 0.0, 0.0, 0.0);
const vec4 blue = vec4(0.0, 0.0, 1.0, 0.0);
const vec4 white = vec4(1.0, 1.0, 1.0, 0.0);
vec4 hook() {
ivec2 pos = ivec2(floor(HOOKED_pos * vec2(float(width), float(height))));
int angle = pos.x / n;
int coherence = pos.y / n / quant_strength;
int strength = pos.y / n % quant_strength;
int id = (pos.x % n) * n + (pos.y % n);
float w = texelFetch(ravu_lut4, ivec2(id / 4, (angle * quant_strength + strength) * quant_coherence + coherence), 0)[id % 4];
w *= n * n;
w = w / (1 + abs(w));
if (w < 0) {
w = -w;
return mix(white, red, vec4(w));
}
return mix(white, blue, vec4(w));
} EDIT: fix |
As I thought, we could almost certainly make use of mirror symmetry in this file, and probably also rotational symmetry within each “section”. So we could reduce the width to one fourth of what it is currently. |
Also, what does it change if you apply a linear factor to the negative weights? (e.g. 0.5 * w) |
Yes, the training time and LUT size could be reduced by 75%. However, it won't make rendering significantly faster since the number of LUT calls is not reduced. Actually, it will become slower since we need to rotate/flip the weight matrix after they are fetched from LUT.
It will make result noticeably blurrier (assuming all weights are normalized after that). If we want to clamp the negative weights, the proper way would be regularize it with cost function. For example, use |
Well, you could still train on the “reduced” image set and then rotate it when generating the weight texture. That way you get more samples and fewer equations to train, which would in theory lead to a better result with fewer images; since the “identical” kernels will all have shared weights. |
I did that already. With rotation and flipping we have 7 times more samples. |
Closing since antiring is solved by |
Rereading through this and related issues to understand RAVU again, this comment stood out to me. Even if the number of LUT calls would not change, making the texture smaller improves cache locality which can make those LUT calls significantly faster. Also, we don't need to extend this by complicated flipping logic, if we can arrange for the texture to be laid out with perfect horizontal/vertical symmetry we can simply use the "mirrored repeat" sampling mode, which reflects/mirrors any out-of-bounds texture read. |
Currently, the weight texture isn't exactly arrange in the way the above visualization pictures have shown. Instead, one dimension is the kernel ID (with size However, the weight texture indeed have been reduced to half of its size sometime ago, utilizing the symmetry. (Yes, the visualization shader above won't work with the current weight texture.) This change was meant to reduce the shader file size at that time though. |
To make sure they don't get forgotten to time:
Add naive anti-ringing. You could do something like bilinear or bicubic sampling along the “edge direction” in a line (or small bundle of offset lines), gathering a few samples, and clamping your output pixel to this value range. Alternatively, you could try adapting the “in-place” antiringing filter from my antiring.hook and adding it as a separate post-RAVU pass, slightly adjusted to account for the fact that you introduce an offset.
Train kernels differently? Right now, you said you use a linear regression to combine multiple scaler kernels - but how do you actually choose the kernels to combine to begin with? Are they jinc functions? Or am I misunderstanding how the algorithm works?
The text was updated successfully, but these errors were encountered: