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