strukturag / libheif

libheif is an HEIF and AVIF file format decoder and encoder.
Other
1.78k stars 306 forks source link

Save matrix_coefficients when exporting AVIF #259

Closed novomesk closed 4 years ago

novomesk commented 4 years ago

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.

farindk commented 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.

farindk commented 4 years ago

Should work now. Please test. There are also options in heif-enc to set the nclx parameters.

novomesk commented 4 years ago

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.

farindk commented 4 years ago

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.

novomesk commented 4 years ago

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.

novomesk commented 4 years ago

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);