Skip to content
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

Add experimental support for reading and writing gain maps. #1501

Merged
merged 8 commits into from
Aug 29, 2023

Conversation

maryla-uc
Copy link
Collaborator

The feature is hidden behind the AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP compile flag.

See https://helpx.adobe.com/camera-raw/using/gain-map.html for an introduction to gain maps. Note that in this change, gain maps are not stored as auxiliary items as proposed by Adobe, but as a hidden input to a new 'tmap' (tone mapped) derived item.
An 'altr' (alternatives group) box is also added to tell the decoder that it may show the tone mapped image (preferrably) or the base image as a fall back. The primary item id still points to the base image for backward compatibility.

CMakeLists.txt Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
src/read.c Show resolved Hide resolved
@maryla-uc maryla-uc force-pushed the apple_gainmaps branch 5 times, most recently from 8801215 to f02b994 Compare August 18, 2023 13:45
@maryla-uc maryla-uc requested a review from y-guyon August 18, 2023 16:42
include/avif/avif.h Outdated Show resolved Hide resolved
src/write.c Outdated
// only process alpha payloads when alphaPass is true
avifBool isAlphaOrGainMap = item->itemCategory == AVIF_ITEM_ALPHA;
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
isAlphaOrGainMap = isAlphaOrGainMap || item->itemCategory == AVIF_ITEM_GAIN_MAP || !memcmp(item->type, "tmap", 4);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be isAlphaOrGainMap |=, fine as is as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would work but it's not exactly the same thing since |= is a bitwise operator, and won't skip evaluating the right side if the value is already true. Not that I think the performance matters much, but I think it's better to keep |= for actual bitwise operations like flags like we do now.

tests/gtest/avifgainmaptest.cc Outdated Show resolved Hide resolved
const int num_cols = (int)cell_rows[0].size();
AVIF_CHECKRES(
testutil::MergeGrid(num_cols, num_rows, cell_images, grid.get()));

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rows below should be removed now that there is testutil::MergeGrid()

Note: testutil::MergeGrid() could be made in another PR to reduce the size of this one

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed I forgot to remove them, thanks for spotting it.
I agree it could be split... but I'm afraid of spending a while fighting with git and merges again... So if it's not a strong request I'd rather leave it as is.

tests/gtest/aviftest_helpers.cc Outdated Show resolved Hide resolved
@maryla-uc
Copy link
Collaborator Author

Note I rebased this patch but had some issues with the merge and I had to add my latests fixes to the last commit, apologies for the confusing history.

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: I only reviewed the public header avif.h. I suggest some changes in anticipation of the libavif 1.0.0 release.

include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
tests/gtest/avifgridapitest.cc Show resolved Hide resolved
src/write.c Outdated Show resolved Hide resolved
include/avif/avif.h Outdated Show resolved Hide resolved
@wantehchang wantehchang dismissed their stale review August 22, 2023 20:27

Requested changes have been made

apps/avifenc.c Outdated
@@ -1945,7 +1945,7 @@ int main(int argc, char * argv[])
}
}

avifIOStats ioStats = { 0, 0, 0 };
avifIOStats ioStats = { 0, 0 };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: It would be good to modify the following code in avifenc.c:

    printf("Encoded successfully.\n");
    printf(" * Color AV1 total size: %" AVIF_FMT_ZU " bytes\n", ioStats.colorOBUSize);
    printf(" * Alpha AV1 total size: %" AVIF_FMT_ZU " bytes\n", ioStats.alphaOBUSize);

to also print the gain map AV1 total size, and see if you find it useful to report that information.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, there's no way to encode a gain map with avifenc right now, only through the API, so this can be done later I think.

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avif.h LGTM.

The feature is hidden behind the AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP compile flag.

See https://helpx.adobe.com/camera-raw/using/gain-map.html for an introduction
to gain maps. Note that in this change, gain maps are not stored as auxiliary
items as proposed by Adobe, but as a hidden input to a new 'tmap' (tone mapped)
derived item.
An 'altr' (alternatives group) box is also added to tell the decoder that it
may show the tone mapped image (preferrably) or the base image as a fall back.
The primary item id still points to the base image for backward compatibility.
Move new fields to the end of structs.
Add back accidentally deleted test code.
Remove gainMapOBUSize.
@maryla-uc maryla-uc merged commit 50a5414 into AOMediaCodec:main Aug 29, 2023
14 checks passed
Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: Could you add an entry to CHANGELOG.md? Please indicate that this is an experimental feature. Thanks.

Copy link
Collaborator

@wantehchang wantehchang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maryla: In this round of review, I only checked whether the pull request is essentially a no-op if the experimental feature is disabled. Unfortunately I didn't make a complete pass through the PR. (I stopped after src/read.c, so I haven't reviewed src/write.c.)

