Files
argyllcms/profile/simpprof.c
T
gronod d0209ec246
Linux x86_64 Release Build / build (push) Failing after 1m5s
Windows x86_64 Release Build / build (push) Has been cancelled
macOS Release Build / build (arm64, macOS_arm64_bin, arm64-apple-darwin, macos-latest) (push) Has been cancelled
macOS Release Build / build (x86_64, osx10.6_x86_64_bin, x86_64-apple-darwin, macos-13) (push) Has been cancelled
Fix macOS UI link errors by adding missing ui.h includes
2026-08-22 21:27:56 +01:00

436 lines
13 KiB
C

/*
* Argyll Color Management System
* Simple CMYK profile generator.
*
* Author: Graeme W. Gill
* Date: 9/11/96
*
* Copyright 1996, 2002 Graeme W. Gill
* All rights reserved.
*
* This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
* see the License.txt file for licencing details.
*/
/* This program generates a simple mathematical profile for a CMYK device. */
/* It is intended for use in bootstrapping the test chart generation. */
#define VERSION "1.1"
#undef DEBUG
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <time.h>
#include "../cgats/cgats.h"
#include "../numlib/numlib.h"
#include "icc.h"
/* A color structure */
/* This holds the test patch results */
typedef struct {
double c,m,y,k;
double bc[8]; /* Gamma corrected blend coefficients of cmyk */
double Lab[3];
double err; /* Delta E squared */
} col;
/* Structure to hold data for optimization function */
struct _edatas {
col *cols; /* Pointer to table of patch results */
int npat; /* Number of patches */
int xyzi; /* current xyz index */
double gam[4]; /* Gamma values */
double k[3][8]; /* Primary combination values */
}; typedef struct _edatas edatas;
/* Definition of the power optimization function handed to powell() */
/* This function is for optimising the gamma function */
double efunc1(void *edata, double p[]) {
edatas *ed = (edatas *)edata;
double rv;
col *cp;
for (rv = 0.0, cp = &ed->cols[ed->npat-1]; cp >= &ed->cols[0]; cp--) {
double cc,mm,yy,kk;
double nc,nm,ny,nk;
double XYZ[3], Lab[3];
int j;
/* Apply gamma correction to each input */
for (j = 0; j < 4; j++) {
if (p[j] < 0.2)
p[j] = 0.2;
else if (p[j] > 5.0)
p[j] = 5.0;
}
cc = pow(cp->c, p[0]);
nc = 1.0 - cc;
mm = pow(cp->m, p[1]);
nm = 1.0 - mm;
yy = pow(cp->y, p[2]);
ny = 1.0 - yy;
kk = pow(cp->k, p[3]);
nk = 1.0 - kk;
/* Then interpolate between all combinations of primaries. */
/* plus one that stands for all that are close to black */
for (j = 0; j < 3; j++) {
XYZ[j] = nc * nm * ny * nk * ed->k[j][0]
+ nc * nm * yy * nk * ed->k[j][1]
+ nc * mm * ny * nk * ed->k[j][2]
+ nc * mm * yy * nk * ed->k[j][3]
+ cc * nm * ny * nk * ed->k[j][4]
+ cc * nm * yy * nk * ed->k[j][5]
+ cc * mm * ny * nk * ed->k[j][6]
+ (cc * mm * yy * nk + kk) * ed->k[j][7];
}
icmXYZ2Lab(&icmD50, Lab, XYZ);
rv += cp->err = icmLabDEsq(Lab, cp->Lab);
}
printf("Efunc1 returning %f\n",rv);
return rv;
}
/* Definition of the primary coefficient optimization function handed to powell() */
/* This function is for optimising the primary values */
double efunc2(void *edata, double p[]) {
edatas *ed = (edatas *)edata;
int j, os = ed->xyzi;
double tt, rv;
col *cp;
rv = 0.0;
for (j = 0; j < 8; j++) {
if (p[j] < 0.0) { /* Protect against silly values */
p[j] = 0.0;
rv += 1000.0;
}
else if (p[j] > 1.5) {
p[j] = 1.5;
rv += 1000.0;
}
ed->k[os][j] = p[j]; /* Load into current */
}
/* Compute error */
for (cp = &ed->cols[ed->npat-1]; cp >= &ed->cols[0]; cp--) {
double XYZ[3], Lab[3];
for (os = 0; os < 3; os++) {
/* Interpolate between all combinations of primaries. */
for (tt = 0.0, j = 0; j < 8; j++)
tt += cp->bc[j] * ed->k[os][j];
XYZ[os] = tt;
}
icmXYZ2Lab(&icmD50, Lab, XYZ);
rv += cp->err = icmLabDEsq(Lab, cp->Lab);
}
printf("Efunc2 returning %f\n",rv);
return rv;
}
/* Calculate blend coefficients */
void calc_bc(edatas *ed) {
int j;
col *cp;
for (cp = &ed->cols[ed->npat-1]; cp >= &ed->cols[0]; cp--) {
double cc,mm,yy,kk;
double nc,nm,ny,nk;
/* Apply gamma correction to each input, and calculate complement */
for (j = 0; j < 4; j++) {
if (ed->gam[j] < 0.2)
ed->gam[j] = 0.2;
else if (ed->gam[j] > 5.0)
ed->gam[j] = 2.0;
}
cc = pow(cp->c, ed->gam[0]);
nc = 1.0 - cc;
mm = pow(cp->m, ed->gam[1]);
nm = 1.0 - mm;
yy = pow(cp->y, ed->gam[2]);
ny = 1.0 - yy;
kk = pow(cp->k, ed->gam[3]);
nk = 1.0 - kk;
/* Go through all 8 combinations */
cp->bc[0] = nc * nm * ny * nk;
cp->bc[1] = nc * nm * yy * nk;
cp->bc[2] = nc * mm * ny * nk;
cp->bc[3] = nc * mm * yy * nk;
cp->bc[4] = cc * nm * ny * nk;
cp->bc[5] = cc * nm * yy * nk;
cp->bc[6] = cc * mm * ny * nk;
cp->bc[7] = cc * mm * yy * nk + kk;
}
}
void usage(void);
#include "ui.h"
int main(int argc, char *argv[])
{
int i,j,k;
int fa,nfa; /* current argument we're looking at */
int verb = 0;
static char inname[MAXNAMEL+10+1] = { 0 }; /* Input cgats file base name */
static char outname[MAXNAMEL+10+1] = { 0 }; /* Output cgats file base name */
cgats *icg; /* input cgats structure */
cgats *ocg; /* output cgats structure */
time_t clk = time(0);
struct tm *tsp = localtime(&clk);
char *atm = asctime(tsp); /* Ascii time */
int ti; /* Temporary index */
edatas ed; /* Optimising function data structure */
double resid[4];
double presid,dresid;
double sarea;
error_program = argv[0];
if (argc <= 1)
usage();
/* Process the arguments */
for(fa = 1;fa < argc;fa++)
{
nfa = fa; /* skip to nfa if next argument is used */
if (argv[fa][0] == '-') /* Look for any flags */
{
char *na = NULL; /* next argument after flag, null if none */
if (argv[fa][2] != '\000')
na = &argv[fa][2]; /* next is directly after flag */
else
{
if ((fa+1) < argc)
{
if (argv[fa+1][0] != '-')
{
nfa = fa + 1;
na = argv[nfa]; /* next is seperate non-flag argument */
}
}
}
if (argv[fa][1] == '?')
usage();
else if (argv[fa][1] == 'v' || argv[fa][1] == 'V')
verb = 1;
else
usage();
}
else
break;
}
/* Get the file name argument */
if (fa >= argc || argv[fa][0] == '-') usage();
strncpy(inname,argv[fa],MAXNAMEL); inname[MAXNAMEL] = '\000';
strcat(inname,".ti3");
strncpy(outname,argv[fa++],MAXNAMEL); outname[MAXNAMEL] = '\000';
strcat(outname,".pr1");
icg = new_cgats(); /* Create a CGATS structure */
icg->add_other(icg, "CTI3"); /* our special input type is Calibration Target Information 3 */
if (icg->read_name(icg, inname))
error("CGATS file read error : %s",icg->e.m);
if (icg->ntables == 0 || icg->t[0].tt != tt_other || icg->t[0].oi != 0)
error ("Input file isn't a CTI3 format file");
if (icg->ntables != 1)
error ("Input file doesn't contain exactly one table");
if ((ed.npat = icg->t[0].nsets) <= 0)
error ("No sets of data");
if (verb) {
printf("No of test patches = %d\n",ed.npat);
}
if ((ed.cols = (col *)malloc(sizeof(col) * ed.npat)) == NULL)
error("Malloc failed!");
/* Setup output cgats file */
/* This is a simple interpolation CMYK -> XYZ device profile */
ocg = new_cgats(); /* Create a CGATS structure */
ocg->add_other(ocg, "PROF1"); /* our special type is Profile type 1 */
ocg->add_table(ocg, tt_other, 0); /* Start the first table */
ocg->add_kword(ocg, 0, "DESCRIPTOR", "Argyll Calibration Device Profile Type 1",NULL);
ocg->add_kword(ocg, 0, "ORIGINATOR", "Argyll sprof", NULL);
atm[strlen(atm)-1] = '\000'; /* Remove \n from end */
ocg->add_kword(ocg, 0, "CREATED",atm, NULL);
/* Figure out the color space */
if ((ti = icg->find_kword(icg, 0, "COLOR_REP")) < 0)
error ("Input file doesn't contain keyword COLOR_REPS");
if (strcmp(icg->t[0].kdata[ti],"CMYK_XYZ") == 0) {
int ci, mi, yi, ki;
int Xi, Yi, Zi;
if ((ci = icg->find_field(icg, 0, "CMYK_C")) < 0)
error ("Input file doesn't contain field CMYK_C");
if (icg->t[0].ftype[ci] != r_t)
error ("Field CMYK_C is wrong type");
if ((mi = icg->find_field(icg, 0, "CMYK_M")) < 0)
error ("Input file doesn't contain field CMYK_M");
if (icg->t[0].ftype[mi] != r_t)
error ("Field CMYK_M is wrong type");
if ((yi = icg->find_field(icg, 0, "CMYK_Y")) < 0)
error ("Input file doesn't contain field CMYK_Y");
if (icg->t[0].ftype[yi] != r_t)
error ("Field CMYK_Y is wrong type");
if ((ki = icg->find_field(icg, 0, "CMYK_K")) < 0)
error ("Input file doesn't contain field CMYK_K");
if (icg->t[0].ftype[ki] != r_t)
error ("Field CMYK_K is wrong type");
if ((Xi = icg->find_field(icg, 0, "XYZ_X")) < 0)
error ("Input file doesn't contain field XYZ_X");
if (icg->t[0].ftype[Xi] != r_t)
error ("Field XYZ_X is wrong type");
if ((Yi = icg->find_field(icg, 0, "XYZ_Y")) < 0)
error ("Input file doesn't contain field XYZ_Y");
if (icg->t[0].ftype[Yi] != r_t)
error ("Field XYZ_Y is wrong type");
if ((Zi = icg->find_field(icg, 0, "XYZ_Z")) < 0)
error ("Input file doesn't contain field XYZ_Z");
if (icg->t[0].ftype[Zi] != r_t)
error ("Field XYZ_Z is wrong type");
for (i = 0; i < ed.npat; i++) {
double XYZ[3];
ed.cols[i].c = *((double *)icg->t[0].fdata[i][ci]) / 100.0;
ed.cols[i].m = *((double *)icg->t[0].fdata[i][mi]) / 100.0;
ed.cols[i].y = *((double *)icg->t[0].fdata[i][yi]) / 100.0;
ed.cols[i].k = *((double *)icg->t[0].fdata[i][ki]) / 100.0;
XYZ[0] = *((double *)icg->t[0].fdata[i][Xi]) / 100.0;
XYZ[1] = *((double *)icg->t[0].fdata[i][Yi]) / 100.0;
XYZ[2] = *((double *)icg->t[0].fdata[i][Zi]) / 100.0;
icmXYZ2Lab(&icmD50, ed.cols[i].Lab, XYZ);
}
/* Initialise the model */
ed.gam[0] = 1.0; /* First four are CMYK gamma values */
ed.gam[1] = 1.0;
ed.gam[2] = 1.0;
ed.gam[3] = 1.0;
/* Initialise interpolation end points for each combination of primary, */
/* with all combinations close to black being represented by param[7]. */
ed.k[0][0] = .82; ed.k[1][0] = .83; ed.k[2][0] = .75; /* White */
ed.k[0][1] = .66; ed.k[1][1] = .72; ed.k[2][1] = .05; /* Y */
ed.k[0][2] = .27; ed.k[1][2] = .12; ed.k[2][2] = .06; /* M */
ed.k[0][3] = .27; ed.k[1][3] = .12; ed.k[2][3] = .00; /* MY */
ed.k[0][4] = .09; ed.k[1][4] = .13; ed.k[2][4] = .44; /* C */
ed.k[0][5] = .03; ed.k[1][5] = .10; ed.k[2][5] = .04; /* C Y */
ed.k[0][6] = .02; ed.k[1][6] = .01; ed.k[2][6] = .05; /* CM */
ed.k[0][7] = .01; ed.k[1][7] = .01; ed.k[2][7] = .01; /* Black */
sarea = 0.3;
presid = dresid = 100.0;
for (k=0; /* dresid > 0.0001 && */ k < 40; k++) { /* Untill we're done */
double sresid;
double sr[8];
double p[8];
/* Adjust the gamma */
for (i = 0; i < 4; i++)
sr[i] = 0.1; /* Device space search radius */
if (powell(&resid[3], 4, &ed.gam[0], sr, 0.1, 1000, efunc1, (void *)&ed, NULL, NULL) != 0)
error ("Powell failed");
/* Adjust the primaries */
calc_bc(&ed); /* Calculate blend coefficients */
for (i = 0; i < 8; i++)
sr[i] = 0.2; /* Device space search radius */
sresid = 99.0;
for (j = 0; j < 3; j++) { /* For each of X, Y and Z */
ed.xyzi = j;
for (i = 0; i < 8; i++)
p[i] = ed.k[j][i];
printf("##############\n");
printf("XYZ = %d\n",j);
if (powell(&resid[j], 8, p, sr, 0.1, 1000, efunc2, (void *)&ed, NULL, NULL) != 0)
error ("Powell failed");
for (i = 0; i < 8; i++)
ed.k[j][i] = p[i];
if (sresid > resid[j])
sresid = resid[j];
}
dresid = presid - sresid;
if (dresid < 0.0)
dresid = 100.0;
presid = sresid;
printf("~1 presid = %f, sresid = %f, dresid = %f\n",presid, sresid, dresid);
}
/* Fields we want */
ocg->add_kword(ocg, 0, "DSPACE","CMYK", NULL);
ocg->add_kword(ocg, 0, "DTYPE","PRINTER", NULL);
ocg->add_field(ocg, 0, "PARAM_ID", i_t);
ocg->add_field(ocg, 0, "PARAM", r_t);
/* Output model parameters */
for (j = 0; j < 4; j++)
ocg->add_set(ocg, 0, j, ed.gam[j]);
for (j = 0; j < 3; j++) {
for (i = 0; i < 8; i++)
ocg->add_set(ocg, 0, 10 * (j + 1) + i, 100.0 * ed.k[j][i]);
}
if (verb) {
double aver = 0.0;
double maxer = 0.0;
for (i = 0; i < ed.npat; i++) {
double err = sqrt(ed.cols[i].err);
if (err > maxer)
maxer = err;
aver += err;
}
aver = aver/((double)i);
printf("Average fit error = %f, maximum = %f\n",aver,maxer);
}
} else if (strcmp(icg->t[0].kdata[ti],"RGB") == 0) {
error ("We can't handle RGB !");
} else if (strcmp(icg->t[0].kdata[ti],"W") == 0) {
error ("We can't handle Grey !");
} else
error ("Input file keyword COLOR_REPS has unknown value");
if (ocg->write_name(ocg, outname))
error("Write error : %s",ocg->e.m);
free(ed.cols);
ocg->del(ocg); /* Clean up */
icg->del(icg); /* Clean up */
return 0;
}
/******************************************************************/
/* Error/debug output routines */
/******************************************************************/
void
usage(void) {
fprintf(stderr,"Create Simple CMYK Device Profile, Version %s\n",VERSION);
fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
fprintf(stderr,"usage: %s [-v] outfile\n",error_program);
fprintf(stderr," -v Verbose mode\n");
fprintf(stderr," outfile Base name for input.tr3/output.pr1 file\n");
exit(1);
}