Log in

View Full Version : HDR10 to SDR with Hable tone-mapping


Pages : [1] 2

age
12th March 2017, 17:12
Hi!
This is a basic script for hdr to sdr conversion, I'm not sure i've used the hable tonemapping in the right way.
it's a tone mapping operator used for videogames but it seems good even for videos.

import vapoursynth as vs


core = vs.get_core()
c = core.ffms2.Source(source = 'path/to/file')

source_peak=1200 #set manually


c=core.resize.Bilinear(clip=c, format=vs.YUV444PS, range_in_s="limited", range_s="full",chromaloc_in_s="center",dither_type="none")

c=core.resize.Bilinear(clip=c, format=vs.RGBS, matrix_in_s="2020ncl", range_in_s="full",dither_type="none")

c=core.resize.Bilinear(clip=c, format=vs.RGBS, transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=source_peak)


exposure_bias=source_peak/100

#hable/uncharted tone mapping
tm = core.std.Expr(c, expr="x {exposure_bias} * 0.15 x {exposure_bias} * * 0.05 + * 0.004 + x {exposure_bias} * 0.15 x {exposure_bias} * * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits

w = core.std.Expr(c, expr="{exposure_bias} 0.15 {exposure_bias} * 0.05 + * 0.004 + {exposure_bias} 0.15 {exposure_bias} * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#


c = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)

#c=core.fmtc.primaries(clip=c, prims="2020", primd="709")
c=core.resize.Bilinear(clip=c, format=vs.RGBS, primaries_in_s="2020", primaries_s="709",dither_type="none")
c=core.resize.Bilinear(clip=c, format=vs.RGBS, transfer_in_s="linear", transfer_s="709",dither_type="none")

c=core.resize.Bilinear(clip=c, format=vs.YUV420P8, matrix_s="709", range_in_s="full",range_s="limited",chromaloc_in_s="center")


c.set_output()


HDR10 is generally mastered with with a peak brightness of 1000,1200,1500 nits and it corrisponds with the parameter in the script "source_peak".

I only have some hdr video sample downloaded so I can't test exhaustively this script, but it produces reasonably good output.

There's at least one issue:
I use the resizer in the core for conversion from bt.2020 to bt.709 and from the github
"Note that "z" is not a color management system and should not be used to perform drastic contrast or gamut reduction, such as BT.2020 to BT.709."


Screenshot before/after:
http://screenshotcomparison.com/comparison/203274

http://screenshotcomparison.com/comparison/203275

http://screenshotcomparison.com/comparison/203276

Cary Knoop
12th March 2017, 18:11
Super!

One quick question, going from full to limited range is designed to capture out of range values (WTW and BTB) and bring them into legal or is the script designed for full range video to be converted to limited range?
Most video obviously uses limited range with the exception of 4:4:4 sources.


It would be great to have an "up" conversion from SDR to HDR as well.

sneaker_ger
12th March 2017, 18:18
Neither. Both input of first filter as well as output of last filter are limited range.

I wonder if and how to handle e.g. color coordinates. The script only asks for peak luminance while HDR(10) metadata looks something like this:
| + Video colour information
| + Colour matrix: 9
| + Colour range: 1
| + Colour transfer: 16
| + Colour primaries: 9
| + Video colour mastering metadata
| + Max luminance: 1000
| + Min luminance: 0.001
| + Red colour coordinate x: 0.68
| + Red colour coordinate y: 0.31996
| + Green colour coordinate x: 0.26494
| + Green colour coordinate y: 0.68996
| + Blue colour coordinate x: 0.15
| + Blue colour coordinate y: 0.05998
| + White colour coordinate x: 0.3127
| + White colour coordinate y: 0.32896
Do only the player and/or display need these? In fmtc.primaries you can supply such coordinates.

age
12th March 2017, 18:26
Neither. Both input of first filter as well as output of last filter are limited range.

I wonder if and how to handle e.g. color coordinates. The script only asks for peak luminance while HDR(10) metadata looks something like this:
| + Video colour information
| + Colour matrix: 9
| + Colour range: 1
| + Colour transfer: 16
| + Colour primaries: 9
| + Video colour mastering metadata
| + Max luminance: 1000
| + Min luminance: 0.001
| + Red colour coordinate x: 0.68
| + Red colour coordinate y: 0.31996
| + Green colour coordinate x: 0.26494
| + Green colour coordinate y: 0.68996
| + Blue colour coordinate x: 0.15
| + Blue colour coordinate y: 0.05998
| + White colour coordinate x: 0.3127
| + White colour coordinate y: 0.32896
Do only the player and/or display need these? In fmtc.primaries you can supply such coordinates.

Max luminance should be the right value for "source_peak"

Bt2020 is the standard for hdr10

Maybe i can try to pass all the info from the metadata

Cary Knoop
12th March 2017, 18:33
Neither. Both input of first filter as well as output of last filter are limited range.

I got it, RGB processing is full while in and out is limited.

age
12th March 2017, 18:35
Right :-)