This PR has more code and comments outside #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP). We should try to move as much code into #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP) as possible. It is more difficult to deal with the comments. I suggested adding "if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defned" to the comments, but that may become a little wordy.

// images containing a gain map encoded with the current version of libavif might
// not decode with a feature future version of libavif. Use are your own risk.
// This is based on ISO/IEC JTC 1/SC 29/WG 3 m64379
// This product includes Gain Map technology under license by Adobe.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Move this comment block inside #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)

@@ -577,6 +680,7 @@ AVIF_API avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t *
// Sets XMP metadata.
AVIF_API avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize);

// Allocate/free/steal planes. These functions do not affect the gain map image if present.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit/optional: In these new comments (lines 668, 670, 683), add "if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined".

@@ -92,6 +93,7 @@ void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage);
// Copies the samples from srcImage to dstImage. dstImage must be allocated.
// srcImage and dstImage must have the same width, height, and depth.
// If the AVIF_PLANES_YUV bit is set in planes, then srcImage and dstImage must have the same yuvFormat and yuvRange.
// The gain map (if present) is not affected.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit/optional: Add "if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined" to this comment.

@@ -681,7 +683,7 @@ typedef struct avifTile
avifCodecDecodeInput * input;
avifCodecType codecType;
// This may point to a codec that it owns or point to a shared codec that it does not own. In the shared case, this will
// point to one of avifDecoderData.codec or avifDecoderData.codecAlpha.
// point to one of avifDecoderData.codec, avifDecoderData.codecAlpha or avifDecoderData.codecGainMap.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add "if AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP is defined" to this comment.

@@ -815,6 +817,7 @@ typedef struct avifDecoderData
avifTileArray tiles;
avifTileInfo color;
avifTileInfo alpha;
avifTileInfo gainMap;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Put inside #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP).

@@ -830,6 +833,7 @@ typedef struct avifDecoderData
// decoder instance (same as above).
avifCodec * codec;
avifCodec * codecAlpha;
avifCodec * codecGainMap;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Put inside #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP).

@@ -4025,7 +4246,7 @@ avifResult avifDecoderReset(avifDecoder * decoder)

if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
decoder->ioStats.alphaOBUSize += sample->size;
} else {
} else if (tile->input->itemCategory == AVIF_ITEM_COLOR) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Reorder this in more natural order:

            if (tile->input->itemCategory == AVIF_ITEM_COLOR) {
                decoder->ioStats.colorOBUSize += sample->size;
            } else if (tile->input->itemCategory == AVIF_ITEM_ALPHA) {
                decoder->ioStats.alphaOBUSize += sample->size;
            }

assert(dstImage);
}
#endif
AVIF_CHECKRES(avifDecoderDataAllocateGridImagePlanes(decoder->data, info, dstImage));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also change decoder->image to dstImage below, at line 4495?

} else
#endif
if ((decoder->image->width != src->width) || (decoder->image->height != src->height) ||
(decoder->image->depth != src->depth)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nesting of #if and if breaks indentation. We should try to restructure this, e.g., as a switch statement:

             switch (tile->input->itemCategory) {
#if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP)
                case AVIF_ITEM_GAIN_MAP:
                    decoder->image->gainMap.image->width = src->width;
                    decoder->image->gainMap.image->height = src->height;
                    decoder->image->gainMap.image->depth = src->depth;
                    break;
#endif
                default:
                    ...
                    break
             }

You can experiment with other alternatives.

typedef struct avifEncoderData
{
avifEncoderItemArray items;
avifEncoderFrameArray frames;
// Map the encoder settings quality and qualityAlpha to quantizer and quantizerAlpha
int quantizer;
int quantizerAlpha;
int quantizerGainMap;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Put this inside #if defined(AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP).

maryla-uc added a commit to maryla-uc/libavif that referenced this pull request Aug 31, 2023
Reverts changes in avifDecoderItemShouldBeSkipped() introduced in AOMediaCodec#1501
The new changes are made in code garded by AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP.
maryla-uc added a commit to maryla-uc/libavif that referenced this pull request Aug 31, 2023
Reverts changes in avifDecoderItemShouldBeSkipped() introduced in AOMediaCodec#1501
The new changes are made in code garded by AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP.
maryla-uc added a commit that referenced this pull request Aug 31, 2023
Reverts changes in avifDecoderItemShouldBeSkipped() introduced in #1501
The new changes are made in code guarded by AVIF_ENABLE_EXPERIMENTAL_GAIN_MAP.
maryla-uc added a commit to maryla-uc/libavif that referenced this pull request Aug 31, 2023
maryla-uc added a commit to maryla-uc/libavif that referenced this pull request Aug 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants