On Wed, 7 Sep 2016 09:24:09 +0200, "Marti Maria" <marti.ma...@littlecms.com> 
wrote:
> 
> Here is the cooking recipe:
> 
> - Create a CMYK to Lab transform by using your CMYK profile.
> - Measure L* of K at regular points by using this transform. i.e. for (k=0;
> k < 255; k++)  transform (0, 0, 0, k) -> Lab;  get L and discard a, b;  
> - Normalize those point from 0..100 to 0..0xffff and create a sampled tone
> curve. This curve will implement L*(k), and should be monotonic. If not
> monotonic, the CMYK profile is not good and the game is over 
> - Reverse the curve by using cmsReverseToneCurveEx. You will end with a
> curve implementing K(L*)
> - Create a constant zero tone curve by using two points set to 0.
> - Build a profile with cmsCreateLinearizationDeviceLink by using the
> reversed curve for L* and two zero curves for a* and *b. Mark this profile
> as Lab as input and 3 channels as output. Probably labeling it as output
> profile would be a good move. The profile has a weir output format (K, 0, 0)
> but this makes things a lot simpler. To do it "well" you would need to use a
> CLUT. 
> 
> Now you can use this profile as output in a sRGB to KXX transform. You will
> find K in the first output channel. As additional bonus you can use any
> input profile other than sRGB and any color other than R=G=B.

Hi,

Thanks for the detailed response (and excellent library)!

I tried to follow your recipe as well as I could.  Could you have a
quick look over the code below?  A couple of things:

  - I modified a copy of cmsCreateLinearizationDeviceLinkTHR to write
    a BToA tag, otherwise creating the sRGB to KXX transform would fail
    with an error "Couldn't link the profiles".

  - I added a final zero entry in the tone curve table, otherwise the
    reversed curve for K(L*) would not be monotonic.

Peter

--------

#include <lcms2.h>

cmsHPROFILE CMSEXPORT cmsCreateLinearizationDeviceLinkTHR_BToA(cmsContext 
ContextID,
                                                          
cmsColorSpaceSignature ColorSpace,
                                                          cmsToneCurve* const 
TransferFunctions[])
{
    cmsHPROFILE hICC;
    cmsPipeline* Pipeline;
    int nChannels;

    hICC = cmsCreateProfilePlaceholder(ContextID);
    if (!hICC)
        return NULL;

    cmsSetProfileVersion(hICC, 4.3);

    cmsSetDeviceClass(hICC,      cmsSigLinkClass);
    cmsSetColorSpace(hICC,       ColorSpace);
    cmsSetPCS(hICC,              ColorSpace);

    cmsSetHeaderRenderingIntent(hICC,  INTENT_PERCEPTUAL);

    // Set up channels
    nChannels = cmsChannelsOf(ColorSpace);

    // Creates a Pipeline with prelinearization step only
    Pipeline = cmsPipelineAlloc(ContextID, nChannels, nChannels);
    if (Pipeline == NULL) goto Error;


    // Copy tables to Pipeline
    if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, 
cmsStageAllocToneCurves(ContextID, nChannels, TransferFunctions)))
        goto Error;

    // Create tags
    //if (!SetTextTags(hICC, L"Linearization built-in")) goto Error;        // 
hack
    if (!cmsWriteTag(hICC, cmsSigBToA0Tag, (void*) Pipeline)) goto Error;   // 
hack
    //if (!SetSeqDescTag(hICC, "Linearization built-in")) goto Error;       // 
hack

    // Pipeline is already on virtual profile
    cmsPipelineFree(Pipeline);

    // Ok, done
    return hICC;

Error:
    cmsPipelineFree(Pipeline);
    if (hICC)
        cmsCloseProfile(hICC);


    return NULL;
}

void error_handler(cmsContext context, cmsUInt32Number error_code,
    const char *text)
{
    (void)context;
    fprintf(stderr, "error %d: %s\n", error_code, text);
}

