Skip to content

Commit

Permalink
Allow lossless compression using 4:0:0 on grayscale input. (#1460)
Browse files Browse the repository at this point in the history
Allow lossless compression using 4:0:0 on grayscale input.

This fixes #1453
  • Loading branch information
vrabaud authored Jul 20, 2023
1 parent 1488373 commit 424b0f9
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ List of incompatible ABI changes in this release:
* Add API for multi-threaded YUV to RGB color conversion.
* Add experimental support for AV2 behind the compilation flag AVIF_CODEC_AVM.
AVIF_CODEC_CHOICE_AVM is now part of avifCodecChoice.
* Allow lossless 4:0:0 on grayscale input.
* Add avifenc --no-overwrite flag to avoid overwriting output file.
* Add support for all transfer functions when using libsharpyuv.

Expand Down
25 changes: 15 additions & 10 deletions apps/avifenc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1488,14 +1488,13 @@ int main(int argc, char * argv[])
// Check lossy/lossless parameters and set to default if needed.
if (lossless) {
// Pixel format.
if (input.requestedFormat != AVIF_PIXEL_FORMAT_NONE && input.requestedFormat != AVIF_PIXEL_FORMAT_YUV444) {
if (input.requestedFormat != AVIF_PIXEL_FORMAT_NONE && input.requestedFormat != AVIF_PIXEL_FORMAT_YUV444 &&
input.requestedFormat != AVIF_PIXEL_FORMAT_YUV400) {
fprintf(stderr,
"When set, the pixel format can only be 444 in lossless "
"mode.\n");
"mode. 400 also works if the input is grayscale.\n");
returnCode = 1;
}
// Don't subsample when using AVIF_MATRIX_COEFFICIENTS_IDENTITY.
input.requestedFormat = AVIF_PIXEL_FORMAT_YUV444;
// Quality.
if ((settings.quality != INVALID_QUALITY && settings.quality != AVIF_QUALITY_LOSSLESS) ||
(settings.qualityAlpha != INVALID_QUALITY && settings.qualityAlpha != AVIF_QUALITY_LOSSLESS)) {
Expand Down Expand Up @@ -1626,10 +1625,12 @@ int main(int argc, char * argv[])
image->alphaPremultiplied = premultiplyAlpha;

if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (input.requestedFormat != AVIF_PIXEL_FORMAT_NONE) &&
(input.requestedFormat != AVIF_PIXEL_FORMAT_YUV444)) {
// User explicitly asked for non YUV444 yuvFormat, while matrixCoefficients was likely
// set to AVIF_MATRIX_COEFFICIENTS_IDENTITY as a side effect of --lossless,
// and Identity is only valid with YUV444. Set matrixCoefficients back to the default.
(input.requestedFormat != AVIF_PIXEL_FORMAT_YUV444) && (input.requestedFormat != AVIF_PIXEL_FORMAT_YUV400)) {
// User explicitly asked for non YUV444/YUV400 yuvFormat, while
// matrixCoefficients was likely set to
// AVIF_MATRIX_COEFFICIENTS_IDENTITY as a side effect of --lossless, and
// Identity is only valid with YUV444/YUV400. Set matrixCoefficients
// back to the default.
image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;

if (cicpExplicitlySet) {
Expand Down Expand Up @@ -1662,7 +1663,8 @@ int main(int argc, char * argv[])
}

// Check again for y4m input (y4m input ignores input.requestedFormat and retains the format in file).
if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444)) {
if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444) &&
(image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)) {
fprintf(stderr, "matrixCoefficients may not be set to identity (0) when subsampling.\n");
returnCode = 1;
goto cleanup;
Expand Down Expand Up @@ -1797,7 +1799,10 @@ int main(int argc, char * argv[])

if (sourceWasRGB) {
if (!using444 && !using400) {
fprintf(stderr, "WARNING: [--lossless] Input data was RGB and YUV subsampling (-y) isn't YUV444. Output might not be lossless.\n");
fprintf(stderr,
"WARNING: [--lossless] Input data was RGB and YUV "
"subsampling (-y) isn't YUV444 or YUV400. Output might "
"not be lossless.\n");
lossless = AVIF_FALSE;
}

Expand Down
6 changes: 3 additions & 3 deletions apps/shared/avifpng.c
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,11 @@ avifBool avifPNGRead(const char * inputFilename,
const avifBool useYCgCoR = AVIF_FALSE;
#endif
if (avif->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
if (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY || useYCgCoR) {
if ((rawColorType == PNG_COLOR_TYPE_GRAY) || (rawColorType == PNG_COLOR_TYPE_GRAY_ALPHA)) {
avif->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
} else if (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY || useYCgCoR) {
// Identity and YCgCo-R are only valid with YUV444.
avif->yuvFormat = AVIF_PIXEL_FORMAT_YUV444;
} else if ((rawColorType == PNG_COLOR_TYPE_GRAY) || (rawColorType == PNG_COLOR_TYPE_GRAY_ALPHA)) {
avif->yuvFormat = AVIF_PIXEL_FORMAT_YUV400;
} else {
avif->yuvFormat = AVIF_APP_DEFAULT_PIXEL_FORMAT;
}
Expand Down
7 changes: 5 additions & 2 deletions src/reformat.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ static avifBool avifPrepareReformatState(const avifImage * image, const avifRGBI
return AVIF_FALSE;
}

if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444)) {
if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444) &&
(image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)) {
return AVIF_FALSE;
}

Expand Down Expand Up @@ -384,7 +385,9 @@ avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb)
}

