New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
range of JzazBz coordinates #41
Comments
Without getting to the bottom of the matter, let me first suggest to vectorize the code. Python loops are awefully slow, so you never want to use them. This import numpy as np
import colorio
colorspace = colorio.SrgbLinear()
colorspace2 = colorio.JzAzBz()
X = np.linspace(0.0, 1.0, 256)
x, y, z = np.meshgrid(X, X, X)
pts = np.array([x, y, z])
jzazbz_test = colorspace2.from_xyz100(colorspace.to_xyz100(colorspace.from_srgb1(pts)))
print("min Jz={}".format(np.min(jzazbz_test[0])))
print("max Jz={}".format(np.max(jzazbz_test[0])))
print("min Az={}".format(np.min(jzazbz_test[1])))
print("max Az={}".format(np.max(jzazbz_test[1])))
print("min Bz={}".format(np.min(jzazbz_test[2])))
print("max Bz={}".format(np.max(jzazbz_test[2]))) is about 70 times faster. Next, a picture of the SRGB gamut, created with import colorio
colorio.show_srgb_gamut(colorio.JzAzBz(), "out.vtu", n=100) Okay, this conincides with what you've measured. In the article, they're referring to the Rec.2020 cube which is larger than sRGB, so there's one difference. The difference in the scales however seems too big to explain that. Perhaps I could add a function that returns the Rec.2020 gamut in any color space to confirm. It'd be awesome if you could check the values of colorio against their MATLAB |
The “XYZ” coordinates they are using are counted in nits, and range from 0 to 10000. It’s not clear if the best way to use the color space is to scale white to 10000, or instead to leave the maximum at whatever the actual luminance of the display is. |
If you want to compare against another implementation I believe https://observablehq.com/@jrus/jzazbz is correct w/r/t the paper. |
I've just checked with the Ebner-Fairchild and Hung-Berns plots of JzAzBz, and they look alright in the current implementation. The Rec2020 plot looks wrong. When changing to nits, Ebner-Fairchild and Hung-Berns are out of whack while the SRGB (Rec. 709) blob looks like what they have as Rec 2020. Something is wrong. I've tried contacting the authors over a year ago, but only got back to M. Safdar who referred me to M.R. Luo who never replied. |
I don’t know what specifically you are plotting or what “look alright” or “out of whack” mean. I would recommend you try scaling white to 10,000 before converting to Jzazbz in every one of your desired plots and see what happens. |
Thank you both for your insightful comments. My group is interested in JzAzBz because it is perceptually uniform under a Euclidean metric. So I'm not concerned about the specific coordinate ranges, even if they are different with respect to the paper, if they just reflect linear transformations of the coordinate system. So, given that the change suggested by @jrus is a rescaling, do you think the coordinate range issue is just a matter of convenience? To me, the Rec. 2020 gamut in @nschloe 's final plot just looks rotated with respect to master, which isn't an issue for my use case. |
I simply multiplied the JzAzBz output by 598.1807563064359 which is 100 divided by the Jz component corresponding to RGB triplet (255,255,255). I find the resulting numbers nice to compare with the output of the LAB/CAM02/CAM16:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I'm closing this, since based on the helpful diagnostic plots from @nschloe, I believe that the normalization/range issue is purely cosmetic, and does not affect the relations between JzAzBz coordinates. |
@eonadler It'd be great to find out what's wrong; there seems something fishy about the Rec.2020 plots in the article. |
This is a clearly false statement. But do what you like. It would be nice to hear back from the authors of the paper, since their intended meaning is not very clear in the text. |
Haven't fully read the thread yet but:
Keep in mind that PQ is an absolute EOTF, there is no free parameter for the display peak luminance, it has been modeled with Barten (1999) CSF thus there is nothing relative about it. There should be no reason for the slightly different constants used by Safdar et al. to modify this postulate. The resulting curve is not very different from PQ: I don't think it was a good idea to change it because the resulting curve does not agree with the CSF anymore. |
The curve in Safdar & al.’s paper has a somewhat different purpose from the PQ curve as far as I can tell (they just used that one as a starting point for convenience). The parameters they changed seem like they were optimized so that the full resulting color model would best match their experimental data. Safdar & al. also have another paper https://doi.org/10.2352/issn.2169-2629.2018.26.96 but I cannot get access to a copy to read it. |
No it is the same, i.e. lightness prediction, they had to slightly modify it so that it fits best the data. |
I noticed that your implementation of Jzazbz scales sRGB white to 10,000 cd/m² which is very far from the correct value! Which explains why the results I got from your implementation bear no relation to the results from running the reference Safdar et al Matlab code. Jzazbz uses a (slightly modified) Dolby PQ transfer function, which has enormous headroom for highlights. There is currently only one, research, display in the world which can actually produce a 10,000 cd/m² highlight (and then, only on a small part of the screen and only for a limited time). Commercial HDR grading monitors have a peak small-area luminance of 4,000or 2,000 cd/m². Commercial HDR TV conform to the VESA DisplayHDR standards, the most stringent grade of which requires a minimum of 1,400 cd/m² for a short-duration, 10% Center Patch test. Meanwhile the sustained, full-screen brightness test requires only 900 cd/m² (again, at the highest grade; the lowest grade requires only 320 cd/m². Since the broadcast TV world has been producing HDR content for some time and has considered mixture of SDR and HDR content and grading monitors, there are already standards and informative reports which define how to map SDR (paper or media or diffuse reflector) white onto PQ-encoded HDR. Rep. ITU-R BT.2390-8 section 5.3.1 describes a peak luminance for SDR (from BT.709 and BT.1886) of 100 cd/m² compared to the PQ peak of 10,000 cd/m². Meanwhile Dolby, in Reference Level Guidelines for PQ (BT.2100), working from the PQ value of 0.34 for an 18% grey card, state that diffuse white at 0.54 PQ value corresponding to a luminance of 140 cd/m². I find the Dolby analysis more detailed and convincing, and thus suggest that all SDR color spaces (sRGB, AdbeRGB, Display P3, etc) have their peak white, which is a full-screen long duration diffuse white, mapped to 140 cd/m² when converting into PQ-based colorspaces like BT.2100 (Bt.2020 primaries and PQ transfer curve) or Jzazbz. Mapping it to 10,000 means you are asking for a blinding white which cannot be displayed. It also means you have zero headroom for specular highlights, defeating the entire point of HDR in general and PQ in particular. |
@svgeesus Thanks for the thorough analysis. Could you run some "random" numbers through the upstream MATLAB script and post them here? I'd love to make sure that colorio gives the same output. Edit: I ran the scripts with Octave and colorio.JzAzBz() returns the same results. |
@svgeesus You’ll notice there is a line in there:
Which can trivially be changed (and which I have fiddled around in various experimenting; a previous published version of this notebook had this set to a value of
I would hope not. The implementation of the Jzazbz model and its inverse (from XYZ coordinates) should be exactly identical to the paper / Matlab / etc. If it is not, please tell me. The Matlab code provided by Safdar & al. does not provide any method at all for obtaining XYZ inputs, but if you put the same XYZ inputs into the Matlab code and into my Javascript you should get identical results. What was not clear to me is precisely how Safdar & al. expect their model to be scaled for non-HDR displays, and how their data fitting of existing colorimetric data was handled in construction of their model. I found their paper quite unclear/sparse about this general topic, with a lot of details of their data fitting left implicit/underspecified. If you set this parameter to a value of
Dramatically changing the value of the exponent applied to the inputs makes a pretty big difference. But then they use this as just one step among several in their (inverse) model: the most important other one is a rational function applied to lightness. The result is that the shape is substantially different: https://www.desmos.com/calculator/pu7ylakohj P.S. if you don’t directly ping someone by name in a github discussion on someone else’s project, github doesn’t make a particularly obvious notification about it. |
I just double checked, and for every input I have tried results match to like 14 digits. Here’s an example screenshot showing Matlab and Javascript: |
Just faced to the same problem. Found another implementation where white luminance parameter is 1 https://github.com/colour-science/colour/blob/develop/colour/models/jzazbz.py#L436 |
We have encountered the following issue, which has been reproduced using a few independent RGB --> JzAzBz implementations (including colorio and https://github.com/quag/JzAzBz/blob/master/python3/srgb255ToJzAzBz.py) ---
The JzAzBz paper (https://doi.org/10.1364/OE.25.015131) seems to suggest that the coordinates should span roughly Jz in (0,1), Az in (-0.5, 0.5), Bz in (-0.5,0.5). However, when we explicitly map all RGB tuples in (0,255) x (0,255) x (0,255) to JzAzBz as follows, we find that (Jz, Az, Bz) span (0,0.167), (-0.09,1.09), (-0.156,0.115).
So we're wondering if the original paper renormalized their coordinates, or if there's a mistake on our end.
Extremely simplified code that demonstrates the basic issue:
The text was updated successfully, but these errors were encountered: