Laythe
Laythe

Reputation: 85

Invalid ICC profile returns opRGB from WCS using WIC

I've been racking my head trying to figure out what Microsoft WIC/WCS library is doing here and would appreciate any help from anyone versed in Windows Colour Management. The documentation on this API is awful.

I'm reading a JPEG with an embedded ICC profile, using OpenColourProfile, but Windows does not seem to parse the embedded ICC information correctly, for some images. For others, it works ok. However, even though it fails to parse the header information, using the Windows API, I am able to display all images correctly, it is just the parsing of the embedded ICC profile header information that is erroneous.


Not Working

Using the exif_tool tool to display the ICC profile header information for the image that does not work from my code, exif_tool correctly returns:

Profile CMM Type                : Adobe Systems Inc.
Profile Version                 : 2.1.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
Profile Connection Space        : XYZ
Profile Date Time               : 2000:08:11 19:51:59
Profile File Signature          : acsp
Primary Platform                : Apple Computer Inc.
CMM Flags                       : Not Embedded, Independent
Device Manufacturer             : none
Device Model                    :
Device Attributes               : Reflective, Glossy, Positive, Color
Rendering Intent                : Perceptual
Connection Space Illuminant     : 0.9642 1 0.82491
Profile Creator                 : Adobe Systems Inc.
Profile ID                      : 0
Profile Copyright               : Copyright 2000 Adobe Systems Incorporated
Profile Description             : Adobe RGB (1998)
Media White Point               : 0.95045 1 1.08905
Media Black Point               : 0 0 0
Red Tone Reproduction Curve     : (Binary data 14 bytes, use -b option to extract)
Green Tone Reproduction Curve   : (Binary data 14 bytes, use -b option to extract)
Blue Tone Reproduction Curve    : (Binary data 14 bytes, use -b option to extract)
Red Matrix Column               : 0.60974 0.31111 0.01947
Green Matrix Column             : 0.20528 0.62567 0.06087
Blue Matrix Column              : 0.14919 0.06322 0.74457

After reading the ICC profile header in my code, Windows returns the CMM Type as 0000, which is undefined (see below). Also the version tag is parsed at v2.0 (0x02000000), not v2.1 as shown in the exif_tool output above.

enter image description here

Also, following this, a query (in my code) to return the ICC tag 0x64657363 (ProfileDescription) returns "opRGB" instead of "Adobe RGB (1998)".

In terms of EXIF data for this image, the Color Space is defined as "Uncalibrated" and there is an "Interoperability Index" specified of value "R03", which I understand is to be interpreted as Adobe RGB.

Working


Using another JPG with an embedded Adobe RGB (1998) profile, I ran it through the exif_tool and it displays:

Profile CMM Type                : Adobe Systems Inc.
Profile Version                 : 2.1.0
Profile Class                   : Display Device Profile
Color Space Data                : RGB
Profile Connection Space        : XYZ
Profile Date Time               : 1999:06:03 00:00:00
Profile File Signature          : acsp
Primary Platform                : Microsoft Corporation
CMM Flags                       : Not Embedded, Independent
Device Manufacturer             : none
Device Model                    :
Device Attributes               : Reflective, Glossy, Positive, Color
Rendering Intent                : Media-Relative Colorimetric
Connection Space Illuminant     : 0.9642 1 0.82491
Profile Creator                 : Adobe Systems Inc.
Profile ID                      : 0
Profile Copyright               : Copyright (c) 1999 Adobe Systems Incorporated. All     Rights Reserved.
Profile Description             : Adobe RGB (1998)
Media White Point               : 0.95045 1 1.08905
Media Black Point               : 0 0 0
Red Tone Reproduction Curve     : (Binary data 14 bytes, use -b option to extract)
Green Tone Reproduction Curve   : (Binary data 14 bytes, use -b option to extract)
Blue Tone Reproduction Curve    : (Binary data 14 bytes, use -b option to extract)
Red Matrix Column               : 0.60974 0.31111 0.01947
Green Matrix Column             : 0.20528 0.62567 0.06087
Blue Matrix Column              : 0.14919 0.06322 0.74457

When using OpenColourProfile, my code (which uses Windows API) correctly parses the header information:

enter image description here

Following this, a query (in my code) to return the ICC tag 0x64657363 (ProfileDescription) correctly returns "Adobe RGB (1998)".

In terms of EXIF data for this image, the Color Space is defined as "Uncalibrated" and there is no "Interoperability Index" specified.

