dylanraga / win11hdr-srgb-to-gamma2.2-icm

Transform Windows 11's virtual SDR-in-HDR curve from piecewise sRGB to Gamma 2.2
393 stars 11 forks source link

Document profile generation #2

Open ylor opened 1 year ago

ylor commented 1 year ago

Greetings @dylanraga!

First, thank you! Your findings and sharing them have not only improved my Diablo IV experience but also generally using SDR-in-HDR on Windows.

I am writing to kindly request documentation regarding the process of generating the fixed color profiles provided in this repo. I'd like to generate profiles with luminance values that more closely align to my display for applications that read that data from the profile. I'm also interested in experimenting with 2.4 gamma for dark room usage.

Thank you again for your this repo as well as for your time and consideration. Please let me know if there are any further details or requirements needed to assist with this request.

dylanraga commented 1 year ago

Hi @ylor,

I actually had originally planned to publish the profile creation process when writing this, I'll reply & update when it's ready.

mspeedo commented 1 year ago

+1 for the guide, that would be very helpful. Now I wonder if such profiles could be used to fix EOTF tracking issues on some monitors.

dylanraga commented 1 year ago

I intended to write a tool to automate the whole process, but for now, I can explain how I've manually created these profiles.

Procedure

  1. Generate a standard sRGB color profile, tagged with your display's proper peak brightness (800 nits for HGIG) and black point (0 nits for OLED).
  2. Create a 1D LUT that maps the sRGB curve within the ST2084 space onto gamma 2.2, using the Windows SDR content brightness value as the white level
  3. Apply this calibration curve to the color profile created in Step 1
  4. Use MHC2Gen to convert the profile into an MCH2 color profile.

For Step 1, I used DisplayCal to generate a synthetic ICC profile (Tools -> Advanced -> Create synthetic ICC profile...) with Preset set to sRGB, White level to 800, Black level to 0. Keep Tone curve at sRGB. Click the "Chromatic adaptation..." button, insert 0.345702 for x and 0.358537 for y, and press "Apply". Then press "Save as..." and save it to some folder.

For Step 2, the easiest way would be to use something like Excel to calculate these values. To understand how the mapping works, take a look at the calibration curve file:

https://pastebin.com/Yr7C6yM7

It's a 1024-point 1D LUT that takes the first column (signal pixel value) and maps them to their corresponding RGB values, as shown on the right three columns (R G B). Since my mapping is an ideal grayscale mapping, R=G=B, so the right three columns are identical. To calculate these values, I've created a spreadsheet in which you can input your desired peak brightness, black level, and gamma power:

https://docs.google.com/spreadsheets/d/1XW6_5p6kDLsPUoGB7sYFrqmAqr8zyOWyJU7XN0h25mI/edit?usp=sharing

The vcgt R/G/B columns are what you should paste into the calibration file. Using an IDE like VSCode makes it simple to copy all the columns from the sheet, and paste it into columns 2/3/4 in the calibration file.

For a brief run-down on the sheet calculations, it takes each 10-bit code word, interprets it as ST2084 (HDR10 tone curve), converts it to its corresponding output luminance, converts that luminance value into its (piecewise) sRGB signal value w.r.t. the input White Level and Black Level, interprets this value as gamma-2.2 (or whatever you inserted), outputs its corresponding luminance value, and converts this back into an ST2084 signal value. At this stage, it might just be fine mapping the first PQ signal column to the gamma re-encoded PQ signal column, but if you look towards the bottom, the signal doesn't finish at 1.0, or ST2084's peak 10000 nits. Within the vcgt R/G/B columns is a shoulder that progressively adjusts the re-encoded value towards the original value, fully coinciding at the inserted White Level. This means that the tone response is untouched past your SDR content brightness value, which is good, and no dynamic range is lost.

With this .cal calibration curve, we use ArgyllCMS's applycal with both our created .cal and .icc/.icm files as input, and some new .icm file as output: applycal calfile.cal inprof.icm outprof.icm

Finally, we can use MHC2Gen to convert it into an MCH2 color profile, using MHC2Gen sdr-acm --calibrate-transfer "input.icm" "output.icm"

At the moment, MHC2Gen converts the profile white point to D50, so we need to switch it back to D65. Using ICC Profile Inspector, edit your final ICC, double-click wtpt in the Tag Table, and insert X = 0.95045, Y = 1.00000, Z = 1.08905. Apply as HDR profile, and you're done.

