1 /*
2 This file is part of darktable,
3 Copyright (C) 2018-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdint.h>
22 #include <math.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <inttypes.h>
27
28 #include <errno.h>
29
30 /* --------------------------------------------------------------------------
31 * curve and histogram resolution
32 * ------------------------------------------------------------------------*/
33
34 #define CURVE_RESOLUTION 0x10000
35
36 extern int
37 exif_get_ascii_datafield(
38 const char* filename,
39 const char* key,
40 char* buf,
41 size_t buflen);
42
43 /* --------------------------------------------------------------------------
44 * Curve code used for fitting the curves
45 * ------------------------------------------------------------------------*/
46
47 #include "../../src/common/curve_tools.c"
48
49 /* --------------------------------------------------------------------------
50 * Basecurve params
51 * copied from iop/basecurve.c. fixed at specific revision on purpose
52 * ------------------------------------------------------------------------*/
53
54 #define BASECURVE_PARAMS_VERSION 2
55
56 typedef struct dt_iop_basecurve_node_t
57 {
58 float x;
59 float y;
60 } dt_iop_basecurve_node_t;
61
62 #define DT_IOP_BASECURVE_MAXNODES 20
63 typedef struct dt_iop_basecurve_params_t
64 {
65 // three curves (c, ., .) with max number of nodes
66 // the other two are reserved, maybe we'll have cam rgb at some point.
67 dt_iop_basecurve_node_t basecurve[3][DT_IOP_BASECURVE_MAXNODES];
68 int basecurve_nodes[3];
69 int basecurve_type[3];
70 } dt_iop_basecurve_params_t;
71
72 /* --------------------------------------------------------------------------
73 * Tonecurve params
74 * copied from iop/toncurve.h. fixed at specific revision on purpose
75 * ------------------------------------------------------------------------*/
76
77 #define TONECURVE_PARAMS_VERSION 4
78
79 typedef struct dt_iop_tonecurve_node_t
80 {
81 float x;
82 float y;
83 } dt_iop_tonecurve_node_t;
84
85 #define DT_IOP_TONECURVE_MAXNODES 20
86 typedef struct dt_iop_tonecurve_params_t
87 {
88 dt_iop_tonecurve_node_t tonecurve[3][DT_IOP_TONECURVE_MAXNODES]; // three curves (L, a, b) with max number of nodes
89 int tonecurve_nodes[3];
90 int tonecurve_type[3];
91 int tonecurve_autoscale_ab;
92 int tonecurve_preset;
93 int tonecurve_unbound_ab;
94 } dt_iop_tonecurve_params_t;
95
96 /* --------------------------------------------------------------------------
97 * utils
98 * ------------------------------------------------------------------------*/
99
100 static void
hexify(uint8_t * out,const uint8_t * in,size_t len)101 hexify(uint8_t* out, const uint8_t* in, size_t len)
102 {
103 static const char hex[] = "0123456789abcdef";
104 for(int i=0; i<len; i++)
105 {
106 out[2*i ] = hex[in[i] >> 4];
107 out[2*i+1] = hex[in[i] & 15];
108 }
109 out[2*len] = '\0';
110 }
111
112 static int
read_ppm_header(FILE * f,int * wd,int * ht)113 read_ppm_header(
114 FILE *f,
115 int* wd,
116 int* ht)
117 {
118 int r = 0;
119 char buf[2];
120
121 r = fseek(f, 0, SEEK_SET);
122 if (r != 0) {
123 r = -1;
124 goto exit;
125 }
126
127 // read and check header
128 r = fread(buf, 1, 2, f);
129 if (r != 2 || buf[0] != 'P' || buf[1] != '6')
130 {
131 r = -1;
132 goto exit;
133 }
134
135 // scan for width and height
136 r = fscanf(f, "%*[^0-9]%d %d\n%*[^\n]", wd, ht);
137
138 // read final newline
139 fgetc(f);
140
141 // finalize return value
142 r = (r != 2) ? -1 : 0;
143
144 exit:
145 return r;
146 }
147
148 static uint16_t*
read_ppm16(const char * filename,int * wd,int * ht)149 read_ppm16(const char *filename, int *wd, int *ht)
150 {
151 FILE *f = NULL;
152 uint16_t *p = NULL;
153
154 f = fopen(filename, "rb");
155 if (!f)
156 {
157 goto exit;
158 }
159
160 if (read_ppm_header(f, wd, ht)) {
161 goto exit;
162 }
163
164 p = (uint16_t *)malloc(sizeof(uint16_t)*3*(*wd)*(*ht));
165 int rd = fread(p, sizeof(uint16_t)*3, (*wd)*(*ht), f);
166 if(rd != (*wd)*(*ht))
167 {
168 fprintf(stderr, "[read_ppm] unexpected end of file! maybe you're loading an 8-bit ppm here instead of a 16-bit one? (%s)\n", filename);
169 free(p);
170 p = NULL;
171 }
172
173 exit:
174 if (f) {
175 fclose(f);
176 f = NULL;
177 }
178
179 return p;
180 }
181
182 static uint8_t*
read_ppm8(const char * filename,int * wd,int * ht)183 read_ppm8(const char *filename, int *wd, int *ht)
184 {
185 FILE* f = NULL;
186 uint8_t *p = NULL;
187
188 f = fopen(filename, "rb");
189 if(!f) {
190 goto exit;
191 }
192
193 if (read_ppm_header(f, wd, ht)) {
194 goto exit;
195 }
196
197 p = (uint8_t *)malloc(sizeof(uint8_t)*3*(*wd)*(*ht));
198 int rd = fread(p, sizeof(uint8_t)*3, (*wd)*(*ht), f);
199 if(rd != (*wd)*(*ht))
200 {
201 fprintf(stderr, "[read_ppm] unexpected end of file! (%s)\n", filename);
202 free(p);
203 p = NULL;
204 }
205
206 exit:
207 if (f) {
208 fclose(f);
209 f = NULL;
210 }
211
212 return p;
213 }
214
get_error(CurveData * c,CurveSample * csample,float * basecurve,uint32_t * cnt)215 static inline float get_error(CurveData *c, CurveSample *csample, float* basecurve, uint32_t* cnt)
216 {
217 CurveDataSample(c, csample);
218 float sqrerr = 0.0f;
219 const float max = 1.0f, min = 0.0f;
220 for(int k=0; k<CURVE_RESOLUTION; k++)
221 {
222 // too few samples? no error if we ignore it.
223 if(cnt[k] > 8)
224 {
225 float d = (basecurve[k] - (min + (max-min)*csample->m_Samples[k]*(1.0f/CURVE_RESOLUTION)));
226 // way more error for lower values of x:
227 d *= CURVE_RESOLUTION-k;
228 if(k < 655) d *= 100;
229 sqrerr += d*d;
230 }
231 }
232 return sqrerr;
233 }
234
mutate(CurveData * c,CurveData * t,float * basecurve)235 static inline void mutate(CurveData *c, CurveData *t, float* basecurve)
236 {
237 for(int k=1;k<c->m_numAnchors-1;k++)
238 {
239 float min = (c->m_anchors[k-1].x + c->m_anchors[k].x)/2.0f;
240 float max = (c->m_anchors[k+1].x + c->m_anchors[k].x)/2.0f;
241 const float x = min + drand48()*(max-min);
242 uint32_t pos = 0;
243 if (x * CURVE_RESOLUTION > CURVE_RESOLUTION) {
244 pos = x * CURVE_RESOLUTION;
245 }
246 if(pos >= CURVE_RESOLUTION) pos = CURVE_RESOLUTION-1;
247 t->m_anchors[k].x = x;
248 t->m_anchors[k].y = basecurve[pos];
249 }
250 t->m_anchors[0].x = 0.0f;
251 t->m_anchors[0].y = 0.0f;//basecurve[0];
252 t->m_anchors[t->m_numAnchors-1].x = 1.0f;
253 t->m_anchors[t->m_numAnchors-1].y = 1.0f;//basecurve[0xffff];
254 }
255
linearize_sRGB(float val)256 static inline float linearize_sRGB(float val)
257 {
258 if(val < 0.04045f)
259 {
260 val /= 12.92f;
261 }
262 else
263 {
264 val = powf(((val + 0.055f)/1.055f), 2.4f);
265 }
266 return val;
267 }
268
269 #define SQUARE(a) ((a)*(a))
270 #define CUBIC(a) ((a)*SQUARE((a)))
271
Lab(float val)272 static inline float Lab(float val)
273 {
274 static const float threshold = CUBIC(6.f) / CUBIC(29.f);
275 if (val>threshold)
276 {
277 val = powf(val, 1.f / 3.f);
278 }
279 else
280 {
281 val = 4.f / 29.f + SQUARE(29.f) / (3.f * SQUARE(6.f)) * val;
282 }
283 return val;
284 }
285
286 // NB: DT uses L*a*b* D50
287 static inline void
RGB2Lab(float * L,float * a,float * b,float R,float G,float B)288 RGB2Lab(float* L, float* a, float*b, float R, float G, float B)
289 {
290 // RGB to CIE 1931 XYZ @D50 first
291 const float X = 0.4360747f*R + 0.3850649f*G + 0.1430804f*B;
292 const float Y = 0.2225045f*R + 0.7168786f*G + 0.0606169f*B;
293 const float Z = 0.0139322f*R + 0.0971045f*G + 0.7141733f*B;
294
295 // Apply D50/ICC illuminant, then transform using the L*a*b* function
296 const float fx = Lab(X/0.9642f);
297 const float fy = Lab(Y);
298 const float fz = Lab(Z/0.8249f);
299
300 *L = 116.f*fy - 16.f;
301 *a = 500.f*(fx - fy);
302 *b = 200.f*(fy - fz);
303 }
304 static inline void
Lab2UnitCube(float * L,float * a,float * b)305 Lab2UnitCube(float* L, float* a, float*b)
306 {
307 *L /= 100.f;
308 *a = (*a + 128.f) / 256.f;
309 *b = (*b + 128.f) / 256.f;
310 }
311
312 enum module_type
313 {
314 MODULE_BASECURVE = 0,
315 MODULE_TONECURVE = 1,
316 MODULE_MAX
317 };
318
319 static void
linearize_8bit(int width,int height,uint8_t * _s,float * _d)320 linearize_8bit(
321 int width, int height,
322 uint8_t* _s,
323 float* _d)
324 {
325 // XXX support ICC profiles here ?
326 #pragma omp parallel for
327 for (int y=0; y<height; y++) {
328 float* d = _d + 3*width*y;
329 uint8_t* s = _s + 3*width*y;
330 for (int x=0; x<width; x++) {
331 d[0] = linearize_sRGB((float)s[0]/255.f);
332 d[1] = linearize_sRGB((float)s[1]/255.f);
333 d[2] = linearize_sRGB((float)s[2]/255.f);
334 d += 3;
335 s += 3;
336 }
337 }
338 }
339
340 static void
linearize_16bit(int width,int height,uint16_t * _s,float * _d)341 linearize_16bit(
342 int width, int height,
343 uint16_t* _s,
344 float* _d)
345 {
346 #pragma omp parallel for
347 for (int y=0; y<height; y++) {
348 float* d = _d + 3*width*y;
349 uint16_t* s = _s + 3*width*y;
350 for (int x=0; x<width; x++) {
351 d[0] = (float)s[0]/65535.f;
352 d[1] = (float)s[1]/65535.f;
353 d[2] = (float)s[2]/65535.f;
354 d += 3;
355 s += 3;
356 }
357 }
358 }
359
360 static void
build_channel_basecurve(int width_jpeg,int height_jpeg,float * buf_jpeg,int offx_raw,int offy_raw,int width_raw,float * buf_raw,int ch,float * curve,uint32_t * cnt)361 build_channel_basecurve(
362 int width_jpeg, int height_jpeg, float* buf_jpeg,
363 int offx_raw, int offy_raw, int width_raw, float* buf_raw,
364 int ch, float* curve, uint32_t* cnt)
365 {
366 for(int j=0;j<height_jpeg;j++)
367 {
368 for(int i=0;i<width_jpeg;i++)
369 {
370 // raw coordinate is offset:
371 const int ri = offx_raw + i;
372 const int rj = offy_raw + j;
373
374 // grab channel from JPEG first
375 float jpegVal = buf_jpeg[3*(width_jpeg*j + i) + ch];
376
377 // grab RGB from RAW
378 float rawVal = buf_raw[3*(width_raw*rj + ri) + ch];
379
380 size_t raw = (size_t)((rawVal*(float)(CURVE_RESOLUTION-1)) + 0.5f);
381 curve[raw] = (curve[raw]*cnt[raw] + jpegVal)/(cnt[raw] + 1.0f);
382 cnt[raw]++;
383 }
384 }
385 }
386
387 static void
build_tonecurve(int width_jpeg,int height_jpeg,float * buf_jpeg,int offx_raw,int offy_raw,int width_raw,float * buf_raw,float * curve,uint32_t * hist)388 build_tonecurve(
389 int width_jpeg, int height_jpeg, float* buf_jpeg,
390 int offx_raw, int offy_raw, int width_raw, float* buf_raw,
391 float* curve, uint32_t* hist)
392 {
393 float* cL = curve;
394 float* ca = cL + CURVE_RESOLUTION;
395 float* cb = ca + CURVE_RESOLUTION;
396
397 uint32_t* hL = hist;
398 uint32_t* ha = hL + CURVE_RESOLUTION;
399 uint32_t* hb = ha + CURVE_RESOLUTION;
400
401 for(int j=0;j<height_jpeg;j++)
402 {
403 for(int i=0;i<width_jpeg;i++)
404 {
405 // raw coordinate is offset:
406 const int ri = offx_raw + i;
407 const int rj = offy_raw + j;
408
409 // grab RGB from JPEG first
410 float r = buf_jpeg[3*(width_jpeg*j + i) + 0];
411 float g = buf_jpeg[3*(width_jpeg*j + i) + 1];
412 float b = buf_jpeg[3*(width_jpeg*j + i) + 2];
413
414 // Compute the JPEG L val
415 float L_jpeg;
416 float a_jpeg;
417 float b_jpeg;
418 RGB2Lab(&L_jpeg, &a_jpeg, &b_jpeg, r, g, b);
419 Lab2UnitCube(&L_jpeg, &a_jpeg, &b_jpeg);
420
421 // grab RGB from RAW
422 r = buf_raw[3*(width_raw*rj + ri) + 0];
423 g = buf_raw[3*(width_raw*rj + ri) + 1];
424 b = buf_raw[3*(width_raw*rj + ri) + 2];
425
426 // Compute the RAW L val
427 float L_raw;
428 float a_raw;
429 float b_raw;
430 RGB2Lab(&L_raw, &a_raw, &b_raw, r, g, b);
431 Lab2UnitCube(&L_raw, &a_raw, &b_raw);
432
433 size_t Li = (size_t)(L_raw*(float)(CURVE_RESOLUTION-1) + 0.5f);
434 size_t ai = (size_t)(a_raw*(float)(CURVE_RESOLUTION-1) + 0.5f);
435 size_t bi = (size_t)(b_raw*(float)(CURVE_RESOLUTION-1) + 0.5f);
436 cL[Li] = (cL[Li]*hL[Li] + L_jpeg)/(hL[Li] + 1.0f);
437 ca[ai] = (ca[ai]*ha[ai] + a_jpeg)/(ha[ai] + 1.0f);
438 cb[bi] = (cb[bi]*hb[bi] + b_jpeg)/(hb[bi] + 1.0f);
439 hL[Li]++;
440 ha[ai]++;
441 hb[bi]++;
442 }
443 }
444 }
445
446 static void
fit_curve(CurveData * best,int * nopt,float * minsqerr,CurveSample * csample,int num_nodes,float * curve,uint32_t * cnt)447 fit_curve(CurveData* best, int* nopt, float* minsqerr, CurveSample* csample, int num_nodes, float* curve, uint32_t* cnt)
448 {
449 // now do the fitting:
450 CurveData curr, tent;
451
452 // type = 2 (monotone hermite)
453 curr.m_spline_type = 2;
454 curr.m_numAnchors = num_nodes;
455 curr.m_min_x = 0.0;
456 curr.m_max_x = 1.0;
457 curr.m_min_y = 0.0;
458 curr.m_max_y = 1.0;
459
460 *best = tent = curr;
461 *nopt = 0;
462 *minsqerr = FLT_MAX;
463
464 const float p_large = .0f;
465 float curr_m = FLT_MIN;
466
467 const int samples = 1000;
468 for(int i=0;i<samples;i++)
469 {
470 if(i == 0 || drand48() < p_large)
471 { // large step
472 for(int k=0;k<tent.m_numAnchors-1;k++)
473 {
474 float x = k/(tent.m_numAnchors-1.0f);
475 x *= x*x; // move closer to 0
476 uint32_t pos = x * CURVE_RESOLUTION;
477 tent.m_anchors[k].x = x;
478 tent.m_anchors[k].y = curve[pos];
479 }
480 tent.m_anchors[tent.m_numAnchors-1].x = 1.0f;
481 tent.m_anchors[tent.m_numAnchors-1].y = curve[CURVE_RESOLUTION-1];
482 }
483 else
484 { // mutate
485 mutate(&curr, &tent, curve);
486 }
487 float m = get_error(&tent, csample, curve, cnt);
488 if(m < *minsqerr)
489 {
490 (*nopt)++;
491 *minsqerr = m;
492 *best = tent;
493
494 }
495 // fittness: 1/MSE
496 const float a = curr_m/m;
497 if(drand48() < a || i == 0)
498 { // accept new state
499 curr = tent;
500 curr_m = m;
501 }
502 }
503 }
504
505 static inline int
is_bigendian()506 is_bigendian()
507 {
508 // swap to host byte, PPM16 are BE
509 static const union { uint8_t u8[4]; uint32_t u32;} byte_order __attribute__((aligned(4))) = { .u8 = {1,2,3,4} };
510 return byte_order.u32 == 0x01020304;
511
512 }
513 struct options
514 {
515 const char* filename_basecurve_fit;
516 const char* filename_tonecurve_fit;
517 const char* filename_basecurve;
518 const char* filename_tonecurve;
519 const char* filename_state;
520 const char* filename_raw;
521 const char* filename_jpeg;
522 const char* filename_exif;
523 int num_nodes;
524 int finalize;
525 int scale_ab;
526 };
527
528 static void
print_usage(const char * name)529 print_usage(
530 const char* name)
531 {
532 fprintf(stderr,
533 "first pass, accumulate statistics (can be repeated to cover all tonal range):\n"
534 "%s [OPTIONS] <inputraw.ppm (16-bit)> <inputjpg.ppm (8-bit)>\n"
535 "\n"
536 "second pass, compute the curves:\n"
537 " %s -z [OPTIONS]\n"
538 "\n"
539 "OPTIONS:\n"
540 " -n <integer> Number of nodes for the curve\n"
541 " -b <filename> Basecurve output filename\n"
542 " -c <filename> Basecurve Fit curve output filename\n"
543 " -t <filename> Tonecurve output filename\n"
544 " -u <filename> Tonecurve Fit curve output filename\n"
545 " -a Tonecurve Fit the a* and b* channels\n"
546 " -s <filename> Save state\n"
547 " -z Compute the fitting curve\n"
548 " -e <filename> Retrieve model and make from file's Exif metadata\n"
549 " -h Print this help message\n"
550 "\n"
551 "convert the raw with `dcraw -6 -W -g 1 1 -w input.raw'\n"
552 "and the jpg with `convert input.jpg output.ppm'\n"
553 "plot the results with `gnuplot plot.(basecurve|tonecurve) depending on target module'\n"
554 "\n"
555 "first do a pass over a few images to accumulate data in the save state file, and then\n"
556 "compute the fit curve using option -z\n",
557 name, name);
558 }
559
560 static void
set_default_options(struct options * opts)561 set_default_options(
562 struct options* opts)
563 {
564 static const char* default_filename_basecurve = "basecurve.dat";
565 static const char* default_filename_basecurve_fit = "basecurve.fit.dat";
566 static const char* default_filename_tonecurve = "tonecurve.dat";
567 static const char* default_filename_tonecurve_fit = "tonecurve.fit.dat";
568 static const char* default_state = "dt-curve-tool.bin";
569 opts->filename_basecurve = default_filename_basecurve;
570 opts->filename_basecurve_fit = default_filename_basecurve_fit;
571 opts->filename_tonecurve = default_filename_tonecurve;
572 opts->filename_tonecurve_fit = default_filename_tonecurve_fit;
573 opts->filename_state = default_state;
574 opts->filename_jpeg = NULL;
575 opts->filename_raw = NULL;
576 opts->num_nodes = 12;
577 opts->finalize = 0;
578 opts->filename_exif = NULL;
579 opts->scale_ab = 0;
580 }
581
582 static int
parse_arguments(int argc,char ** argv,struct options * opts)583 parse_arguments(
584 int argc,
585 char** argv,
586 struct options* opts)
587 {
588 opterr = 1;
589
590 int c;
591 int ex = 0;
592 while ((c = getopt(argc, argv, "hn:b:c:t:u:s:ze:a")) >= 0)
593 {
594 switch (c)
595 {
596 case 'n':
597 opts->num_nodes = atoi(optarg);
598 break;
599 case 'b':
600 opts->filename_basecurve = optarg;
601 break;
602 case 'c':
603 opts->filename_basecurve_fit = optarg;
604 break;
605 case 't':
606 opts->filename_tonecurve = optarg;
607 break;
608 case 'u':
609 opts->filename_tonecurve_fit = optarg;
610 break;
611 case 's':
612 opts->filename_state = optarg;
613 break;
614 case 'z':
615 opts->finalize = 1;
616 break;
617 case 'e':
618 opts->filename_exif = optarg;
619 break;
620 case 'a':
621 opts->scale_ab = 1;
622 break;
623 case ':':
624 fprintf(stderr, "missing argument for option -%c, ignored\n", optopt);
625 print_usage(argv[0]);
626 ex = 1;
627 break;
628 case '?':
629 fprintf(stderr, "unknown option -%c\n", optopt);
630 print_usage(argv[0]);
631 ex = 1;
632 break;
633 case 'h':
634 print_usage(argv[0]);
635 ex = 1;
636 }
637 }
638
639 if (!ex)
640 {
641 if (!opts->finalize)
642 {
643 if (optind < argc - 1)
644 {
645 opts->filename_raw = argv[optind];
646 opts->filename_jpeg = argv[optind+1];
647 }
648 else
649 {
650 print_usage(argv[0]);
651 ex = 1;
652 }
653 }
654 }
655
656 return ex;
657 }
658
659 void
read_curveset(FILE * f,float * curve,uint32_t * hist)660 read_curveset(
661 FILE* f,
662 float* curve,
663 uint32_t* hist)
664 {
665 size_t r = fread(curve, 1, sizeof(float) * 3 * CURVE_RESOLUTION, f);
666 if (r != sizeof(float) * 3 * CURVE_RESOLUTION)
667 {
668 /* could not read save state, either missing stats in that save file or
669 * corrupt data. both cases need to clean state */
670 memset(curve, 0, sizeof(float) * 3 * CURVE_RESOLUTION);
671 }
672 else
673 {
674 r = fread(hist, 1, sizeof(uint32_t) * 3 * CURVE_RESOLUTION, f);
675 if (r != sizeof(uint32_t) * 3 * CURVE_RESOLUTION)
676 {
677 /* could not read save state, either missing stats in that save file or
678 * corrupt data. both cases need to clean state */
679 memset(curve, 0, sizeof(float) * 3 * CURVE_RESOLUTION);
680 memset(hist, 0, sizeof(uint32_t) * 3 * CURVE_RESOLUTION);
681 }
682 }
683 }
684
685 int
write_curveset(FILE * f,float * curve,uint32_t * hist)686 write_curveset(
687 FILE* f,
688 float* curve,
689 uint32_t* hist)
690 {
691 int ret = -1;
692
693 int w = fwrite(curve, 1, 3*CURVE_RESOLUTION*sizeof(float), f);
694 if (w != 3*CURVE_RESOLUTION*sizeof(float))
695 {
696 fprintf(stderr, "error: failed writing curves to save state file\n");
697 goto exit;
698 }
699
700 w = fwrite(hist, 1, 3*CURVE_RESOLUTION*sizeof(uint32_t), f);
701 if (w != 3*CURVE_RESOLUTION*sizeof(uint32_t))
702 {
703 fprintf(stderr, "error: failed writing histograms to save state file\n");
704 goto exit;
705 }
706
707 ret = 0;
708
709 exit:
710
711 return ret;
712 }
713
714 /* --------------------------------------------------------------------------
715 * main
716 * ------------------------------------------------------------------------*/
717
718 int
main(int argc,char ** argv)719 main(int argc, char** argv)
720 {
721 int ret = 1;
722
723 // program options
724 struct options opt;
725
726 // raw related vars
727 int raw_width = -1;
728 int raw_height = -1;
729 int raw_offx = -1;
730 int raw_offy = -1;
731 uint16_t *raw_buff = NULL;
732 float* raw_buff_f = NULL;
733
734 // jpeg related vars
735 int jpeg_width = -1;
736 int jpeg_height = -1;
737 uint8_t *jpeg_buff = NULL;
738 float* jpeg_buff_f = NULL;
739
740 // all in one FILE handle
741 FILE* f = NULL;
742
743 // curve related vars
744 float* curve = NULL;
745 float* curve_base = NULL;
746 float* curve_tone = NULL;
747 uint32_t* hist = NULL;
748 uint32_t* hist_base = NULL;
749 uint32_t* hist_tone = NULL;
750 CurveData fit;
751 int accepts = -1;
752 float sqerr = -1.f;
753 CurveSample csample = {0};
754
755 set_default_options(&opt);
756 int shallexit = parse_arguments(argc, argv, &opt);
757 if (shallexit)
758 {
759 goto exit;
760 }
761
762 if(opt.num_nodes > 20)
763 {
764 // basecurve and tonecurve do not support more than that.
765 opt.num_nodes = 20;
766 }
767
768 curve = calloc(1, sizeof(float) * 6 * CURVE_RESOLUTION);
769 if (!curve) {
770 fprintf(stderr, "error: failed allocating curve\n");
771 ret = -1;
772 goto exit;
773 }
774 curve_base = curve;
775 curve_tone = curve + 3*CURVE_RESOLUTION;
776
777 hist = calloc(1, sizeof(uint32_t) * 6 * CURVE_RESOLUTION);
778 if (!hist) {
779 fprintf(stderr, "error: failed allocating histogram\n");
780 ret = -1;
781 goto exit;
782 }
783 hist_base = hist;
784 hist_tone = hist + 3*CURVE_RESOLUTION;
785
786 // read saved state if any
787 f = fopen(opt.filename_state, "rb");
788 if (f)
789 {
790 read_curveset(f, curve_base, hist_base);
791 read_curveset(f, curve_tone, hist_tone);
792 fclose(f);
793 f = NULL;
794 }
795
796 if (opt.finalize)
797 {
798 goto fit;
799 }
800
801 // read the raw PPM file
802 raw_buff = read_ppm16(opt.filename_raw, &raw_width, &raw_height);
803 if(!raw_buff)
804 {
805 fprintf(stderr, "error: failed reading the raw file data `%s'\n", opt.filename_raw);
806 goto exit;
807 }
808
809 // read the JPEG PPM file
810 jpeg_buff = read_ppm8(opt.filename_jpeg, &jpeg_width, &jpeg_height);
811 if(!jpeg_buff)
812 {
813 fprintf(stderr, "error: failed reading JPEG file\n");
814 goto exit;
815 }
816
817 // discard rotated JPEGs for now
818 raw_offx = (raw_width - jpeg_width)/2;
819 raw_offy = (raw_height - jpeg_height)/2;
820 if(raw_offx < 0 || raw_offy < 0)
821 {
822 fprintf(stderr, "error: jpeg has a higher resolution than the raw ? (%dx%d vs %dx%d)\n", jpeg_width, jpeg_height, raw_width, raw_height);
823 goto exit;
824 }
825
826 // swap to host byte, PPM16 are BE
827 if (!is_bigendian())
828 {
829 for (int k=0; k<3*raw_width*raw_height; k++)
830 {
831 raw_buff[k] = ((raw_buff[k]&0xff) << 8) | (raw_buff[k] >> 8);
832 }
833 }
834
835 raw_buff_f = calloc(1, sizeof(float)*3*raw_width*raw_height);
836 if (!raw_buff_f) {
837 fprintf(stderr, "error: failed allocating raw file float buffer\n");
838 goto exit;
839 }
840
841 // normalize to [0,1] cube once for all
842 linearize_16bit(raw_width, raw_height, raw_buff, raw_buff_f);
843
844 // get rid of original 16bit data
845 free(raw_buff);
846 raw_buff = NULL;
847
848 jpeg_buff_f = calloc(1, sizeof(float)*3*jpeg_width*jpeg_height);
849 if (!jpeg_buff_f) {
850 fprintf(stderr, "error: failed allocating JPEG file float buffer\n");
851 goto exit;
852 }
853
854 // linearize and normalize to unit cube
855 linearize_8bit(jpeg_width, jpeg_height, jpeg_buff, jpeg_buff_f);
856
857 // get rid of original 8bit data
858 free(jpeg_buff);
859 jpeg_buff = NULL;
860
861 /* ------------------------------------------------------------------------
862 * Overflow test, we test for worst case scenario, all pixels would be
863 * concentrated on the sample with maximum histogram
864 * ----------------------------------------------------------------------*/
865
866 {
867 uint32_t maxhist = 0;
868
869 for (int i=0 ; i<6; i++)
870 {
871 for (int j=0; j<CURVE_RESOLUTION; j++)
872 {
873 if (maxhist < hist[i*CURVE_RESOLUTION + j])
874 {
875 maxhist = hist[i*CURVE_RESOLUTION + j];
876 }
877 }
878 }
879
880 if ((UINT32_MAX - maxhist) < (uint32_t)(jpeg_width*jpeg_height))
881 {
882 fprintf(stderr, "error: analyzing this image could overflow internal counters. Refusing to process\n");
883 goto exit;
884 }
885 }
886
887 /* ------------------------------------------------------------------------
888 * Basecurve part
889 * ----------------------------------------------------------------------*/
890
891 f = fopen(opt.filename_basecurve, "wb");
892 if (!f)
893 {
894 fprintf(stderr, "error: could not open '%s'\n", opt.filename_basecurve);
895 goto exit;
896 }
897
898 for (int ch=0; ch<3; ch++)
899 {
900 build_channel_basecurve(jpeg_width, jpeg_height, jpeg_buff_f, raw_offx, raw_offy, raw_width, raw_buff_f, ch, curve_base+ch*CURVE_RESOLUTION, hist_base+ch*CURVE_RESOLUTION);
901 }
902
903 {
904 // for writing easiness
905 float* ch0 = &curve_base[0*CURVE_RESOLUTION];
906 float* ch1 = &curve_base[1*CURVE_RESOLUTION];
907 float* ch2 = &curve_base[2*CURVE_RESOLUTION];
908 uint32_t* h0 = &hist_base[0*CURVE_RESOLUTION];
909 uint32_t* h1 = &hist_base[1*CURVE_RESOLUTION];
910 uint32_t* h2 = &hist_base[2*CURVE_RESOLUTION];
911
912 // output the histograms:
913 fprintf(f, "# basecurve-red basecurve-green basecurve-blue basecurve-avg cnt-red cnt-green cnt-blue\n");
914 for(int k = 0; k < CURVE_RESOLUTION; k++)
915 fprintf(f, "%f %f %f %f %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", ch0[k], ch1[k], ch2[k],
916 (ch0[k] + ch1[k] + ch2[k]) / 3.0f, h0[k], h1[k], h2[k]);
917 }
918
919 fclose(f);
920 f = NULL;
921
922 /* ------------------------------------------------------------------------
923 * Tonecurve part
924 * ----------------------------------------------------------------------*/
925
926 f = fopen(opt.filename_tonecurve, "wb");
927 if (!f)
928 {
929 fprintf(stderr, "error: could not open '%s'\n", opt.filename_tonecurve);
930 goto exit;
931 }
932
933 build_tonecurve(jpeg_width, jpeg_height, jpeg_buff_f, raw_offx, raw_offy, raw_width, raw_buff_f, curve_tone, hist_tone);
934
935 {
936 float* ch0 = &curve_tone[0*CURVE_RESOLUTION];
937 float* ch1 = &curve_tone[1*CURVE_RESOLUTION];
938 float* ch2 = &curve_tone[2*CURVE_RESOLUTION];
939 uint32_t* h0 = &hist_tone[0*CURVE_RESOLUTION];
940 uint32_t* h1 = &hist_tone[1*CURVE_RESOLUTION];
941 uint32_t* h2 = &hist_tone[2*CURVE_RESOLUTION];
942
943 // output the histogram
944 fprintf(f, "# tonecurve-L tonecurve-a tonecurve-b cnt-L cnt-a cnt-b\n");
945 for(int k = 0; k < CURVE_RESOLUTION; k++)
946 fprintf(f, "%f %f %f %" PRIu32 " %" PRIu32 " %" PRIu32 "\n", ch0[k], ch1[k], ch2[k], h0[k], h1[k], h2[k]);
947 }
948
949 fclose(f);
950 f = NULL;
951
952 free(raw_buff_f);
953 raw_buff_f = NULL;
954
955 free(jpeg_buff_f);
956 jpeg_buff_f = NULL;
957
958 /* ------------------------------------------------------------------------
959 * Write save state w/ the gathered data
960 * ----------------------------------------------------------------------*/
961
962 f = fopen(opt.filename_state, "r+");
963 if (!f && errno == ENOENT)
964 {
965 f = fopen(opt.filename_state, "w+");
966 }
967 if (f)
968 {
969 if (write_curveset(f, curve_base, hist_base))
970 {
971 goto exit;
972 }
973 if (write_curveset(f, curve_tone, hist_tone))
974 {
975 goto exit;
976 }
977 }
978 else
979 {
980 fprintf(stdout, "failed opening save file errno=%d\n", errno);
981 goto exit;
982 }
983
984 if (!opt.finalize)
985 {
986 goto exit;
987 }
988
989 fit:;
990
991 char maker[32];
992 char model[32];
993
994 if (opt.filename_exif)
995 {
996 exif_get_ascii_datafield(opt.filename_exif, "Exif.Image.Model", model, sizeof(model));
997 exif_get_ascii_datafield(opt.filename_exif, "Exif.Image.Make", maker, sizeof(maker));
998 }
999
1000 csample.m_samplingRes = CURVE_RESOLUTION;
1001 csample.m_outputRes = CURVE_RESOLUTION;
1002 csample.m_Samples = (uint16_t *)calloc(1, sizeof(uint16_t)*CURVE_RESOLUTION);
1003
1004 /* ------------------------------------------------------------------------
1005 * Basecurve fit
1006 * ----------------------------------------------------------------------*/
1007
1008 f = fopen(opt.filename_basecurve_fit, "w+b");
1009 if (!f)
1010 {
1011 fprintf(stderr, "error: could not open '%s'\n", opt.filename_basecurve_fit);
1012 goto exit;
1013 }
1014
1015 // fit G channel curve only, this seems to be the best choice for now
1016 fit_curve(&fit, &accepts, &sqerr, &csample, opt.num_nodes, curve_base+CURVE_RESOLUTION, hist_base+CURVE_RESOLUTION);
1017
1018 fprintf(f, "# err %f improved %d times\n", sqerr, accepts);
1019 fprintf(f, "# copy paste into iop/basecurve.c (be sure to insert name, maker, model, and set the last 0 to 1 if happy to filter it):\n");
1020 fprintf(f, "# { \"%s\", \"%s\", \"%s\", 0, FLT_MAX, {{{",
1021 opt.filename_exif ? model : "new measured basecurve",
1022 opt.filename_exif ? maker : "insert maker",
1023 opt.filename_exif ? model : "insert model");
1024 for(int k=0;k<fit.m_numAnchors;k++)
1025 {
1026 fprintf(f, "{%f, %f}%s", fit.m_anchors[k].x, fit.m_anchors[k].y, k<fit.m_numAnchors-1?", ":"}}, ");
1027 }
1028 fprintf(f, "{%d}, {m}}, 0, 0},\n", fit.m_numAnchors);
1029 CurveDataSample(&fit, &csample);
1030 for(int k=0; k<CURVE_RESOLUTION; k++)
1031 {
1032 fprintf(f, "%f %f\n", k*(1.0f/CURVE_RESOLUTION), 0.0 + (1.0f-0.0f)*csample.m_Samples[k]*(1.0f/CURVE_RESOLUTION));
1033 }
1034
1035 fclose(f);
1036 f = NULL;
1037
1038 {
1039 uint8_t encoded[2048];
1040
1041 dt_iop_basecurve_params_t params;
1042 memset(¶ms, 0, sizeof(params));
1043 for(int k=0;k<fit.m_numAnchors;k++)
1044 {
1045 params.basecurve[0][k].x = fit.m_anchors[k].x;
1046 params.basecurve[0][k].y = fit.m_anchors[k].y;
1047 }
1048 params.basecurve_nodes[0] = fit.m_numAnchors;
1049 params.basecurve_type[0] = MONOTONE_HERMITE;
1050
1051 hexify(encoded, (uint8_t *)¶ms, sizeof(params));
1052
1053 fprintf(stdout, "#!/bin/sh\n");
1054 fprintf(stdout, "# to test your new basecurve, copy/paste the following line into your shell.\n");
1055 fprintf(stdout, "# note that it is a smart idea to backup your database before messing with it on this level.\n");
1056 fprintf(stdout, "# (you have been warned :) )\n\n");
1057 // the big binary blob is a canonical blend mode option (switched off).
1058 fprintf(stdout, "echo \"INSERT INTO presets (name,description,operation,op_version,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name,model,maker,lens,iso_min,iso_max,exposure_min,exposure_max,aperture_min,aperture_max,focal_length_min,focal_length_max,writeprotect,autoapply,filter,def,format) VALUES('%s','','basecurve',%d,X'%s',1,X'00000000180000000000C842000000000000000000000000000000000000000000000000000000000000000000000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F',7,0,'','%%','%%','%%',0.0,340282346638528859812000000000000000000,0.0,10000000.0,0.0,100000000.0,0.0,1000.0,0,0,0,0,2);\" | sqlite3 ~/.config/darktable/data.db\n",
1059 opt.filename_exif ? model : "new measured basecurve",
1060 BASECURVE_PARAMS_VERSION, encoded);
1061
1062 fprintf(stdout, "\n\n\n"
1063 "# if it pleases you, then in iop/basecurve.c append the following line to the array basecurve_presets and modify its name\n"
1064 "# {\"%s\", \"%s\", \"%s\", 0, FLT_MAX, {{{",
1065 opt.filename_exif ? model : "new measured basecurve",
1066 opt.filename_exif ? maker : "<MAKER>",
1067 opt.filename_exif ? model : "<MODEL>");
1068 for(int k=0;k<fit.m_numAnchors;k++)
1069 {
1070 fprintf(stdout, "{%f, %f}%s", params.basecurve[0][k].x, params.basecurve[0][k].y, k==fit.m_numAnchors-1?"":", ");
1071 }
1072 fprintf(stdout, "}}, {%d}, {m}}, 0, 1},\n\n\n", fit.m_numAnchors);
1073 }
1074
1075 /* ------------------------------------------------------------------------
1076 * Fit the tonecurve
1077 * ----------------------------------------------------------------------*/
1078
1079 f = fopen(opt.filename_tonecurve_fit, "w+b");
1080 if (!f)
1081 {
1082 fprintf(stderr, "error: could not open '%s'\n", opt.filename_tonecurve_fit);
1083 goto exit;
1084 }
1085
1086 {
1087 struct dt_iop_tonecurve_params_t params;
1088 memset(¶ms, 0, sizeof(params));
1089
1090 for (int i=0; i<(opt.scale_ab ? 3 : 1); i++)
1091 {
1092 fit_curve(&fit, &accepts, &sqerr, &csample, opt.num_nodes, curve_tone+i*CURVE_RESOLUTION, hist_tone+i*CURVE_RESOLUTION);
1093
1094 CurveDataSample(&fit, &csample);
1095 for(int k=0; k<CURVE_RESOLUTION; k++)
1096 {
1097 fprintf(f, "%f %f\n", k*(1.0f/CURVE_RESOLUTION), 0.0 + (1.0f-0.0f)*csample.m_Samples[k]*(1.0f/CURVE_RESOLUTION));
1098 }
1099 fprintf(f, "\n\n");
1100
1101 for (int k=0; k<fit.m_numAnchors; k++)
1102 {
1103 params.tonecurve[i][k].x = fit.m_anchors[k].x;
1104 params.tonecurve[i][k].y = fit.m_anchors[k].y;
1105 }
1106 params.tonecurve_nodes[i] = fit.m_numAnchors;
1107 params.tonecurve_type[i] = 2; // monotone hermite
1108 }
1109
1110 fclose(f);
1111 f = NULL;
1112
1113 if (opt.scale_ab)
1114 {
1115 params.tonecurve_autoscale_ab = 0;
1116 }
1117 else
1118 {
1119 for (int i=1; i<3; i++)
1120 {
1121 for (int k=0; k<opt.num_nodes; k++)
1122 {
1123 params.tonecurve[i][k].x = (float)k/(float)opt.num_nodes;
1124 params.tonecurve[i][k].y = (float)k/(float)opt.num_nodes;
1125 }
1126 params.tonecurve_nodes[i] = opt.num_nodes;
1127 params.tonecurve_type[i] = 2; // monotone hermite
1128 }
1129
1130 params.tonecurve_autoscale_ab = 1;
1131 }
1132 params.tonecurve_unbound_ab = 0;
1133
1134 uint8_t encoded[2048];
1135 hexify(encoded, (uint8_t*)¶ms, sizeof(params));
1136 fprintf(stdout, "#!/bin/sh\n");
1137 fprintf(stdout, "# to test your new tonecurve, copy/paste the following line into your shell.\n");
1138 fprintf(stdout, "# note that it is a smart idea to backup your database before messing with it on this level.\n\n");
1139 fprintf(stdout, "echo \"INSERT INTO presets (name,description,operation,op_version,op_params,enabled,blendop_params,blendop_version,multi_priority,multi_name,model,maker,lens,iso_min,iso_max,exposure_min,exposure_max,aperture_min,aperture_max,focal_length_min,focal_length_max,writeprotect,autoapply,filter,def,format) VALUES('%s','','tonecurve',%d,X'%s',1,X'00000000180000000000C842000000000000000000000000000000000000000000000000000000000000000000000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F00000000000000000000803F0000803F',7,0,'','%%','%%','%%',0.0,340282346638528859812000000000000000000,0.0,10000000.0,0.0,100000000.0,0.0,1000.0,0,0,0,0,2);\" | sqlite3 ~/.config/darktable/data.db\n",
1140 opt.filename_exif ? model : "new measured tonecurve",
1141 TONECURVE_PARAMS_VERSION, encoded);
1142 fprintf(stdout, "\n\n\n"
1143 "# if it pleases you, then in iop/tonecurve.c append the following line to the array preset_camera_curves and modify its name\n"
1144 "# {\"%s\", \"%s\", \"%s\", 0, FLT_MAX, {{",
1145 opt.filename_exif ? model : "new measured tonecurve",
1146 opt.filename_exif ? maker : "<MAKER>",
1147 opt.filename_exif ? model : "<MODEL>");
1148 for(int i = 0; i < 3; i++)
1149 {
1150 fprintf(stdout, "{");
1151 for(int k = 0; k < params.tonecurve_nodes[i]; k++)
1152 fprintf(stdout, "{%f, %f}%s", params.tonecurve[i][k].x, params.tonecurve[i][k].y,
1153 (k + 1 < params.tonecurve_nodes[i]) ? ", " : "");
1154 fprintf(stdout, (i + 1 < 3) ? "}, " : "}");
1155 }
1156 fprintf(stdout, "}, {%d, %d, %d}, {%d, %d, %d}, %d, 0, %d}},\n",
1157 params.tonecurve_nodes[0], params.tonecurve_nodes[1], params.tonecurve_nodes[2],
1158 params.tonecurve_type[0], params.tonecurve_type[1], params.tonecurve_type[2],
1159 params.tonecurve_autoscale_ab, params.tonecurve_unbound_ab);
1160 }
1161
1162 exit:
1163 if (f)
1164 {
1165 fclose(f);
1166 f = NULL;
1167 }
1168 if (raw_buff) {
1169 free(raw_buff);
1170 raw_buff = NULL;
1171 }
1172 if (jpeg_buff) {
1173 free(jpeg_buff);
1174 jpeg_buff = NULL;
1175 }
1176 if (raw_buff_f) {
1177 free(raw_buff_f);
1178 raw_buff_f = NULL;
1179 }
1180 if (jpeg_buff_f) {
1181 free(jpeg_buff_f);
1182 jpeg_buff_f = NULL;
1183 }
1184 if (csample.m_Samples)
1185 {
1186 free(csample.m_Samples);
1187 csample.m_Samples = NULL;
1188 }
1189 if (curve)
1190 {
1191 free(curve);
1192 curve = NULL;
1193 }
1194 if (hist)
1195 {
1196 free(hist);
1197 hist = NULL;
1198 }
1199
1200 return ret;
1201 }
1202