Skip to content

Commit d3ec9d4

Browse files
authored
Remove Exif orientation after saving it to irot/imir when reading JPEGs.
Currently we read the Exif Orientation tag and translate it to irot/imir, but also leave the Exif as is. However, MIAF says that AVIF files should not contain Exif transformations. Fixes #3078.
1 parent c67bc45 commit d3ec9d4

4 files changed

Lines changed: 33 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The changes are relative to the previous release, unless the baseline is specifi
2727
* Add --sato flag to avifdec to enable Sample Transforms support at decoding.
2828
* Add --grid option to avifgainmaputil.
2929
* Apply clean aperture crop, rotation and mirror when decoding to PNG or JPEG.
30+
Remove orientation information from Exif if present.
3031

3132
### Changed since 1.3.0
3233

@@ -61,6 +62,8 @@ The changes are relative to the previous release, unless the baseline is specifi
6162
libaom v3.13.0 or later.
6263
* Converting an image containing a gain map using avifenc with the --grid flag
6364
now also splits the gain map into a grid.
65+
* In avifenc, set Exif orientation to 1 (no transformation) when converting
66+
JPEGs to Avif.
6467

6568
### Removed since 1.3.0
6669

apps/shared/avifjpeg.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,15 +1394,22 @@ static avifBool avifJPEGReadInternal(FILE * f,
13941394
goto cleanup;
13951395
}
13961396

1397-
// Exif orientation, if any, is imported to avif->irot/imir and kept in avif->exif.
1398-
// libheif has the same behavior, see
1399-
// https://github.com/strukturag/libheif/blob/ea78603d8e47096606813d221725621306789ff2/examples/heif_enc.cc#L403
1397+
// Exif orientation, if any, is imported to avif->irot/imir, and the Exif data is saved to avif->exif.
14001398
if (avifImageSetMetadataExif(avif,
14011399
marker->data + AVIF_JPEG_EXIF_HEADER_LENGTH,
14021400
marker->data_length - AVIF_JPEG_EXIF_HEADER_LENGTH) != AVIF_RESULT_OK) {
14031401
fprintf(stderr, "Setting Exif metadata failed: %s (out of memory)\n", inputFilename);
14041402
goto cleanup;
14051403
}
1404+
// Set the Exif orientation to 1 (no transformation).
1405+
// ISO/IEC 23000-22:2024 (MIAF), Section 7.3.10.1:
1406+
// There should be no image transformations expressed by Exif (rotation,
1407+
// mirroring, etc.) indicated in the Exif metadata, in files encoded according
1408+
// to this document.
1409+
// Do not check for errors, it's a "should" so ok to do on a best-effort basis.
1410+
// Moreover it should only fail if the Exif is marlformed or there is no orientation
1411+
// tag to begin with.
1412+
(void)avifSetExifOrientation(&avif->exif, 1);
14061413
found = AVIF_TRUE;
14071414
}
14081415
}

apps/shared/avifutil.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ avifBool avifImageSplitGrid(const avifImage * gridSplitImage, uint32_t gridCols,
622622
}
623623
}
624624
if (gridSplitImage->exif.size > 0) {
625-
const avifResult result = avifImageSetMetadataExif(firstCell, gridSplitImage->exif.data, gridSplitImage->exif.size);
625+
const avifResult result = avifRWDataSet(&firstCell->exif, gridSplitImage->exif.data, gridSplitImage->exif.size);
626626
if (result != AVIF_RESULT_OK) {
627627
fprintf(stderr, "ERROR: Failed to set Exif metadata on grid cell: %s\n", avifResultToString(result));
628628
return AVIF_FALSE;

tests/gtest/avifmetadatatest.cc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,11 +246,21 @@ TEST(MetadataTest, ExifOrientation) {
246246
testutil::ReadImage(data_path, "paris_exif_orientation_5.jpg");
247247
ASSERT_NE(image, nullptr);
248248
// The Exif metadata contains orientation information: 5.
249+
// When reading the JPEG file, the Exif orientation is set to 1.
249250
EXPECT_GT(image->exif.size, 0u);
250251
EXPECT_EQ(image->transformFlags & (AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR),
251252
avifTransformFlags{AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR});
252253
EXPECT_EQ(image->irot.angle, 1u);
253254
EXPECT_EQ(image->imir.axis, 0u);
255+
testutil::AvifRwData originalReadExif;
256+
ASSERT_EQ(
257+
avifRWDataSet(&originalReadExif, image->exif.data, image->exif.size),
258+
AVIF_RESULT_OK);
259+
// For testing purposes, set back the orientation.
260+
ASSERT_EQ(avifSetExifOrientation(&image->exif, 5), AVIF_RESULT_OK);
261+
// The two Exifs are different, showing that the Orientation was indeed
262+
// modified when reading.
263+
EXPECT_FALSE(testutil::AreByteSequencesEqual(originalReadExif, image->exif));
254264

255265
const testutil::AvifRwData encoded =
256266
testutil::Encode(image.get(), AVIF_SPEED_FASTEST);
@@ -313,6 +323,15 @@ TEST(MetadataTest, ExifOrientationAndForcedImir) {
313323
EXPECT_GT(image->exif.size, 0u);
314324
image->transformFlags = AVIF_TRANSFORM_IMIR;
315325
image->imir.axis = 1;
326+
testutil::AvifRwData originalReadExif;
327+
ASSERT_EQ(
328+
avifRWDataSet(&originalReadExif, image->exif.data, image->exif.size),
329+
AVIF_RESULT_OK);
330+
// For testing purposes, set back the orientation.
331+
ASSERT_EQ(avifSetExifOrientation(&image->exif, 5), AVIF_RESULT_OK);
332+
// The two Exifs are different, showing that the Orientation was indeed
333+
// modified when reading.
334+
EXPECT_FALSE(testutil::AreByteSequencesEqual(originalReadExif, image->exif));
316335

317336
const testutil::AvifRwData encoded =
318337
testutil::Encode(image.get(), AVIF_SPEED_FASTEST);

0 commit comments

Comments
 (0)