I think tone mapping is needed for hdr to sdr conversion,
for sdr to hdr up conversion is needed an inverse tone mapping algo but i can't find such algorithm on google.

zub35
13th March 2017, 01:46
I only have some hdr video sample downloaded so I can't test exhaustively this script, but it produces reasonably good output.

more samples SDR and HDR :
https://cloud.mail.ru/public/2iEk/CiyUARGZy

kolak
14th March 2017, 12:09
Hi!
This is a basic script for hdr to sdr conversion, I'm not sure i've used the hable tonemapping in the right way.
it's a tone mapping operator used for videogames but it seems good even for videos.

HDR10 is generally mastered with with a peak brightness of 1000,1200,1500 nits and it corrisponds with the parameter in the script "source_peak".

I only have some hdr video sample downloaded so I can't test exhaustively this script, but it produces reasonably good output.

There's at least one issue:
I use the resizer in the core for conversion from bt.2020 to bt.709 and from the github
"Note that "z" is not a color management system and should not be used to perform drastic contrast or gamut reduction, such as BT.2020 to BT.709."


Screenshot before/after:
http://screenshotcomparison.com/comparison/203274

http://screenshotcomparison.com/comparison/203275

http://screenshotcomparison.com/comparison/203276

Have a look here:
http://www.mysterybox.us/blog/2016/10/27/hdr-video-part-5-grading-mastering-and-delivering-hdr

and than use free Resolve and compare your result to Resolve.

kolak
14th March 2017, 12:12
Right :-)


I think tone mapping is needed for hdr to sdr conversion,
for sdr to hdr up conversion is needed an inverse tone mapping algo but i can't find such algorithm on google.

Dolby and Technicolor are working on such a algorithms with adaptive per scene analysis.

MadVR does it quite well, can you still some math from there?

kolak
14th March 2017, 13:22
Max luminance should be the right value for "source_peak"

Bt2020 is the standard for hdr10

Maybe i can try to pass all the info from the metadata

I assume fact that these videos are graded to P3 gamut doesn't change anything here as it's within Bt2020.

CruNcher
15th March 2017, 01:31
Looks nice at least not like such a over saturated mess ;)

https://www.youtube.com/watch?v=vI91heLv_v4

sorry i know it gets old but i find this a master example of how your HDR->SDR tone mapping shouldn't endup ;)

You should also try

https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/

kolak
15th March 2017, 11:23
This sample is terrible.

age
15th March 2017, 13:02
Thank you all for your suggestion and the hdr samples.
Well I've discovered some interesting things....
First, a better version of the hable tonemapping that gives better contrast,brightness and saturation.
http://screenshotcomparison.com/comparison/203550


I've tried different algorithms, the aces tonemapping too but I don't like it very much.
Here a good link for a lot of open source tm operators :

https://github.com/tizian/tonemapper/tree/master/src/operators

And a different version of aces:

https://github.com/keijiro/PostProcessingUtilities/blob/master/Assets/PostProcessing%20Imported/Resources/Shaders/Tonemapping.cginc


new script with correct parameters

import vapoursynth as vs


core = vs.get_core()
c = core.ffms2.Source(source = 'C:/bla')

source_peak=1000 #set manually
LDR_nits=100 #set 150 or 200 for lowering the brightness

c=core.resize.Bilinear(clip=c, format=vs.YUV444PS, range_in_s="limited", range_s="full",chromaloc_in_s="center",dither_type="none")

c=core.resize.Bicubic(clip=c, format=vs.RGBS, matrix_in_s="2020ncl", range_in_s="full",dither_type="none")

c=core.resize.Bilinear(clip=c, format=vs.RGBS, transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=source_peak)

exposure_bias=(source_peak/LDR_nits)

