You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/lprof/cmsscn.cpp

423 lines
12 KiB

/* */
/* Little cms - profiler construction set */
/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
/* */
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
/* OF THIS SOFTWARE. */
/* */
/* This file is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
/* General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* */
/* As a special exception to the GNU General Public License, if you */
/* distribute this file as part of a program that contains a */
/* configuration script generated by Autoconf, you may include it under */
/* the same distribution terms that you use for the rest of that program. */
/* */
/* Version 1.08a */
#include "lcmsprf.h"
#include <stdio.h>
/* The scanner profiler */
BOOL cdecl cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys);
BOOL cdecl cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys);
/* ------------------------------------------------------------ Implementation */
/* Does create regression matrix */
static
void ComputeGlobalRegression(LPSCANNERPROFILERDATA sys)
{
BOOL lAllOk;
int nTerms;
MLRSTATISTICS Stat;
nTerms = cmsxFindOptimumNumOfTerms(&sys ->hdr, 55, &lAllOk);
if (!lAllOk) {
if (sys -> hdr.printf)
sys -> hdr.printf("*** WARNING: Inconsistence found, profile may be wrong. Check the target!");
nTerms = 4;
}
/* Create high terms matrix used by interpolation */
cmsxRegressionCreateMatrix(&sys -> hdr.m,
sys -> hdr.m.Allowed,
nTerms,
sys -> hdr.PCSType,
&sys -> HiTerms,
&Stat);
if (sys -> hdr.printf)
sys -> hdr.printf("Global regression: %d terms, R2Adj = %g", nTerms, Stat.R2adj);
/* Create low terms matrix used by extrapolation */
cmsxRegressionCreateMatrix(&sys -> hdr.m,
sys -> hdr.m.Allowed,
(nTerms > 10 ? 10 : nTerms),
sys -> hdr.PCSType,
&sys -> LoTerms,
&Stat);
if (sys -> hdr.printf)
sys -> hdr.printf("Extrapolation: R2Adj = %g", Stat.R2adj);
}
/* Fill struct with default values */
BOOL cmsxScannerProfilerInit(LPSCANNERPROFILERDATA sys)
{
if (sys == NULL) return false;
ZeroMemory(sys, sizeof(SCANNERPROFILERDATA));
sys->hdr.DeviceClass = icSigInputClass;
sys->hdr.ColorSpace = icSigRgbData;
sys->hdr.PCSType = PT_Lab;
sys->hdr.Medium = MEDIUM_REFLECTIVE_D50;
/* Default values for generation */
sys -> hdr.lUseCIECAM97s = false;
sys -> hdr.CLUTPoints = 16;
/* Default viewing conditions for scanner */
sys -> hdr.device.Yb = 20;
sys -> hdr.device.La = 20;
sys -> hdr.device.surround = AVG_SURROUND;
sys -> hdr.device.D_value = 1.0; /* Complete adaptation */
/* Viewing conditions of PCS */
cmsxInitPCSViewingConditions(&sys -> hdr);
sys -> HiTerms = NULL;
sys -> LoTerms = NULL;
strcpy(sys -> hdr.Description, "no description");
strcpy(sys -> hdr.Manufacturer, "little cms profiler construction set");
strcpy(sys -> hdr.Copyright, "No copyright, use freely");
strcpy(sys -> hdr.Model, "(unknown)");
sys ->lLocalConvergenceExtrapolation = false;
sys ->hdr.ProfileVerbosityLevel = 0;
return true;
}
/* Auxiliar: take RGB and update gauge */
static
void GetRGB(LPPROFILERCOMMONDATA hdr, register WORD In[], double* r, double* g, double* b)
{
static int Count = 0, n_old = 0;
double R, G, B;
int n;
R = _cmsxSaturate65535To255(In[0]); /* Convert from the sheet notation */
G = _cmsxSaturate65535To255(In[1]); /* 0..255.0, to our notation */
B = _cmsxSaturate65535To255(In[2]); /* of 0..0xffff, 0xffff/255 = 257 */
if (R == 0 && G == 0 && B == 0) {
Count = 0; n_old = -1;
}
n = (int) (double) (100. * Count) / (hdr->CLUTPoints * hdr->CLUTPoints * hdr->CLUTPoints);
Count++;
if (n > n_old) {
if (hdr->Gauger) hdr->Gauger("", 0, 100, (int) n);
}
n_old = n;
*r = R; *g = G; *b = B;
}
/* The sampler for Lab */
static
int RegressionSamplerLab(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
cmsCIEXYZ xyz;
cmsCIELab Lab;
double r, g, b;
LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
char code;
GetRGB(&sys->hdr, In, &r, &g, &b);
code = cmsxHullCheckpoint(sys->hdr.hRGBHull,
(int) floor(r + .5),
(int) floor(g + .5),
(int) floor(b + .5));
if (code == 'i') { /* Inside gamut */
if (!cmsxRegressionRGB2Lab(r, g, b, sys -> HiTerms, &Lab)) return false;
}
else
if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
if (!cmsxRegressionRGB2Lab(r, g, b, sys -> LoTerms, &Lab)) return false;
}
else { /* At gamut hull boundaries */
if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
PT_Lab,
10,
true,
30,
r, g, b,
&Lab)) return false;
}
/* Regression CAN deliver wrong values. Clamp these. */
cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
/* Normalize */
cmsLab2XYZ(cmsD50_XYZ(), &xyz, &Lab);
cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
cmsXYZ2Lab(cmsD50_XYZ(), &Lab, &xyz);
/* Clamping again, adaptation could move slightly values */
cmsClampLab(&Lab, 127.9961, -128, 127.9961, -128);
/* To PCS encoding */
cmsFloat2LabEncoded(Out, &Lab);
return true; /* And done with success */
}
/* The sampler for XYZ */
static
int RegressionSamplerXYZ(register WORD In[], register WORD Out[], register LPVOID Cargo)
{
cmsCIEXYZ xyz;
double r, g, b;
LPSCANNERPROFILERDATA sys = (LPSCANNERPROFILERDATA) Cargo;
char code;
GetRGB(&sys -> hdr, In, &r, &g, &b);
code = cmsxHullCheckpoint(sys ->hdr.hRGBHull,
(int) floor(r + .5),
(int) floor(g + .5),
(int) floor(b + .5));
if (code == 'i') { /* Inside gamut */
if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> HiTerms, &xyz)) return false;
}
else
if (!sys -> lLocalConvergenceExtrapolation && code == 'o') { /* outside gamut */
if (!cmsxRegressionRGB2XYZ(r, g, b, sys -> LoTerms, &xyz)) return false;
}
else { /* At gamut hull boundaries */
if (!cmsxRegressionInterpolatorRGB(&sys -> hdr.m,
PT_XYZ,
10,
true,
30,
r, g, b,
&xyz)) return false;
}
xyz.X /= 100.;
xyz.Y /= 100.;
xyz.Z /= 100.;
cmsxChromaticAdaptationAndNormalization(&sys->hdr, &xyz, false);
/* To PCS encoding. It also claps bad values */
cmsFloat2XYZEncoded(Out, &xyz);
return true; /* And done witch success */
}
/* The main scanner profiler */
BOOL cmsxScannerProfilerDo(LPSCANNERPROFILERDATA sys)
{
LPLUT AToB0;
DWORD dwNeedSamples;
if (!*sys -> hdr.OutputProfileFile)
return false;
if (!cmsxChoosePCS(&sys->hdr))
return false;
dwNeedSamples = PATCH_HAS_RGB;
if (sys ->hdr.PCSType == PT_Lab)
dwNeedSamples |= PATCH_HAS_Lab;
else
dwNeedSamples |= PATCH_HAS_XYZ;
if (sys->hdr.printf) {
sys->hdr.printf("Loading sheets...");
if (sys->hdr.ReferenceSheet[0])
sys->hdr.printf("Reference sheet: %s", sys->hdr.ReferenceSheet);
if (sys->hdr.MeasurementSheet[0])
sys->hdr.printf("Measurement sheet: %s", sys->hdr.MeasurementSheet);
}
if (!cmsxPCollBuildMeasurement(&sys->hdr.m,
sys->hdr.ReferenceSheet,
sys->hdr.MeasurementSheet,
dwNeedSamples)) return false;
sys->hdr.hProfile = cmsCreateRGBProfile(NULL, NULL, NULL);
cmsSetDeviceClass(sys->hdr.hProfile, sys->hdr.DeviceClass);
cmsSetColorSpace(sys->hdr.hProfile, sys->hdr.ColorSpace);
cmsSetPCS(sys->hdr. hProfile, _cmsICCcolorSpace(sys->hdr.PCSType));
/* Save char target tag */
if (sys->hdr.ProfileVerbosityLevel >= 2) {
cmsxEmbedCharTarget(&sys ->hdr);
}
AToB0 = cmsAllocLUT();
cmsAlloc3DGrid(AToB0, sys->hdr.CLUTPoints, 3, 3);
cmsxComputeLinearizationTables(&sys-> hdr.m,
sys -> hdr.PCSType,
sys -> Prelinearization,
1024,
MEDIUM_REFLECTIVE_D50);
/* Refresh RGB of all patches. This converts all regression into */
/* near linear RGB->Lab or XYZ */
cmsxPCollLinearizePatches(&sys->hdr.m, sys -> hdr.m.Allowed, sys -> Prelinearization);
cmsxComputeGamutHull(&sys->hdr);
ComputeGlobalRegression(sys);
cmsAllocLinearTable(AToB0, sys -> Prelinearization, 1);
/* Set CIECAM97s parameters */
sys -> hdr.device.whitePoint.X = sys -> hdr.WhitePoint.X * 100.;
sys -> hdr.device.whitePoint.Y = sys -> hdr.WhitePoint.Y * 100.;
sys -> hdr.device.whitePoint.Z = sys -> hdr.WhitePoint.Z * 100.;
sys->hdr.hDevice = cmsCIECAM97sInit(&sys->hdr.device);
sys->hdr.hPCS = cmsCIECAM97sInit(&sys->hdr.PCS);
if (sys -> hdr.PCSType == PT_Lab)
cmsSample3DGrid(AToB0, RegressionSamplerLab, sys, 0);
else
cmsSample3DGrid(AToB0, RegressionSamplerXYZ, sys, 0);
cmsCIECAM97sDone(sys->hdr.hDevice);
cmsCIECAM97sDone(sys->hdr.hPCS);
cmsAddTag(sys->hdr.hProfile, icSigAToB0Tag, AToB0);
cmsxEmbedTextualInfo(&sys -> hdr);
cmsAddTag(sys->hdr.hProfile, icSigMediaWhitePointTag, &sys->hdr.WhitePoint);
cmsAddTag(sys->hdr.hProfile, icSigMediaBlackPointTag, &sys->hdr.BlackPoint);
/* Save primaries & gamma curves */
if (sys->hdr.ProfileVerbosityLevel >= 1) {
cmsxEmbedMatrixShaper(&sys ->hdr);
}
_cmsSaveProfile(sys->hdr.hProfile, sys->hdr.OutputProfileFile);
cmsCloseProfile(sys->hdr.hProfile);
sys->hdr.hProfile = NULL;
cmsxPCollFreeMeasurements(&sys->hdr.m);
cmsFreeLUT(AToB0);
if (sys -> HiTerms)
MATNfree(sys -> HiTerms);
sys -> HiTerms = NULL;
if (sys -> LoTerms)
MATNfree(sys -> LoTerms);
sys -> LoTerms = NULL;
if (sys ->Prelinearization[0])
cmsFreeGammaTriple(sys -> Prelinearization);
if (sys ->hdr.Gamma)
cmsFreeGammaTriple(sys->hdr.Gamma);
return true;
}