Example #1
0
// Return the CIE94 Delta E 
double LCMSEXPORT cmsCIE94DeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
{
    cmsCIELCh LCh1, LCh2;
    double dE, dL, dC, dh, dhsq;
    double c12, sc, sh;

    if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;

    dL = fabs(Lab1 ->L - Lab2 ->L);

    cmsLab2LCh(&LCh1, Lab1);
    cmsLab2LCh(&LCh2, Lab2);

    dC  = fabs(LCh1.C - LCh2.C);
    dE  = cmsDeltaE(Lab1, Lab2);
    
    dhsq = Sqr(dE) - Sqr(dL) - Sqr(dC);
    if (dhsq < 0)
        dh = 0;
    else
        dh = pow(dhsq, 0.5);

    c12 = sqrt(LCh1.C * LCh2.C);

    sc = 1.0 + (0.048 * c12);
    sh = 1.0 + (0.014 * c12);
            
    return sqrt(Sqr(dL)  + Sqr(dC) / Sqr(sc) + Sqr(dh) / Sqr(sh));
}
Example #2
0
// bfd - gets BFD(1:1) difference between Lab1, Lab2
double LCMSEXPORT cmsBFDdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
{
    double lbfd1,lbfd2,AveC,Aveh,dE,deltaL,
        deltaC,deltah,dc,t,g,dh,rh,rc,rt,bfd;
    cmsCIELCh LCh1, LCh2;
    
    
    if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;
    
    lbfd1 = ComputeLBFD(Lab1);
    lbfd2 = ComputeLBFD(Lab2);
    deltaL = lbfd2 - lbfd1;
    
    cmsLab2LCh(&LCh1, Lab1);
    cmsLab2LCh(&LCh2, Lab2);
    
    deltaC = LCh2.C - LCh1.C;
    AveC = (LCh1.C+LCh2.C)/2;
    Aveh = (LCh1.h+LCh2.h)/2;
    
    dE = cmsDeltaE(Lab1, Lab2);
    
    if (Sqr(dE)>(Sqr(Lab2->L-Lab1->L)+Sqr(deltaC)))
        deltah = sqrt(Sqr(dE)-Sqr(Lab2->L-Lab1->L)-Sqr(deltaC));
    else
        deltah =0;
    
    
    dc   = 0.035 * AveC / (1 + 0.00365 * AveC)+0.521;
    g    = sqrt(Sqr(Sqr(AveC))/(Sqr(Sqr(AveC))+14000));
    t    = 0.627+(0.055*cos((Aveh-254)/(180/M_PI))-       
        0.040*cos((2*Aveh-136)/(180/M_PI))+
        0.070*cos((3*Aveh-31)/(180/M_PI))+
        0.049*cos((4*Aveh+114)/(180/M_PI))-
        0.015*cos((5*Aveh-103)/(180/M_PI)));
    
    dh    = dc*(g*t+1-g);
    rh    = -0.260*cos((Aveh-308)/(180/M_PI))-
        0.379*cos((2*Aveh-160)/(180/M_PI))-
        0.636*cos((3*Aveh+254)/(180/M_PI))+
        0.226*cos((4*Aveh+140)/(180/M_PI))-
        0.194*cos((5*Aveh+280)/(180/M_PI));
    
    rc = sqrt((AveC*AveC*AveC*AveC*AveC*AveC)/((AveC*AveC*AveC*AveC*AveC*AveC)+70000000));
    rt = rh*rc;
    
    bfd = sqrt(Sqr(deltaL)+Sqr(deltaC/dc)+Sqr(deltah/dh)+(rt*(deltaC/dc)*(deltah/dh)));
    
    return bfd;
}
Example #3
0
//  cmc - CMC(1:1) difference between Lab1, Lab2
double LCMSEXPORT cmsCMCdeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
{
  double dE,dL,dC,dh,sl,sc,sh,t,f,cmc;
  cmsCIELCh LCh1, LCh2;

  if (Lab1 ->L == 0 && Lab2 ->L == 0) return 0;

  cmsLab2LCh(&LCh1, Lab1);
  cmsLab2LCh(&LCh2, Lab2);

  
  dL = Lab2->L-Lab1->L;
  dC = LCh2.C-LCh1.C;

  dE = cmsDeltaE(Lab1, Lab2);
  if (Sqr(dE)>(Sqr(dL)+Sqr(dC))) 
            dh = sqrt(Sqr(dE)-Sqr(dL)-Sqr(dC));
  else
            dh =0;

  if ((LCh1.h > 164) && (LCh1.h<345)) 
      t = 0.56 + fabs(0.2 * cos(((LCh1.h + 168)/(180/M_PI))));
  else 
      t = 0.36 + fabs(0.4 * cos(((LCh1.h + 35 )/(180/M_PI))));

   sc  = 0.0638   * LCh1.C / (1 + 0.0131  * LCh1.C) + 0.638;
   sl  = 0.040975 * Lab1->L /(1 + 0.01765 * Lab1->L);
   
   if (Lab1->L<16)
         sl = 0.511; 

   f   = sqrt((LCh1.C * LCh1.C * LCh1.C * LCh1.C)/((LCh1.C * LCh1.C * LCh1.C * LCh1.C)+1900));
   sh  = sc*(t*f+1-f);
   cmc = sqrt(Sqr(dL/sl)+Sqr(dC/sc)+Sqr(dh/sh));

   return cmc;
}
Example #4
0
static
int GamutSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
{
    GAMUTCHAIN*  t = (GAMUTCHAIN* ) Cargo;
    cmsCIELab LabIn1, LabOut1;
    cmsCIELab LabIn2, LabOut2;
    cmsUInt16Number Proof[cmsMAXCHANNELS], Proof2[cmsMAXCHANNELS];
    cmsFloat64Number dE1, dE2, ErrorRatio;

    // Assume in-gamut by default.
    ErrorRatio = 1.0;

    // Convert input to Lab
    cmsDoTransform(t -> hInput, In, &LabIn1, 1);

    // converts from PCS to colorant. This always
    // does return in-gamut values,
    cmsDoTransform(t -> hForward, &LabIn1, Proof, 1);

    // Now, do the inverse, from colorant to PCS.
    cmsDoTransform(t -> hReverse, Proof, &LabOut1, 1);

    memmove(&LabIn2, &LabOut1, sizeof(cmsCIELab));

    // Try again, but this time taking Check as input
    cmsDoTransform(t -> hForward, &LabOut1, Proof2, 1);
    cmsDoTransform(t -> hReverse, Proof2, &LabOut2, 1);

    // Take difference of direct value
    dE1 = cmsDeltaE(&LabIn1, &LabOut1);

    // Take difference of converted value
    dE2 = cmsDeltaE(&LabIn2, &LabOut2);


    // if dE1 is small and dE2 is small, value is likely to be in gamut
    if (dE1 < t->Thereshold && dE2 < t->Thereshold)
        Out[0] = 0;
    else {

        // if dE1 is small and dE2 is big, undefined. Assume in gamut
        if (dE1 < t->Thereshold && dE2 > t->Thereshold)
            Out[0] = 0;
        else
            // dE1 is big and dE2 is small, clearly out of gamut
            if (dE1 > t->Thereshold && dE2 < t->Thereshold)
                Out[0] = (cmsUInt16Number) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
            else  {

                // dE1 is big and dE2 is also big, could be due to perceptual mapping
                // so take error ratio
                if (dE2 == 0.0)
                    ErrorRatio = dE1;
                else
                    ErrorRatio = dE1 / dE2;

                if (ErrorRatio > t->Thereshold)
                    Out[0] = (cmsUInt16Number)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
                else
                    Out[0] = 0;
            }
    }


    return TRUE;
}
Example #5
0
// The CLUT will be stored at 16 bits, but calculations are performed at cmsFloat32Number precision
static
int BlackPreservingSampler(register const cmsUInt16Number In[], register cmsUInt16Number Out[], register void* Cargo)
{
    int i;
    cmsFloat32Number Inf[4], Outf[4];
    cmsFloat32Number LabK[4];
    cmsFloat64Number SumCMY, SumCMYK, Error, Ratio;
    cmsCIELab ColorimetricLab, BlackPreservingLab;
    PreserveKPlaneParams* bp = (PreserveKPlaneParams*) Cargo;

    // Convert from 16 bits to floating point
    for (i=0; i < 4; i++)
        Inf[i] = (cmsFloat32Number) (In[i] / 65535.0);

    // Get the K across Tone curve
    LabK[3] = cmsEvalToneCurveFloat(bp ->KTone, Inf[3]);

    // If going across black only, keep black only
    if (In[0] == 0 && In[1] == 0 && In[2] == 0) {

        Out[0] = Out[1] = Out[2] = 0;
        Out[3] = _cmsQuickSaturateWord(LabK[3] * 65535.0);
        return TRUE;
    }

    // Try the original transform,
    cmsPipelineEvalFloat( Inf, Outf, bp ->cmyk2cmyk);

    // Store a copy of the floating point result into 16-bit
    for (i=0; i < 4; i++)
            Out[i] = _cmsQuickSaturateWord(Outf[i] * 65535.0);

    // Maybe K is already ok (mostly on K=0)
    if ( fabs(Outf[3] - LabK[3]) < (3.0 / 65535.0) ) {
        return TRUE;
    }

    // K differ, mesure and keep Lab measurement for further usage
    // this is done in relative colorimetric intent
    cmsDoTransform(bp->hProofOutput, Out, &ColorimetricLab, 1);

    // Is not black only and the transform doesn't keep black.
    // Obtain the Lab of output CMYK. After that we have Lab + K
    cmsDoTransform(bp ->cmyk2Lab, Outf, LabK, 1);

    // Obtain the corresponding CMY using reverse interpolation
    // (K is fixed in LabK[3])
    if (!cmsPipelineEvalReverseFloat(LabK, Outf, Outf, bp ->LabK2cmyk)) {

        // Cannot find a suitable value, so use colorimetric xform
        // which is already stored in Out[]
        return TRUE;
    }

    // Make sure to pass thru K (which now is fixed)
    Outf[3] = LabK[3];

    // Apply TAC if needed
    SumCMY   = Outf[0]  + Outf[1] + Outf[2];
    SumCMYK  = SumCMY + Outf[3];

    if (SumCMYK > bp ->MaxTAC) {

        Ratio = 1 - ((SumCMYK - bp->MaxTAC) / SumCMY);
        if (Ratio < 0)
            Ratio = 0;
    }
    else
       Ratio = 1.0;

    Out[0] = _cmsQuickSaturateWord(Outf[0] * Ratio * 65535.0);     // C
    Out[1] = _cmsQuickSaturateWord(Outf[1] * Ratio * 65535.0);     // M
    Out[2] = _cmsQuickSaturateWord(Outf[2] * Ratio * 65535.0);     // Y
    Out[3] = _cmsQuickSaturateWord(Outf[3] * 65535.0);

    // Estimate the error (this goes 16 bits to Lab DBL)
    cmsDoTransform(bp->hProofOutput, Out, &BlackPreservingLab, 1);
    Error = cmsDeltaE(&ColorimetricLab, &BlackPreservingLab);
    if (Error > bp -> MaxError)
        bp->MaxError = Error;

    return TRUE;
}
Example #6
0
double dkCmsDeltaE(LPcmsCIELab Lab1, LPcmsCIELab Lab2)
{
    return static_cast<double>( cmsDeltaE(static_cast<cmsCIELab*>( Lab1 ), static_cast<cmsCIELab*>( Lab2 )) );
}
Example #7
0
LCMSAPI double LCMSEXPORT cmsEvalLUTreverse(LPLUT Lut, WORD Target[], WORD Result[], LPWORD Hint)
{
    int      i;
    double     error, LastError = 1E20;
    cmsCIELab  fx, Goal;
    VEC3       tmp, tmp2, x;
    MAT3       Jacobian;
    WORD       FixedK;
    WORD       LastResult[4];
    
        
    // This is our Lab goal
    cmsLabEncoded2Float(&Goal, Target);
    
    // Special case for CMYK->Lab 

    if (Lut ->InputChan == 4)
            FixedK = Target[3];
    else
            FixedK = 0;
        
    
    // Take the hint as starting point if specified

    if (Hint == NULL) {

        // Begin at any point, we choose 1/3 of neutral CMY gray

        x.n[0] = x.n[1] = x.n[2] = 0.3;

    }
    else {

        FromEncoded(&x, Hint);
    }
    

    // Iterate
    
    for (i = 0; i < INVERSION_MAX_ITERATIONS; i++) {

        // Get beginning fx
        EvalLUTdoubleKLab(Lut, &x, FixedK, &fx);
    
        // Compute error
        error = cmsDeltaE(&fx, &Goal);
                        
        // If not convergent, return last safe value
        if (error >= LastError) 
            break;

        // Keep latest values
        LastError = error;

        ToEncoded(LastResult, &x);
        LastResult[3] = FixedK;
                
        // Obtain slope
        ComputeJacobianLab(Lut, &Jacobian, &x, FixedK);

		// Solve system
		tmp2.n[0] = fx.L - Goal.L;
		tmp2.n[1] = fx.a - Goal.a;
		tmp2.n[2] = fx.b - Goal.b;

		if (!MAT3solve(&tmp, &Jacobian, &tmp2))
			break;
		
       	// Move our guess
		x.n[0] -= tmp.n[0];
	    x.n[1] -= tmp.n[1];
		x.n[2] -= tmp.n[2];
               
        // Some clipping....
        VEC3saturate(&x);                
    }

    Result[0] = LastResult[0];
    Result[1] = LastResult[1];
    Result[2] = LastResult[2];
    Result[3] = LastResult[3];

    return LastError;    
    
}
Example #8
0
static
int GamutSampler(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
    LPGAMUTCHAIN t = (LPGAMUTCHAIN) Cargo;
    WORD Proof[MAXCHANNELS], Check[MAXCHANNELS];
    WORD Proof2[MAXCHANNELS], Check2[MAXCHANNELS];
    cmsCIELab LabIn1, LabOut1;  
    cmsCIELab LabIn2, LabOut2;  
    double dE1, dE2, ErrorRatio;
    
    // Assume in-gamut by default.
    dE1 = 0.;
    dE2 = 0;
    ErrorRatio = 1.0;
    

    // Any input space? I can use In[] no matter channels 
    // because is just one pixel

    if (t -> hInput != NULL) cmsDoTransform(t -> hInput, In, In, 1);

    // converts from PCS to colorant. This always
    // does return in-gamut values, 
    cmsDoTransform(t -> hForward, In, Proof, 1);
    
    // Now, do the inverse, from colorant to PCS.
    cmsDoTransform(t -> hReverse, Proof, Check, 1);
    
    
    // Try again, but this time taking Check as input
    cmsDoTransform(t -> hForward, Check, Proof2,  1);
    cmsDoTransform(t -> hReverse, Proof2, Check2, 1);
    
    
    
    // Does the transform returns out-of-gamut?
    if (Check[0] == 0xFFFF && 
        Check[1] == 0xFFFF && 
        Check[2] == 0xFFFF) 
        
        Out[0] = 0xFF00;            // Out of gamut!
    else {
        
        // Transport encoded values
        cmsLabEncoded2Float(&LabIn1,  In);
        cmsLabEncoded2Float(&LabOut1, Check);
        
        // Take difference of direct value
        dE1 = cmsDeltaE(&LabIn1, &LabOut1);        
                
        cmsLabEncoded2Float(&LabIn2,  Check);
        cmsLabEncoded2Float(&LabOut2, Check2);
        
        // Take difference of converted value
        dE2 = cmsDeltaE(&LabIn2, &LabOut2);                 
               
        
        // if dE1 is small and dE2 is small, value is likely to be in gamut
        if (dE1 < t->Thereshold && dE2 < t->Thereshold) 
            Out[0] = 0;
        else
            // if dE1 is small and dE2 is big, undefined. Assume in gamut
            if (dE1 < t->Thereshold && dE2 > t->Thereshold)
                Out[0] = 0;
            else
                // dE1 is big and dE2 is small, clearly out of gamut
                if (dE1 > t->Thereshold && dE2 < t->Thereshold)
                    Out[0] = (WORD) _cmsQuickFloor((dE1 - t->Thereshold) + .5);
                else  {
                    
                    // dE1 is big and dE2 is also big, could be due to perceptual mapping
                    // so take error ratio
                    if (dE2 == 0.0)
                        ErrorRatio = dE1;
                    else
                        ErrorRatio = dE1 / dE2;
                    
                    if (ErrorRatio > t->Thereshold) 
                        Out[0] = (WORD)  _cmsQuickFloor((ErrorRatio - t->Thereshold) + .5);
                    else
                        Out[0] = 0;
                }
            
    }    
    
    return TRUE;
}