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