int main(int argc, char *argv[])
{
    if (argc < 2)
        return 1;

    cmsContext context = cmsCreateContext(NULL, NULL);
    if (!context)
        return 1;

    cmsSetLogErrorHandlerTHR(context, error_handler);

    cmsHPROFILE cmyk_profile = cmsOpenProfileFromFileTHR(context, argv[1], "r");
    if (!cmyk_profile)
        return 1;

    cmsHPROFILE lab_profile = cmsCreateLab2ProfileTHR(context, cmsD50_xyY());
    if (!lab_profile)
        return 2;

    cmsHTRANSFORM cmyk_to_lab_transform = cmsCreateTransformTHR(context,
        cmyk_profile, TYPE_CMYK_8,
        lab_profile, TYPE_Lab_FLT,
        INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_BLACKPOINTCOMPENSATION);
    if (!cmyk_to_lab_transform)
        return 3;

    int k;
    cmsUInt8Number cmyk[256 * 4];
    cmsFloat32Number lab[256 * 3];
    cmsFloat32Number normL[257];
    for (k = 0; k < 256; k++) {
        cmyk[k*4 + 0] = 0;
        cmyk[k*4 + 1] = 0;
        cmyk[k*4 + 2] = 0;
        cmyk[k*4 + 3] = k;
    }
    cmsDoTransform(cmyk_to_lab_transform, cmyk, lab, 256);

    for (k = 0; k < 256; k++) {
        normL[k] = lab[k*3]/100.0;
    }
    normL[k] = 0.0; // ???

    cmsToneCurve *tonecurve = cmsBuildTabulatedToneCurveFloat(context, 257, 
normL);
    if (!tonecurve)
        return 4;
    if (!cmsIsToneCurveMonotonic(tonecurve))
        return 5;

    cmsToneCurve *revtonecurve = cmsReverseToneCurveEx(256, tonecurve);
    if (!revtonecurve)
        return 6;
    if (!cmsIsToneCurveMonotonic(revtonecurve))
        return 66;

    const cmsFloat32Number zero2[2] = {0.0, 0.0};
    cmsToneCurve *zerotonecurve = cmsBuildTabulatedToneCurveFloat(context, 2, 
zero2);
    if (!zerotonecurve)
        return 7;

    cmsToneCurve *transferfuncs[3] = {
        revtonecurve,
        zerotonecurve,
        zerotonecurve
    };
    cmsHPROFILE kxx_profile = cmsCreateLinearizationDeviceLinkTHR_BToA(context,
        PT_Lab, transferfuncs);
    if (!kxx_profile)
        return 8;

    cmsSetPCS(kxx_profile, cmsSigLabData);
    cmsSetColorSpace(kxx_profile, cmsSigCmyData); // 3 channel output
    cmsSetDeviceClass(kxx_profile, cmsSigOutputClass);

    // cmsSaveProfileToFile(kxx_profile, "kxx.icc");

    cmsHPROFILE srgb_profile = cmsCreate_sRGBProfileTHR(context);
    if (!srgb_profile)
        return 9;

    cmsHTRANSFORM srgb_to_kxx_transform = cmsCreateTransformTHR(context,
        srgb_profile, TYPE_RGB_8,
        kxx_profile, TYPE_CMY_8, // 3 channel output
        INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_BLACKPOINTCOMPENSATION);
    if (!srgb_to_kxx_transform)
        return 10;

    int i;
    cmsUInt8Number rgb[256 * 3];
    cmsUInt8Number kxx[256 * 3];
    for (i = 0; i < 256; i++) {
        rgb[i*3 + 0] = i;
        rgb[i*3 + 1] = i;
        rgb[i*3 + 2] = i;
    }
    cmsDoTransform(srgb_to_kxx_transform, rgb, kxx, 256);

    printf("// to K\n");
    for (i = 0; i < 256; i++) {
        printf("%3d, ", kxx[i*3]);
        if (i%16 == 15)
            printf("\n");
        if (i == 127)
            printf("\n");
    }

    /* for comparison */
    cmsHTRANSFORM srgb_to_cmyk_transform = cmsCreateTransformTHR(context,
        srgb_profile, TYPE_RGB_8,
        cmyk_profile, TYPE_CMYK_8,
        INTENT_RELATIVE_COLORIMETRIC, cmsFLAGS_BLACKPOINTCOMPENSATION);
    if (!srgb_to_cmyk_transform)
        return 11;
    cmsDoTransform(srgb_to_cmyk_transform, rgb, cmyk, 256);

    printf("\n// to CMYK\n");
    for (i = 0; i < 256; i++) {
        printf("%3d,%3d,%3d,%3d,  ",
            cmyk[i*4+0], cmyk[i*4+1], cmyk[i*4+2], cmyk[i*4+3]);
        if (i%4 == 3)
            printf("\n");
        if (i == 127)
            printf("\n");
    }

    return 0;
}
// end

------------------------------------------------------------------------------
_______________________________________________
Lcms-user mailing list
Lcms-user@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/lcms-user

Reply via email to