// Populate any subsampled channels with averages from the 2x2 block
if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
// Do nothing on chroma planes.
} else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
// YUV420, average 4 samples (2x2)

float sumU = 0.0f;
Expand Down
2 changes: 1 addition & 1 deletion tests/test_cmd_avm_lossless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ pushd ${TMP_DIR}
"${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}"

# Combining some arguments with lossless should fail.
for option in "-y 400" "--min 0 --max 1" "-r limited" "--cicp 2/2/8"; do
for option in "-y 420" "--min 0 --max 1" "-r limited" "--cicp 2/2/8"; do
"${AVIFENC}" -c avm $option -s 10 -l "${DECODED_FILE}" -o "${ENCODED_FILE}" && exit 1
done

Expand Down
2 changes: 1 addition & 1 deletion tests/test_cmd_icc_profile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ pushd ${TMP_DIR}

"${AVIFENC}" -s 8 -l "${INPUT_GRAY_PNG}" -o "${ENCODED_FILE}"
"${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}"
"${IMAGEMAGICK}" "${DECODED_FILE}" -profile "${SRGB_ICC}" "${CORRECTED_FILE}"
"${IMAGEMAGICK}" "${DECODED_FILE}" -define png:color-type=2 -profile "${SRGB_ICC}" "${CORRECTED_FILE}"
"${ARE_IMAGES_EQUAL}" "${REFERENCE_GRAY_PNG}" "${CORRECTED_FILE}" 0 45
popd

Expand Down
9 changes: 8 additions & 1 deletion tests/test_cmd_lossless.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ ARE_IMAGES_EQUAL="${BINARY_DIR}/tests/are_images_equal"

# Input file paths.
INPUT_PNG="${TESTDATA_DIR}/paris_icc_exif_xmp.png"
INPUT_GRAY_PNG="${TESTDATA_DIR}/kodim03_grayscale_gamma1.6.png"
# Output file names.
ENCODED_FILE="avif_test_cmd_lossless_encoded.avif"
DECODED_FILE="avif_test_cmd_lossless_decoded.png"
Expand All @@ -62,7 +63,7 @@ pushd ${TMP_DIR}
"${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE}"

# Combining some arguments with lossless should fail.
for option in "-y 400" "--min 0 --max 1" "-r limited" "--cicp 2/2/8"; do
for option in "-y 420" "--min 0 --max 1" "-r limited" "--cicp 2/2/8"; do
"${AVIFENC}" $option -s 10 -l "${DECODED_FILE}" -o "${ENCODED_FILE}" && exit 1
done

Expand All @@ -76,6 +77,12 @@ pushd ${TMP_DIR}
"${AVIFENC}" -s 10 -l "${INPUT_PNG}" -o "${ENCODED_FILE}"
"${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE_LOSSLESS}"
"${ARE_IMAGES_EQUAL}" "${INPUT_PNG}" "${DECODED_FILE_LOSSLESS}" 0

# 400 test.
echo "Testing 400 lossless"
"${AVIFENC}" -y 400 -s 10 -l "${INPUT_GRAY_PNG}" -o "${ENCODED_FILE}"
"${AVIFDEC}" "${ENCODED_FILE}" "${DECODED_FILE_LOSSLESS}"
"${ARE_IMAGES_EQUAL}" "${INPUT_GRAY_PNG}" "${DECODED_FILE_LOSSLESS}" 0
popd

exit 0

0 comments on commit 424b0f9

Please sign in to comment.