Welcome to Doom9's Forum, THE in-place to be for everyone interested in DVD conversion.

Before you start posting please read the forum rules. By posting to this forum you agree to abide by the rules.

 

Go Back   Doom9's Forum > Capturing and Editing Video > VapourSynth

Reply
 
Thread Tools Search this Thread Display Modes
Old 12th March 2017, 17:12   #1  |  Link
age
Registered User
 
Join Date: Oct 2015
Posts: 54
HDR10 to SDR with Hable tone-mapping

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.
Code:
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
age is offline   Reply With Quote
Old 12th March 2017, 18:11   #2  |  Link
Cary Knoop
Cary Knoop
 
Cary Knoop's Avatar
 
Join Date: Feb 2017
Location: Newark CA, USA
Posts: 397
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.
Cary Knoop is offline   Reply With Quote
Old 12th March 2017, 18:18   #3  |  Link
sneaker_ger
Registered User
 
Join Date: Dec 2002
Posts: 5,565
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:
Code:
|   + 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.

Last edited by sneaker_ger; 12th March 2017 at 18:24.
sneaker_ger is offline   Reply With Quote
Old 12th March 2017, 18:26   #4  |  Link
age
Registered User
 
Join Date: Oct 2015
Posts: 54
Quote:
Originally Posted by sneaker_ger View Post
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:
Code:
|   + 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

Last edited by age; 12th March 2017 at 18:33.
age is offline   Reply With Quote
Old 12th March 2017, 18:33   #5  |  Link
Cary Knoop
Cary Knoop
 
Cary Knoop's Avatar
 
Join Date: Feb 2017
Location: Newark CA, USA
Posts: 397
Quote:
Originally Posted by sneaker_ger View Post
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.
Cary Knoop is offline   Reply With Quote
Old 12th March 2017, 18:35   #6  |  Link
age
Registered User
 
Join Date: Oct 2015
Posts: 54
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.

Last edited by age; 12th March 2017 at 19:00.
age is offline   Reply With Quote
Old 13th March 2017, 01:46   #7  |  Link
zub35
Registered User
 
Join Date: Oct 2016
Posts: 56
Quote:
Originally Posted by age View Post
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
zub35 is offline   Reply With Quote
Old 14th March 2017, 12:09   #8  |  Link
kolak
Registered User
 
Join Date: Nov 2004
Location: Poland
Posts: 2,843
Quote:
Originally Posted by age View Post
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/1...delivering-hdr

and than use free Resolve and compare your result to Resolve.
kolak is offline   Reply With Quote
Old 14th March 2017, 12:12   #9  |  Link
kolak
Registered User
 
Join Date: Nov 2004
Location: Poland
Posts: 2,843
Quote:
Originally Posted by age View Post
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?

Last edited by kolak; 14th March 2017 at 12:14.
kolak is offline   Reply With Quote
Old 14th March 2017, 13:22   #10  |  Link
kolak
Registered User
 
Join Date: Nov 2004
Location: Poland
Posts: 2,843
Quote:
Originally Posted by age View Post
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.
kolak is offline   Reply With Quote
Old 15th March 2017, 01:31   #11  |  Link
CruNcher
Registered User
 
CruNcher's Avatar
 
Join Date: Apr 2002
Location: Germany
Posts: 4,926
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/201...mapping-curve/
__________________
all my compares are riddles so please try to decipher them yourselves :)

It is about Time

Join the Revolution NOW before it is to Late !

http://forum.doom9.org/showthread.php?t=168004

Last edited by CruNcher; 15th March 2017 at 01:47.
CruNcher is offline   Reply With Quote
Old 15th March 2017, 11:23   #12  |  Link
kolak
Registered User
 
Join Date: Nov 2004
Location: Poland
Posts: 2,843
This sample is terrible.
kolak is offline   Reply With Quote
Old 15th March 2017, 13:02   #13  |  Link
age
Registered User
 
Join Date: Oct 2015
Posts: 54
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.../src/operators

And a different version of aces:

https://github.com/keijiro/PostProce...emapping.cginc


new script with correct parameters
Code:
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()
age is offline   Reply With Quote
Old 11th June 2017, 21:29   #14  |  Link
qtwigg
Registered User
 
Join Date: Mar 2015
Posts: 8
Help Please

Greyed

Saturated


Normal Bluray

UHD BluRay


Ran that script, thank you so so very much for it. I just need a few tweaks, any ideas? Thanks
qtwigg is offline   Reply With Quote
Old 12th August 2017, 16:38   #15  |  Link
george84
Registered User
 
Join Date: Jan 2012
Posts: 104
@age

Screenshot comparison does not work since 1.8.2017
george84 is offline   Reply With Quote
Old 17th December 2017, 14:43   #16  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,259
for easier use, I wrapped the corrected version into a function:
(UPDATED VERSION in later post)
Code:
'''
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
__________________
Hybrid here in the forum, homepage

Last edited by Selur; 1st January 2018 at 14:25.
Selur is offline   Reply With Quote
Old 28th December 2017, 13:51   #17  |  Link
Magik Mark
Registered User
 
Join Date: Dec 2014
Posts: 666
Is there a way to convert this to avisynth+?
__________________
Asus ProArt Z790 - 13th Gen Intel i9 - RTX 3080 - DDR5 64GB Predator - LG OLED C9 - Yamaha A3030 - Windows 11 x64 - PotPlayerr - Lav - MadVR
Magik Mark is offline   Reply With Quote
Old 1st January 2018, 14:04   #18  |  Link
jkilez
Registered User
 
Join Date: May 2014
Posts: 33
Quote:
Originally Posted by Magik Mark View Post
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 is certainly helpful, but it would great to do it all within Avisynth+ scripting.
jkilez is offline   Reply With Quote
Old 1st January 2018, 14:26   #19  |  Link
Selur
Registered User
 
Selur's Avatar
 
Join Date: Oct 2001
Location: Germany
Posts: 7,259
small adjustment to the above code,.. (made color location available)
Code:
'''
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).
__________________
Hybrid here in the forum, homepage

Last edited by Selur; 1st January 2018 at 14:29.
Selur is offline   Reply With Quote
Old 4th January 2018, 05:10   #20  |  Link
ifb
Registered User
 
Join Date: Dec 2009
Posts: 72
I have a vapoursynth port available. I've only tested it with HLG content. YMMV.

https://github.com/ifb/vapoursynth-tonemap
ifb is offline   Reply With Quote
Reply

Tags
hdr, sdr

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +1. The time now is 12:20.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.