View Full Version : Sin squared kernel for resize (Gaussresize mod)
As numerical modeling shows sinus square function gives a bit narrower single pulse with given comparable outside non-linearity and non-monotonicality (ringing) in compare with gaussian function.
The modeling was done with web-accessible setup - you can check it at http://drakan.ru/FTProot/vid_work/LR_sig_rest/sig_rest_en.html
For example at sigma = 0.85 gauss pulse has 0.5-level duration about 2T
http://drakan.ru/FTProot/vid_work/LR_sig_rest/gaus_sigma085.png
and with T=3.7 sin squared pulse has 0.5-level duration 1.9T and significally better form near lower values.
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinsq_t3d7.png
Sinus squared function (pulse/kernel) is f(arg,T) (T as parameter)
for(abs(arg) < PI/2)
{
(cos(arg*T*PI))^2
}
else
0
(To be centered around 0 we replace sin(arg+pi/2) to cos() )/
Currently in avisynth only GaussResize gives acceptable results in attempts to get good anti-alized fonts for example or doing other high-quality anti-alized work. May be because of better pre-filtering.
So I assume using of sinus squared kernel instead of gaussian may gives a bit sharper results with same level of artifacts (ringing) control.
As building todays avisynth (+) is enough difficult for low-skilled programmer it will be nice if anyone with existing anisynth-build enviroment will make an additional resize function almost completely based on current GaussResize function with changed gauss kernel to sinus-squared kernel.
Also with additional parameter similair as GaussianResize have.
For example of using GaussResize for getting antialiased video-font (compared with curent simple built-in avisynth font raserizer) the script can be used:
function Ast(clip c, int isize)
{
return Subtitle(c, "Font",font="Arial",size=isize,align=6,halo_color=$FF000000, text_color=$00e0e0e0)
}
function Ast2(clip c, int isize)
{
return Subtitle(c, "Font",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,480,290,"RGB24",25)
Animate(last, 0,100,"Ast2", 80, 180)
AddBorders(100,100,600,200)
Levels(0, 1, 255, 10, 235)
GaussResize(width/8,height/8,p=15)
Animate(0,100,"Ast", 11, 22)
SincResize(last,width*10,height*10,taps=20) (last resize must be omitted to get 100% size output, it only simulates video control monitor processing)
Looking in the sources of current avisynth+ release (3.6.1) it may looks like (derived from Sinc and Gauss resize existing filters):
Addition to resample_functions.cpp:
/***********************
*** SinSquared filter ***
***********************/
SinSquaredFilter::SinSquaredFilter(double p) {
param = clamp(p, 0.1, 100.0);
}
double SinSquaredFilter::f(double value) {
value = fabs(value);
double p = param*0.1;
value *= M_PI*p;
if (value < M_PI/2)
{
return cos(value)*cos(value);
}
else
return 0;
}
and to resample_functions.h :
class SinSquaredFilter : public ResamplingFunction
{
public:
SinSquaredFilter(double p = 30.0);
double f(double x);
double support() { return 4.0; };
private:
double param;
};
and may be to other places to 'register' new filter. At first tests may be one can just rewrite existing GaussResize function.
The changes are small enough but to test them it is required to build locally full avisynth build enviroment. Or may be any online way for building avisynth available to get working binary for test ? Ofcourse debugging ability will be lost in compare with full building and debugging local enviroment.
jpsdr
8th July 2020, 08:50
Oh, interesting, i think i can easely add this to my plugin. PM an email i can send you a build test.
One more promising kernel around powered-sinus/cosinus:
(for double *Filter::f(double value) function with all the same other lines )
if (value < M_PI/2)
{
return pow(cos(value), 1.7);
}
else
if (value < M_PI * 0.8)
return (-1)*cos(value) * cos(value)/value;
else
return 0;
While gaussian and limited to +-pi/2 cosine-squared kernels gives both ringing-controlled and halo/peaking(over/undershooting) controlled shape it gives significally lower sharpness. We can try to make kernel with halo/peaking control but with reduced ringing (outside our impulse borders) as possible. So it is my attempt to combine cosine-squared (real ^1.7 looks works better) impulse with inverted cosine-squared (unfortunately pow() functions looks like illegal of powering to 2.0- of negative numbers, but we can possibly replace negative cos(pi/2+) to positive sin/cos shape shifted to pi..pi/2 in the future versions) impulse dividev to argument for better decay.
In the range of T parameter 2.6 to 3.6 it gives sharpened shape with controlled undershoots amplitude and duration with enough control ringing outside undershoots. The lowest possible 0.5-level duration is about 1.4T with T-parameter 2.6. It is significally closer to theoretical 1.2T limit of 0.5-level of sinc base impulse in compare with gaussian and sin/cos suqared kernels.
May this can be called SinPowResize to separate with SinSqResize filter.
Though due to significant negative values this kernel definetly require float processing engine or at least signed integer instead of unsigned integer. I hope in todays avisynth resample in ASM is designed for handling negative coefficients as well as positive.
Updated web-simulator - http://drakan.ru/FTProot/vid_work/LR_sig_rest/sig_rest_en.html it has Sin^p button now.
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinp_t2d6.png
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinp_t3d5.png
Unfortunately using limits like "value < M_PI * 0.8" looks like dirty hacking that works with simple 5-point simulator and some limited range or T-parameter but may cause bugs with real resample engine because I do not know which values of 'value' parameter actually passes to f(value) kernel function. Still do not have working avisynth-building enviroment for testing.
Quick tests of the over/under shooted kernel in real processing in avisynth (built-in sincresize to size*4):
T=2.7, downscaled 3 times to fit 0..255 range (without negative values illegal to integer 8-bit coding of typical video/images). Samples put by hand in ms-paint into image array. (16),1,31,89,31,1,(16) (in the right side of image, at the left is sort of sin^2 or gauss pulse without over/undershoots (16),46,89,46,(16). 800% screenshot of ms-paint.
Later loaded in avisynth with imagereader, scaled with sincresize to size*4 with max available taps=20 and displayed in virtualdub as 50% and 100% size. The undershooted pulse shows very good conditioned against ringing with visually sharper view. Ofcourse vertically it is unconditioned against any Nyquist limits becaue it is 1-dimentional test, so it do shows ringing vertically.
Thanks to jpsdr for providing test builds. He makes permission to share results and executable. It is in attach.
Current report on results:
Test script for testing 64x SSAA text with float size (or at least non-integer size) also compared to built-in Subtitle() text engine.
Loadplugin("ResampleMT.dll")
function Ast(clip c, int isize)
{
return Subtitle(c, "Font",font="Arial",size=isize,align=6,halo_color=$FF000000, text_color=$00e0e0e0)
}
function Ast2(clip c, int isize)
{
return Subtitle(c, "Font",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,480,150,"RGB24",25)
Animate(last, 0,100,"Ast2", 80, 180)
AddBorders(100,100,600,200)
Levels(0, 1, 255, 10, 235)
a=GaussResize(last,width/8,height/8,p=17)
b=SinSqrResizeMT(last,width/8,height/8,p=3.1)
c=SinPowResizeMT(last,width/8,height/8,p=3.4)
a=Animate(a,0,100,"Ast", 11, 22)
b=Animate(b,0,100,"Ast", 11, 22)
c=Animate(c,0,100,"Ast", 11, 22)
StackVertical(a,b,c)
SincResize(width*4,height*4,taps=20)
Levels(0, 1.2, 255, 0, 255)
2 new test functions: SinSqrResizeMT and SinPowResizeMT. Usable range of p-parameter around 2..5 float.
SinSqrResizeMT uses simple cos(arg)^2 kernel. It shows results very close to built-in Gauss resize. In given range of control parameter both ringing and over/undershoots are nicely conditioned. The contrast and sharpen of result is average.
SinPowResizeMT uses more complex kernel of cos(arg)^1.7 at +-pi/2 agrument range and -cos(arg)^2/(arg) in +-pi/2 to +-0.8pi argument range. The results are significally different: It still very good conditioned against ringing but gives more sharp results in both higher contrast and some under/overshooting (half-period non-ringed).
So we can define 2 different classes of kernels:
1. Both anti-ringing and anti-over/undershoots conditioned (gauss and cos^2).
2. Anti-ringing conditioned with (adjustable) over/undershoots that increases visual sharpness.
Unfortunately inside avisynth resampling engine the advanced kernel works in some different way in compare with my modeling and parameter p gives very small or not at all control for amplitude of over/undershooting. It mostly control overall filter frequency response from more blurry to extra sharp and ringy. Also it looks the hack of 0.8pi limiting is too illegal for sin/cos functions and gives discontiniuty. So it is good to adjust kernel function to +-pi limit with more fading members or parameters tuning. Also this class of resampling kernerls definetly needs more than 1 user-defined parameter for control both frequency response and 'sharpening' effect.
May someone can continue this work or may be I will someday get avisynth building enviremont for experiments.
Oh - I found my error in parameters description, that cause difference between T-values in my simulator and 'param' in avisynth build.
In my simulator the T-param is converted to 'frequency' with reciprocal f=1/T. And I forgot it in function description. So correct relation between simulator T-param and current build avisynth function p-param is p=10/T. 10x because of 0.1 multiplication inside avisynth function.
Now things goes close as they should. The 'SinPow' kernel still has limited enough usable range in current approximation math (from T > 2.55 to T < 3.75 or p < 3.9 and p > 2.66) and I will try to fix it with my simulation work. The limited and mostly interested case is lowest duration side (lowest T-value) and as hand-positioned samples shows it can reach close to theoretical duration of sinc-pulse of 1.2T at 0.5-level limit. With about 35% undershoots of positive pluse amplitude. Keeping outside ringing acceptably controlled (at least for 8-bit SDR video).
http://drakan.ru/FTProot/vid_work/LR_sig_rest/th_lim1d2.png
Ofcourse it is illegal for 100% amplitide pulses in 16..235 8-bit video coding, but may work in scaled-down for contrast pulses. Like 0.2x scaled - (16),1,17,44,17,1,(16)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/th_lim1d2_x0d2.png
Selur
12th July 2020, 10:30
@DTL: would be nice if you could use 'code'-tags, it would really help making your posts more readable.
Finally I was able to build ResampleMT with VS2013 and made corrections and debug to functions more quickly. The most important was limiting support() member to 2 instead of 4 as in GaussFilter because 4 gives errors in following avisynth resampler. The param processing is now follows web-simulator T-value (no reciprocacity and 0.1 multiplication because todays avisynth good accepts float params values).
Current kernel func:
double SinPowFilter::f(double value)
{
value = fabs(value);
value *= M_PI / param;
if (value<(M_PI / 2)) return pow(cos(value), 1.8);
else
{
if (value<(M_PI)) return -(cos(value)*cos(value)) / (0.9*value);
else return 0;
}
}
In testing with typical videocamera frames with p-parameter = 2.5 it gives comparable sharpness with Lanczos4Resize() but with better conditioned anti-ringing. Parameter p<2.5 gives better sharpness but may cause more artifacts, p>2.5 gives less sharpness. Intendent for using for downsampling at anti-alizing processing and lower frame size rip creation.
Name of function SinPowResizeMT(), parameters are same as GaussResizeMT except for p-param as descriped above. Default p=2.5. Usable range of p-param is about 2..5 float. Internally limited to 1.0..10.0.
Currently only x64 build available because win32 config gives some errors.
As for sin^2 simple kernel: It gives very close to GaussResize results, so may be no any value for most users.
cork_OS
13th July 2020, 20:43
Seems the secret of madshi's non-ringing Lanczos (https://forum.doom9.org/showthread.php?t=145358) has revealed.
patul
14th July 2020, 02:37
@DTL, I suggest you put in the samples, source and binary somewhere else (google drive, github) instead of as attachment.. With all due respect to the moderators, attachments will need to get manual approval from the moderators and it could take sometime.
Build (with sample / demo script ) https://drive.google.com/file/d/1AFOLztdtY1Qvjsw0FeeTZRKy3bwJzzV6/view?usp=sharing .
Sources https://drive.google.com/file/d/1rhEwMeWkzRbV3XNMT7xfH8x9mgCZGW80/view?usp=sharing .
Seems the secret of madshi's non-ringing Lanczos (https://forum.doom9.org/showthread.php?t=145358) has revealed.
I think there was not very the same task. It looks they try to use Lancsoz (sinc-based) resampler on 'non-conditioned' input data for upsample. That definetly will cause ringing effect (even in samples-space without signal/image restoring for viewing as required). Also in that thread users post about requirement of co-operative adjusting 2 or even 3 components of digital (discrete) workflow:
1. Samples source
2. Resampling engine
3. Restoring engine for image viewing
I assume current mature video (broadcasting) processing still based on the classical digital (discreete) processing of continious (analog) (image) data.
So 1. Samples sources (like video cameras, or digital/graphic data sources - CG systems, 3D- and 2D image 'renderers') must provide 'conditioned' samples arrays describing some continious/analog image. 'Conditioned' means if we check this data array on correct monitor (see 3.) it have (minimized or controlled by author/producer) ringing and over/under shooting (may also somewhere called 'peaking') in the state defined by image data authour/producer. As my practic shows different producers has its own opinion how 'sharp' must image looks in terms of over/under-shooting.
2. Resample engine (if we need for some reason change number of samples, spatial density) must keep the 'conditioning' of the output samples array. Or it may adjust 'sharpness' as person making resampling wants with ringing controlled to be as minimum as possible.
3. Restoring engine for image viewing must process input samples as described in Nyquist/Kotelnikov/etc theorem of restoring continious data from its sampled form. In math it is just Sinc-lowpass filtering with large enough taps (to better restore the highest still legal frequencies). I think its spirit still exists in typical documents for good quality video monitors like https://tech.ebu.ch/docs/tech/tech3320.pdf .
Here is a bit different tasks: The filter is designed for getting 'conditioned' samples output arrays for 'non'conditioned' sources (during operation like 'antialiasing', though it is a bit more complex because it is not only spectrum-limiting with simple 'ideal-low-pass filter', but also spectrum-conditioning in the legal low frequencies pass-range). For example buit-in avisynth font rasteriser gives badly conditioned for video samples data of glyphs and using down-resize we can enchance its 'conditioning' and adjust looking in terms of 'sharpness' while keeping aliasing and ringing in low (controlled) state.
And second typical task is down-resize of already formed 'conditioned' sources (2D samples array describing some continious image) because it is limiting-spectrum operation and must be performed with respect to correct math operation to get also 'conditioned' for video output result (2D samples array describing some continious image with lesser number of samples).
For up-sizing of good 'conditioned' video data we can simply use already working SincResize. It is typical operation on geometry video data processing for example for images overlay with float instead of integer coordinates. I hope somedays avisynth will be capable for geometry operations with float instead of integer coordinates. For example for better accuracy image positioning and smoother animations, smoother crawl/zoom effects etc.
Made some waveform testing on Tektronix WFM7120 signal control equipment.
Production script for 1920x1080 frame
Loadplugin("ResampleMT.dll")
function Ast2(clip c, int isize)
{
return Subtitle(c, " Сергей | председатель комтитета по государственному контролю,",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00202020)
}
BlankClip(100,1700*4,180*4,"RGB24",25,color=$00e0e0e0)
Animate(last, 0,100,"Ast2", 45, 180)
AddBorders(280,3400,600,200,color=$00202020)
SinPowResizeMT(last,width/4,height/4,p=2.9)
Output data passed 1 time HDCAM 422 MPEG-2 50 Mbit/s compression-decompression I suppose via import and output from broadcast server.
Photos taken with poor-people camera from handheld android (smart)phone.
Control mon frame (near 70..80% of animation length)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_201919.jpg
Tek line select setup pos in the frame height
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_201931.jpg
Full screen waveform window with p=2.5
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_2d5_fs.jpg
Full screen waveform window with p=2.7
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_2d7_fs.jpg
Full screen waveform window with p=2.9
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_2d9_fs.jpg
Full screen waveform window with p=3.1
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_3d1_fs.jpg
4-windows tek screen with p=3.1
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinpow/IMG_20200720_3d1_01.jpg
For comparison some close for text size output from badly-conditioned CG title station with lots of badly controlled ringing artefacts:
http://drakan.ru/FTProot/vid_work/LR_sig_rest/cmp_ba/IMG_20200720_153815.jpg
http://drakan.ru/FTProot/vid_work/LR_sig_rest/cmp_ba/IMG_20200720_154023.jpg
http://drakan.ru/FTProot/vid_work/LR_sig_rest/cmp_ba/IMG_20200720_154432.jpg
FranceBB
21st July 2020, 14:14
It took me a while to look at the math behind it and I gotta say that it's a very good catch.
Well done for finding this out.
I guess it took a Russian to prove us wrong xD
The 'SinPow' kernel still has limited enough usable range in current approximation math (from T > 2.55 to T < 3.75 or p < 3.9 and p > 2.66) and I will try to fix it with my simulation work. The limited and mostly interested case is lowest duration side (lowest T-value) and as hand-positioned samples shows it can reach close to theoretical duration of sinc-pulse of 1.2T at 0.5-level limit. With about 35% undershoots of positive pluse amplitude. Keeping outside ringing acceptably controlled (at least for 8-bit SDR video).
Ok, I may have got it completely wrong, but isn't that gonna shoot to the roof (i.e give out of range values) on signals that peak at 770mV (0.7V), namely 235 8bit?
In testing with typical videocamera frames with p-parameter = 2.5 it gives comparable sharpness with Lanczos4Resize() but with better conditioned anti-ringing. Parameter p<2.5 gives better sharpness but may cause more artifacts, p>2.5 gives less sharpness. Intendent for using for downsampling at anti-alizing processing and lower frame size rip creation.
I see... Can it also be used as a brick-wall low-pass filter for antialiasing only, possibly with a cutoff that the user can choose? I'm just asking 'cause now I'm curious.
@DTL, I suggest you put in the samples, source and binary somewhere else (google drive, github) instead of as attachment.. With all due respect to the moderators, attachments will need to get manual approval from the moderators and it could take sometime.
I was actually lurking ResampleMT and now I understand where those commits come from: https://github.com/jpsdr/ResampleMT/commit/6d22478f94a9fce0ed3f8c35bff3bf459bffc87b
In other words, don't worry, they're being merged into the main repository created and maintained by Jean Philippe.
Photos taken with poor-people camera from handheld android (smart)phone.
We work in broadcast, but we're all broke (me included). It's a common place for people here on Doom9 xD
Output data passed 1 time HDCAM 422 MPEG-2 50 Mbit/s compression-decompression I suppose via import and output from broadcast server.
XDCAM50 spotted here xD
Made some waveform testing on Tektronix WFM7120 signal control equipment.
I've been using Waveform Monitors by Tektronix (https://i.imgur.com/HRLItw3.jpg) ever since January 2016; very good choice. A part of me is missing whenever I'm not at work with a Waveform Monitor next to me.
Anyway, very well done! ;)
"Can it also be used as a brick-wall low-pass filter for antialiasing only, possibly with a cutoff that the user can choose? I'm just asking 'cause now I'm curious."
If you attempt to do 'anti-alias' work with 'ideal brick wall low-pass' filter you will typically got ringing caused by 'Gibbs-phenomenon' ( https://en.wikipedia.org/wiki/Gibbs_phenomenon ) . So as my practice shows good 'conditioning' filter must AND cut-off illegal frequencies outside Nyquist limit AND control shape of remaining spectrum components inside legal Nyquist frequencies (from zero to Fd/2).
To do filtering with ideal brick-wall low-pass filter I think you can use already existed in avisynth SincResize filter.
'but isn't that gonna shoot to the roof (i.e give out of range values) on signals that peak at 770mV (0.7V), namely 235 8bit?'
Legal values for 8bit video data encoding samples values (for limited transitions) can reach limits from 1 to 254 (0 and 255 are system sync code values). So smart digital encoding of video data in Rec.601 (709, etc) already have some headroom for proper handling (limited) over and under shoots even below system-black 16 level and system-nominal-white 235 level. So it can encode and transfer more sharper image data in compare with 0(system black)..255(system while and max white) encoding with <0 and >255 non-legal and non-possible data values. More (visually) sharper with still good conditioning against ringing.
Example: if we try to encode sharp transition from system-black 16 level to system-nom-white 235 level we can either stuck with 16..235 range (for example using non-ringing/non-over/under-shooting Gauss kernel filter) and got something like:
16,16,70,191,235,235,235,... samples sequence.
But it will give less sharp transition - more time (or actually restored image space dimention) from 0.1 to 0.9 level rise.
Like 1.6T from 0.1 to 0.9 system nominal amplitude.
http://drakan.ru/FTProot/vid_work/LR_sig_rest/no_pk01.png
in compare with over/under shooting like: 16,16,1,92,254,254,235,235,... samples sequence. This samples sequence has 'out-of range' 16..235 encoding samples, but only shortly around transition.
It allows to get about 1.05T rise transition.
http://drakan.ru/FTProot/vid_work/LR_sig_rest/ous_pk01.png
So it is legal to encode over/undershoots with 1..15 and 235..254 code values, and also 235..254 code levels may be used to encode super-white 'knee-d' values (with losing more and more ability to encode properly over-shooting for small details and transitions on large detail borders).
More exact you can test in in web-simulator at the link in the first post.
jpsdr
21st July 2020, 17:21
Don't forget my version has the range parameter... ;)
I hope i'll have time to make a build soon.
Check eventualy, as it's possible that i put a build, and post info on doom9 later...
FranceBB
22nd July 2020, 13:25
Don't forget my version has the range parameter... ;)
I hope i'll have time to make a build soon.
Check eventualy, as it's possible that i put a build, and post info on doom9 later...
Got it.
I just downloaded the new version and it's testing time!
So, I grabbed one of the spare BetaCAM contents I had available in 720x576 anamorphic 16:9. I picked two random scenes and I upscaled them to HD 1280x720 using different resizing kernels, including the new SinPowResizeMT().
Here are the results:
Original anamorphic 576p
https://i.imgur.com/2TNDlhI.png
PointResize Upscale
https://i.imgur.com/J1duK0t.png
BilinearResize Upscale
https://i.imgur.com/jC9nOeo.png
BicubicResize Upscale
https://i.imgur.com/eQLyS5x.png
LanczosResize Upscale
https://i.imgur.com/mPtXeDN.png
GaussResize Upscale
https://i.imgur.com/vl08vUa.png
SincResize Upscale
https://i.imgur.com/5NJX5xY.png
SinPowResize Upscale
https://i.imgur.com/TlWiSoS.png
Spline16Resize Upscale
https://i.imgur.com/M2eNQiJ.png
Spline36Resize Upscale
https://i.imgur.com/ayUeatt.png
Spline64Resize Upscale
https://i.imgur.com/vpUuwn0.png
Spline144Resize Upscale
https://i.imgur.com/Fxhhtq0.png
Spline64Resize + NNEDI Upscale
https://i.imgur.com/RXneDTt.png
FranceBB
22nd July 2020, 14:16
And now another scene:
Original anamorphic 576p
https://i.imgur.com/d28urSd.png
PointResize Upscale
https://i.imgur.com/vklMJCJ.png
BilinearResize Upscale
https://i.imgur.com/AUKoXeR.png
BicubicResize Upscale
https://i.imgur.com/f0zOGEY.png
LanczosResize Upscale
https://i.imgur.com/AtOTNoY.png
GaussResize Upscale
https://i.imgur.com/w4i7dam.png
SincResize Upscale
https://i.imgur.com/2rnR8Lj.png
SinPowResize Upscale
https://i.imgur.com/C7FhHji.png
Spline16Resize Upscale
https://i.imgur.com/tHSMbmS.png
Spline36Resize Upscale
https://i.imgur.com/peoAvmz.png
Spline64Resize Upscale
https://i.imgur.com/WeRvQS1.png
Spline144Resize Upscale
https://i.imgur.com/E2O28Wu.png
Spline64Resize + NNEDI Upscale
https://i.imgur.com/Y3BBwA8.png
As you can see, PointResize is crappy and doesn't really achieve anything usable in the real world, as expected.
BilinearResize is very blurry, just as expected...
BicubicResize is slightly less blurry, but with a bit more ringing...
LanczosResize is sharper than both Bilinear and Bicubic, however aliasing and ringing are now more pronounced...
GaussResize is very blurry; it somewhat hides ringing and aliasing at the expense of sharpness...
SincResize... mother of sharpness...! It's so sharp, that it looks like someone actually used a poor sharpening filter on it... It's way too sharp and it shows every possible problem, from aliasing to ringing and even haloing for that matter! Those issues are so pronounced that it's barely watchable...
SinPowResize... it's actually trying to achieve something better than Sinc, preserving the sharpness of it but getting less artifacts. Indeed, there are less artifacts, but the result still shows very bad aliasing all over the edges. As you can see from the first picture in my former post (I had to split the post in two due to the max number of images allowed), it kinda made it worse 'cause volleyball players are now a blocky mess on the edges. The math is ok, but on real world examples it misbehaved apparently...
Spline16Resize, Spline36Resize, Spline64Resize, Spline144Resize... I gotta be fair, the only reason why I included Spline144 is not to end up in the Katie's blacklist and / or receive complaints about the fact that I didn't include her beloved resizing kernel, so... xD
Anyway, the difference between them is barely noticeable; the result of Spline64Resize is pretty sharp and although it does show some aliasing on the edges, it's not so terrible. When used in combination with NNEDI some of the aliasing around the edges gets smoothed out and the result is actually pretty pleasant to watch. So, again, I'm gonna go with Spline64 + NNEDI as the best solution.
I'm sorry for Sin Squared, it looked promising on the paper, but it doesn't seem suited for real life scenarios, at least on my test. Perhaps it will behave differently on synthetic videos like animations / anime / cartoons etc.
Got it.
I just downloaded the new version and it's testing time!
So, I grabbed one of the spare BetaCAM contents I had available in 720x576 anamorphic 16:9. I picked two random scenes and I upscaled them to HD 1280x720 using different resizing kernels, including the new SinPowResizeMT().
For upscale it works close to LanczosResize with taps 2..3 with max sharpness with p about 2.5, and may be closer to GaussResize with p > 3.
The whole point of this method is for production any finite-resolution system data (SD,HD,UHDetc), 'conditioned', from for example infinite-res source (3D render or vector-defined image data like TrueType fonts). Or make down-scaled rips from hi-res sources to down-res targets.
I think it will not have any benefits in upscale (because in classic digital processing there defined the one and only one method of 'upscale' i.e. analog data restoration from digital form is Sinc-based filtering).
The good testing scenario is for example creating DVD-sized rip (SD 576 lines) from HD or better UHD source. And ofcourse the testing for output ringing must be performed with syntetic SincResize restoration at 2x..8x size in avisynth or real hardware DVD high quality DAC up to 13.5x8=108 MHz upsampling or broadcast quality waveform monitors like Tektronics/Leader products. Or may be producing subtitling or titling for any resolution system.
Current Subtitle() filter in avisynth produces too badly conditioned output and it is now possible to fix it significally using SSAA workflow:
1. Get Subtitle() output in 2x..8x+ size
2. SinPowResize (or GaussResize) /2../8+ to get conditioned output system size text.
Also if using 10x upsize and /10 down-resize (100x SSAA) it useful for .1 sample precision glyphs or text positioning (and sizing) for example for smooth crawls or other text animations. Same applying to layers composing work with sub-sample output positioning precision etc.
jpsdr
22nd July 2020, 17:42
used in combination with NNEDI
SH####T!!! F###K!!!!!!!!!!!!!!!!
I forgot to add the new resample in the resample function list of nnedi3_rpow2...:eek:
I'm good for a new version of NNEDI3.:(
And now another scene:
SincResize... mother of sharpness...!
Because SincResize is core engine for video data quality analyzer for 'poor-people' and 'Personal Computer -based -people' I have at least one complain on its current (small, but visible) bug in avisynth.
It looks like some error on some edge-conditions in calculations. May this bug also harms all resizers in avisynth because it possibly deep inside resampler engine (I think it is kernel-func independent, because Sinc-kernel function is simple and looks bugless and bug depend on frame size).
At least it simply reproductible in SincResize:
Given input image data as 'ideal video/image point' for example defined with 2D cross-sections as (16),27,132,235,132,27,(16) samples (not gamma-corrected, linear, 16..235 video levels coded) sequence and 2D full (sinc-interpolated using radius-vector) 5x5 array
16,20,27,20,16
20,76,132,76,20
27,132,235,132,27
20,76,132,76,20
16,20,27,20,16
This 5x5 data array actually defined by only 2 numbers - 27 and 132 for video/image ideal 'conditioned' elementary 'point'. Not ringed, not over/under shooting variant. Also it is interpolation with float argument defines resize kernel with support=2. I.e. it is also kernel-image itself. Because its central 235 is 100% contrast data value and all other integers are just rotation 2d cross-section around its center. And all surrounding other values are 16-black code value. So we can even define 1 or 2 more Resize functions to avisynth (with names like Support2Resize and Support3Resize) with only 2 user-input parameters for support2 resizer and 3 input parameter for support3 resizer. And user can simulate almost any support2(3) kernel without recompile resizer binary (including Sinc, SinPow, Lanczos and many other resizers). Though its 2 (or 3) parameters are harder to adjust because they are points of kernel instead of adjustments like 'sharpness'.
We can set its values in poor-people win tool 'ms-paint' to get image-formatted file:
http://drakan.ru/FTProot/vid_work/LR_sig_rest/1point_27_132.bmp (downloadable ofcourse)
And load into avisynth and Sinc-Resize:
ImageReader("1point_27_132.bmp")
Crop(20,20,-20,-20)
SincResize(width*8,height*8,taps=20)
With above Crop() parameters output image size is 1032x904 and output shows 4 errors artifacts (marked with circles)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_bug01.png
If we lower resizer input image size the bug looks like disappear and output is clear:
ImageReader("1point_27_132.bmp")
Crop(50,50,-50,-50)
SincResize(width*8,height*8,taps=20)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_no_bug01_sm.png
Same result with SincResizeMT from JPSDR pluging because I think it uses same avisynth resample core functions. So if possible may some advanced programmers may chek resamlper core engine and fix it someday.
Add: To make bug much more visible it can be added
Levels(0, 1, 25, -300, 255)
To end of script. And it shows as (cropped)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_bug01e_cr.png
The distance to buggy output samples from center of filter depends on 'taps' parameter. With taps=10
SincResizeMT(width*8,height*8,taps=10)
The output looks
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_bug01e_cr_taps10.png I.e. distance ~ taps parameter.
So it may be errors on the end/edge of SIMD registers processing and the end/edge of filter's 'support' because 'support' of SincResize=taps or error on getting/applying kernel data on the kernel edge, etc.
It all processing in RGB24 (8bit integer) space. May be in Float32 samples data the errors will be different.
real.finder
23rd July 2020, 14:12
it's same with Dither resize
ImageSource("1point_27_132.bmp")
Crop(20,20,-20,-20)
ConvertToYv24
Dither_convert_8_to_16().Dither_resize16(kernel="sinc",width*8,height*8,taps=20)
Ditherpost
Levels(0, 1, 25, -300, 255)
it's same with Dither resize
Open issue on github - https://github.com/AviSynth/AviSynthPlus/issues/182
As SetMaxCPU in AVS+ 3.6.1 to "none" do nothing it may be not too deeply in ASM code bug and equal to vertical and horizontal resamplers. Hope it will be fixed.
poisondeathray
23rd July 2020, 20:40
Affects vapoursynth fmtc sinc too, although differently (fmtc by same author as Dither Tools , but checked in case some other underlying issue)
Is there another implementation that does not get affected ? Maybe matlab or something ? Maybe this is how it's supposed to be given the math ?
"Maybe this is how it's supposed to be given the math ?"
No ofcourse. Sinc function (its medium abs() value) fades monotonically as distance from zero increases. Nothing special happens at the point where we truncate it (especially at zero point). I sure it is just error in resampler code.
One more idea: As artifacts has negative polarity so if code programmer has tested on full scale positive image in 0..255 levels, so negative artifacts in 'blacker than black' levels may be truncated as negative numbers and become invisible. But when we process grayscale and bipolar image data the negative artifacts do become visible (on syntetic clear noiseless fons and good quality low noise video data).
But LanczosResize (taps=10) looks do not suffers from this bug. That is strange because the resample core is the same.
"Is there another implementation that does not get affected ?"
There may be variuos SincResize upsize methods. One possible is (in 2 separate stages)
PointResize(width*8,height*8)
IdealLowPassFilter(sinc-based/brick-wall)(F/8).
PointResize already built-in in avisynth and for ideal LPfilter I will search. It may be GeneralConvolution() but we need many taps - about 5..10+ for better results and its 2D input params are too large array for manual input.
May be some FFTw-based avisynth plugin can provide ideal LPF functionality.
Test with FFTQuiver: http://avisynth.nl/index.php/FFTQuiver
LoadPlugin("FFTQuiver.dll")
ImageReader("1point_27_132.bmp")
Crop(20,20,-21,-21)
ConvertToYV12()
PointResize(width*8,height*8)
F1Quiver(last,2,30,0,3)
TurnLeft()
F1Quiver(last,2,30,0,2)
TurnRight()
Though it is not provide as sharp frequency cut-off as sinc. May be custom filter params in FFTQuiver may get closer to sinc result.
LigH
24th July 2020, 09:25
Bold guess: The more taps you use, the more probably you will get the Gibbs phenomenon (https://en.wikipedia.org/wiki/Gibbs_phenomenon), an overshooting of a polynomial estimation due to a finite number of parameters in a sequence. The high number of taps does not eliminate it, it only sharpens the effect.
For further bug isolation and easier step-by step walking through code tried to lower number of samples in resizer's input as much as possible:
1 sample (230 at field of 16) input
http://drakan.ru/FTProot/vid_work/LR_sig_rest/1sample_230.bmp
retrives kernel itself image output (acting as delta-function input to filter).
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_1sample_taps20.png
And ringing at its edges masks the bug.
The minimum good bug visible input is 3 non-zero samples (for 1d horizontal resampler and taps 10..20),(127,230,127):
http://drakan.ru/FTProot/vid_work/LR_sig_rest/3sample_127_230_127.bmp
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_3sample_taps20_bug.png
It shows how sincs (3 sincs waves from 3 non-zero input samples) sum already good converges/balances to zero (i.e. 16 field code level) close enough to samples position. But something buggy happens at the edges of truncated sincs.
May the resampler engine losts some members of kernel at the very edges of processing so the very edges of sum lost ability to converges to zero (i.e. 16 field code level) and stays in small negative (or zero for black) values.
Script is
ImageReader("3sample_127_230_127.bmp")
#ImageReader("1sample_230.bmp")
Crop(20,20,-20,-21)
ConvertToYV12()
SincResize(width*8,height*8,taps=20)
Crop(700,600,800,800)
Levels(0, 1, 25, -400, 255)
So comes the easiest (temporal) bugfix or at least workaround - just truncate resampler's sums a bit so we will get good converged central output. Like perform taps=20/support=20 processing and use only 16..18 output max which are not buggy.
May be simply try to lower 'support' return value in SincResize header like
class SincFilter : public ResamplingFunction
261 /**
262 * Sinc filter, used in SincResize
263 **/
264 {
265 public:
266 SincFilter(int _taps=4);
267 double f(double x);
268 double support() { return taps - 2; /* or taps - 4 etc * / }; << HERE
269
270 private:
271 double taps;
272 };
This may end with 2 results: 1. Buggy samples will be truncated or 2. Buggy samples may come closer to kernel center.
With SinPowResize I found too large 'support' return value also cause some (more) bugs in processing. While kernel already returns 0 if argument > 'support equal to 2'.
I have no build enviroment at my home place to test this idea now.
wonkey_monkey
24th July 2020, 10:24
Does the same problem occur if, after cropping, you add a pure black (0) border?
Does the same problem occur if, after cropping, you add a pure black (0) border?
Yes - it keeps the same.
Bold guess: The more taps you use, the more probably you will get the Gibbs phenomenon (https://en.wikipedia.org/wiki/Gibbs_phenomenon), an overshooting of a polynomial estimation due to a finite number of parameters in a sequence. The high number of taps does not eliminate it, it only sharpens the effect.
Gibbs usually gives oscillations around mid level. And here we have always black bugs in all examples.
It may be some old coder workaround for fix unwanted effects on discontinuity on frame edges ? Like zero filling.
And I think the test with 1sample shows significally wrong realisation of SincResize inside avisynth. Its attempt to use separated V and H processing fails for some reason.
Because 2D sinc function is rotation of 1D sinc around its center and at 1 sample test we see significally worse image. I suppose it must consists of faded rings instead of something like cheker-board.
The kernel function is right ofcourse, but 2D resampler works differently in compare with awaited 2D convolution with 2D sinc.
May it fails because 2D sinc kernel uses radius-vector length as argument to kernel function and for 'diagonal' samples it defined as sqrt(x_coord^2+y_coord^2). And with V + H separated current processing avisynth's resampler gets only 'linear' kernel values.
V + H processing may be significally faster in compare with memory access with 2D kernel stepping, but the results looks significally different.
I.e. current avisynth SincResize has minor bugs in 1D processing and significally wrong in 2D processing.
wonkey_monkey
24th July 2020, 13:45
Yes - it keeps the same.
But not if you add a 16 grey level border?
In that case it seems to me it's just because it assumes that anything outside the border is 0, which is not unreasonable, if not necessarily ideal. It's just another one of those situations where there's no right answer to a problem.
jpsdr
24th July 2020, 17:11
You can also eventualy check with zimg.
poisondeathray
24th July 2020, 17:47
You can also eventualy check with zimg.
Does it have "sinc" ? I haven't seen it in avsresize, vapoursynth resize, or ffmpeg zscale.
jpsdr
25th July 2020, 08:50
Ah... I just know zimg has a resample (i don't use zimg), but i don't know the list of it. Maybe you're right.
As it rainy days here there is less work ouside home and I try to make demo for Sinc 2D upsizing. Using Visual Studio 6 that good works at my old PC at home.
Uploaded project files and zipped-builded executable at my new account at github - https://github.com/DTL2020/Sinc2D-master . It uses in.tif file (RGB 8 bit encoded, no compression) as input and even reads its size now. Produces out.tif file. It tries to perform 'second stage' - 2D convolution with Sinc-kernel. First stage (point upsize to desired scale must be performed already - like PointResize in avisynth).
It has lots of limitations and bugs - it do not correctly process edges of input samples array and make error borders about taps*Div-factor size. It is ofcourse very slow because of point by point memory read/write access in C functions, so some progress messages to command line after each row added. The good working taps value are odd-numbered (5,7,9, etc) - with even numbers the kernel normalizing pass makes high-magnitude kernel and processeed to 0 and 255 limited output (not normal greyscale). It accepts RGB image data but process ony one channel (R) and make copy to all RGB output (for speed).
Hardcoded at build time params
int iDiv = 4;
int iTaps = 11;
iTaps is standard sinc taps param and iDiv is amount of low-pass divider to input data i.e. if we PointResize(width*4,height*4) we must use iDiv=4 at filtering. Also input data must be point-upsampled to the exact 1/iDiv value or the output will be additionally corrupted (with unfiltered frequencies or more useful frequencies will be filtered-out).
Anyway it produces some results:
1sample input (magnified internally about 10x in amplitude) processing - gives true 2D sinc kernel image output
http://drakan.ru/FTProot/vid_work/Sinc2D/1s_kernel2D.png
1point input processing (4x upsampling) with default kernel truncation (KernelSize/2)
http://drakan.ru/FTProot/vid_work/Sinc2D/1p_d4_default.png
It produces some artifact positive-white ring at the edge of kernel so I make attempts to fix it with slight kernel truncation:
1point input processing (4x upsampling) with kernel truncation (KernelSize/2)*0.95f
http://drakan.ru/FTProot/vid_work/Sinc2D/1p_d4_default_0d95krn_r.png
It looks affects kernel normalisation too for some reason so produces some positive gain too.
And 3samples example processing (4x upsampling)
http://drakan.ru/FTProot/vid_work/Sinc2D/3d_d4_0d95krn_r.png
It shows how good conditioned (in H direction) data converges at horizontal axis.
Curernt results:
1.The 2D sinc convolution significally differs from V + H 1d SincResize processing in avisynth now.
2. It really may have some edge effects at the end of kernel and additional work required to cut them out of output result.
3. '2-stage' method upsampling - point-upsize + low-pass filtering may be not give very accurate results so it is better to use input samples in its original space and produce output samples in larger samples-number space directly (by sinc-interpolation between input samples). Will try to do it in next versions.
Bold guess: The more taps you use, the more probably you will get the Gibbs phenomenon (https://en.wikipedia.org/wiki/Gibbs_phenomenon), an overshooting of a polynomial estimation due to a finite number of parameters in a sequence. The high number of taps does not eliminate it, it only sharpens the effect.
Oh - at the morning the head is clearer and it looks I understand the simple source of bug:
In my web-simulator I user very large sinc sequences like 200 or may be 600 taps so the edge non-converged sums are out of valuable 'image' output result. Also Sin(x)/600 is significally small already.
At fast image resizers we use very truncated sinc kernels to a few taps (lobes). So when resampler stepping engine make step to next sample there occur real lost of a (part because we upsize and kernel lobes cover a number of output samples i think) of kernel lobe at the very edge of a kernel that was required for good convergence at that output sample. So at each step of resampler core we got ouput edge sample value error.
If image-object data is less in size in compare with 'taps' parameter that cause even forming of 'ghost' parasitic image at 'taps' distance from the object (sometime in many directions - V and/or H).
Loadplugin("ResampleMT.dll")
function Ast2(clip c, int isize)
{
return Subtitle(c, "7",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,200,180,"RGB24",25,color=$00202020)
Animate(last, 0,100,"Ast2", 45, 180)
AddBorders(100,100,100,100,color=$00202020)
SinPowResizeMT(last,width/4,height/4,p=3.1)
SincResize(width*8,height*8,taps=20)
Levels(25, 1, 55, 0, 255)
At animation frame about 40:
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_bug_taps_edge01.png
And the 'ghost' looking almost independent of frequency spectrum of useful data (defined with p-param of SinPowResize()).
The simple but almost non-possible at current computer's performance solution is to make taps > output_image_size/2 (or may be even output_image_size). So we will move our non-converged defective output samples out of valuable image data array.
The other solutions is think how to workaround with current possible very few taps about tenths maximum. May be try to weight edge of kernel to zero starting from for example taps/2 with linear or whatever interpolation function. So loosing part of last kernel lobe will give less errors and may they finally drops lower 8bit precision output at least (for HDR work they will be still visible).
So with SincResize(taps=20) we will have full sinc kernel from tap=0 to tap=10 and fading sinc (like Lanczos) from tap=11 to tap=20.
Or think how to supply resampler with required part of full sinc kernel at edge in other way.
In the above example parasitic 'ghost' non-exist at the left side - and it is close to frame's edge. May be some frame-edge workaround already exist in the resampler code but simply do not work in the middle of frame ? So the bugfix may be simple ?
If we move image data from frame edges at some distance the bug appear as full 4-ghosts form:
Loadplugin("ResampleMT.dll")
function Ast2(clip c, int isize)
{
return Subtitle(c, "7",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,200,180,"RGB24",25,color=$00202020)
Animate(last, 0,100,"Ast2", 45, 180)
AddBorders(150,140,100,100,color=$00202020)
SinPowResizeMT(last,width/4,height/4,p=3.1)
SincResize(width*8,height*8,taps=20)
Levels(25, 1, 55, 0, 255)
At animation frame about 40:
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sinc_resize_bug_taps_edge02.png
The other way of at least V + H separated SincResize for upsampling without this bug is using FFT forward and backward processing. It is not limited with 'taps' parameter and works usually much faster in compare with convolution with sinc kernel when taps increases.
Is there another implementation that does not get affected ? Maybe matlab or something ?
Made another project SincResizeFFT as like 'reference' SincResize based on FFT lib (libfftw3) - https://github.com/DTL2020/SincResizeFFT-master
It works like 'infinite-taps' SincResize and outputs looks like without this bug:
http://drakan.ru/FTProot/vid_work/SincResizeFFT/num_7_out_x8.png
Leveled for better compare:
http://drakan.ru/FTProot/vid_work/SincResizeFFT/sincresize_fft_cmp.png
Ofcourse it is not convolution-based and slow enough and uses tif-file input and output for now. May be ported as plugin for avisynth I think if we have not still fixed existing bug.
With FFT the SincResize processing is very simple because we need not define kernel function as it built-in into FFT math and also (highly ASM-optimized with per-execution hardware self-tuning ability) resampler engine is built-it into FFT library. The plugin acts as simple front-end to underlying FFT library. It just copies FFTed samples from forward-FFT to backward-FFT and zero-extends to desired length. The FFT math performs sinc-interpolation in between samples.
Also newer FFT libs may be GPU-offloaded and not limited with host CPU memory speed. So FFT-based SincResize may have comparable or even better speed in compare with avisynth resampler with large enough taps.
One more example why current V+H resampling processing in avisynth is not completely correct for 2D image data arrays:
It process 'diagonal frequencies' very poor. So to simulate more close to true 2D resize I tried to use 45-degrees rotated second stream.
Loadplugin("ResampleMT.dll")
LoadPlugin("Rotate_x64.dll")
function Ast2(clip c, int isize)
{
return Subtitle(c, "7",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,200,180,"RGB32",25,color=$00202020)
Animate(last, 0,100,"Ast2", 45, 180)
AddBorders(100,100,100,100,color=$00202020)
rotated=Rotate(last,45.0)
nonrot=SinPowResizeMT(last,width/4,height/4,p=2.7)
rotated=SinPowResizeMT(rotated,width/4,height/4,p=2.7)
nonrot=SincResize(nonrot,width*4,height*4,taps=20)
rotated=SincResize(rotated,width*4,height*4,taps=20)
bkrotated=Rotate(rotated,-45.0)
Layer(bkrotated,nonrot,"fast")
http://drakan.ru/FTProot/vid_work/LR_sig_rest/resize_1d_vh_dvh_01.png
Rotate pluging downloaded from avisynth.nl (Rotate_25_26_x86_x64_dll_v1.6.0_20200627) and its processing in avisynth+ looks causes bugs with buffer sizes - it increases in a few times. Also its math is not absolutely correct for sharp images so I start rotated stream from large unconditioned size. Also backward rotation can not be performed good in 1x conditioned size and it performed in SincResized (restored) increased size. The averaging of 2 streams (V+H processed and 45-degree rotated forth and back) with Layer filter looks not very correct idea because it gives only 50% out of each stream processing to output but already shows the difference on 'diagonal frequencies' processing - now we get less 'unprocessed corners' of 2D objects.
As for bug in resampler I think it cause and solution are in small enough C-function resample_functions.cpp: ResamplingProgram* ResamplingFunction::GetResamplingProgram() that creates array of exact kernel values for each output sample of resampler.
If we disable ASM processing in latest 3.6.1 avisynth+ the SincResize only calls 3 functions: kernel-func, GetResamplingProgram func and convolition C-func void resize_h_c_planar() (only H processing - looks 90degrees rotation performs somewhere and it is faster). Convolution func is simple and works equal for all samples across width. So it needed to debug GetResamplingProgram to look how it creates kernels arrays for close to image width and image center to understand where is the difference.
Have finally spend more time with checking this bug in debugger. It looks caused with combined reasons:
1.Too high amplitude of edge lobes of sinc function
2. Too few taps used even for 8bit-coded output.
At the edges if image buffer bug is not exist because current resampling program simply copy 'taps' (or may be 2x taps) number of edge samples of image buffer and supply resampler with static input (the changing is kernel shifted samples) untill it travels on taps (or 2x taps) distance from frame edge. So at that places if we have 'convergible' samples at resampler's input it always output correct. But then convolution 'reading frame' starts to travel from image buffer edge it, at some step, losts for example 1 of 3 input sample required for exact convergence of 3 sinc input_samples-weighted sum and we got an error in output. If this error > 1/255 for 8-bit encoding we got 'ghost' output error and it follows image object at 'taps' distance. The most of other resize kernels are weighted to almost or full zero at the edge of kernel so this error falls below 1/255 and not visible at output.
So I see 2 ways of workarounding:
The bug in C-based integer short 16 bit resampler looks like disappears at taps parameter about 70 or more. Though the processing speed is slow enough ofcourse.
We can try to weight the edge of Sinc kernel so to get bug disappear with lower number of taps (at least with 8bit output samples) while keeping this filter closer to SincResize instead of LanczosResize with similair number of taps. Because LanczosResize is simply Sinc-kernel full-size weighted with ‘Lanczos-weighting’ that is just narrow sinc-too.
I made a tests and found one possible weighting more sharper in compare with ‘full-Lanczos’ — it keeps more Sinc lobes unchanged so gets more sharpness etc. It is simple linear fading starting from 0.5 of kernel width (taps).
So I add one new function SincLin2Resize and it works at 3-sample test and other quick tests without exposing edge-bug with taps as lower as 15. Though for varios cases I increase available ‘taps’ from 20 to 30 if sometime it will be needed. And default taps to 15. Also I think in separate from avisynth plugin we can increase limit of ‘taps’ paramerter from 20 to 70 or more to ‘pure/reference SincResize filter’ so users can test ‘pure/reference SincResize’ with possible workaround using just significant increasing of taps-number though loosing speed processing.
Current sources with x64-build .dll are in
https://drive.google.com/file/d/13KiyDqNEnrOQWCePilHJRvkTh3VoRAAO/view?usp=sharing
Kernel func:
/***********************
*** SincLin2 filter ***
***********************/
SincLin2Filter::SincLin2Filter(int _taps)
{
taps = (double)clamp(_taps, 1, 30);
}
double SincLin2Filter::sinc(double value)
{
if (value > 0.000001)
{
value *= M_PI;
return sin(value) / value;
}
else
{
return 1.0;
}
}
double SincLin2Filter::f(double value)
{
value = fabs(value);
if (value < taps / 2)
return sinc(value);
else
return sinc(value)*((2 - (2 * value / taps)));
}
Header
class SincLin2Filter : public ResamplingFunction
/**
* SincLin2 filter, used in SincLin2Resize
**/
{
public:
SincLin2Filter(int _taps = 15);
double f(double x);
double support() { return taps; };
private:
double sinc(double value);
double taps;
};
In addition to 'ghosting' effect outside object's image the old SincResize has also 'grid' effect at high levels inside object:
Loadplugin("ResampleMT.dll")
function Ast2(clip c, int isize)
{
return Subtitle(c, "7",font="Arial",size=isize,x=10,y=20,halo_color=$FF000000, text_color=$00e0e0e0)
}
BlankClip(100,200,180,"RGB24",25,color=$00202020)
Animate(last, 0,100,"Ast2", 45, 180)
AddBorders(150,140,100,100,color=$00202020)
SinPowResizeMT(last,width/4,height/4,p=3.1)
SincResizeMT(width*8,height*8,taps=20)
Levels(0,0.3,255,0,255)
http://drakan.ru/FTProot/vid_work/LR_sig_rest/sincresize_grid_eff01.png
The new method fixes this too.
DTL
2nd August 2020, 16:24
Oh I just found - the correct (very close to) '2D sinc' is already implemented as JincResize and it gives correct 2D round point from 1point_27_132.bmp and round sinc kernel for 1sample.bmp. Though ofcourse very slow even with taps-equiv around 8. And it looks it have sinc() function also weighted (looks Lanczos-weighted).
So may be will try to ask JincResize filter developer to extend its functionality for 'SinPow' kernel for production/downsize work and extend JincResize to less weighted or unweighted sinc kernel to have '2D/round' versions of SincResize, SincLin2Resize too. Because if that build have already implemented '2D' resampler core it may be easy enough to add more kernels. The JincResize thread is https://forum.doom9.org/showthread.php?t=169813&highlight=JincResize
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.