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