1 /*
2 * This file is part of Siril, an astronomy image processor.
3 * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4 * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5 * Reference site is https://free-astro.org/index.php/Siril
6 *
7 * Siril is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Siril is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "core/siril.h"
22 #include "core/proto.h"
23 #include "core/undo.h"
24 #include "core/processing.h"
25 #include "core/OS_utils.h"
26 #include "algos/colors.h"
27 #include "algos/statistics.h"
28 #include "io/single_image.h"
29 #include "gui/image_display.h"
30 #include "gui/progress_and_log.h"
31 #include "gui/utils.h"
32 #include "gui/histogram.h"
33 #include "gui/dialogs.h"
34
35 #include "scnr.h"
36
37 // idle function executed at the end of the scnr processing
end_scnr(gpointer p)38 static gboolean end_scnr(gpointer p) {
39 stop_processing_thread();
40 adjust_cutoff_from_updated_gfit();
41 redraw(com.cvport, REMAP_ALL);
42 redraw_previews();
43 update_gfit_histogram_if_needed();
44 set_cursor_waiting(FALSE);
45
46 return FALSE;
47 }
48
49 /* Subtractive Chromatic Noise Reduction.
50 * No unprotected GTK+ calls can go there. */
scnr(gpointer p)51 gpointer scnr(gpointer p) {
52 struct scnr_data *args = (struct scnr_data *) p;
53 double m;
54 size_t i, nbdata = args->fit->naxes[0] * args->fit->naxes[1];
55 struct timeval t_start, t_end;
56 double norm = get_normalized_value(args->fit);
57
58 siril_log_color_message(_("SCNR: processing...\n"), "green");
59 gettimeofday(&t_start, NULL);
60
61 #ifdef _OPENMP
62 #pragma omp parallel for num_threads(com.max_thread) private(i) schedule(static)
63 #endif
64 for (i = 0; i < nbdata; i++) {
65 double red, green, blue;
66 if (args->fit->type == DATA_USHORT) {
67 red = (double)args->fit->pdata[RLAYER][i] / norm;
68 green = (double)args->fit->pdata[GLAYER][i] / norm;
69 blue = (double)args->fit->pdata[BLAYER][i] / norm;
70 }
71 else if (args->fit->type == DATA_FLOAT) {
72 red = (double)args->fit->fpdata[RLAYER][i];
73 green = (double)args->fit->fpdata[GLAYER][i];
74 blue = (double)args->fit->fpdata[BLAYER][i];
75 }
76 double x, y, z, L, a, b;
77
78 if (args->preserve) {
79 rgb_to_xyz(red, green, blue, &x, &y, &z);
80 xyz_to_LAB(x, y, z, &L, &a, &b);
81 }
82 switch (args->type) {
83 case 0:
84 m = 0.5 * (red + blue);
85 green = min(green, m);
86 break;
87 case 1:
88 m = max(red, blue);
89 green = min(green, m);
90 break;
91 case 2:
92 m = max(red, blue);
93 green = (green * (1.0 - args->amount) * (1.0 - m)) + (m * green);
94 break;
95 case 3:
96 m = min(1.0, red + blue);
97 green = (green * (1.0 - args->amount) * (1.0 - m)) + (m * green);
98 }
99 if (args->preserve) {
100 double tmp;
101 rgb_to_xyz(red, green, blue, &x, &y, &z);
102 xyz_to_LAB(x, y, z, &tmp, &a, &b);
103 LAB_to_xyz(L, a, b, &x, &y, &z);
104 xyz_to_rgb(x, y, z, &red, &green, &blue);
105 }
106 if (args->fit->type == DATA_USHORT) {
107 args->fit->pdata[RLAYER][i] = round_to_WORD(red * (double)norm);
108 args->fit->pdata[GLAYER][i] = round_to_WORD(green * (double)norm);
109 args->fit->pdata[BLAYER][i] = round_to_WORD(blue * (double)norm);
110 }
111 else if (args->fit->type == DATA_FLOAT) {
112 args->fit->fpdata[RLAYER][i] = (float)red;
113 args->fit->fpdata[GLAYER][i] = (float)green;
114 args->fit->fpdata[BLAYER][i] = (float)blue;
115 }
116 }
117
118 invalidate_stats_from_fit(args->fit);
119 free(args);
120 gettimeofday(&t_end, NULL);
121 show_time(t_start, t_end);
122 siril_add_idle(end_scnr, NULL);
123 return GINT_TO_POINTER(0);
124 }
125
on_SCNR_dialog_show(GtkWidget * widget,gpointer user_data)126 void on_SCNR_dialog_show(GtkWidget *widget, gpointer user_data) {
127 GtkComboBox *comboscnr = GTK_COMBO_BOX(
128 gtk_builder_get_object(builder, "combo_scnr"));
129 int type = gtk_combo_box_get_active(comboscnr);
130
131 if (type == -1)
132 gtk_combo_box_set_active(comboscnr, 0);
133 }
134
on_SCNR_Apply_clicked(GtkButton * button,gpointer user_data)135 void on_SCNR_Apply_clicked(GtkButton *button, gpointer user_data) {
136 /* Type 0: Average Neutral protection
137 * Type 1: Maximum Neutral protection
138 */
139 int type = gtk_combo_box_get_active(
140 GTK_COMBO_BOX(gtk_builder_get_object(builder, "combo_scnr")));
141 GtkToggleButton *light_button = GTK_TOGGLE_BUTTON(
142 gtk_builder_get_object(builder, "preserve_light"));
143 gboolean preserve = gtk_toggle_button_get_active(light_button);
144 double amount = gtk_range_get_value(
145 GTK_RANGE(gtk_builder_get_object(builder, "scale_scnr")));
146
147 if (get_thread_run()) {
148 PRINT_ANOTHER_THREAD_RUNNING;
149 return;
150 }
151
152 struct scnr_data *args = malloc(sizeof(struct scnr_data));
153 undo_save_state(&gfit, _("SCNR (type=%d, amount=%0.2lf, preserve=%s)"),
154 type, amount, preserve ? "true" : "false");
155
156 args->fit = &gfit;
157 args->type = type;
158 args->amount = amount;
159 args->preserve = preserve;
160 set_cursor_waiting(TRUE);
161 start_in_new_thread(scnr, args);
162 }
163
on_SCNR_cancel_clicked(GtkButton * button,gpointer user_data)164 void on_SCNR_cancel_clicked(GtkButton *button, gpointer user_data) {
165 siril_close_dialog("SCNR_dialog");
166 }
167
on_combo_scnr_changed(GtkComboBoxText * box,gpointer user_data)168 void on_combo_scnr_changed(GtkComboBoxText *box, gpointer user_data) {
169 int type = gtk_combo_box_get_active(
170 GTK_COMBO_BOX(gtk_builder_get_object(builder, "combo_scnr")));
171 GtkScale *scale = GTK_SCALE(lookup_widget("scale_scnr"));
172 GtkLabel *label = GTK_LABEL(lookup_widget("label56"));
173 GtkSpinButton *spinButton = GTK_SPIN_BUTTON(lookup_widget("spin_scnr"));
174
175 gtk_widget_set_sensitive(GTK_WIDGET(scale), type > 1);
176 gtk_widget_set_sensitive(GTK_WIDGET(label), type > 1);
177 gtk_widget_set_sensitive(GTK_WIDGET(spinButton), type > 1);
178 }
179
180