Log in

View Full Version : resize.Point transfer function


Cary Knoop
1st September 2020, 17:54
This should be virtually lossless for YUV444PS clips, right?

c = core.resize.Point(c, transfer_in_s='709', transfer_s='linear')
c = core.resize.Point(c, transfer_in_s='linear', transfer_s='709')

I notice a shift.

Original frame:
https://www.dropbox.com/s/jb5y1f8r3hgdeuu/original.png?dl=0
Difference:
https://www.dropbox.com/s/gjiw54buds51hdv/diff.png?dl=0

poisondeathray
1st September 2020, 20:01
You can use full range going to /from YUV444PS , with the transfer steps in between

range_in_s="full", range_s="full"

Afterwards, you can mark it as limited range
c = core.std.SetFrameProp(c, prop="_ColorRange", intval=1) # to mark as limited range

Cary Knoop
1st September 2020, 20:06
You can full range going to /from YUV444PS

range_in_s="full", range_s="full"

SetFrameProp to mark as limited range afterwards
Full and limited range, how is that interpreted for floats in Vapoursynth?
I am used to understanding 0.0f as the 1.0f as the visible range for video processing regardless whether it is imported as full or limited while <0.0f and >1.0f values are never clipped.

I convert fixed-bit video levels as:
c = core.fmtc.bitdepth(c, fulls=False, bits=32, dmode=7)

I expected the video range to be mapped to 0.0f and 1.0f without any clipping (so we could have <0.0f and >1.0f values (WFT, BTB) after this conversion)

And data levels as:
c = core.fmtc.bitdepth(c, fulls=True, bits=32, dmode=7)

I expected the full range also to be mapped to 0.0f and 1.0f without any clipping (< 0.0f and > 1.0f values are impossible after this conversion).

So that is incorrect? That is how it works for most tools in the industry.

Also, float operations should never clip, right?

Cary Knoop
1st September 2020, 20:23
Transfer functions are evaluated through a LUT, so even on FP32 input, the transform will be of lower precision. You can set the CPU mode to "none" and that will force the gamma equations to actually be evaluated, but it will be extremely slow.
A LUT? Seriously?
That really should be documented:
http://www.vapoursynth.com/doc/functions/resize.html

I disagree that those calculations are "extremely slow" unless you have a computer from the previous century.

So I set cpu_type=None for using a float32 calculation?

How about the transfer functions in the fmtconv package, the same thing?

Added: I just tested the fmrtconv the same problem.

poisondeathray
1st September 2020, 20:26
Actually, earlier I tried both core.std.SetMaxCPU("none") globally, and cpu_type="none" at each step, and there was still a difference until using full range. Tested on 10 bit colorbars as the starting source


core.std.SetMaxCPU("none")
c = core.colorbars.ColorBars(format=vs.YUV444P10)

c1 = core.resize.Point(c, format=vs.YUV444PS, matrix_in_s="709", cpu_type="none")
c2 = core.resize.Point(c1, transfer_in_s='709', transfer_s='linear', cpu_type="none")
c3 = core.resize.Point(c2, transfer_in_s='linear', transfer_s='709', cpu_type="none")
c4 = core.resize.Point(c3, format=vs.YUV444P10, matrix_s="709", cpu_type="none")
c4 = core.std.SetFrameProp(c4, prop="_ColorRange", intval=1) #mark it as limited

d = core.std.MakeDiff(c, c4)
da = core.std.Levels(d, min_in=511, max_in=513, gamma=1, min_out=0, max_out=1023, planes=[0,1,2])

da.set_output()

Cary Knoop
1st September 2020, 20:38
So what is the model for float32 in Vapoursynth?

Most industry video processing software does away with the full versus limited distinction once clips are converted to floats.
0.0f to 1.0f simply means the visible range.

Is this not the case in Vapoursynth?
So a limited range is just scaled within a 0.0f to 1.0f range where the visible range is >> 0.1f and <<1.0f?

Cary Knoop
1st September 2020, 21:06
Floating-point 0 is black, and 1 is white. BTB and WTW are <0 and >1. If the clip has BTB/WTW, then even with direct-evaluation, there will be some error in the round-trip, because the gamma functions are only defined from 0 to 1.
Ok, so to confirm the model that Vapoursynth uses:

Both limited and full range get mapped between 0.0f and 1.0f for float32, where 0.0f is black and 1.0f is white.

So only when converting from fixed-bit to float32 you only need to specify whether the source is full or limited while converting from float32 to fixed-bit you only need to specify whether the destination is full or limited.
While for float to float functions there is never any need to specify full or limited because it is not relevant.

Is that correct?

Cary Knoop
2nd September 2020, 02:59
So I can confirm the difference was in the transients (WFT).

How do you folks handle a gracefull rolloff of the WTWs?
Is there a filter?

In CUDA I use this (with p being an RGB component, with max - mar_r being the rolloff region:

__DEVICE__ inline float rolloff (float p, float max, float max_r)
{
if (p <= max - max_r)
return p;
else if (p >= max + max_r)
return max;
else
return -1/(4*max_r)*(p*p - 2*(max + max_r)*p + (max - max_r)*(max - max_r));


in Vapoursynth could I use exp() for this using reverse polish?
I have not used exp() before, how does it handle the conditionals?

age
2nd September 2020, 17:21
This should works

def highlights_rolloff(clip="",max="",max_r="" ) :
c=clip
r="-1 4 {max_r} * / x x * {max} {max_r} + 2 * x * - {max} {max_r} - {max} {max_r} - * + *".format(max=max,max_r=max_r)
c=core.std.Expr(c, expr=" x {max} {max_r} - <= x x {max} {max_r} + >= {max} {r} ? ? ".format(max=max,max_r=max_r,r=r))
return c
c=highlights_rolloff(clip=c,max=1.08510638298,max_r=0.385)


Is (max - max_r ) the knee point?

Could I ask where this formula comes from and what should be the correct parameters?

Cary Knoop
2nd September 2020, 19:16
Thanks for the moving the function to expr()!


Is (max - max_r ) the knee point?

Yes, it's the point where the roll-off starts.


Could I ask where this formula comes from and what should be the correct parameters?
It's old, I honestly do not remember where I got it from. But I do remember it is open source.

It works great, I use it in DaVinci Resolve (although Resolve has it own roll-off function this one is a bit more flexible).

I often combine it with a "bend" function (but be careful using exponential functions if you can have negative code values!)


__DEVICE__ inline float bend (float p, float t)
{
float adjust = 0.5f - 0.5f/_powf(1.5f, t);
if (t != 0.0f)
return p * 1.0f/_powf(p + 1.0f, t) + adjust;
else
return p;
}


It would be great if Vapoursynth would support a CUDA or OpenCL (in case of a non-open software allergy) precompiler so we can use a C++ (or Python) function to use the GPU for those operations.

All we need is a call interface specification obtaining:

- The current RGB pixel structure
- The current X, Y position in the frame
- A function call to get any other RGB pixel structure using X, Y position in the frame
- A (recursive) next->, and prior-> frame pointer to get pixels form nearby frames to support temporal operations.
- Info on the width and height of the frame