Log in

View Full Version : Internaly multi-threaded desampling functions (DeBilinear, DeBicubic,...)


jpsdr
1st September 2017, 11:54
I've created a specific thread even if it will be implemented inside the ResampleMT dll, because i've thought it would be more appropriate. And if i didn't create a specific dll, it's because it's exactly the same thing, and just adding around 1% of code add the functions.

Download is on the ResampleMT dll page here (http://forum.doom9.org/showthread.php?p=1776920#post1776920).

The new functions are :
DeBilinearResizeMT
DeBicubicResizeMT
DeLanczosResizeMT
DeLanczos4ResizeMT
DeBlackmanResizeMT
DeSpline16ResizeMT
DeSpline36ResizeMT
DeSpline64ResizeMT
DeGaussResizeMT
DeSincResizeMT
DeSinSqrResizeMT (same parameters than DeGauss)
DeSincLin2ResizeMT (same parameters than Sinc, default param 15)
DeUserDefined2ResizeMT (Same parameters as BicubicResize)

Parameters are identicals to the resampling functions, except there is two others parameters at the end.

accuracy -
Will specify the accuracy used for the desampling.
0 : Average
1 : A little less than 0.
2 : A little better than 0.

Default: 0 (int)

order -
Will specify in what order the desampling will be done (process horizontal or vertical first).
0 : Automatic, will choose according the same method implemented in the core filters.
1 : Process vertical first.
2 : Process horizontal first.

Default: 0 (int)

So, syntax is :
DesampleFunction([parameters of ResampleMT],int accuracy,int order)

The usage is the following : You have to enter in the parameters exactly the same that have been
used for the original resampling, except the size of course, where you specify the original size you want back.

For exemple, if the source file is an 720x480 video :


Spline36ResizeMT(1280,720,src_left=17.2,src_width=680,src_top=-8.7,src_height=440)
# or Spline36Resize(1280,720,src_left=17.2,src_width=680,src_top=-8.7,src_height=440)
# To revert :
DeSpline36ResizeMT(720,480,src_left=17.2,src_width=680,src_top=-8.7,src_height=440,accuracy=0,order=0)


This is intended to be a new version of the actual DeBilinear, working with all data formats of avs+, and having an x64 version. There is no intended link between the parameters of this new version and the old original. If some parameters happen to be identical, it's pure coincidence.


Now, a little explaination of how it works.

I'll note 'A the transpose matrix of A.
If X is your input line to resample, you can write the resampling method like this : Y=A*X.
Using the less square method, you can revert by the following method :
'A*Y='A*A*X
B = inverse_matrix ('A*A)
C = B*'A
We have X=C*Y.
So applying the matrix C, we can revert the resampling process.

Now, if in the A matrix, around 95% are 0.0 value (size of rif resampler is only around 3 or 4), it's absolutely not the same for the C matrix.

For exemple, in a case of reverting a 720 -> 1080 upscale, with an upscale rif filter of 4.
The scan in the lines of resulting matrix C showed that it will produce a rif size filter of 311.

This this where the accuracy parameter is used.

For the default value, 0, it will use what i would call "integer" precision.
In the filter, the integer values of the filter are converted with the following formula : int(0.5+f*16384).
So, if this result in 0, the coefficient in the C matrix is set to 0.0.
In our example, it will produce a rif filter of size 30.

With the value of 1, it will keep a 0.1% of precision. On each line of C, the max(abs(f)) is scanned, and any value lower than 0.1% of this max is set to 0. In our example, it will produce a rif filter of size 20.

With the value of 2, any value within C which abs()<1e-6 is set to 0. In our example case, it will produce a rif filter of size 41.

So, for a 720 data vector input [0, 1, 2, ..., 719] inside, "upscaled" to 1080 values, and then reduce back to 720 values, the results are the following :

accuracy 0 :
[-0.000209 1.000103 1.999724 3.000118 3.999680 5.000134
.... 714.983521 716.005859 716.983643 718.006226 718.985046]

accuracy 1 :
[-0.000512 1.003681 1.999033 3.004512 3.998633 5.005422
... 714.856384 716.323120 716.855835 718.331421 718.868530]
Rounded it's still good.

accuracy 2 :
[-0.000005 1.000017 1.999993 3.000018 3.999994 5.000021
... 714.999573 716.000732 716.999756 718.000977 718.999634]

Now, the order parameter.
This will set if the image of the video will be processed horizontaly first, or verticaly first.
If the value is 0, in automatic mode, the filter will figure out, according the internal avisynth core filter algorithm, in what order the image would have been processed by the original resampling, to do the oposite.
For example, if the original resampling would result in resampling horizontal first, then vertical, the filter in that case will do vertical, then horizontal.
Of course, if original resampling is not done by avisynth, you can't know for sure, this is why you can set the order to the filter, to check what produce the best result.

Quality improvement tip
There is the accuracy parameter, but another way to eventualy improve the quality is to increase the bit depth if you're using avs+. If you are in 8 bits, the resampler compute using integer rif parameters converted from the float rif parameters. But, if you are in bit depth>8, the resampler uses the float coefficients. In desampling cases, where rif size can easely be of 40 or 50, if you're trying to be the most accurate, i would advise to increase the bitdepth to 16 (or even to float if you're searching for ultra accuracy) just for the desampling filter.

WARNING !!!!
The init phase of the filter is very slow, it can take easely several seconds, and the bigger the picture is, the more it will take. If you don't even have SSE2 instructions, it may take several dozen of seconds !

So, in a standard filter chain doing :
Create,Constructor,getframe(n=0),getframe(n=1),getframe(n=2),...destructor, there will be no issue.
Now, if you are using something like ScriptClip, which works like this :
Create,Constructor,getframe(n=0),Destructor,Create,Constructor,getframe(n=1),Destructor,Create,Constructor, getframe(n=2),Destructor,...
It will be slow as hell !!! Avoid absolutely using it in this case.

FranceBB
6th September 2017, 03:52
Works.
Quality-wise it does its job and, although it doesn't use all my cores and threads at 100%, it's really fast (10fps in a 1080 -> 720 reverse upscale using debilinear is quite good).

http://i.imgur.com/XdIe61L.png


FFMpegSource2("GTO (2014) - 02 [1080p].mkv", fpsnum=30000, fpsden=1001, atrack=-1)

DeBilinearResizeMT(1280, 720, threads=8, logicalCores=true, MaxPhysCore=true, SetAffinity=true)

TextSub("GTO_2014_ep2.ass")

ResampleAudio(48000)

Normalize(0.89, show=false)

video=last

FFMpegSource2("logo_finaleGTO.avi", fpsnum=30000, fpsden=1001, atrack=-1)

Converttoyv12()
ResampleAudio(48000)
TextSub("Onizuka Credits.ass")

logo=last

logo++video++logo



The screen of Task Manager Windows is of when it already passed the initial logo part and is encoding the mkv video using debilinear, of course.

jpsdr
6th September 2017, 08:52
For now it works if fabs(shift)<1 and no cropping. Working on to make it works even with things like this (source 720x480) :

Spline36ResizeMT(a,1280,720,src_left=17.2,src_width=680,src_top=-8.7,src_height=440)
DeSpline36ResizeMT(720,480,src_left=17.2,src_width=680,src_top=-8.7,src_height=440)

Expect a new release soon.

jpsdr
7th September 2017, 19:02
New release, now Desample works with high shift value and/or croped on origin.

real.finder
3rd November 2017, 05:25
tried to play with it but I think there are some bug!


some SD source here
w=Width
h=Height
IResize(1080,1440,resifilter="BilinearResizeMT")
IResize(w,h,resifilter="DeBilinearResizeMT")


you may see this strange but there are many sources that resized in interlaced (like FAIRY TAIL S1 anime TV)

even some interlaced bluray (seen in some movie of detective conan for example)

edit: tried with dither_resize16

w=Width
h=Height
IResize(1080,1440,resifilter="resizex",res_params=""",lsb=true,kernel="Bilinear"""")
IResize(w,h,resifilter="resizex",res_params=""",lsb=true,kernel="Bilinear",dither_params=",invks=true"""")

and it work fine, Debilinear by Prunedtree work too, but it need to run the script in avs2.5 since Debilinear support only yv12 and rgb, so the problem in Desample

edit: now it work with Desample, forget about old resizex and IResize above (http://forum.doom9.org/showthread.php?t=174121)

and even with resizex (Important if you work with 422 and 411 sources cuz there are some chroma shift, but don't worry when you work with 420 and 444 of even Y*)

IResize(720,480,resifilter="resizex",res_params=""",desampling=true,kernel="Bilinear"""")

jpsdr
3rd November 2017, 10:58
I think the issue comes from the fact the it resizes splitting planes and shifting chroma according resize size.
As i said in the 1rst post :

The usage is the following : You have to enter in the parameters exactly the same that have been
used for the original resampling, except the size of course, where you specify the original size you want back.

And your IResize is messing this requirement, putting different src_left on resampling and desampling phase.
See the example to look how exactly how it has to be done.
I've never said the filter behave exactly like "old/original" desampler, because i don't have the code and the author gave me only the global description of the algorithm, and i don't know how it handles shift parameters, but i will not change what i've done in my filter.
The way IResize works, or you use it, is not compatible with the way my desample works. You have either to change adapt the IResize, or manualy set the shift parameters, or use it differently. I don't look deep enough to say what exactly is the correct answer.

My desample will try to retreive the original before resampling, it will not do any shift, if you want to to shift, you have to do it afterward. For a desample to do shift, it would need "2" shift parameters instead of "1". One for the shift used in original resampling, one for the shift you want to produce afterward. "Original" desampler didn't have the shift paramaters doubled, neither mine. I choose to consider that the shift parameter you put is the one you've put in the original resampler, and not a shift you want to produce afterward, otherwise, it means you'll be unable to desample a resampling with shift, if you're unable to provide the original shift information to your desample.

real.finder
3rd November 2017, 18:36
My desample will try to retreive the original before resampling, it will not do any shift, if you want to to shift, you have to do it afterward. For a desample to do shift, it would need "2" shift parameters instead of "1". One for the shift used in original resampling, one for the shift you want to produce afterward. "Original" desampler didn't have the shift paramaters doubled, neither mine. I choose to consider that the shift parameter you put is the one you've put in the original resampler, and not a shift you want to produce afterward, otherwise, it means you'll be unable to desample a resampling with shift, if you're unable to provide the original shift information to your desample.

yes, the "it will not do any shift" is why it not work correctly

anyway IResize do the shift in fields, it needed for resize interlaced sources, without shift the output will be wrong

jpsdr
6th November 2017, 10:45
So, if "original" version can do shift, it means it's not able to properly desample shifted resampled video. Because the shift parameter is used like the shift parameter in the original resample to shift the video, and not as an information of the shift value in the original resampler.
So, even if with the "original" desampler the little 2 lines script seems to work, i think it's not also working realy properly, because the desampling phase doesn't know the shift value used in the sampling phase.

Honestly, i don't think you can use IResize for both sampling and desampling with real proper result. You need to create a specific IDesize, where you'll add another shift parameter which is the shift value used in the resampling phase, or, automaticaly compute what you think the shift value in the previous resampling phase may have been to properly desample, and then, after desampling and retrieve the real proper original picture, before the shift&resample, add another shift if you want.

All i can say, it's if you're doing this :

a=SD Video
w=a.Width()
h=a.Height()
b=BilinearResize(a,1080,1440,src_left=0.5) (or MT, whatever)
c=DeBilinearResizeMT(b,w,h,src_left=0.5)

You'll have c=a.
If you change the value of src_left, you'll have improper result.

If you want after to shift the result, you'll have to do :

a=SD Video
w=a.Width()
h=a.Height()
b=BilinearResize(a,1080,1440,src_left=0.5) (or MT, whatever)
c=DeBilinearResizeMT(b,w,h,src_left=0.5)
BilinearResize(c,c.Width(),c.Height(),src_left="the shift you want")


Warning : If in the resample phase, you'll have high (>1) src_left shift value for exemple, you'll get c.Witdh()<w ! Because i can't do magic and retreive the croped/discarded pixels.

real.finder
6th November 2017, 15:08
well, I already did some edit (https://pastebin.com/LkhCCnbR) for that, even did support resizex (https://pastebin.com/kM1mip5n) case, the result is better now but not perfect

jpsdr
6th November 2017, 15:54
Can you tell/describe me what you want to do exactly, with a precise specific exemple ?
(Like : I have a 1080i upsample video i want do desample to 520i, assuming the resampling have been done using xxxx).
I think you use IResize for the chroma resize shift adjustment, but, just assume for one moment, we ignore this, and don't try to correct/this issue, and don't split planes.
With just Bilinear and Debilinear, what would be your script ? (But maybe before doing the script, the description would be enough...).

Maybe with the description, i could be able to make a script (even if specific) which will do what you want to do ?

real.finder
6th November 2017, 20:50
here the original post by IanB https://forum.doom9.org/showpost.php?p=1173977&postcount=35

and here Gavino one https://forum.doom9.org/showpost.php?p=1185790&postcount=51

yes, lets say we have 1080i but the original of it was in SD (480i or 520i) and they did upscale (resize) it to 1080i without deinterlacing (VirtualDub has an option for that for example)

jpsdr
7th November 2017, 09:58
By resize without deinterlacing, you mean they resize each field separetely. It's at my request that Avery Lee added the option on VDub, but he "refused" to make it work on YV12, because he "stuck" on the fact that YV12 is only progressive by format definition/specification, so if you check the interlaced box in resize filter on YV12 video, it automaticaly converts to YV16... :(
I'll think/work on something later during my lunch break for a script.

jpsdr
7th November 2017, 13:43
Ok, if i didn't make any mistakes and understood properly :

function IBilinearYV12(clip c,int NewWidth,int NewHeight)
{
Wr=Float(NewWidth)
Hr=Float(NewHeight)/2.0
v=SeparateFields(c)
W0=Float(v.Width())
H0=Float(v.Height())
C_Shift=0.25*(1.0-W0/Wr)
V_Shift=(GetParity(v) ? -0.25 : 0.25)*((H0/Hr)-1.0)

Video=SelectEven(v)
Ye=ConvertToY8(Video).BilinearResizeMT(NewWidth,NewHeight/2,src_top=V_Shift)
Ue=UToY8(Video).BilinearResizeMT(NewWidth/2,NewHeight/4,src_top=V_Shift,src_left=C_Shift)
Ve=VToY8(Video).BilinearResizeMT(NewWidth/2,NewHeight/4,src_top=V_Shift,src_left=C_Shift)

Video=SelectOdd(v)
Yo=ConvertToY8(Video).BilinearResizeMT(NewWidth,NewHeight/2,src_top=-V_Shift)
Uo=UToY8(Video).BilinearResizeMT(NewWidth/2,NewHeight/4,src_top=-V_Shift,src_left=C_Shift)
Vo=VToY8(Video).BilinearResizeMT(NewWidth/2,NewHeight/4,src_top=-V_Shift,src_left=C_Shift)

Y=Interleave(Ye,Yo)
U=Interleave(Ue,Uo)
V=Interleave(Ve,Vo)

YToUV(U,V,Y).Weave()
}


function IDeBilinearYV12(clip c,int OriginalWidth,int OriginalHeight,int "order")
{
order=default(order,0)

W0=Float(OriginalWidth)
H0=Float(OriginalHeight)/2.0
v=SeparateFields(c)
Wr=Float(v.Width())
Hr=Float(v.Height())
Original_C_Shift=0.25*(1.0-W0/Wr)
Original_V_Shift=(GetParity(v) ? -0.25 : 0.25)*((H0/Hr)-1.0)

Video=SelectEven(v)
Ye=ConvertToY8(Video).DeBilinearResizeMT(OriginalWidth,OriginalHeight/2,src_top=Original_V_Shift,order=order)
Ue=UToY8(Video).DeBilinearResizeMT(OriginalWidth/2,OriginalHeight/4,src_top=Original_V_Shift,src_left=Original_C_Shift,order=order)
Ve=VToY8(Video).DeBilinearResizeMT(OriginalWidth/2,OriginalHeight/4,src_top=Original_V_Shift,src_left=Original_C_Shift,order=order)

Video=SelectOdd(v)
Yo=ConvertToY8(Video).DeBilinearResizeMT(OriginalWidth,OriginalHeight/2,src_top=-Original_V_Shift)
Uo=UToY8(Video).DeBilinearResizeMT(OriginalWidth/2,OriginalHeight/4,src_top=-Original_V_Shift,src_left=Original_C_Shift,order=order)
Vo=VToY8(Video).DeBilinearResizeMT(OriginalWidth/2,OriginalHeight/4,src_top=-Original_V_Shift,src_left=Original_C_Shift,order=order)

Y=Interleave(Ye,Yo)
U=Interleave(Ue,Uo)
V=Interleave(Ve,Vo)

YToUV(U,V,Y).Weave()
}


a=AVISource("Your_720x480i_video.avi",FALSE,"YV12")
b=IBilinearYV12(a,1920,1080)
IDeBilinearYV12(b,720,480)


Warning : You're assuming that the video has been upscaling using a specific method, a specific resizer and from YV12.
I don't know what format professionnals use as master and work with. Theres is also possibility that master was 4:4:4 or 4:2:2, upscaled, and ther afterward converted to YV12.

So to figure out the best desampling option, a lot of tests and investigations has to be done, considering a lot of possibility of "upscaling method" (with and without shift when upscaling, play with the order parameter of Debilinear, upscale was done on 4:4:4 and then converted to YV12, etc...)

real.finder
7th November 2017, 16:03
yes, it work now, I was think of I must did some reverse in shift, thanks to your editions of IResize give me the incentive after test your script

and now it even successfully work with FAIRY TAIL S1 anime TV sample I have :)

IResize(720,480,resifilter="resizex",res_params=""",desampling=true,kernel="Bilinear"""")

the links of new edit in the bottom part of same post https://forum.doom9.org/showpost.php?p=1823545&postcount=5

real.finder
3rd May 2018, 01:22
can these desampling functions used in undownscale nowdays DVD anime OVA/OAD that has halos? Of course we can't get the details back, but I ask for this to dehalo these sources in more details and more clean output than old dehalo methods

jpsdr
3rd May 2018, 11:46
I don't know if it will work.
You can reverse upscale, because mathematicaly, in a certain way, you kept all the informations. Basicaly, you added pixels, so you can say that you've "only" added information, and all the original information may still be retrieved.
Downscale, basicaly you remove pixels, so information is loss.
I was even thinking of forbid reverse downscale, but it wasn't realy easy to make accurate test, because of possible akward croping cases. I thought i add, but a quick look on the code, it seems i didn't.
You still can try, but you may encounter cases of matrix not being reversible, and i don't know what it can produce (well case of matrix not reversible should theoricaly be handled and produce an error message). But if by any luck it produces good result...

real.finder
24th March 2021, 23:36
You still can try, but you may encounter cases of matrix not being reversible, and i don't know what it can produce (well case of matrix not reversible should theoricaly be handled and produce an error message). But if by any luck it produces good result...

it give
https://i.postimg.cc/nc3VcNLr/New-File-5-002983.png (https://postimages.org/)

I did some test with this

https://i.postimg.cc/qt35GMRd/Annotation-2021-03-25-040959.png (https://postimg.cc/qt35GMRd)


yahr2

https://i.postimg.cc/ZCLxzZr8/Annotation-2021-03-25-013222.png (https://postimg.cc/ZCLxzZr8)



Dither_convert_8_to_16()
Dither_resize16(854,Height()/2,invksh=true, kernel="spline36")
Dither_resize16(720,480, kernel="bilinear")
Ditherpost()


https://i.postimg.cc/Mcn7BQg5/Annotation-2021-03-25-013258.png (https://postimg.cc/Mcn7BQg5)

it seems do something

tormento
16th February 2025, 20:40
I have a PQ UHD with chroma subsampling 4:2:0 type 2 and it is a bilinear resize from 1080p.

Is

DeBicubicResizeMT(1920, 1080, threads=1, prefetch=2, accuracy=2, src_left=-0.50, src_top=0)

correct or have I to choose different values for src_left, src_top?

Is there any "formula" to know exactly what they should be, according to original and wanted desampled size?

jpsdr
17th February 2025, 18:57
If it's bilinear resize, you have to use DeBilinear, not DeBicubic.
I don't know what src_left/top you have to use, as i don't know what values were used for doing the upscaling.
If there is any formula, i don't know what it is.

tormento
17th February 2025, 19:09
If it's bilinear resize, you have to use DeBilinear, not DeBicubic.
That was a wrong copy and paste. :p
I don't know what src_left/top you have to use, as i don't know what values were used for doing the upscaling.
So, if we have some upscaled material, given that we find the correct native resolution, there is no way to guess them?

jpsdr
17th February 2025, 19:14
If there is a way, i don't know it.

FranceBB
18th February 2025, 05:40
I'm gonna try to elaborate, but the reason why Tormento and I wrote here is that we both have a doubt. So, when I have a 4:2:0 3840x2160 file I have 3840x2160 luma and 1920x1080 chroma which means that I can downscale the luma to 1920x1080 while preserving the chroma to get a 4:4:4 1920x1080 output. When the source chroma location is "left" (i.e the old MPEG-2 chroma placement, or standard 4:2:0) I just need to adjust by -0.5 on the left like this:

Y = ConvertToY8(matrix="Rec709").DeBilinearResizeMT(1920, 1080, src_left=-0.50, src_top=0)
U = UToY8()
V = VToY8()
YToUV(U, V, Y)

however if my source is a classic BT2020 HDR PQ stream (i.e HDR10) and has a "top left" chroma placement (i.e 4:2:0 Type 2) I don't need to do that and I can just do:

Y = ConvertToY8(matrix="Rec2020").DeBilinearResizeMT(1920, 1080, src_left=0, src_top=0)
U = UToY8()
V = VToY8()
YToUV(U, V, Y)


In this latter case I have src_left=0 and src_top=0. Our line of reasoning here is that while in the left chroma placement (MPEG-2, normal 4:2:0) the chroma blocks are positioned at the left edge of the corresponding 2x2 luma block and need to be shifted by half a sample to the left, in the new top left chroma placement (H.265, 4:2:0 Type 2) the samples are already aligned and when I'm downscaling the luma they land in the top left corner. Like, in our heads, if we had an imaginary checkboard with the squares being the luma samples, in the top_left alignment the chroma would be a dot sitting at the top left, so when downscaling the luma it would still be sitting at the top left of the checkboard, in other words it would still be in the same position compared to the corresponding 2x2 luma blocks even though the squares are now smaller, however with the classic left alignment the chroma would be a dot sitting on the left edge of the checkboard made by imaginary luma samples and therefore when we're downscaling it's no longer aligned, hence the need for the shift.


Are we correct or are we talking nonsense?

tormento
18th February 2025, 19:52
When the source chroma location is "left" I just need to adjust by -0.5 on the left
Is that -0.5 resolution dependent? Is there any way to compute it, based on original and final resolution and/or chroma placement?

jpsdr
18th February 2025, 20:00
The "issue" with the Desample, if i remember my tests, it produces "perfect" results if you're you're reversing exactly what have been done => You get back exactly the original, otherwise, it's not better than just resampling.

If for chroma "left", the luma have been upscaled doing Bilinear(3840,2160, src_left=-0.50, src_top=0), it's correct, otherwise, it's not.
The Desampling function are just trying to revert a matrix computation. When resampling (up or down), you can have this kind chroma/placement thought. But the Desampling is not a resampling function, it's a revert matrix function.

You've done a processing, resulting =[M]*[A], i'm computing [M-1] to get back [A]=[M-1]*[B]. (Read again explaination of 1rst post).
If you're using or seeing the Desampling function like a resampler, and so begin to think of chroma placement, you're wrong.

Edit:
@[B]tormento
http://forum.doom9.org/showthread.php?t=170029
http://forum.doom9.org/showpost.php?p=1506374&postcount=385
http://forum.doom9.org/showpost.php?p=1705237&postcount=34

tormento
18th February 2025, 20:52
http://forum.doom9.org/showpost.php?p=1705237&postcount=34
I was looking at your last link, where it states:

Y = ConvertToY8().Spline16Resize(704, 576)
U = UToY8().Spline16Resize(Width(Y)/2, Height(Y)/2, src_left=0.25*(1-Float(Width(Last))/Width(Y)))
V = VToY8().Spline16Resize(Width(Y)/2, Height(Y)/2, src_left=0.25*(1-Float(Width(Last))/Width(Y)))
YToUV(U, V, Y)

For a very simple luma descale (1080p) to chroma resolution (540p), would it be

Y = ConvertToY(matrix="Rec709").DeBilinearResizeMT(960, 540, threads=1, prefetch=2, accuracy=2)
U = UToY()
V = VToY()
YToUV(U, V, Y)

or

Y = ConvertToY(matrix="Rec709").DeBilinearResizeMT(960, 540, threads=1, prefetch=2, accuracy=2, src_left=-0.25)
U = UToY()
V = VToY()
YToUV(U, V, Y)

or

Y = ConvertToY(matrix="Rec709").DeBilinearResizeMT(960, 540, threads=1, prefetch=2, accuracy=2, src_left=-0.5)
U = UToY()
V = VToY()
YToUV(U, V, Y)

?

jpsdr
18th February 2025, 21:18
It seems you have hard time to understand, or i don't explain properly.

Assuming s=video(960x540).
If your upscale is the following :
Y=ConvertToY8().Bilinear(1920,1080)
U=UToY8().Bilinear(Width(Y)/2, Height(Y)/2, src_left=0.25*(1-Float(Width(Last))/Width(Y)))
V=VToY8().Bilinear(Width(Y)/2, Height(Y)/2, src_left=0.25*(1-Float(Width(Last))/Width(Y)))
YToUV(U,V,Y)

As no src_xxx has been used on luma, to Desample you have to do Debilinear(960,540) without any src_xxx value.
Adding src_xxx on Desampling is incorrect and will produce wrong result on Desampling. Again, you MUSN'T see Desampling like a resampler.
But... Your final result will be as if you have done :
Y=ConvertToY8() (nothing, as upscale has been reverted)
U=UToY8().Bilinear(1920/2, 1080/2, src_left=0.25*(1-Float(960)/1920))
V=VToY8().Bilinear(Width(Y)/2, Height(Y)/2, src_left=0.25*(1-Float(960)/1920))
YToUV(U,V,Y)

This is not resulting in a constant shift of 0.25 as it should be for 4:4:4 upscale, according the post.

tormento
12th November 2025, 19:34
Please, consider adding border_handling from vapoursynth-descale (https://github.com/Jaded-Encoding-Thaumaturgy/vapoursynth-descale) and the two missing kernels too (depoint, custom).

jpsdr
13th November 2025, 18:49
Custom forget it, from what i saw, it needs to implement a formula syntax analysis !!!!! I don't even know if it's possible with avisynth, and totaly incompatible with the current structure filter.
Depoint..??? If you have upscaled with point resize, just downscale with point resize, no need to implement this, so i won't.
I don't understand what border_handling is at first glance with just the description (maybe DTL has an idea of what it is), it will need a code reverse analysis to understand it. So this one, maybe.
Anyway, for now, i don't intend to work on it.

Jamaika
13th November 2025, 22:31
I don't know if it works. Spline16Resize(1280, 720, border_handling=2)
https://www.sendspace.com/file/qm5cqi

I'm adding open source. Any corrections to my errors are yours to make. This is a very old project being improved by Asd-g. https://github.com/Asd-g/vapoursynth-descale
https://www.sendspace.com/file/rkti04

DTL
13th November 2025, 22:54
There is a description -

The border_handling argument can take the following values:

0: Assume the image was resized with mirror padding
1: Assume the image was resized with zero padding
2: Assume the image was resized with extend padding, where the outermost row was extended infinitely far

In current AVS+ resizers (and also jpsdr ResizeMT() ?) it looks we use method 2. It is all about edge conditions handling in resampling program generation at - https://github.com/jpsdr/ResampleMT/blob/1ac80bdcf1a47a8bf2ee9af13173bd952c8df808/ResampleMT/resample_functions.cpp#L577 .

Method 2 looks like create lowest possible discontinuity (and so lowest ringing at edges with ringing kernels). But some resize engines may use different methods of edge discontinuity handling like 1 or 2.

jpsdr
15th November 2025, 00:14
I don't know how to make this border_handling as it doesn't exist on avisynth, there is only one way to compute coeff in avisynth as DTL said, so there is only one method.

orchid
15th November 2025, 04:40
Custom forget it, from what i saw, it needs to implement a formula syntax analysis !!!!! I don't even know if it's possible with avisynth, and totaly incompatible with the current structure filter.
Depoint..??? If you have upscaled with point resize, just downscale with point resize, no need to implement this, so i won't.
I don't understand what border_handling is at first glance with just the description (maybe DTL has an idea of what it is), it will need a code reverse analysis to understand it. So this one, maybe.
Anyway, for now, i don't intend to work on it.

Depoint is equivalent to downscaling with a box filter, so downscaling with point isn't equivalent.