Feature: colprof -u (Structured JSON Calculation Progress) #13

Merged
gronod merged 1 commits from feature/colprof-json-progress into main 2026-08-21 21:52:12 +01:00
6 changed files with 86 additions and 25 deletions
+4
View File
@@ -2700,6 +2700,10 @@ White            &nb
useful in creating a profile for a device that is known to have a
perfectly linear response, such as a camera in RAW mode.<br>
<br>
<a name="u_progress"></a> <span style="font-weight: bold;">-u:</span>
Emit structured JSON progress events to <tt>stdout</tt> during profile
calculation (e.g. <tt>{"event": "progress", "stage": "gamut_mapping", "percent": 75}</tt>).<br>
<br>
<a name="u"></a> <span style="font-weight: bold;">-u:</span> Input
profiles will normally be created such that the white patch of the
test chart will be mapped to perfect white when used with any of the
+14 -4
View File
@@ -75,6 +75,14 @@
#define DEMPH_DEFAULT 1.0 /* Default dark region emphasis == none */
int json_progress = 0;
void emit_json_progress(const char *stage, int percent) {
if (!json_progress) return;
printf("{\"event\": \"progress\", \"stage\": \"%s\", \"percent\": %d}\n", stage, percent);
fflush(stdout);
}
/*
Flags used:
@@ -129,6 +137,7 @@ void usage(char *diag, ...) {
fprintf(stderr," X = display XYZ cLUT + matrix, Y = display XYZ cLUT + debug matrix\n");
fprintf(stderr," g = gamma+matrix, s = shaper+matrix, m = matrix only,\n");
fprintf(stderr," G = single gamma+matrix, S = single shaper+matrix\n");
fprintf(stderr," -u Emit structured JSON calculation progress to stdout\n");
fprintf(stderr," -u If input profile, auto scale WP to allow extrapolation\n");
fprintf(stderr," -ua If input profile, force absolute intent\n");
fprintf(stderr," -uc If input profile, clip cLUT values above WP\n");
@@ -492,8 +501,6 @@ int main(int argc, char *argv[]) {
}
else if (argv[fa][1] == 'u') {
autowpsc = 1;
clipovwp = 0;
if (argv[fa][2] == 'a') {
autowpsc = 2;
clipovwp = 0;
@@ -502,12 +509,15 @@ int main(int argc, char *argv[]) {
clipovwp = 1;
} else if (argv[fa][2] != '\000') {
usage("Unknown flag '%c' after -u",argv[fa][2]);
} else if (na != NULL) {
} else if (na != NULL && (isdigit(na[0]) || (na[0] == '.' && isdigit(na[1])))) {
fa = nfa;
iwpscale = atof(na);
if (iwpscale < 0.0 || iwpscale > 200.0)
usage("Argument '%s' to flag -u out of range",na);
autowpsc = 1;
clipovwp = 0;
} else {
json_progress = 1;
}
}
/* Clip primaries */
+3
View File
@@ -32,6 +32,9 @@ typedef enum {
prof_matonly = 7 /* XYZ matrix, linear */
} prof_atype;
extern int json_progress;
void emit_json_progress(const char *stage, int percent);
/* Output or Display device */
void make_output_icc(
prof_atype ptype, /* Profile output type */
+15 -5
View File
@@ -229,14 +229,19 @@ void in_b2a_clut(void *cntx, double *out, double in[3]
DBG(("convert PCS' to DEV' got %f %f %f %f\n",out[0],out[1],out[2],out[3]))
DBG(("in_b2a_clut returning DEV' %f %f %f\n",out[0],out[1],out[2]))
if (p->verb
if ((p->verb || json_progress)
&& tn == 0
) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
if (p->verb) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
}
if (json_progress) {
emit_json_progress("b2a_table", pc);
}
p->last = pc;
}
}
@@ -1171,7 +1176,7 @@ make_input_icc(
/* We now setup an exact inverse, colorimetric style */
/* Use helper function to do the hard work. */
if (cx.verb) {
if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -1182,8 +1187,13 @@ make_input_icc(
for (extra = 1, ui = 0; ui < 3; extra *= (blut_clutPoints[ui++]-1))
;
cx.total += extra;
printf("Creating B to A tables\n");
printf(" 0%%"); fflush(stdout);
if (cx.verb) {
printf("Creating B to A tables\n");
printf(" 0%%"); fflush(stdout);
}
if (json_progress) {
emit_json_progress("b2a_table", 0);
}
}
if (wr_icco->create_lut_xforms(
+49 -15
View File
@@ -431,12 +431,17 @@ void in_a2b_clut(void *cntx, double out[3], double *in, int itn) {
}
DBG(("convert PCS to PCS' got %f %f %f\n",out[0],out[1],out[2]))
if (p->verb && itn == 0) { /* Output percent intervals */
if ((p->verb || json_progress) && itn == 0) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
if (p->verb) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
}
if (json_progress) {
emit_json_progress("a2b_table", pc);
}
p->last = pc;
}
}
@@ -658,12 +663,17 @@ void out_b2a_clut(void *cntx, double *out, double in[3], int itn) {
DBG(("out_b2a_clut returning DEV' %s\n",icmPdv(p->ochan, out)))
if (p->verb && itn == 0) { /* Output percent intervals */
if ((p->verb || json_progress) && itn == 0) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
if (p->verb) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
}
if (json_progress) {
emit_json_progress("b2a_table", pc);
}
p->last = pc;
}
}
@@ -728,12 +738,17 @@ static void PCSp_bdist(void *cntx, double out[1], double in[3], int tn
out[0] = (gdist + 20.0)/40.0;
//printf("~1 bdist returning %f\n",out[0]);
if (p->verb) { /* Output percent intervals */
if (p->verb || json_progress) { /* Output percent intervals */
int pc;
p->count++;
pc = (int)(p->count * 100.0/p->total + 0.5);
if (pc != p->last) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
if (p->verb) {
printf("%c%2d%%",cr_char,pc); fflush(stdout);
}
if (json_progress) {
emit_json_progress("gamut_table", pc);
}
p->last = pc;
}
}
@@ -2551,6 +2566,7 @@ make_output_icc(
if (csgamp == NULL) {
if (verb)
printf(" Finding Source Colorspace Perceptual Gamut\n");
emit_json_progress("gamut_mapping", 10);
if ((csgamp = cx.ixp->get_gamut(cx.ixp, gres)) == NULL)
error ("%d, %s",src_xicc->e.c, src_xicc->e.m);
@@ -2567,6 +2583,7 @@ make_output_icc(
if (verb)
printf(" Finding Source Colorspace Saturation Gamut\n");
emit_json_progress("gamut_mapping", 25);
if ((csgams = ixs->get_gamut(ixs, gres)) == NULL)
error ("%d, %s",src_xicc->e.c, src_xicc->e.m);
@@ -2614,6 +2631,7 @@ make_output_icc(
/* Creat the destination gamut surface */
if (verb)
printf(" Finding Destination Gamut\n");
emit_json_progress("gamut_mapping", 40);
if ((ogam = cx.ox->get_gamut(cx.ox, gres)) == NULL)
error ("%d, %s",wr_xicc->e.c, wr_xicc->e.m);
@@ -2657,6 +2675,7 @@ make_output_icc(
if (verb)
printf(" Creating Gamut match\n");
emit_json_progress("gamut_mapping", 75);
/* The real range of Lab 0..100,-128..128,1-28..128 cube */
/* when mapped to CAM is ridiculously large (ie. */
@@ -2673,6 +2692,7 @@ make_output_icc(
);
if (cx.pmap == NULL)
error ("Failed to make perceptual gamut map transform");
emit_json_progress("gamut_mapping", 100);
if (sepsat) {
@@ -2784,7 +2804,7 @@ make_output_icc(
for (i = 0; i < cx.ochan; i++)
a2bgres[i] = a2bres;
if (cx.verb) {
if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -2797,9 +2817,13 @@ make_output_icc(
;
cx.total += extra;
#endif
printf("Creating inverse gamut mapped A to B tables\n");
printf(" 0%%"); fflush(stdout);
if (cx.verb) {
printf("Creating inverse gamut mapped A to B tables\n");
printf(" 0%%"); fflush(stdout);
}
if (json_progress) {
emit_json_progress("a2b_table", 0);
}
}
#ifdef DEBUG_IGM_ONE
@@ -2879,7 +2903,7 @@ make_output_icc(
/* for perceptual and saturation intents */
/* Use helper function to do the hard work. */
if (cx.verb) {
if (cx.verb || json_progress) {
unsigned int ui;
int extra;
cx.count = 0;
@@ -2892,8 +2916,13 @@ make_output_icc(
;
cx.total += extra;
#endif
printf("Creating B to A tables\n");
printf(" 0%%"); fflush(stdout);
if (cx.verb) {
printf("Creating B to A tables\n");
printf(" 0%%"); fflush(stdout);
}
if (json_progress) {
emit_json_progress("b2a_table", 0);
}
}
#ifdef DEBUG_ONE
@@ -3137,13 +3166,18 @@ make_output_icc(
for (i = 0; i < cx.ichan; i++)
clutres[i] = b2ares;
if (cx.verb) {
if (cx.verb || json_progress) {
unsigned int ui;
cx.count = 0;
cx.last = -1;
for (cx.total = 1, ui = 0; ui < cx.ichan; cx.total *= clutres[ui++])
;
printf(" 0%%"); fflush(stdout);
if (cx.verb) {
printf(" 0%%"); fflush(stdout);
}
if (json_progress) {
emit_json_progress("gamut_table", 0);
}
}
+1 -1
View File
@@ -8917,7 +8917,7 @@ static void calc_ocent(rspl *s) {
/* Hmm. This isn't very reliable in detecting failure. */
if (ctx.oog)
printf("calc_ocent failed to return in-gamut focal point!\n");
warning("calc_ocent failed to return in-gamut focal point!");
//printf("Final angle = %f\n", ctx.ret);