Closed novomesk closed 4 years ago
Ok. My plan is as follows: when a nclx profile was set, RGB input will be converted with these coefficients. Otherwise, ITU_R_BT_601_6 will be used (and saved) as default. If the input is YCbCr, it will be assumed that these are already in the correct colorspace. No conversion is done.
Should work now. Please test. There are also options in heif-enc
to set the nclx parameters.
I tried heif-enc and the value 6 is saved:
* Resolution : 2048x1368
* Bit Depth : 8
* Format : YUV420
* Alpha : Absent
* Range : Limited
* Color Primaries: 0
* Transfer Char. : 0
* Matrix Coeffs. : 6
But I think the Color Primaries and Transfer Characteristics should not be zero, better use 2 == unspecified.
BTW, why limited range?
I tried to save AVIF from my GIMP plug-in and the Matrix Coeffs. was still 2.
I changed the defaults. When no nclx profile is set, the default matrix coeffs should be 6. And now primaries and transfer = undefined, full_range = true. According to my tests, this appears to work.
OK, now we have matrix_coefficients saved when exporting AVIF without ICC profile.
But when we have ICC (stored colour_type="prof" so we can't have colour_type="nclx" now), will you save the coefficients to AV1 stream? (so libavif and other implementations will find it)
Even in situations when you are saving colour_type="nclx" only (no ICC), you can pass the 3 values (prim/transf_char./matrix_coefficients) to AV1 encoder, there is no problem to have identical values in NCLX and in AV1 stream.
The good news is that now when I use heif_image_set_nclx_color_profile
when saving AVIF, the image looks visually OK when decoded via libavif-based SW.
BTW there was a bug in libavif (https://github.com/AOMediaCodec/libavif/issues/281), it used full_range_flag from AV1 stream and not from NCLX profile. Libavif thought the image is limited range instead of full range. I think when NCLX is present, same values can be sent to AV1 stream too.
When I save AVIF with ICC profile using heif_image_set_raw_color_profile (h_image, "prof", icc_data, icc_length);
I still get different colours when decoding picture (both heif-convert or libavif-based SW). The result looks like decoding with Limited range. That's why there is a need to signal full range in AV1 stream.
When I added following line to heif_encoder_aom.cc, the decoded picture looked much better:
aom_codec_control(&codec, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE);
When saving AVIF with ICC profile, RGB_to_YcbCr conversion is done according r*0.299f + g*0.587f + b*0.114f coefficients. This represents matrix coefficients 6 (BT.601).
Unfortunately other AVIF implementations don’t know that these coefficients were used during RGB->YUV conversion, and they might use different coefficients when they open the saved file and perform YUV->RGB conversion. User will get different colors and that’s wrong.
If you tell AOM encoder what coefficients where used, other implementation will use the corresponding coefficients too. I think something like this should work:
aom_codec_control(&encoder->codec, AV1E_SET_MATRIX_COEFFICIENTS, 6 /*heif_matrix_coefficients_ITU_R_BT_601_6*/);
I observed there is a possibility to set NCLX profile when saving AVIF:
heif_image_set_nclx_color_profile
If user sets matrix_coefficients via heif_color_profile_nclx, they should be used in RGB->YUV conversion too. If only BT.601 coefficients are used during export, value 6 should be forced on output.