#hable tone mapping correct parameters
#["A"] = 0.22
#["B"] = 0.3
#["C"] = 0.1
#["D"] = 0.2
#["E"] = 0.01
#["F"] = 0.3
#["W"] = 11.2
#((x * (A*x + C*B) + D*E) / (x * (A*x+B) + D*F)) - E/F"
tm = core.std.Expr(c, expr="x {exposure_bias} * 0.22 x {exposure_bias} * * 0.03 + * 0.002 + x {exposure_bias} * 0.22 x {exposure_bias} * * 0.3 + * 0.06 + / 0.01 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits



w = core.std.Expr(c, expr="{exposure_bias} 0.22 {exposure_bias} * 0.03 + * 0.002 + {exposure_bias} 0.22 {exposure_bias} * 0.3 + * 0.06 + / 0.01 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)

c = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)

#c=core.fmtc.primaries(clip=c, prims="2020", primd="709")
c=core.resize.Bilinear(clip=c, format=vs.RGBS, primaries_in_s="2020", primaries_s="709",dither_type="none")
c=core.resize.Bilinear(clip=c, format=vs.RGBS, transfer_in_s="linear", transfer_s="709",dither_type="none")

c=core.resize.Bilinear(clip=c, format=vs.YUV420P8, matrix_s="709", range_in_s="full",range_s="limited",chromaloc_in_s="center")


c.set_output()

qtwigg
11th June 2017, 21:29
Greyed
https://cdn.discordapp.com/attachments/213375923263635456/323321320009891841/Planet.Earth.II.S01E01.2160p.HDR..mkv_thumbs_2017.06.10_23.42.29.jpghttp://
Saturated
https://cdn.discordapp.com/attachments/213375923263635456/323321403019362314/Planet.Earth.II.S01E01.2160p.HDR._1080p_x265_10bit_Joy.mkv_thumbs_2017.06.10_23.41.59.jpg

Normal Bluray
https://cdn.discordapp.com/attachments/213375923263635456/323510893704904715/Planet_Earth_II_S01E01_Islands.mkv_snapshot_05.05_2017.06.11_13.16.29.jpg
UHD BluRay
https://cdn.discordapp.com/attachments/213375923263635456/323511260920545282/Planet.Earth.II.S01E01.2160p.HDR._1080p_x265_10bit_Joy2.mkv_snapshot_00.05_2017.06.11_13.18.00.jpg

Ran that script, thank you so so very much for it. I just need a few tweaks, any ideas? Thanks

george84
12th August 2017, 16:38
@age

Screenshot comparison does not work since 1.8.2017

Selur
17th December 2017, 14:43
for easier use, I wrapped the corrected version (https://forum.doom9.org/showthread.php?p=1801023#post1801023) into a function:
(UPDATED VERSION in later post)
'''
HDR-10 to SDR mapping, from 'age' (https://forum.doom9.org/showthread.php?p=1800675)
'''
import vapoursynth as vs

def hablehdr10tosdr(clip, source_peak=1200, ldr_nits=100, tFormat=vs.YUV420P8, tMatrix="709", tRange="limited"):
core = vs.get_core()
clip=core.resize.Bilinear(clip=clip, format=vs.YUV444PS, range_in_s="limited", range_s="full",chromaloc_in_s="center",dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, matrix_in_s="2020ncl", range_in_s="full",dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=source_peak)
exposure_bias=(source_peak/ldr_nits) #set 150 or 200 for lowering the brightness

#hable tone mapping
#["A"] = 0.22
#["B"] = 0.3
#["C"] = 0.1
#["D"] = 0.2
#["E"] = 0.01
#["F"] = 0.3
#["W"] = 11.2
#((x * (A*x + C*B) + D*E) / (x * (A*x+B) + D*F)) - E/F"
tm = core.std.Expr(clip, expr="x {exposure_bias} * 0.22 x {exposure_bias} * * 0.03 + * 0.002 + x {exposure_bias} * 0.22 x {exposure_bias} * * 0.3 + * 0.06 + / 0.01 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits

w = core.std.Expr(clip, expr="{exposure_bias} 0.15 {exposure_bias} * 0.05 + * 0.004 + {exposure_bias} 0.15 {exposure_bias} * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#

clip = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, primaries_in_s="2020", primaries_s=tMatrix,dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, transfer_in_s="linear", transfer_s=tMatrix,dither_type="none")

if format != vs.RGBS:
clip=core.resize.Bilinear(clip=clip, format=tFormat, matrix_s=tMatrix, range_in_s="full",range_s=tRange,chromaloc_in_s="center")
return clip;


Cu Selur

Magik Mark
28th December 2017, 13:51
Is there a way to convert this to avisynth+?

jkilez
1st January 2018, 14:04
Is there a way to convert this to avisynth+?

I would really like to see that as well. The thread on HDR to SDR using FFmpeg (https://forum.doom9.org/showthread.php?t=175125) is certainly helpful, but it would great to do it all within Avisynth+ scripting.

Selur
1st January 2018, 14:26
small adjustment to the above code,.. (made color location available)
'''
HDR-10 to SDR mapping, from 'age' (https://forum.doom9.org/showthread.php?p=1800675)
'''
def hablehdr10tosdr(clip, source_peak=1200, ldr_nits=100, tFormat=vs.YUV420P8, tMatrix="709", tRange="limited", color_loc="center"):
core = vs.get_core()
clip=core.resize.Bilinear(clip=clip, format=vs.YUV444PS, range_in_s="limited", range_s="full",chromaloc_in_s=color_loc,dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, matrix_in_s="2020ncl", range_in_s="full",dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=source_peak)
exposure_bias=(source_peak/ldr_nits) #set 150 or 200 for lowering the brightness

#hable tone mapping
#["A"] = 0.22
#["B"] = 0.3
#["C"] = 0.1
#["D"] = 0.2
#["E"] = 0.01
#["F"] = 0.3
#["W"] = 11.2
#((x * (A*x + C*B) + D*E) / (x * (A*x+B) + D*F)) - E/F"
tm = core.std.Expr(clip, expr="x {exposure_bias} * 0.22 x {exposure_bias} * * 0.03 + * 0.002 + x {exposure_bias} * 0.22 x {exposure_bias} * * 0.3 + * 0.06 + / 0.01 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits

w = core.std.Expr(clip, expr="{exposure_bias} 0.15 {exposure_bias} * 0.05 + * 0.004 + {exposure_bias} 0.15 {exposure_bias} * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#

clip = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, primaries_in_s="2020", primaries_s=tMatrix,dither_type="none")
clip=core.resize.Bilinear(clip=clip, format=vs.RGBS, transfer_in_s="linear", transfer_s=tMatrix,dither_type="none")

if format != vs.RGBS:
clip=core.resize.Bilinear(clip=clip, format=tFormat, matrix_s=tMatrix, range_in_s="full",range_s=tRange,chromaloc_in_s=color_loc)
return clip;

I agree that it would be nice to have a full port of ffmpegs tone map in Vapoursynth (and Avisynth).

ifb
4th January 2018, 05:10
I have a vapoursynth port available. I've only tested it with HLG content. YMMV.

https://github.com/ifb/vapoursynth-tonemap

Selur
5th January 2018, 05:47
@ifb: Thanks for that! :D

melp
19th January 2018, 21:38
Is HLG-based content easier to tone-map? It's built to be compatible with SDR displays, which makes me think tone-mapping it would be trivial and yield higher-quality results. If this is the case, PQ-based HDR content can be transcoded to HLG for higher-quality tone-mapping.

Gser
20th January 2018, 13:27
Is HLG-based content easier to tone-map? It's built to be compatible with SDR displays, which makes me think tone-mapping it would be trivial and yield higher-quality results. If this is the case, PQ-based HDR content can be transcoded to HLG for higher-quality tone-mapping.

The only reason tone mapping is done is to make HDR footage look "normal" on SDR screens, we can't make SDR screens show any more than what the technology is limited to. HLG content is designed to look normal on both types of screens already hence tone mapping it would be pointless.

On a side note HLG technology is really limited, it only shows more detail in highlights. You might not even notice you are looking at HLG footage. I tried some demo HLG footage on my tv and I wasn't really convinced of its benefits. The real benefit of next generation tv is the use of HEVC, only progressive video and higher framerates. I've seen trial broadcasts and it looks amazing.

ifb
20th January 2018, 15:56
HLG content is designed to look normal on both types of screens already hence tone mapping it would be pointless.
HLG certainly does not look "normal" on a reference monitor in SDR mode (BVM-X300). You can however crank the brightness and contrast and get an acceptable picture. That's what the "backwards-compatibility" of HLG gets you. That is not the case with S-Log3 and PQ.

Now, once you add 2020 to the mix, the backwards compatibility claims gets a little more dubious. 2020 on a 709 monitor is quite desaturated.

And HLG certainly will blow out an SDR display when you push the iris. It's HDR. You still need to do some sort of knee/tonemapping/clipping to convert to SDR.

Whether HLG or PQ is easier to convert to SDR, I guess it's about the same. The problem is fundamentally the same in both cases (squeezing HDR into SDR).

melp
20th January 2018, 17:58
Good info, thanks. It sounds like the methods in the OP and other posts on page 1 produce a better picture than just playing HLG on an SDR display.

Gser
20th January 2018, 18:27
HLG certainly does not look "normal" on a reference monitor in SDR mode (BVM-X300). You can however crank the brightness and contrast and get an acceptable picture. That's what the "backwards-compatibility" of HLG gets you. That is not the case with S-Log3 and PQ.

Now, once you add 2020 to the mix, the backwards compatibility claims gets a little more dubious. 2020 on a 709 monitor is quite desaturated.

And HLG certainly will blow out an SDR display when you push the iris. It's HDR. You still need to do some sort of knee/tonemapping/clipping to convert to SDR.

Whether HLG or PQ is easier to convert to SDR, I guess it's about the same. The problem is fundamentally the same in both cases (squeezing HDR into SDR).

Actually HLG is supported in Rec. 2100, not 2020 and definitely not 709. A simple clipping of highlights is enough to produce a usable picture if not a perfect one but for millionaires with access to reference monitors like yourself BBC licenses 3D LUTs http://downloads.bbc.co.uk/rd/pubs/papers/HDR/BBC_HDRTV_HLG_LUT_Licensing_v2.pdf

One of the reasons why HLG was selected was because of its unreliance on meta-data supposedly, but the more I read the more bullshit I see spread across my screen. HLG makes so many compromises that it's not good for anything. I have a feeling PQ will become more popular if broadcasters have any sense left in them. For example the amount of meta data used in HDR10 is tiny and could be broadcasted for example once per second. It could be left up to the device manufacturer to decide whether to show an incorrect picture for the remainder of that second or to wait for the HDR meta data and in case of a mid broadcast dropout to use the previous meta data until new meta data has been received.

melp
22nd January 2018, 01:21
Actually HLG is supported in Rec. 2100, not 2020 and definitely not 709. A simple clipping of highlights is enough to produce a usable picture if not a perfect one but for millionaires with access to reference monitors like yourself BBC licenses 3D LUTs http://downloads.bbc.co.uk/rd/pubs/papers/HDR/BBC_HDRTV_HLG_LUT_Licensing_v2.pdf

One of the reasons why HLG was selected was because of its unreliance on meta-data supposedly, but the more I read the more bullshit I see spread across my screen. HLG makes so many compromises that it's not good for anything. I have a feeling PQ will become more popular if broadcasters have any sense left in them. For example the amount of meta data used in HDR10 is tiny and could be broadcasted for example once per second. It could be left up to the device manufacturer to decide whether to show an incorrect picture for the remainder of that second or to wait for the HDR meta data and in case of a mid broadcast dropout to use the previous meta data until new meta data has been received.

I don't believe the motivating factor for broadcasters in deciding between PQ and HLG is bandwidth-related (i.e., can we squeeze one frame per second with HDR metadata into our stream?), but rather it's driven by a desire for backwards compatibility with older set-top boxes and displays.

age
22nd January 2018, 17:18
I've recently done a new script based always on the hale tone mapping , it works mainly in linear rgb with bt2020 primaries and the luma channel, it has an option for the desaturation of the super bright colors and for linearize the first part of the tonemapper curve.


There's only few parameters:
source_peak = max brightness expressed in nits
desat = how much desaturate the bright regions, it goes from 0 to 100, with 0 there isn't any desaturation and with 100 it desaturate totally the bright colors, default is 50
lin= True or False, with True it linearize the first part of the curve
show_satmask=True or False, with true it show the mask used for the reduction of the saturation
show_clipped=True or False, show the image with clipped highlights

hable only in rgb
(sorry for the horrible compression)
https://i.imgur.com/d4gZXt9.jpg
new script
https://i.imgur.com/Cb8CinM.jpg
show_satmask=True
https://i.imgur.com/zI5yv20.jpg
show_clipped=True
https://i.imgur.com/UpWz6iG.jpg




HLG with rec709 primaries
https://i.imgur.com/cPkx7Sf.jpg
HLG tonemapped
https://i.imgur.com/oexPnrB.jpg


example of usage with HLG content(convert always to linear with 2020 primaries)

import vapoursynth as vs
import tmap

core = vs.get_core()
c = core.ffms2.Source(source = 'C:/..../TravelXP 4K HDR HLG Demo.ts')

c=core.resize.Bicubic(clip=c, format=vs.RGBS,filter_param_a=0.0,filter_param_b=0.75, range_in_s="limited", matrix_in_s="2020ncl", primaries_in_s="2020", primaries_s="2020", transfer_in_s="std-b67", transfer_s="linear",dither_type="none", nominal_luminance=1000)

c=tmap.tm(c,source_peak=1000,desat=50,show_satmask=False,lin=True,show_clipped=False)

c=core.resize.Bicubic(clip=c, format=vs.YUV422P10, filter_param_a=0.0, filter_param_b=0.75, matrix_s="709", primaries_in_s="2020", primaries_s="709", transfer_in_s="linear", transfer_s="709",dither_type="none")


c.set_output()



I don't have tested it much but I think sometimes it gives acceptable results :-)
Here's the script

MonoS
24th January 2018, 14:06
Hi age, you provided an example to convert an hlg source, what if i want to tonemap a standard hdr bt2020 to bt709 sdr result?
Also, what is the rationale with using the lin parameter?

If i need to downscale the tonemapped clip in linear light can i just do it before the second resize call or do i need some other steps?

age
24th January 2018, 19:14
With frameprops and clipinfo you should get all the needed metadata


import vapoursynth as vs
import tmap

core = vs.get_core()

c = core.ffms2.Source(source = 'C:/../Videos/test.mkv')

#c=core.text.FrameProps(c)
#c=core.text.ClipInfo(c)
c=core.resize.Bicubic(clip=c, format=vs.RGBS,filter_param_a=0.0,filter_param_b=0.75, range_in_s="limited", matrix_in_s="2020ncl", primaries_in_s="2020", primaries_s="2020", transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=1000)

c=tmap.tm(c,source_peak=1000,desat=50,show_satmask=False,lin=True,show_clipped=False)

c=core.resize.Bicubic(clip=c, format=vs.YUV420P8, filter_param_a=0.0, filter_param_b=0.75, matrix_s="709", primaries_in_s="2020", primaries_s="709", transfer_in_s="linear", transfer_s="709",dither_type="ordered")


c.set_output()



The original tonemapper curve is s-shaped, with lin=true the first part of the curve is linear.
It's a bit more contrasted in the shadows

age
24th January 2018, 19:24
If i need to downscale the tonemapped clip in linear light can i just do it before the second resize call or do i need some other steps?
Yes, you can do it before the second resize call :)

ZMachine95
24th January 2018, 20:04
Hi, I would like to download your attachment (here (https://forum.doom9.org/showthread.php?p=1831451#post1831451)), but it says "Attachments Pending Approval". Can you please provide another link or just tell the staff to approve it? Wonderful job by the way! :thanks:

age
24th January 2018, 20:37
Hi, I would like to download your attachment (here (https://forum.doom9.org/showthread.php?p=1831451#post1831451)), but it says "Attachments Pending Approval". Can you please provide another link or just tell the staff to approve it? Wonderful job by the way! :thanks:
Thanks!!
Here it is
https://filebin.net/uvi8bgwe76hmc5ft
Edit V2
https://filebin.net/qkzfw0kssfxfipst

MonoS
24th January 2018, 23:04
Thank you for your response, i'm trying the script right now and i have a question about the source_peak parameter.
When i examine the original file with mediainfo it reports me two value for the source peak

Mastering display luminance : min: 0.0050 cd/m2, max: 4000.0000 cd/m2
Maximum Content Light Level : 500 cd/m2

Which of the two should i use? If i understood correctly i should use the MCLL parameter, am i right?
Also does the source_peak and nominal_luminance needs to be the same?

Sorry for the amount of question but i fucked up quite a lot of time all those colors conversion :D

Selur
25th January 2018, 04:35
Here's how I understood it so far:

1. Luminance
Mastering display luminance : min: 0.0001 cd/m2, max: 1000.0000 cd/m2
translates to:
L(max/0.0001,min/0.0001)=L(1000000/1)
2. Color Primaries
Mastering display color primaries : R: x=0.680000 y=0.320000, G: x=0.265000 y=0.690000, B: x=0.150000 y=0.060000, White point: x=0.312700 y=0.329000
translates to:
G(Gx/0.00002,Gy/0.00002)B(Bx/0.00002,By/0.00002)R(R/0.00002x,Ry/0.00002)WP(Wx/0.00002,Wy/0.00002)=G(13250,34499)B(7499,2999)R(34000,15999)WP(15634,16450)

ColorPrimaries and Luminance combined give:
G(13250,34499)B(7499,2999)R(34000,15999)WP(15634,16450)L(10000000,1)
which you set under "--master-display"

3. Max CLL
Maximum Content Light Level : 1000 cd/m2
Maximum Frame-Average Light Level : 400 cd/m2
translates to:
--max-cll "1000,400"

Cu Selur

Ps.: Does someone know how to translate:
Transfer characteristics : PQ
to x265? My problem is that x265 --transfer (https://x265.readthedocs.io/en/latest/cli.html#cmdoption-transfer) doesn't have 'PQ'.

age
25th January 2018, 11:00
Thank you for your response, i'm trying the script right now and i have a question about the source_peak parameter.
When i examine the original file with mediainfo it reports me two value for the source peak

Mastering display luminance : min: 0.0050 cd/m2, max: 4000.0000 cd/m2
Maximum Content Light Level : 500 cd/m2

Which of the two should i use? If i understood correctly i should use the MCLL parameter, am i right?
Also does the source_peak and nominal_luminance needs to be the same?

Sorry for the amount of question but i fucked up quite a lot of time all those colors conversion :D


Both source_peak and nominal_luminance have the same value
yes, it's the Maximum Content Light Level

age
25th January 2018, 11:11
Ps.: Does someone know how to translate:
Transfer characteristics : PQ
to x265? My problem is that x265 --transfer (https://x265.readthedocs.io/en/latest/cli.html#cmdoption-transfer) doesn't have 'PQ'.

smpte2084 in x265=st2084 in vapoursynth resize=pq

ZMachine95
25th January 2018, 11:19
Thanks!!
Here it is
https://filebin.net/uvi8bgwe76hmc5ft

Great!

age
25th January 2018, 15:59
v2 with hopefully (slighty) better general saturation

https://filebin.net/qkzfw0kssfxfipst

Same usage as before

jpsdr
25th January 2018, 16:33
@age or also someone else... :p
Interesting, but as i'm not used to all this expressions syntax.
Is it possible to have the mathematical formulas (even is there is several steps) ?

Could be something like this :
[Y0 U0 V0] direct ffxxx or anything HDR output.
R= fr(Y0,U0,V0) Give fr formula
G=Fg(Y0,U0,V0) etc...

R1=f1(R) Give f1 mathematical formula with "A","B","C","exposure_bias", etc...
R2=f2(R1) etc...

Y1=fy(Rn,Gn,Bn) Give fy formula.
Etc...

So, if this could be possible, it would be a great help.
:thanks:

age
25th January 2018, 17:25
I've taken the formula for decomposing rgb to luma and come back(it gives different result from a classic rgb->yuv->rgb and it is faster) from here:https://www.google.it/amp/s/imdoingitwrong.wordpress.com/2010/08/19/why-reinhard-desaturates-my-blacks-3/amp/

The jhon hable tonemapping from here:http://filmicworlds.com/blog/filmic-tonemapping-operators/

jpsdr
25th January 2018, 19:36
... I'm still unable to see the whole mathematical formula path :(.
I also don't see any formula with exposed_bias in your links...

I have no idea what is the mathematical results of such things
tm = core.std.Expr(c, expr="x {exposure_bias} * 0.15 x {exposure_bias} * * 0.05 + * 0.004 + x {exposure_bias} * 0.15 x {exposure_bias} * * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits

w = core.std.Expr(c, expr="{exposure_bias} 0.15 {exposure_bias} * 0.05 + * 0.004 + {exposure_bias} 0.15 {exposure_bias} * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#


c = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)


This is why, a "unrolled" explicit mathematical formula would be wonderfull...
I mean... Your script result in several mathematic formulas apllied step by step.
Is it possible to have the formula of each step ?

ifb
25th January 2018, 21:56
... I'm still unable to see the whole mathematical formula path :(.
I also don't see any formula with exposed_bias in your links...

I have no idea what is the mathematical results of such things
tm = core.std.Expr(c, expr="x {exposure_bias} * 0.15 x {exposure_bias} * * 0.05 + * 0.004 + x {exposure_bias} * 0.15 x {exposure_bias} * * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#12=1200 nits / 100 nits

w = core.std.Expr(c, expr="{exposure_bias} 0.15 {exposure_bias} * 0.05 + * 0.004 + {exposure_bias} 0.15 {exposure_bias} * 0.50 + * 0.06 + / 0.02 0.30 / - ".format(exposure_bias=exposure_bias),format=vs.RGBS)#


c = core.std.Expr(clips=[tm,w], expr=" x 1 y / * ",format=vs.RGBS)


This is why, a "unrolled" explicit mathematical formula would be wonderfull...
I mean... Your script result in several mathematic formulas apllied step by step.
Is it possible to have the formula of each step ?
I haven't looked at his script, but what you've quoted is the Hable operator from earlier in the thread. You can look at the VS plugin or ffmpeg if you want to see a C version.

https://github.com/ifb/vapoursynth-tonemap/blob/master/tonemap.c#L44

jpsdr
26th January 2018, 09:55
Ok, i'll try to figure out things with all i have for now.

jpsdr
27th January 2018, 10:40
I've tried to read again the BT.2100 and BT.2390.
I'm still confused.
I'm unable to relate the formulas of OETF, EOTF, OOTF functions with the formulas of the tonemap functions you've all provided in your links. Where from the BT.2100 and BT.2390 did you get the formulas of the tonemap functions (what page, chapter ) ?
I'm still unable to figure out where the "white max" information is involved in the OETF, EOTF, OOTF functions... :(

age
27th January 2018, 19:40
EOTF and tonemapping are two different things, you could read about the st2084 formula in the official documentation or in the zimg code or elsewhere.
I've founded an interesting pdf about hdr and tonemapping
http://www.eusipco2016.org/documents/391615/2699873/EUSIPCO_HDR_tutorial.pdf
at page 119 the EOTF formula.
At pages 100-107 some methods for the correction of the saturation.
From the first one I've done a vapoursynth function with only two parameters,(clip and source_peak)it's simple but at my eyes is better then my previous attempt

https://filebin.net/7c6kocrmxxnat1cp



import vapoursynth as vs
import tmsimple

core = vs.get_core()

c = core.ffms2.Source(source = 'C:/.../testhdr.mkv')

c=core.resize.Bicubic(clip=c, format=vs.RGBS,filter_param_a=0.0,filter_param_b=0.75, range_in_s="limited", matrix_in_s="2020ncl", primaries_in_s="2020", primaries_s="2020", transfer_in_s="st2084", transfer_s="linear",dither_type="none", nominal_luminance=1000)


c=tmsimple.tm(clip=c,source_peak=1000)

c=core.resize.Bicubic(clip=c, format=vs.YUV420P8, filter_param_a=0.0, filter_param_b=0.75, matrix_s="709", primaries_in_s="2020", primaries_s="709", transfer_in_s="linear", transfer_s="709",dither_type="ordered")


c.set_output()

jpsdr
28th January 2018, 14:18
Ok thanks, i'll read it.

jkilez
14th April 2018, 22:59
Is there a way to convert this to avisynth+?

There is now a tone-mapping plug-in for avisynth+: http://rationalqm.us/DGTonemap.rar

It supports both Hable and Reinhard methods.

MonoS
16th April 2018, 20:32
Some times ago i wrote a c++ implementation of the hable implementation in tmap, i don't remeber how much faster it is, but IIRC about 2x .

It only works for float input with mod8 resolution video and AVX CPU, i didn't bother writing an SSE implementation due to the high amount of FMAD instruction used.
The static buffer optimization only work on windows right now.

If i see any interest in this i'll try to add some functionality like the debug views, non mod8 support, etc

Here the repo https://github.com/MonoS/tmap-vapoursynth/tree/V1

lansing
2nd January 2019, 06:31
Japanese television recently launched their 4k/8k broadcasting last month, here's a same frame comparison between a 1080p HD version and the 4k HDR version:

1080p:
https://i.imgur.com/iiSWMofh.png

4K HDR:
https://i.imgur.com/dM6bahWh.jpg

note: delete the last letter ("h") on the image name to get the original size.


None of the three methods in the tonemap plugin I've tested can map the 4k one to look like 1080 one. They all failed on the color mapping.