If you wanted to use this method to apply an RGB correction based on a characterization of your display, DisplayCal has a tool where you can dump your profile's calibration curve as a .cal file (Tools -> Show Curves -> Calibration Curves -> (Save as... (*.cal)). However, keep in mind the domain differences, as this .cal file might be with respect to some SDR space. You might be inclined to convert up from SDR to HDR, but I don't believe the calibration takes in arbitrarily-spaced input code words (first column), so you wouldn't just be able to compress the domain into ST2084.

mspeedo commented 1 year ago

Thank you very much @dylanraga , would you mind sharing your synthetic ICM profile please? I followed your guide, but for some reason, my final profile screw colors in madTPG pattern generator when running in compatibility mode with legacy ICC management enabled. Your profile does not do that, it works pretty fine. So I guess I did some error when generating synthetic profile. The only change I did in the guide was that I'm using 1000cd/m2 white level instead of 800.

dylanraga commented 1 year ago

@mspeedo I missed a step, my apologies. MHC2Gen converts the profile white point to D50, so you need to switch it back to D65. Using ICC Profile Inspector, edit your final ICC, double-click wtpt in the Tag Table, and insert X = 0.95045, Y = 1.00000, Z = 1.08905. Make sure to reapply your color profile, you might have to delete your old one under C:\Windows\System32\spool\drivers\color.

mspeedo commented 1 year ago

It did the trick, thanks again @dylanraga , but I had to use values X = 0.95045 and Z = 1.08905, then it worked fine, like your profiles.

dylanraga commented 1 year ago

Haha I put in the d50 xyz, another oopsie 🤦. Edited.

JudgmentJay commented 11 months ago

@dylanraga I was following your post above to try and create my own profile for my LG C2, but I'm having trouble on the MHC2Gen step.

Unhandled exception: LittleCms.CmsException: Error occurred in lcms2
at LittleCms.CmsNative.ThrowLastTlsError() + 0x7f
at MHC2Gen.DeviceIccContext.CreateSdrAcmIcc(Boolean) + 0xe89
at MHC2Gen.Program.<>c.<Main>b__1_2(Boolean calibrate, String deviceProfile, String outputProfile, String profdesc, Nullable`1 profver, Boolean useChromaticAdaptation) + 0x92

Any ideas? Thanks!

isham17 commented 11 months ago

@dylanraga I was following your post above to try and create my own profile for my LG C2, but I'm having trouble on the MHC2Gen step.

Unhandled exception: LittleCms.CmsException: Error occurred in lcms2
at LittleCms.CmsNative.ThrowLastTlsError() + 0x7f
at MHC2Gen.DeviceIccContext.CreateSdrAcmIcc(Boolean) + 0xe89
at MHC2Gen.Program.<>c.<Main>b__1_2(Boolean calibrate, String deviceProfile, String outputProfile, String profdesc, Nullable`1 profver, Boolean useChromaticAdaptation) + 0x92

Any ideas? Thanks!

Was trying to make a 2.4 gamma profile using this guide and got the same error as this after repeated attempts.

dylanraga commented 11 months ago

Could you send me the exact command you ran & the provided input.icm ?

JudgmentJay commented 11 months ago

Sure. ./MHC2Gen sdr-acm --calibrate-transfer "merged.icm" "final.icm" is the command I'm using. All the files are in the same directory of course. Here is the merged.icm file. I built a little node app for myself that generates the numbers to plug into the cal file since your spreadsheet had some errors in a few of the rows that I could not seem to fix. The numbers seemed to line up with what the spreadsheet was generating though so I doubt that's an issue. I can provide my icm file from step 1 and cal file from step 2 if that would help.

dylanraga commented 11 months ago

Hmm, the pre-release build @dantmnf released 5 days ago seems to be broken. Use an older version: https://drive.google.com/file/d/1q8q-fUuVFr6BRvak1oTeyYi0t4O_xqZe/view?usp=sharing

JudgmentJay commented 11 months ago

Yup, that was the issue. Thank you! The profile works great.

yunuset commented 11 months ago

The excel sheet shows an error when the black level is set to a different value than 0. sRGB values become negative.

die-morrigan commented 11 months ago

I was following your post above to create my own profile for my LG C1. I know where to put the white, black an gamma values but at which point in your instructions can I specify the SDR brightness value/SDR white screen luminance (i.e. 100 /480 nits)?

dylanraga commented 11 months ago

The excel sheet shows an error when the black level is set to a different value than 0. sRGB values become negative.

Changed that row to clip values to zero. Should work now.

I was following your post above to create my own profile for my LG C1. I know where to put the white, black an gamma values but at which point in your instructions can I specify the SDR brightness value/SDR white screen luminance (i.e. 100 /480 nits)?

In your Windows Display Settings, under Use HDR -> SDR content brightness.