Code


    /* COLOUR PROFILE MANAGEMENT */
    IWICColorContext* pContextSrc = NULL;
    IWICColorContext* pContextDst = NULL;

    hr = factory->CreateColorContext(&pContextSrc);
    if (!SUCCEEDED(hr))
        return nullptr;

    UINT numColourContexts = 0;
    hr = wic->mFrame->GetColorContexts(1, &pContextSrc, &numColourContexts);

    if (numColourContexts > 0)
    {
        hr = factory->CreateColorContext(&pContextDst);
        if (!SUCCEEDED(hr))
            return nullptr;

        WCHAR destColourContextFilename[_MAX_PATH + 1];
        DWORD destColourContextFilenameSize = sizeof(destColourContextFilename);

        if (GetColorDirectory(NULL, destColourContextFilename, &destColourContextFilenameSize))
        {
            hr = StringCchCatW(destColourContextFilename,
                sizeof(destColourContextFilename) / sizeof(destColourContextFilename[0]),
                L"\\sRGB Color Space Profile.icm");
        }
        else
        {
            hr = E_UNEXPECTED;
        }

        if (SUCCEEDED(hr))
        {
            hr = pContextDst->InitializeFromFilename(destColourContextFilename);
        }


        if (SUCCEEDED(hr))
        {
            hr = factory->CreateColorTransformer(&wic->pColorTransform);
        }

        if (SUCCEEDED(hr))
        {
            hr = wic->pColorTransform->Initialize(wic->mFrame.get(),
                pContextSrc,
                pContextDst,
                wic->mPixelFormat);

            if (!SUCCEEDED(hr))
            {
                return nullptr;
            }
        }

            //  WICColorContextUninitialized = 0,
            //  WICColorContextProfile = 0x1,
            //  WICColorContextExifColorSpace = 0x2
            WICColorContextType type;
            pContextSrc->GetType(&type);

            if (type == WICColorContextType::WICColorContextProfile)
            {
                UINT cbProfile = 0;
                hr = pContextSrc->GetProfileBytes(0, NULL, &cbProfile);

                if (!SUCCEEDED(hr))
                    return nullptr;

                VOID* pvProfile = NULL;

                if (SUCCEEDED(hr))
                {
                    // allocate the block
                    pvProfile = HeapAlloc(
                        GetProcessHeap(),
                        HEAP_ZERO_MEMORY,
                        cbProfile);

                    hr = pvProfile ? S_OK : E_FAIL;
                }

                UINT cbSize = 0;
                if (SUCCEEDED(hr))
                {
                    // copy the profile into the block
                    hr = pContextSrc->GetProfileBytes(cbProfile, (BYTE*)pvProfile, &cbSize);
                }

                // ***Open the memory block as a HPROFILE***
                HPROFILE hProfile = NULL;

                // fill out a PROFILE structure
                PROFILE prof =
                {
                      PROFILE_MEMBUFFER,
                      pvProfile,
                      cbProfile
                };

                // create the HPROFILE
                if (SUCCEEDED(hr))
                {
                    /*hProfile = WcsOpenColorProfile(
                        &prof,
                        NULL,
                        NULL,
                        PROFILE_READ,
                        FILE_SHARE_READ,
                        OPEN_EXISTING,
                        0);*/

                    hProfile = OpenColorProfile(
                            &prof,
                            PROFILE_READ,
                            FILE_SHARE_READ,
                            OPEN_EXISTING);


                    hr = hProfile ? S_OK : E_FAIL;
                    HeapFree(GetProcessHeap(), 0, pvProfile);
                }

                PROFILEHEADER ph;
                GetColorProfileHeader(hProfile,  &ph);


                DWORD size = 100;
                CHAR description[100];
                for (int i = 0; i < 100; i++)
                {
                    description[i] = '\0';
                }
                BOOL b;
                BOOL s = GetColorProfileElement(hProfile, 0x64657363, 12, &size, &description, &b);
                string profileDescription = std::string(description);

The variable ph represents the blue image screencaptures above and profileDescription represents the ICC profile description (which works for one image and not for the other)


Differences between ICC headers

I've summarised the differences in the ICC header information as reported by exif_tool, which result in the failure of the Windows API to parse the embedded ICC header information correctly:

Working

Profile Date Time               : 2000:08:11 19:51:59
Primary Platform                : Apple Computer Inc.
Rendering Intent                : Perceptual
Profile Copyright               : Copyright 2000 Adobe Systems Incorporated

Not Working

Profile Date Time               : 1999:06:03 00:00:00
Primary Platform                : Microsoft Corporation
Rendering Intent                : Media-Relative Colorimetric
Profile Copyright               : Copyright (c) 1999 Adobe Systems Incorporated. All Rights Reserved.

Other image editing software reports both images correctly as "Adobe RGB (1998)"

It seems that the windows API does not like it when the embedded ICC information is destined for a microsoft platform (?)

It may be caused by the lack of "Interoperability Index" (specified as "R03" for the image that is not working, and unspecified for the image that does work), but if that was the case, this would seem like a bug in Windows (?)

Is it maybe, that "Adobe RGB" and "Adobe RGB (1998)" are two different profiles, and all the other software that reports them both as "Adobe RGB (1998)" simply does not distinguish between them?

ooof my head hurts...Does anyone know what is going on here?

Cheers

EDIT:

Working image (Profile Description = Adobe RGB (1998) when read with Windows API):

enter image description here

Not Working (Profile Description = opRGB when read with Windows API): enter image description here

Upvotes: 1

Views: 439

Answers (1)

Maybe Rendering Intent: Perceptual means there is no need in CMM. While Media-Relative Colorimetric, on the other hand...

Also opRGB is the same as Adobe RGB 1998 (in primaries and transfer function). See IEC 61966-2-5-2007. I have the standard, matrix to XYZ is also the same. Even Black XYZ is the same. Even "normalizing absolute XYZ tristimulus values for encoding" are the same and say that "XYZ tristimulus values 0,000 0, 0,000 0, 0,000 0 should correspond to the viewer observed reference display black point" and "normalized XYZ tristimulus values 0,9505, 1,0000, 1,0891 should correspond to the reference display luminance level and white point", that is like that in ICC profile.

Upvotes: 0

Related Questions