56static const point_t bb_ref[] = {{.
x=.0, .y=.0}, {.x=1., .y=0.}, {.x=1., .y=1.}, {.x=0., .y=1.}};
97static void add_column(GtkTreeView *treeview,
const char *title,
int column_id,
int sort_column);
110 int *x_end,
int *y_end);
224 const int prev_corner = (closest_corner + 3) % 4;
225 const int opposite_corner = (closest_corner + 2) % 4;
226 const int next_corner = (closest_corner + 1) % 4;
228 const float x1 = image->
bb[prev_corner].
x;
229 const float y1 = image->
bb[prev_corner].
y;
230 const float x2 = image->
bb[next_corner].
x;
231 const float y2 = image->
bb[next_corner].
y;
232 const float x3 = image->
bb[opposite_corner].
x;
233 const float y3 = image->
bb[opposite_corner].
y;
235 const float denom = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
236 const float l1 = ((y2 - y3) * (
x - x3) + (x3 - x2) * (y - y3)) / denom;
237 const float l2 = ((y3 - y1) * (
x - x3) + (x1 - x3) * (y - y3)) / denom;
238 const float l3 = 1.0 -
l1 -
l2;
240 if(
l1 < 0.0 ||
l2 < 0.0 || l3 < 0.0)
242 image->
bb[closest_corner].
x =
x;
243 image->
bb[closest_corner].
y = y;
246 gtk_widget_queue_draw(widget);
253 int closest_corner = 0;
254 float distance = G_MAXFLOAT;
255 for(
int i = 0;
i < 4;
i++)
257 const float d_x = (
x - bb[
i].
x);
258 const float d_y = (y - bb[
i].
y);
259 float d = d_x * d_x + d_y * d_y;
267 return closest_corner;
272 guint
width = gtk_widget_get_allocated_width(widget);
273 guint
height = gtk_widget_get_allocated_height(widget);
307 char *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
315 gtk_widget_set_sensitive(self->
cht_button, res);
325 char *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
332 char *last_slash = g_strrstr(filename,
"/");
334 return g_strdup(last_slash + 1);
336 return g_strdup(filename);
377 fprintf(stderr,
"error reading image `%s'\n", filename);
386 if(cairo_surface_status(image_surface) != CAIRO_STATUS_SUCCESS)
388 fprintf(stderr,
"error creating cairo surface from `%s'\n", filename);
389 cairo_surface_destroy(image_surface);
393 image->
surface = image_surface;
394 image->
image = cairo_pattern_create_for_surface(image_surface);
402 guint widget_width = gtk_widget_get_allocated_width(image->
drawing_area);
403 guint widget_height = gtk_widget_get_allocated_height(image->
drawing_area);
413 char *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
421 const gboolean res = ((self->
chart =
parse_cht(filename)) != NULL);
432 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->
it8_button));
443 gtk_widget_set_sensitive(self->
it8_button, res);
458 const int selected = gtk_combo_box_get_active(widget);
466 g_signal_emit_by_name(self->
it8_button,
"file-set", user_data);
483 char *new_filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
499 gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(self->
it8_button));
512 gboolean *basecurve, gboolean *colorchecker, gboolean *colorin, gboolean *tonecurve)
514 GtkWidget *name_entry = NULL, *description_entry = NULL;
516 = gtk_file_chooser_dialog_new(
"save file", GTK_WINDOW(self->
window), GTK_FILE_CHOOSER_ACTION_SAVE,
517 _(
"_cancel"), GTK_RESPONSE_CANCEL, _(
"_save"), GTK_RESPONSE_ACCEPT, NULL);
519 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
TRUE);
522 char *last_dot = g_strrstr(reference_filename,
".");
526 char *new_filename = g_strconcat(reference_filename,
extension, NULL);
527 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), new_filename);
535 gtk_grid_set_row_homogeneous(GTK_GRID(grid),
TRUE);
539 char *name_dot = g_strrstr(*
name,
".");
540 if(name_dot) *name_dot =
'\0';
542 name_entry = gtk_entry_new();
543 description_entry = gtk_entry_new();
545 gtk_entry_set_text(GTK_ENTRY(name_entry), *
name);
546 gtk_entry_set_text(GTK_ENTRY(description_entry), *
description);
553 label = gtk_label_new(
"style name");
554 gtk_widget_set_halign(label, GTK_ALIGN_START);
555 gtk_grid_attach(GTK_GRID(grid), label, 0, 0, 1, 1);
556 gtk_grid_attach(GTK_GRID(grid), name_entry, 1, 0, 1, 1);
557 label = gtk_label_new(
"style description");
558 gtk_widget_set_halign(label, GTK_ALIGN_START);
559 gtk_grid_attach(GTK_GRID(grid), label, 0, 1, 1, 1);
560 gtk_grid_attach(GTK_GRID(grid), description_entry, 1, 1, 1, 1);
563 label = gtk_label_new(
"modules included in the style:");
564 gtk_widget_set_halign(label, GTK_ALIGN_START);
565 g_object_set(label,
"margin-left", 50, NULL);
567 GtkWidget *cb_basecurve = gtk_check_button_new_with_label(
"base curve");
568 GtkWidget *cb_colorchecker = gtk_check_button_new_with_label(
"color look up table");
569 GtkWidget *cb_colorin = gtk_check_button_new_with_label(
"input color profile");
570 GtkWidget *cb_tonecurve = gtk_check_button_new_with_label(
"tone curve");
572 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_basecurve),
TRUE);
573 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_colorchecker),
TRUE);
574 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_colorin),
TRUE);
575 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(cb_tonecurve),
TRUE);
579 gtk_grid_attach(GTK_GRID(grid), label, 2, 0, 1, 1);
580 gtk_grid_attach_next_to(GTK_GRID(grid), cb_basecurve, label, GTK_POS_RIGHT, 1, 1);
581 gtk_grid_attach_next_to(GTK_GRID(grid), cb_colorchecker, cb_basecurve, GTK_POS_BOTTOM, 1, 1);
582 gtk_grid_attach_next_to(GTK_GRID(grid), cb_colorin, cb_colorchecker, GTK_POS_BOTTOM, 1, 1);
583 gtk_grid_attach_next_to(GTK_GRID(grid), cb_tonecurve, cb_colorin, GTK_POS_BOTTOM, 1, 1);
586 gtk_widget_show_all(grid);
588 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(dialog), grid);
590 char *filename = NULL;
591 int res = gtk_dialog_run(GTK_DIALOG(dialog));
592 if(res == GTK_RESPONSE_ACCEPT)
594 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
595 *
name = g_strdup(gtk_entry_get_text(GTK_ENTRY(name_entry)));
596 *
description = g_strdup(gtk_entry_get_text(GTK_ENTRY(description_entry)));
600 *basecurve = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_basecurve));
601 *colorchecker = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_colorchecker));
602 *colorin = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_colorin));
603 *tonecurve = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(cb_tonecurve));
606 gtk_widget_destroy(dialog);
613 for(GList *iter = patch_names; iter; iter = g_list_next(iter))
616 char *
key = (
char *)iter->data;
621 fprintf(stderr,
"error: missing patch `%s'\n",
key);
629 fprintf(fd,
"%s",
key);
630 for(
int i = 0;
i < 3;
i++) fprintf(fd,
";%s", g_ascii_dtostr(s,
sizeof(s), source_Lab[
i]));
631 for(
int i = 0;
i < 3;
i++) fprintf(fd,
";%s", g_ascii_dtostr(s,
sizeof(s), reference_Lab[
i]));
636static void print_xml_plugin(FILE *fd,
int num,
int op_version,
const char *operation,
const char *op_params,
639 fprintf(fd,
" <plugin>\n");
640 fprintf(fd,
" <num>%d</num>\n", num);
641 fprintf(fd,
" <module>%d</module>\n", op_version);
642 fprintf(fd,
" <operation>%s</operation>\n", operation);
643 fprintf(fd,
" <op_params>%s</op_params>\n", op_params);
644 fprintf(fd,
" <enabled>%d</enabled>\n", enabled);
645 fprintf(fd,
" <blendop_params>gz12eJxjYGBgkGAAgRNODESDBnsIHll8ANNSGQM=</blendop_params>\n");
646 fprintf(fd,
" <blendop_version>7</blendop_version>\n");
647 fprintf(fd,
" <multi_priority>0</multi_priority>\n");
648 fprintf(fd,
" <multi_name></multi_name>\n");
649 fprintf(fd,
" </plugin>\n");
653 gboolean include_basecurve, gboolean include_colorchecker, gboolean include_colorin,
654 gboolean include_tonecurve)
658 FILE *fd = g_fopen(filename,
"w");
661 fprintf(fd,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
662 fprintf(fd,
"<darktable_style version=\"1.0\">\n");
663 fprintf(fd,
"<info>\n");
664 fprintf(fd,
" <name>%s</name>\n",
name);
665 fprintf(fd,
" <description>%s</description>\n",
description);
666 fprintf(fd,
"</info>\n");
667 fprintf(fd,
"<style>\n");
670 if(include_basecurve)
673 "gz09eJxjYIAAM6vnNnqyn22E9n235b6aa3cy6rVdRaK9/Y970fYf95bbMzA0QPEoGEqADYnNhMQGAO0WEJo=",
FALSE);
684 if(include_tonecurve)
689 if(include_colorchecker)
694 fprintf(fd,
"</style>\n");
695 fprintf(fd,
"</darktable_style>\n");
702 GHashTableIter table_iter;
705 FILE *fd = g_fopen(filename,
"w");
708 GList *patch_names = NULL;
710 fprintf(fd,
"name;%s\n",
name);
712 fprintf(fd,
"num_gray; 0\n");
714 fprintf(fd,
"patch;L_source;a_source;b_source;L_reference;a_reference;b_reference\n");
717 while(g_hash_table_iter_next(&table_iter, &
key, &
value))
719 patch_names = (GList *)
value;
744 gboolean include_basecurve, include_colorchecker, include_colorin, include_tonecurve;
746 &include_basecurve, &include_colorchecker, &include_colorin, &include_tonecurve);
748 include_basecurve, include_colorchecker, include_colorin, include_tonecurve);
758 for(GList *iter = patch_names; iter; iter = g_list_next(iter))
760 const char *
key = (
char *)iter->data;
765 fprintf(stderr,
"error: missing patch `%s'\n",
key);
774 target_L[*
i] = reference_Lab[0];
775 target_a[*
i] = reference_Lab[1];
776 target_b[*
i] = reference_Lab[2];
781 fprintf(stderr,
"warning: ignoring patch %s with large difference deltaE %g!\n",
key, deltaE);
782 fprintf(stderr,
" %g %g %g -- %g %g %g\n", source_Lab[0], source_Lab[1], source_Lab[2],
783 reference_Lab[0], reference_Lab[1], reference_Lab[2]);
791static void add_hdr_patches(
int *
N,
double **target_L,
double **target_a,
double **target_b,
794 gboolean need_hdr00 =
TRUE, need_hdr01 =
TRUE;
795 int n_extra_patches = 0;
796 double extra_target_L[2], extra_target_a[2], extra_target_b[2], extra_colorchecker_Lab[2 * 3];
798 for(
int j = 0; j < *
N; j++)
800 if((*target_L)[j] == 100.0 && (*target_a)[j] == 0.0 && (*target_b)[j] == 0.0 && (*
colorchecker_Lab)[j * 3] == 100.0
805 else if((*target_L)[j] == 200.0 && (*target_a)[j] == 0.0 && (*target_b)[j] == 0.0 && (*
colorchecker_Lab)[j * 3] == 200.0
814 extra_target_L[n_extra_patches] = 100.0;
815 extra_target_a[n_extra_patches] = 0.0;
816 extra_target_b[n_extra_patches] = 0.0;
817 extra_colorchecker_Lab[n_extra_patches * 3] = 100.0;
818 extra_colorchecker_Lab[n_extra_patches * 3 + 1] = 0.0;
819 extra_colorchecker_Lab[n_extra_patches * 3 + 2] = 0.0;
825 extra_target_L[n_extra_patches] = 200.0;
826 extra_target_a[n_extra_patches] = 0.0;
827 extra_target_b[n_extra_patches] = 0.0;
828 extra_colorchecker_Lab[n_extra_patches * 3] = 200.0;
829 extra_colorchecker_Lab[n_extra_patches * 3 + 1] = 0.0;
830 extra_colorchecker_Lab[n_extra_patches * 3 + 2] = 0.0;
834 if(n_extra_patches > 0)
836 *target_L = realloc(*target_L,
sizeof(
double) * (*
N + n_extra_patches + 4));
837 *target_a = realloc(*target_a,
sizeof(
double) * (*
N + n_extra_patches + 4));
838 *target_b = realloc(*target_b,
sizeof(
double) * (*
N + n_extra_patches + 4));
841 memmove(&(*target_L)[n_extra_patches], *target_L,
sizeof(
double) * *
N);
842 memmove(&(*target_a)[n_extra_patches], *target_a,
sizeof(
double) * *
N);
843 memmove(&(*target_b)[n_extra_patches], *target_b,
sizeof(
double) * *
N);
846 memcpy(*target_L, extra_target_L,
sizeof(
double) * n_extra_patches);
847 memcpy(*target_a, extra_target_a,
sizeof(
double) * n_extra_patches);
848 memcpy(*target_b, extra_target_b,
sizeof(
double) * n_extra_patches);
849 memcpy(*
colorchecker_Lab, extra_colorchecker_Lab,
sizeof(
double) * 3 * n_extra_patches);
851 *
N += n_extra_patches;
875 memset(¶ms, 0,
sizeof(params));
876 params.tonecurve_autoscale_ab = 3;
878 params.tonecurve_type[0] = 2;
879 params.tonecurve_nodes[0] = 20;
880 for(
int k = 0;
k < 20;
k++)
882 const double x = (
k / 19.0) * (
k / 19.0);
883 params.tonecurve[0][
k].x =
x;
887 params.tonecurve_type[1] = 2;
888 params.tonecurve_nodes[1] = 2;
889 params.tonecurve[1][0].x = 0.0f;
890 params.tonecurve[1][0].y = 0.0f;
891 params.tonecurve[1][1].x = 1.0f;
892 params.tonecurve[1][1].y = 1.0f;
894 params.tonecurve_type[2] = 2;
895 params.tonecurve_nodes[2] = 2;
896 params.tonecurve[2][0].x = 0.0f;
897 params.tonecurve[2][0].y = 0.0f;
898 params.tonecurve[2][1].x = 1.0f;
899 params.tonecurve[2][1].y = 1.0f;
907#define MAX_PATCHES 49
920 memset(¶ms, 0,
sizeof(params));
923 params.num_patches = num;
925 for(
int k = 0;
k < num;
k++)
937 const float tmp = (a); \
942 for(
int k = 0;
k < num - 1;
k++)
943 for(
int j = 0; j < num -
k - 1; j++)
946 <
thinplate_color_pos(params.source_L[j + 1], params.source_a[j + 1], params.source_b[j + 1]))
948 SWAP(params.source_L[j], params.source_L[j + 1]);
949 SWAP(params.source_a[j], params.source_a[j + 1]);
950 SWAP(params.source_b[j], params.source_b[j + 1]);
951 SWAP(params.target_L[j], params.target_L[j + 1]);
952 SWAP(params.target_a[j], params.target_a[j + 1]);
953 SWAP(params.target_b[j], params.target_b[j + 1]);
964 const double x = *(
const double *)x_;
965 const double y = *(
const double *)y_;
966 return x < y ? -1 : (
x > y ? +1 : 0);
973 double *cx = malloc(
sizeof(
double)*
N);
974 double *cy = malloc(
sizeof(
double)*
N);
975 double *grays = malloc(
sizeof(
double) * 6 *
N);
977 int num_tonecurve = 0;
983 const double sat_in =
986 const double sat_out =
987 target_a[
i] * target_a[
i] +
988 target_b[
i] * target_b[
i];
990 if(sat_in < 15.0 && sat_out < 15.0)
996 grays[6*cnt + 3] = target_L[
i];
997 grays[6*cnt + 4] = target_a[
i];
998 grays[6*cnt + 5] = target_b[
i];
1001 fprintf(stderr,
"detected %d/%d as gray patches for tonecurve computation\n", cnt,
N);
1006 cx[0] = cy[0] = 0.0;
1007 cx[cnt+1] = cy[cnt+1] = 100.0;
1010 num_tonecurve = cnt + 2;
1011 for(
int k = 0;
k < cnt;
k++) cx[
k + 1] = grays[6*
k+0];
1012 for(
int k = 0;
k < cnt;
k++) cy[
k + 1] = grays[6*
k+3];
1017 for(
int k = 0;
k < num_tonecurve;
k++)
1018 fprintf(stderr,
"L[%g] = %g\n", 100.0 *
k / (num_tonecurve - 1.0),
1028 cx = malloc(
sizeof(
double)*num_tonecurve);
1029 cy = malloc(
sizeof(
double)*num_tonecurve);
1030 cx[0] = cy[0] = 0.0;
1031 cx[num_tonecurve - 1] = cy[num_tonecurve - 1] = 100.0;
1032 for(
int k = 1;
k < num_tonecurve-1;
k++)
1035 Lab[0] = grays[6*
k+0];
1036 dt_Lab_to_prophotorgb(
Lab,
rgb);
1039 dt_Lab_to_prophotorgb(
Lab,
rgb);
1046 for(
int k = 0;
k <
N;
k++)
1049 Lab[0] = target_L[
k];
1050 Lab[1] = target_a[
k];
1051 Lab[2] = target_b[
k];
1052 dt_Lab_to_prophotorgb(
Lab,
rgb);
1056 dt_prophotorgb_to_Lab(
rgb,
Lab);
1057 target_L[
k] =
Lab[0];
1058 target_a[
k] =
Lab[1];
1059 target_b[
k] =
Lab[2];
1064 const double *target[3] = { target_L, target_a, target_b };
1065 double *coeff_L = malloc(
sizeof(
double) * (
N + 4) );
1066 double *coeff_a = malloc(
sizeof(
double) * (
N + 4) );
1067 double *coeff_b = malloc(
sizeof(
double) * (
N + 4) );
1068 double *
coeff[] = { coeff_L, coeff_a, coeff_b };
1069 int *
perm = malloc(
sizeof(
int) * (
N + 4));
1070 double avgerr, maxerr;
1076 char *result_string = g_strdup_printf(_(
"average dE: %.02f\nmax dE: %.02f"), avgerr, maxerr);
1077 gtk_label_set_text(GTK_LABEL(self->
result_label), result_string);
1086 int cperm[300] = { 0 };
1087 for(
int k = 0;
k < sparsity;
k++)
1089 cperm[sp++] =
perm[
k];
1092 fprintf(stderr,
"found %d basis functions:\n", sp);
1093 for(
int k = 0;
k < sp;
k++)
1094 fprintf(stderr,
"perm[%d] = %d source %g %g %g\n",
k, cperm[
k],
colorchecker_Lab[3 * cperm[
k]],
1120 double *target_L = (
double *)calloc(
sizeof(
double), (
N + 4));
1121 double *target_a = (
double *)calloc(
sizeof(
double), (
N + 4));
1122 double *target_b = (
double *)calloc(
sizeof(
double), (
N + 4));
1125 GHashTableIter table_iter;
1126 gpointer set_key,
value;
1129 while(g_hash_table_iter_next(&table_iter, &set_key, &
value))
1131 GList *patch_names = (GList *)
value;
1137 int sparsity = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(self->
number_patches)) + 4;
1154 if(
flags & GTK_STATE_FLAG_INSENSITIVE)
1167 image->
shrink = gtk_range_get_value(range);
1177 gtk_box_pack_start(GTK_BOX(page), hbox,
FALSE,
TRUE, 0);
1179 GtkWidget *image_button = gtk_file_chooser_button_new(
"image of a color chart", GTK_FILE_CHOOSER_ACTION_OPEN);
1183 = gtk_file_chooser_button_new(
"description of a color chart", GTK_FILE_CHOOSER_ACTION_OPEN);
1186 GtkWidget *source_shrink = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0.5, 2.0, 0.01);
1187 gtk_scale_set_value_pos(GTK_SCALE(source_shrink), GTK_POS_RIGHT);
1190 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(
"image:"),
FALSE,
TRUE, 0);
1191 gtk_box_pack_start(GTK_BOX(hbox), image_button,
TRUE,
TRUE, 0);
1192 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(
"chart:"),
FALSE,
TRUE, 0);
1193 gtk_box_pack_start(GTK_BOX(hbox), cht_button,
TRUE,
TRUE, 0);
1194 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(
"size:"),
FALSE,
TRUE, 0);
1195 gtk_box_pack_start(GTK_BOX(hbox), source_shrink,
TRUE,
TRUE, 0);
1201 g_signal_connect(cht_button,
"state-flags-changed", G_CALLBACK(
cht_state_callback), self);
1215 gtk_box_pack_start(GTK_BOX(page), hbox,
FALSE,
TRUE, 0);
1217 GtkWidget *reference_mode = gtk_combo_box_text_new();
1218 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(reference_mode), NULL,
"cie/it8 file");
1219 gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(reference_mode), NULL,
"color chart image");
1220 gtk_combo_box_set_active(GTK_COMBO_BOX(reference_mode), 0);
1224 = gtk_file_chooser_button_new(
"reference data of a color chart", GTK_FILE_CHOOSER_ACTION_OPEN);
1228 = gtk_file_chooser_button_new(
"image of a color chart", GTK_FILE_CHOOSER_ACTION_OPEN);
1231 GtkWidget *reference_shrink = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0.5, 2.0, 0.01);
1232 gtk_scale_set_value_pos(GTK_SCALE(reference_shrink), GTK_POS_RIGHT);
1235 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(
"mode:"),
FALSE,
TRUE, 0);
1236 gtk_box_pack_start(GTK_BOX(hbox), reference_mode,
TRUE,
TRUE, 0);
1239 gtk_box_pack_start(GTK_BOX(reference_it8_box), gtk_label_new(
"reference it8:"),
FALSE,
TRUE, 0);
1240 gtk_box_pack_start(GTK_BOX(reference_it8_box), it8_button,
TRUE,
TRUE, 0);
1241 gtk_box_pack_start(GTK_BOX(hbox), reference_it8_box,
TRUE,
TRUE, 0);
1244 gtk_box_pack_start(GTK_BOX(reference_image_box), gtk_label_new(
"reference image:"),
FALSE,
TRUE, 0);
1245 gtk_box_pack_start(GTK_BOX(reference_image_box), reference_image_button,
TRUE,
TRUE, 0);
1246 gtk_box_pack_start(GTK_BOX(reference_image_box), gtk_label_new(
"size:"),
FALSE,
TRUE, 0);
1247 gtk_box_pack_start(GTK_BOX(reference_image_box), reference_shrink,
TRUE,
TRUE, 0);
1248 gtk_box_pack_start(GTK_BOX(hbox), reference_image_box,
TRUE,
TRUE, 0);
1254 gtk_widget_show_all(reference_it8_box);
1255 gtk_widget_show_all(reference_image_box);
1257 gtk_widget_hide(reference_image_box);
1259 gtk_widget_set_no_show_all(reference_it8_box,
TRUE);
1260 gtk_widget_set_no_show_all(reference_image_box,
TRUE);
1283 GtkWidget *number_patches = gtk_spin_button_new_with_range(0, 49, 1);
1284 gtk_spin_button_set_value(GTK_SPIN_BUTTON(number_patches), 24);
1285 gtk_grid_attach(GTK_GRID(page), gtk_label_new(
"number of final patches"), 0, line, 1, 1);
1286 gtk_grid_attach(GTK_GRID(page), number_patches, 1, line++, 1, 1);
1288 GtkWidget *process_button = gtk_button_new_with_label(
"process");
1289 GtkWidget *export_button = gtk_button_new_with_label(
"export");
1290 GtkWidget *export_raw_button = gtk_button_new_with_label(
"export raw data as csv");
1291 gtk_grid_attach(GTK_GRID(page), process_button, 1, line, 1, 1);
1292 gtk_grid_attach(GTK_GRID(page), export_button, 2, line, 1, 1);
1293 gtk_grid_attach(GTK_GRID(page), export_raw_button, 3, line++, 1, 1);
1296 gtk_widget_set_halign(self->
result_label, GTK_ALIGN_START);
1297 gtk_grid_attach(GTK_GRID(page), self->
result_label, 1, line++, 3, 1);
1314 GtkWidget *notebook = gtk_notebook_new();
1318 gtk_label_new(
"source image"));
1322 gtk_label_new(
"reference values"));
1332 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1333 gtk_widget_set_size_request(scrolled_window, -1, 15);
1334 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1335 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window), GTK_SHADOW_ETCHED_IN);
1350 gtk_container_add(GTK_CONTAINER(scrolled_window), self->
treeview);
1359 return scrolled_window;
1362static void add_column(GtkTreeView *treeview,
const char *title,
int column_id,
int sort_column)
1364 GtkCellRenderer *renderer;
1365 GtkTreeViewColumn *column;
1366 renderer = gtk_cell_renderer_text_new();
1367 column = gtk_tree_view_column_new_with_attributes(title, renderer,
"text", column_id, NULL);
1368 gtk_tree_view_column_set_sort_column_id(column, sort_column);
1369 gtk_tree_view_append_column(treeview, column);
1376 gboolean valid = gtk_tree_model_get_iter_first(self->
model, &iter);
1388 char *s_Lab_in, *s_RGB_in, *s_deltaE_1976, *s_deltaE_2000;
1389 float deltaE_1976 = 0.0, deltaE_2000 = 0.0;
1398 s_RGB_in = g_strdup_printf(
"%d; %d; %d", (
int)(patch->
rgb[0] * 255 + 0.5),
1399 (
int)(patch->
rgb[1] * 255 + 0.5), (
int)(patch->
rgb[2] * 255 + 0.5));
1400 s_Lab_in = g_strdup_printf(
"%.02f; %.02f; %.02f", in_Lab[0], in_Lab[1], in_Lab[2]);
1403 s_deltaE_1976 = g_strdup_printf(
"%.02f", deltaE_1976);
1404 s_deltaE_2000 = g_strdup_printf(
"%.02f", deltaE_2000);
1408 s_Lab_in = g_strdup(
"?");
1409 s_RGB_in = g_strdup(
"?");
1410 s_deltaE_1976 = g_strdup(
"-");
1411 s_deltaE_2000 = g_strdup(
"-");
1413 char *s_Lab_ref = g_strdup_printf(
"%.02f; %.02f; %.02f",
Lab[0],
Lab[1],
Lab[2]);
1426 valid = gtk_tree_model_iter_next(self->
model, &iter);
1453 gtk_list_store_clear(GTK_LIST_STORE(self->
model));
1457 GList *patch_names = g_hash_table_get_keys(self->
chart->
box_table);
1458 patch_names = g_list_sort(patch_names, (GCompareFunc)g_strcmp0);
1461 gtk_list_store_append(GTK_LIST_STORE(self->
model), &iter);
1464 g_list_free(patch_names);
1511 g_hash_table_insert(table, g_strdup(
key), patch);
1522 int x_start, y_start, x_end, y_end;
1524 xyz[0] = xyz[1] = xyz[2] = 0.0;
1543 double sample_x = 0.0, sample_y = 0.0, sample_z = 0.0;
1544 size_t n_samples = 0;
1546 for(
int y = y_start; y < y_end; y++)
1547 for(
int x = x_start;
x < x_end;
x++)
1554 float *pixel = &image->
xyz[(
x + y * image->
width) * 3];
1555 sample_x += pixel[0];
1556 sample_y += pixel[1];
1557 sample_z += pixel[2];
1562 xyz[0] = sample_x / n_samples;
1563 xyz[1] = sample_y / n_samples;
1564 xyz[2] = sample_z / n_samples;
1569 for(
int i = 0;
i < 4;
i++)
1578 box_t inner_box = *outer_box;
1580 inner_box.
p.
x += x_shrink;
1581 inner_box.
p.
y += y_shrink;
1582 inner_box.
w -= 2.0 * x_shrink;
1583 inner_box.
h -= 2.0 * y_shrink;
1599 int *x_end,
int *y_end)
1633 memset(image, 0x00,
sizeof(
image_t));
1636 gtk_widget_set_size_request(image->
drawing_area, -1, 50);
1638 GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1641 g_signal_connect(image->
drawing_area,
"motion-notify-event", G_CALLBACK(motion_cb), self);
1648 if(image->
image) cairo_pattern_destroy(image->
image);
1651 image->
image = NULL;
1659 for(
int y = 0; y <
height; y++)
1662 float *pixel = &image[(
x + y *
width) * 3];
1669 gtk_init(&argc, &argv);
1671 char *source_filename = argc >= 2 ? argv[1] : NULL;
1672 char *cht_filename = argc >= 3 ? argv[2] : NULL;
1673 char *it8_filename = NULL;
1674 char *reference_filename = NULL;
1677 char *upper_string = g_ascii_strup(argv[3], -1);
1678 if(g_str_has_suffix(upper_string,
".PFM"))
1679 reference_filename = argv[3];
1681 it8_filename = argv[3];
1686 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
1688 gtk_window_set_title(GTK_WINDOW(window),
"darktable LUT tool");
1689 gtk_container_set_border_width(GTK_CONTAINER(window), 3);
1690 gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
1691 g_signal_connect(GTK_WINDOW(window),
"destroy", G_CALLBACK(gtk_main_quit), NULL);
1694 GtkWidget *vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
1695 gtk_container_add(GTK_CONTAINER(window), vpaned);
1710 gtk_widget_show_all(window);
1715 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->
image_button), source_filename);
1716 if(cht_filename &&
open_cht(self, cht_filename))
1718 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->
cht_button), cht_filename);
1719 if(it8_filename &&
open_it8(self, it8_filename))
1720 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(self->
it8_button), it8_filename);
1724 gtk_combo_box_set_active(GTK_COMBO_BOX(self->
reference_mode), 1);
1729#ifdef GDK_WINDOWING_QUARTZ
1737static int parse_csv(
dt_lut_t *self,
const char *filename,
double **target_L_ptr,
double **target_a_ptr,
1738 double **target_b_ptr,
double **source_Lab_ptr,
int *num_gray,
char **
name,
char **
description)
1740 *target_L_ptr = NULL;
1741 *target_a_ptr = NULL;
1742 *target_b_ptr = NULL;
1743 *source_Lab_ptr = NULL;
1747 FILE *
f = g_fopen(filename,
"rb");
1750 while(fscanf(
f,
"%*[^\n]\n") != EOF)
N++;
1751 fseek(
f, 0, SEEK_SET);
1760 char key[16] = {0},
value[256] = {0};
1761 int read_res = fscanf(
f,
"%15[^;];%255[^\n]\n",
key,
value);
1762 if(g_strcmp0(
key,
"name") || read_res == EOF)
1764 fprintf(stderr,
"error: expected `name' in the first line\n");
1771 read_res = fscanf(
f,
"%15[^;];%255[^\n]\n",
key,
value);
1772 if(g_strcmp0(
key,
"description") || read_res == EOF)
1774 fprintf(stderr,
"error: expected `description' in the second line\n");
1781 read_res = fscanf(
f,
"%15[^;];%d\n",
key, num_gray);
1782 if(g_strcmp0(
key,
"num_gray") || read_res == EOF)
1784 fprintf(stderr,
"error: missing num_gray in csv\n");
1791 read_res = fscanf(
f,
"%*[^\n]\n");
1794 double *target_L = (
double *)calloc(
sizeof(
double), (
N + 4));
1795 double *target_a = (
double *)calloc(
sizeof(
double), (
N + 4));
1796 double *target_b = (
double *)calloc(
sizeof(
double), (
N + 4));
1797 double *source_Lab = (
double *)calloc(
sizeof(
double) * 3,
N);
1798 *target_L_ptr = target_L;
1799 *target_a_ptr = target_a;
1800 *target_b_ptr = target_b;
1801 *source_Lab_ptr = source_Lab;
1804 for(
int i = 0;
i <
N;
i++)
1806 char *patchname = line, *iter = line, *endptr;
1807 if(fgets(line,
sizeof(line) /
sizeof(*line),
f) == 0)
break;
1808 while(*iter !=
';') iter++;
1811 source_Lab[3 *
i] = g_ascii_strtod(iter, &endptr);
1812 if(iter == endptr || *endptr !=
';')
break;
1814 source_Lab[3 *
i + 1] = g_ascii_strtod(iter, &endptr);
1815 if(iter == endptr || *endptr !=
';')
break;
1817 source_Lab[3 *
i + 2] = g_ascii_strtod(iter, &endptr);
1818 if(iter == endptr || *endptr !=
';')
break;
1820 target_L[
i] = g_ascii_strtod(iter, &endptr);
1821 if(iter == endptr || *endptr !=
';')
break;
1823 target_a[
i] = g_ascii_strtod(iter, &endptr);
1824 if(iter == endptr || *endptr !=
';')
break;
1826 target_b[
i] = g_ascii_strtod(iter, &endptr);
1827 if(iter == endptr || *endptr !=
'\n')
break;
1829 const double d[3] = { target_L[
i], target_a[
i], target_b[
i] };
1830 if(sqrt(
d[0] *
d[0] +
d[1] *
d[1] +
d[2] *
d[2]) >
thrs)
1832 fprintf(stderr,
"warning: ignoring patch %s with large difference deltaE %g!\n", patchname,
1833 sqrt(
d[0] *
d[0] +
d[1] *
d[1] +
d[2] *
d[2]));
1834 fprintf(stderr,
" %g %g %g -- %g %g %g\n", source_Lab[3 *
i + 0], source_Lab[3 *
i + 1],
1835 source_Lab[3 *
i + 2], target_L[
i], target_a[
i], target_b[
i]);
1847 const char *filename_csv = argv[2];
1848 const int num_patches = atoi(argv[3]);
1849 const char *filename_style = argv[4];
1851 const int sparsity = num_patches + 4;
1862 fprintf(stderr,
"error parsing `%s', giving up\n", filename_csv);
1891 fprintf(stderr,
"Usage: %s [<input Lab pfm file>] [<cht file>] [<reference cgats/it8 or Lab pfm file>]\n"
1892 " %s --csv <csv file> <number patches> <output dtstyle file>\n",
1902 SetErrorMode(SEM_FAILCRITICALERRORS);
1906 omp_set_num_threads(omp_get_num_procs());
1913 if(argc >= 2 && !strcmp(argv[1],
"--help"))
1915 else if(argc >= 2 && !g_strcmp0(argv[1],
"--csv"))
const char ** description(struct dt_iop_module_t *self)
static __DT_CLONE_TARGETS__ void homography(float *homograph, const float angle, const float shift_v, const float shift_h, const float shear, const float f_length_kb, const float orthocorr, const float aspect, const int width, const int height, dt_iop_ashift_homodir_t dir)
const char * extension(dt_imageio_module_data_t *data)
static char * get_filename_base(const char *filename)
static void add_patches_to_array(dt_lut_t *self, GList *patch_names, int *N, int *i, double *target_L, double *target_a, double *target_b, double *colorchecker_Lab)
static gboolean handle_motion(GtkWidget *widget, GdkEventMotion *event, dt_lut_t *self, image_t *image)
static int main_gui(dt_lut_t *self, int argc, char *argv[])
static box_t * find_patch(GHashTable *table, gpointer key)
static void cht_state_callback(GtkWidget *widget, GtkStateFlags flags, gpointer user_data)
static void init_image(dt_lut_t *self, image_t *image, GCallback motion_cb)
static void get_Lab_from_box(box_t *box, float *Lab)
static int find_closest_corner(point_t *bb, float x, float y)
static void process_button_clicked_callback(GtkButton *button, gpointer user_data)
static box_t get_sample_box(chart_t *chart, box_t *outer_box, float shrink)
static point_t map_point_to_view(image_t *image, point_t p)
static void export_raw(dt_lut_t *self, char *filename, char *name, char *description)
static void reference_mode_changed_callback(GtkComboBox *widget, gpointer user_data)
static void reset_bb(image_t *image)
static int parse_csv(dt_lut_t *self, const char *filename, double **target_L_ptr, double **target_a_ptr, double **target_b_ptr, double **source_Lab_ptr, int *num_gray, char **name, char **description)
static void print_patches(dt_lut_t *self, FILE *fd, GList *patch_names)
static void get_pixel_region(const image_t *const image, const point_t *const corners, int *x_start, int *y_start, int *x_end, int *y_end)
static void collect_reference_patches(dt_lut_t *self)
static const point_t bb_ref[]
static int main_csv(dt_lut_t *self, int argc, char *argv[])
static char * encode_colorchecker(int num, const double *point, const double **target, int *permutation)
static void map_mouse_to_0_1(GtkWidget *widget, GdkEventMotion *event, image_t *image, float *x, float *y)
static void free_image(image_t *image)
static gboolean open_cht(dt_lut_t *self, const char *filename)
static void get_corners(const float *homography, box_t *box, point_t *corners)
static void export_style(dt_lut_t *self, const char *filename, const char *name, const char *description, gboolean include_basecurve, gboolean include_colorchecker, gboolean include_colorin, gboolean include_tonecurve)
static void collect_reference_patches_foreach(gpointer key, gpointer value, gpointer user_data)
static void get_xyz_sample_from_image(const image_t *const image, float shrink, box_t *box, float *xyz)
static gboolean draw_image_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data)
static void export_button_clicked_callback(GtkButton *button, gpointer user_data)
static void show_usage(const char *exe)
static void export_raw_button_clicked_callback(GtkButton *button, gpointer user_data)
static gboolean open_it8(dt_lut_t *self, const char *filename)
static char * get_export_filename(dt_lut_t *self, const char *extension, char **name, char **description, gboolean *basecurve, gboolean *colorchecker, gboolean *colorin, gboolean *tonecurve)
static gboolean open_image(image_t *image, const char *filename)
static void init_table(dt_lut_t *self)
static void source_image_changed_callback(GtkFileChooserButton *widget, gpointer user_data)
static void size_allocate_callback(GtkWidget *widget, GdkRectangle *allocation, gpointer user_data)
static void print_xml_plugin(FILE *fd, int num, int op_version, const char *operation, const char *op_params, gboolean enabled)
static void add_hdr_patches(int *N, double **target_L, double **target_a, double **target_b, double **colorchecker_Lab)
static void process_data(dt_lut_t *self, double *target_L, double *target_a, double *target_b, double *colorchecker_Lab, int N, int sparsity)
static GtkWidget * create_notebook_page_reference(dt_lut_t *self)
static gboolean motion_notify_callback_reference(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
static gboolean open_reference_image(dt_lut_t *self, const char *filename)
static void add_column(GtkTreeView *treeview, const char *title, int column_id, int sort_column)
static void collect_source_patches(dt_lut_t *self)
static void update_table(dt_lut_t *self)
static GtkWidget * create_notebook_page_process(dt_lut_t *self)
static void collect_source_patches_foreach(gpointer key, gpointer value, gpointer user_data)
static int compare_L_source(const void *x_, const void *y_)
static void ref_image_changed_callback(GtkFileChooserButton *widget, gpointer user_data)
static void cht_changed_callback(GtkFileChooserButton *widget, gpointer user_data)
static void image_lab_to_xyz(float *image, const int width, const int height)
static char * encode_tonecurve(const tonecurve_t *c)
static void update_corner(image_t *image, int which, float *x, float *y)
static GtkWidget * create_table(dt_lut_t *self)
static GtkWidget * create_notebook(dt_lut_t *self)
static void it8_changed_callback(GtkFileChooserButton *widget, gpointer user_data)
static GtkWidget * create_notebook_page_source(dt_lut_t *self)
static void map_boundingbox_to_view(image_t *image, point_t *bb)
static gboolean open_source_image(dt_lut_t *self, const char *filename)
static void shrink_changed_callback(GtkRange *range, gpointer user_data)
static void get_boundingbox(const image_t *const image, point_t *bb)
static gboolean motion_notify_callback_source(GtkWidget *widget, GdkEventMotion *event, gpointer user_data)
float * read_pfm(const char *filename, int *wd, int *ht)
double tonecurve_apply(const tonecurve_t *c, const double L)
double tonecurve_unapply(const tonecurve_t *c, const double L)
void tonecurve_delete(tonecurve_t *c)
void tonecurve_create(tonecurve_t *c, double *Lin, double *Lout, const int32_t num)
static const dt_aligned_pixel_simd_t const dt_adaptation_t const float p
chart_t * parse_cht(const char *filename)
void free_chart(chart_t *chart)
int parse_it8(const char *filename, chart_t *chart)
void checker_set_color(box_t *box, dt_colorspaces_color_profile_type_t color_space, float c0, float c1, float c2)
static dt_aligned_pixel_t rgb
const dt_aligned_pixel_t f
static dt_aligned_pixel_t XYZ
static dt_aligned_pixel_t Lab
point_t apply_homography(point_t p, const float *h)
int get_homography(const point_t *source, const point_t *target, float *h)
static void dt_free_gpointer(gpointer ptr)
#define __OMP_PARALLEL_FOR__(...)
static const dt_aligned_pixel_simd_t value
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
float dt_colorspaces_deltaE_1976(dt_aligned_pixel_t Lab0, dt_aligned_pixel_t Lab1)
float dt_colorspaces_deltaE_2000(dt_aligned_pixel_t Lab0, dt_aligned_pixel_t Lab1)
void draw_image(cairo_t *cr, image_t *image)
void draw_color_boxes_outline(cairo_t *cr, const float *homography, chart_t *chart)
void draw_no_image(cairo_t *cr, GtkWidget *widget)
void center_image(cairo_t *cr, image_t *image)
void stroke_boxes(cairo_t *cr, float line_width)
void clear_background(cairo_t *cr)
void draw_color_boxes_inside(cairo_t *cr, const float *homography, chart_t *chart, float shrink, float line_width, gboolean colored)
cairo_surface_t * cairo_surface_create_from_xyz_data(const float *const image, const int width, const int height)
void draw_f_boxes(cairo_t *cr, const float *homography, chart_t *chart)
void draw_boundingbox(cairo_t *cr, point_t *bb)
void draw_d_boxes(cairo_t *cr, const float *homography, chart_t *chart)
void set_offset_and_scale(image_t *image, float width, float height)
char * dt_exif_xmp_encode_internal(const unsigned char *input, const int len, int *output_len, gboolean do_compress)
#define DT_GUI_BOX_SPACING
static const float colorchecker_Lab[]
const float *const const float coeff[3]
float *const restrict const size_t k
dt_mipmap_buffer_dsc_flags flags
float dt_aligned_pixel_t[4]
void dt_osx_focus_window()
void dt_osx_prepare_environment()
struct _GtkWidget GtkWidget
dt_colorspaces_color_profile_type_t color_space
dt_iop_tonecurve_node_t tonecurve[3][20]
int tonecurve_autoscale_ab
char * colorchecker_encoded
GtkWidget * reference_image_box
char * reference_filename
GtkWidget * source_shrink
GtkWidget * reference_mode
GtkWidget * reference_shrink
GtkWidget * process_button
GtkWidget * reference_image_button
GtkWidget * reference_it8_box
GtkWidget * number_patches
GtkWidget * export_raw_button
GtkWidget * export_button
GHashTable * picked_source_patches
cairo_surface_t * surface
float thinplate_color_pos(float L, float a, float b)
int thinplate_match(const tonecurve_t *curve, int dim, int N, const double *point, const double **target, int S, int *permutation, double **coeff, double *avgerr, double *maxerr)