baazaar13 commented 11 months ago

When you say clip it at 0, does that mean I set any F column (PQ-Reencode) values that say error to 0? Or what should I do if that's not right, please explain it to me.

fai2523 commented 11 months ago

I followed your guide and made a profile for my mini-led display. I noticed that blacks is crushed on netflix. Can I have some advice on how to fix it. Like it did not look good to begin with and everything is too dim, but with the ICC profile, I can't see anything. Look like disney+ streaming have the same issue as well..HDR look way too dark and black is crushed. Youtube and every game I tested so far work great with the profile especially diablo 4.

edit: it has 1000 nits peak brightness.

Jason-GitH commented 11 months ago

I intended to write a tool to automate the whole process, but for now, I can explain how I've manually created these profiles.

Procedure

  1. Generate a standard sRGB color profile, tagged with your display's proper peak brightness (800 nits for HGIG) and black point (0 nits for OLED).
  2. Create a 1D LUT that maps the sRGB curve within the ST2084 space onto gamma 2.2, using the Windows SDR content brightness value as the white level
  3. Apply this calibration curve to the color profile created in Step 1
  4. Use MHC2Gen to convert the profile into an MCH2 color profile.

For Step 1, I used DisplayCal to generate a synthetic ICC profile (Tools -> Advanced -> Create synthetic ICC profile...) with Preset set to sRGB, White level to 800, Black level to 0. Keep Tone curve at sRGB. Click the "Chromatic adaptation..." button, insert 0.345702 for x and 0.358537 for y, and press "Apply". Then press "Save as..." and save it to some folder.

For Step 2, the easiest way would be to use something like Excel to calculate these values. To understand how the mapping works, take a look at the calibration curve file:

https://pastebin.com/Yr7C6yM7

It's a 1024-point 1D LUT that takes the first column (signal pixel value) and maps them to their corresponding RGB values, as shown on the right three columns (R G B). Since my mapping is an ideal grayscale mapping, R=G=B, so the right three columns are identical. To calculate these values, I've created a spreadsheet in which you can input your desired peak brightness, black level, and gamma power:

https://docs.google.com/spreadsheets/d/1XW6_5p6kDLsPUoGB7sYFrqmAqr8zyOWyJU7XN0h25mI/edit?usp=sharing

The vcgt R/G/B columns are what you should paste into the calibration file. Using an IDE like VSCode makes it simple to copy all the columns from the sheet, and paste it into columns 2/3/4 in the calibration file.

For a brief run-down on the sheet calculations, it takes each 10-bit code word, interprets it as ST2084 (HDR10 tone curve), converts it to its corresponding output luminance, converts that luminance value into its (piecewise) sRGB signal value w.r.t. the input White Level and Black Level, interprets this value as gamma-2.2 (or whatever you inserted), outputs its corresponding luminance value, and converts this back into an ST2084 signal value. At this stage, it might just be fine mapping the first PQ signal column to the gamma re-encoded PQ signal column, but if you look towards the bottom, the signal doesn't finish at 1.0, or ST2084's peak 10000 nits. Within the vcgt R/G/B columns is a shoulder that progressively adjusts the re-encoded value towards the original value, fully coinciding at the inserted White Level. This means that the tone response is untouched past your SDR content brightness value, which is good, and no dynamic range is lost.

With this .cal calibration curve, we use ArgyllCMS's applycal with both our created .cal and .icc/.icm files as input, and some new .icm file as output: applycal calfile.cal inprof.icm outprof.icm

Finally, we can use MHC2Gen to convert it into an MCH2 color profile, using MHC2Gen sdr-acm --calibrate-transfer "input.icm" "output.icm"

At the moment, MHC2Gen converts the profile white point to D50, so we need to switch it back to D65. Using ICC Profile Inspector, edit your final ICC, double-click wtpt in the Tag Table, and insert X = 0.95045, Y = 1.00000, Z = 1.08905. Apply as HDR profile, and you're done.

If you wanted to use this method to apply an RGB correction based on a characterization of your display, DisplayCal has a tool where you can dump your profile's calibration curve as a .cal file (Tools -> Show Curves -> Calibration Curves -> (Save as... (*.cal)). However, keep in mind the domain differences, as this .cal file might be with respect to some SDR space. You might be inclined to convert up from SDR to HDR, but I don't believe the calibration takes in arbitrarily-spaced input code words (first column), so you wouldn't just be able to compress the domain into ST2084.

Hi, I am unclear how to use the Google sheet. What I am supposed to do after copying the three columns into VSCode Web? Should the White be 800 nits or equivalent to your SDR Content Brightness (e.g., 100 nits)?