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(&params, 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 *)&params, 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(&params, 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*)&params, 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