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. |
![]() |
#1 | Link |
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Hardware challenged HDR to SDR conversions
Being the retro person I am, my computer hardware is a little bit autdated, but I like it and I have no desire to upgrade my stuff anytime soon. I still want to watch some downloaded HDR sources (PQ and HLG) which my display hardware cannot handle, so I have to convert them to standard SDR. The target format is somewhat restricted by my hardware, though...
For watching the stuff on my old CRT TV through my Xtreamer streaming box it needs to be in the H264 format. Full HD would be possible, but I prefer Half HD because none of my hardware devices can go higher than Half HD. My computer is an older Thinkpad T530, it does not support Vulkan GPU converting. I also refuse to deal with LUTs for the tone mapping, this would be overkill for my needs. Conclusion: For these HDRtoSDR conversions there are only three practical methods: 1. DGHDRtoSDR running under AviSynth+ 2. FFmpeg based low quality method by GeoffreyA 3. FFmpeg based high quality method by Gyan (found it at Reddit) Quality ranking: 1. The DG method has the highest quality and is still fast. It has separate parameters for PQ and HLG sources which I find very helpful. 2. The FFmpeg high method by Gyan has the same high quality, but is painfully slow (only half the speed of the DG method). 3. The FFmpeg low method has a tiny little bit lower quality, but it is fast, and for most sources the lower quality will not be visible. NOTE: The link for the test conversions has expired. The conversion scripts can be found here: https://forum.doom9.org/showthread.p...17#post2014617 Cheers manolito Last edited by manolito; 19th February 2025 at 16:35. |
![]() |
![]() |
![]() |
#2 | Link |
Registered User
Join Date: Jun 2024
Location: South Africa
Posts: 293
|
Hi, Manolito.
Great test. I downloaded the files, and you're right, Gyan's method is a big improvement over my earlier one. For my part, I put that version aside when I found that libplacebo gave superior results. One of its key strengths is the frame-by-frame peak detection, and there are various tone- and gamut-mapping operators to choose from. From my testing, it rivals "official" SDR versions, perhaps even beating them, and I use nothing else when converting HDR to SDR. Last December, I got great results with the 4K version of The Lord of the Rings, and more recently, The Substance. It does require a GPU with Vulkan support. I'm curious to give these videos a go with libplacebo. Have you got the links to them? |
![]() |
![]() |
![]() |
#3 | Link | ||
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Quote:
Quote:
manolito Last edited by manolito; 6th February 2025 at 15:15. |
||
![]() |
![]() |
![]() |
#6 | Link | |
Registered User
Join Date: Aug 2024
Posts: 447
|
The reason of the high quality command being painfuilly slow is probably that it has many extra filtering.
All the extra color adjusments aside, the reason of the "low quality" command feels bad is probably the desat is too high. For record this is the description found in the archive Quote:
Last edited by Z2697; 6th February 2025 at 18:48. |
|
![]() |
![]() |
![]() |
#7 | Link | |
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Quote:
You can mainly see the reduced quality of "low" samples when the source has lots of bright sky and white clouds, and it is very visible in this file: "Ostuni in 4k UHD - HLG_FFmpeg Low.mkv". But if you know where to look you can also see the reduced resolution in such bright areas in the other samples. Mainly the transitions from the blue sky to the white clouds are very abrupt and not smooth, very low effective resolution. Whatever, I picked very demanding sources, in many cases you will not notice this reduced quality. In your other post you say that the "desat" value is probably too high. I don`t think so, there was a reason I changed it from the original value of 0 to 2.5. A value of 0 means that the parameter is disabled, the default is 2.0. With a value of 0 I get very annoying halos around the bright sun, the default of 2.0 causes colors which are a little too pale. A value of 2.5 was the best compromise for me. Cheers manolito |
|
![]() |
![]() |
![]() |
#8 | Link |
Registered User
Join Date: Jun 2024
Location: South Africa
Posts: 293
|
I've found that desat seems to be problematic, if not broken. It's also better, but slower, to perform the tone mapping on the 2160p image before downscaling. That eliminates artefacts around bright text, for example, a sort of halo.
Then, zscale is using bilinear by default, causing a loss of sharpness. Changing to a better scaler on both sides of the chain (because of chroma) would be more ideal. Gyan's version is also using the bilinear default but perhaps those extra filters are sharpening along the way. And it's subjective, but Mobius has a better range and is less "flat" than Reinhard. Code:
zscale=m=gbr:t=linear:dither=none:npl=203:f=spline36,format=gbrpf32le,tonemap=reinhard:desat=0,zscale=1280:-2:f=spline36,zscale=m=709:t=709:p=709:r=limited:dither=error_diffusion:f=spline36,format=yuv420p,sidedata=delete Last edited by GeoffreyA; 6th February 2025 at 21:01. |
![]() |
![]() |
![]() |
#11 | Link |
Registered User
Join Date: Jun 2024
Location: South Africa
Posts: 293
|
Here's another test. I copied Manolito's Low and Gyan commands verbatim. In addition, I made encodes with libplacebo and a better FFmpeg command. The second video, in particular, demonstrates that FFmpeg's desat implementation is either too aggressive or broken. As Z2697 noted, desat seems largely the culprit in the low-quality version.
The sources were Samsung's "Travel with My Pet," and the UHD theatrical version of The Fellowship of the Ring. Code:
Low zscale=1280:-2,zscale=m=gbr:t=linear:dither=none:npl=225,format=gbrpf32le,tonemap=reinhard:desat=2.5, zscale=m=709:t=709:p=709:r=limited:dither=error_diffusion,format=yuv420p,sidedata=delete Gyan zscale=1280:-2,zscale=t=linear:npl=100,format=gbrpf32le,tonemap=tonemap=gamma:param=1.2:desat=0:peak=15, zscale=p=709:t=709:m=709:r=full:d=error_diffusion, noise=alls=3:allf=t+u,eq=saturation=1.2:brightness=0.155:contrast=1.15:gamma=0.85,huesaturation=colors=y:saturation=-0.5:intensity=0.40, curves=all=0.05/0 0.35/0.5 1/1,curves=all=0/0 0.75/0.76 0.9/0.94 1/1, deband=1thr=0.015:2thr=0.015:3thr=0.015:4thr=0.015:range=16:blur=true:coupling=true,noise=alls=2:allf=p+t, colorspace=iall=bt709:all=bt709:range=tv:format=yuv420p:dither=fsb GeoffreyA zscale=m=gbr:t=linear:dither=none:npl=203:f=spline36,format=gbrpf32le,tonemap=reinhard:desat=0,zscale=1280:-1:f=spline36, zscale=m=709:t=709:p=709:r=limited:c=left:dither=error_diffusion:f=spline36,format=yuv420p,sidedata=delete libplacebo libplacebo=upscaler=spline36:downscaler=spline36:w=-1:h=720:peak_detect=true:tonemapping=spline:gamut_mode=perceptual: colorspace=bt709:color_trc=bt709:color_primaries=bt709:range=limited:dithering=blue:format=yuv420p,sidedata=delete Last edited by GeoffreyA; 7th February 2025 at 11:59. |
![]() |
![]() |
![]() |
#12 | Link |
Registered User
Join Date: Aug 2024
Posts: 447
|
In my opinion the Mobius method is overall a better choice in a lot of case, when you are limited to the "traditional" tonemap.
I don't think the LUT methods will be much slower (if that's your concern), because the underlying mechanisms of tonemapping should be similarily complex, I assume. By the way sorry for not reading the docs. The desat parameter described by "ffmpeg -h" is "desaturation strength" which I guess most will assume the bigger the value, the stronger the desaturation. While in reality the doc says it's a "light level threshold" and bigger the values means less desaturation... yeah. Which also corresponds to a quick test. (value 0, is a special case as it disables the desaturation) Maybe it's FFmpeg, but libplacebo has an option to use d3d11 instead of vulkan, at least it does in mpv. And... mpv does even come with video encoding support... yeah... so you can technically use libplacebo as well even when no vulkan... haha. (sorry again, there seems no way to use the "render" output in the encoding) Last edited by Z2697; 7th February 2025 at 16:39. |
![]() |
![]() |
![]() |
#14 | Link | |
Registered User
Join Date: Jun 2024
Location: South Africa
Posts: 293
|
Quote:
|
|
![]() |
![]() |
![]() |
#15 | Link |
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Sorry for the late reply, I was busy updating the FFmpeg Low Quality method, and this required a lot of test encodes. But I believe I got excellent results now from this method...
![]() It turned out that I had to create two separate commands for PQ and HLG sources (just like for the DG method). Those input formats need a couple of different parameters for good results, and it took a lot of test encodes until I was happy with the results. This means that before starting the conversion you have to determine the source format. MediaInfo will tell you if the transfer characteristics of your source are PQ or HLG, and then you can select the correct command. Code:
AviSynth + (Script by Donald Graft. Separate parameters for PQ and HLG source files) ConvertBits(16) DGHDRToSDR(mode="pq", white=1460, black=0, gamma=0.42, hue=0.00, r=1.00, g=1.00, b=1.15, tm=0.90, roll=0.70, fulldepth=false, impl="255") # DGHDRtoSDR(mode="hlg", white=2000, gamma=0.50, b=1.15) ______________________________________________________________________________________ FFmpeg Fast (Slightly edited script by GeoffreyA) Half HD target (1280x720): -------------------------- PQ: === zscale=1280:-2,zscale=m=gbr:t=linear:dither=none:npl=225,format=gbrpf32le,tonemap=mobius:desat=3.0,zscale=m=709:t=709:p=709:r=limited:c=left:dither=error_diffusion,format=yuv420p,sidedata=delete HLG: ==== zscale=1280:-2,zscale=m=gbr:t=linear:dither=none:npl=203,format=gbrpf32le,tonemap=mobius:desat=0,zscale=m=709:t=709:p=709:r=limited:c=left:dither=error_diffusion,format=yuv420p,sidedata=delete _______________________________________________________________________________________ FFmpeg High Quality (Script by Gyan I found at Reddit. Edited for more natural colors) Half HD target (1280x720): -------------------------- zscale=1280:-2,zscale=t=linear:npl=100,format=gbrpf32le,tonemap=tonemap=gamma:param=1.2:desat=0:peak=15,zscale=p=709:t=709:m=709:r=full:d=error_diffusion,noise=alls=3:allf=t+u,eq=saturation=1.2:brightness=0.155:contrast=1.15:gamma=0.85,huesaturation=colors=y:saturation=-0.5:intensity=0.40,curves=all=0.05/0 0.35/0.5 1/1,curves=all=0/0 0.75/0.76 0.9/0.94 1/1,deband=1thr=0.015:2thr=0.015:3thr=0.015:4thr=0.015:range=16:blur=true:coupling=true,noise=alls=2:allf=p+t,colorspace=iall=bt709:all=bt709:range=tv:format=yuv420p:dither=fsb Cheers manolito Last edited by manolito; 4th March 2025 at 02:40. |
![]() |
![]() |
![]() |
#17 | Link |
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Well, I modified GeoffreyA's original method a little bit, and I moved the downscaling command from its original position to the very beginning, so it is executed before the tonemapping (in the original version all scaling is done after the tonemapping resulting in a much lower speed).
Using a separate zscale command makes sure that zscale does not change the execution order of the resizing command. (I believe that a while ago I found it in a FranceBB post that z-lib has its own mind about the execution order of the paramters whithin one block) Whatever, I am not the only one who thinks that downscaling before applying tonemapping does not hurt quality, and it takes only about half of the time. (Ask Donald Graft and huhn, and I agree with them) Cheers manolito |
![]() |
![]() |
![]() |
#18 | Link | |
Broadcast Encoder
Join Date: Nov 2013
Location: Royal Borough of Kensington & Chelsea, UK
Posts: 3,210
|
Quote:
![]() And yes, you're right, I did post about this, but there was a reason behind this. Essentially zlib performs the conversion by making an RGB roundtrip, which means that - unless you're starting with a 4:4:4 source (or indeed from RGB) - it will first upscale the chroma resolution to make it match the one of the luma, then convert to RGB, then apply the tonemapping and then convert back to YUV to whatever output you want. In other words, supposing your input is a 3840x2160 4:2:0 HDR PQ source you have 3840x2160 luma and 1920x1080 chroma, which means that it will first upscale the chroma to 3840x2160, then bring everything to RGB, then tonemap, then convert back to YUV and downscale the chroma back to 4:2:0. This means that if you start with the actual resolution, you're gonna be processing everything while preserving the information until the very end when you're gonna be downscaling. By downscaling first the process becomes faster, however you're losing information 'cause you're first going to 1920x1080 luma and 960x540 chroma, then you're upscaling the already downscaled chroma to 1920x1080, converting to RGB, applying the tonemapping, converting back and then downscaling again, which is a "no no". What you could be doing to still save CPU cycles while avoiding compromising quality, however, is downscale the luma while preserving the chroma of the original UHD source, in other words going from 3840x2160 4:2:0 to 1920x1080 4:4:4 and from there run the tonemapping process before finally outputting 1920x1080 4:2:0 with the last chroma downscaling. ![]() |
|
![]() |
![]() |
![]() |
#19 | Link | |
Registered User
Join Date: Sep 2003
Location: Berlin, Germany
Posts: 3,113
|
Hey FranceBB,
good to see you after a long time, thanks for your reply. Even though I do not really understand this stuff any more. I am just result oriented these days. This is from the DG readme: Quote:
Cheers manolito |
|
![]() |
![]() |
![]() |
#20 | Link |
Broadcast Encoder
Join Date: Nov 2013
Location: Royal Borough of Kensington & Chelsea, UK
Posts: 3,210
|
Ah, right, what I said about the RGB roundtrip actually applies to zlib, so essentially z_ConvertFormat(), and it's also true if you use my LinearTransformation() and indeed for Jean Philippe's Hable/Mobius/Reinhard tonemapping in HDRTools (although the last one also supports going to XYZ instead of RGB). I don't actually have any idea of what DGHDRtoSDR() does internally, but if Donald said that you can crop and downscale before using it, then I guess you're good to go
![]() |
![]() |
![]() |
![]() |
Thread Tools | Search this Thread |
Display Modes | |
|
|