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 <stdlib.h>
22 #include <stdio.h>
23 #include <math.h>
24 #include <assert.h>
25 #include <float.h>
26
27 #include "core/siril.h"
28 #include "core/proto.h"
29 #include "core/processing.h"
30 #include "core/undo.h"
31 #include "core/OS_utils.h"
32 #include "gui/progress_and_log.h"
33 #include "gui/utils.h"
34 #include "gui/image_display.h"
35 #include "gui/image_interactions.h"
36 #include "gui/message_dialog.h"
37 #include "gui/histogram.h"
38 #include "gui/dialogs.h"
39 #include "io/single_image.h"
40 #include "io/image_format_fits.h"
41 #include "algos/colors.h"
42 #include "algos/statistics.h"
43
44 /*
45 * A Fast HSL-to-RGB Transform
46 * by Ken Fishkin
47 * from "Graphics Gems", Academic Press, 1990
48 * */
49 /*
50 * * given h,s,l on [0..1],
51 * * return r,g,b on [0..1]
52 * */
hsl_to_rgb_float_sat(float h,float sl,float l,float * r,float * g,float * b)53 void hsl_to_rgb_float_sat(float h, float sl, float l, float * r, float * g,
54 float * b) {
55 float v;
56
57 h = h >= 6.f ? h - 6.f : h;
58
59 v = (l <= 0.5f) ? (l * (1.f + sl)) : (l + sl - l * sl);
60 if (v <= 0.f) {
61 *r = *g = *b = 0.f;
62 } else {
63 float m;
64 float sv;
65 int sextant;
66 float fract, vsf, mid1, mid2;
67
68 m = l + l - v;
69 sv = (v - m) / v;
70 sextant = h;
71 fract = h - sextant;
72 vsf = v * sv * fract;
73 mid1 = m + vsf;
74 mid2 = v - vsf;
75 switch (sextant) {
76 case 0:
77 *r = v;
78 *g = mid1;
79 *b = m;
80 break;
81 case 1:
82 *r = mid2;
83 *g = v;
84 *b = m;
85 break;
86 case 2:
87 *r = m;
88 *g = v;
89 *b = mid1;
90 break;
91 case 3:
92 *r = m;
93 *g = mid2;
94 *b = v;
95 break;
96 case 4:
97 *r = mid1;
98 *g = m;
99 *b = v;
100 break;
101 case 5:
102 *r = v;
103 *g = m;
104 *b = mid2;
105 break;
106 }
107 }
108 }
109 /*
110 * * RGB-HSL transforms.
111 * * Ken Fishkin, Pixar Inc., January 1989.
112 * */
113
114 /*
115 * * given r,g,b on [0 ... 1],
116 * * return (h,s,l) on [0 ... 1]
117 * */
rgb_to_hsl_float_sat(float r,float g,float b,float low,float * h,float * s,float * l)118 void rgb_to_hsl_float_sat(float r, float g, float b, float low, float *h, float *s, float *l) {
119 float v;
120 float m;
121 float vm;
122 float r2, g2, b2;
123
124 v = max(r, g);
125 v = max(v, b);
126 m = min(r, g);
127 m = min(m, b);
128
129 if (m + v < low + low) {
130 *l = 0.f;
131 return;
132 }
133 *l = (m + v) / 2.f;
134 *h = 0.f;
135 *s = 0.f; // init values
136
137 if ((*s = vm = v - m) > 0.f) {
138 *s /= (*l <= 0.5f) ? (v + m) : (2.f - v - m);
139 } else
140 return;
141
142 if (r == v) {
143 g2 = (v - g) / vm;
144 b2 = (v - b) / vm;
145 *h = (g == m ? 5.f + b2 : 1.f - g2);
146 }else if (g == v) {
147 r2 = (v - r) / vm;
148 b2 = (v - b) / vm;
149 *h = (b == m ? 1.f + r2 : 3.f - b2);
150 } else {
151 r2 = (v - r) / vm;
152 g2 = (v - g) / vm;
153 *h = (r == m ? 3.f + g2 : 5.f - r2);
154 }
155
156 }
157
158 /*
159 * A Fast HSL-to-RGB Transform
160 * by Ken Fishkin
161 * from "Graphics Gems", Academic Press, 1990
162 * */
163 /*
164 * * given h,s,l on [0..1],
165 * * return r,g,b on [0..1]
166 * */
hsl_to_rgb(double h,double sl,double l,double * r,double * g,double * b)167 void hsl_to_rgb(double h, double sl, double l, double * r, double * g,
168 double * b) {
169 double v;
170
171 assert(h >= 0.0 && h <= 1.0);
172 if (h >= 1.0) h -= 1.0; // this code doesn't work for h = 1
173 v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
174 if (v <= 0) {
175 *r = *g = *b = 0.0;
176 } else {
177 double m;
178 double sv;
179 int sextant;
180 double fract, vsf, mid1, mid2;
181
182 m = l + l - v;
183 sv = (v - m) / v;
184 h *= 6.0;
185 sextant = h;
186 fract = h - sextant;
187 vsf = v * sv * fract;
188 mid1 = m + vsf;
189 mid2 = v - vsf;
190 switch (sextant) {
191 case 0:
192 *r = v;
193 *g = mid1;
194 *b = m;
195 break;
196 case 1:
197 *r = mid2;
198 *g = v;
199 *b = m;
200 break;
201 case 2:
202 *r = m;
203 *g = v;
204 *b = mid1;
205 break;
206 case 3:
207 *r = m;
208 *g = mid2;
209 *b = v;
210 break;
211 case 4:
212 *r = mid1;
213 *g = m;
214 *b = v;
215 break;
216 case 5:
217 *r = v;
218 *g = m;
219 *b = mid2;
220 break;
221 }
222 }
223 }
224
225 /* RGB-HSL transforms.
226 * Ken Fishkin, Pixar Inc., January 1989.
227 *
228 * given r,g,b on [0 ... 1],
229 * return (h,s,l) on [0 ... 1]
230 */
rgb_to_hsl(double r,double g,double b,double * h,double * s,double * l)231 void rgb_to_hsl(double r, double g, double b, double *h, double *s, double *l) {
232 double v;
233 double m;
234 double vm;
235 double r2, g2, b2;
236
237 v = max(r, g);
238 v = max(v, b);
239 m = min(r, g);
240 m = min(m, b);
241 *h = 0.0;
242 *s = 0.0; // init values
243
244 if ((*l = (m + v) / 2.0) <= 0.0) {
245 *l = 0.0;
246 return;
247 }
248 if ((*s = vm = v - m) > 0.0) {
249 *s /= (*l <= 0.5) ? (v + m) : (2.0 - v - m);
250 } else
251 return;
252
253 r2 = (v - r) / vm;
254 g2 = (v - g) / vm;
255 b2 = (v - b) / vm;
256
257 if (r == v)
258 *h = (g == m ? 5.0 + b2 : 1.0 - g2);
259 else if (g == v)
260 *h = (b == m ? 1.0 + r2 : 3.0 - b2);
261 else
262 *h = (r == m ? 3.0 + g2 : 5.0 - r2);
263
264 *h /= 6;
265 }
266
267 /* all variables are between 0 and 1. h takes 0 for grey */
rgb_to_hsv(double r,double g,double b,double * h,double * s,double * v)268 void rgb_to_hsv(double r, double g, double b, double *h, double *s, double *v) {
269 double cmax, cmin, delta;
270
271 cmax = max(r, g);
272 cmax = max(cmax, b);
273 cmin = min(r, g);
274 cmin = min(cmin, b);
275 delta = cmax - cmin;
276 *v = cmax;
277 if (delta == 0.0) {
278 *s = 0.0;
279 *h = 0.0;
280 return;
281 }
282 *s = delta / cmax;
283
284 if (cmax == r)
285 *h = (((g - b) / delta)) / 6.0;
286 else if (cmax == g)
287 *h = (((b - r) / delta) + 2.0) / 6.0;
288 else
289 *h = (((r - g) / delta) + 4.0) / 6.0;
290
291 if (*h < 0.0)
292 *h += 1.0;
293 }
294
hsv_to_rgb(double h,double s,double v,double * r,double * g,double * b)295 void hsv_to_rgb(double h, double s, double v, double *r, double *g, double *b) {
296 double p, q, t, f;
297 int i;
298
299 if (h >= 1.0)
300 h -= 1.0;
301 h *= 6.0;
302 i = (int)h;
303 f = h - (double)i;
304 p = v * (1.0 - s);
305 q = v * (1.0 - (s * f));
306 t = v * (1.0 - (s * (1.0 - f)));
307
308 switch (i) {
309 case 0:
310 *r = v;
311 *g = t;
312 *b = p;
313 break;
314 case 1:
315 *r = q;
316 *g = v;
317 *b = p;
318 break;
319 case 2:
320 *r = p;
321 *g = v;
322 *b = t;
323 break;
324 case 3:
325 *r = p;
326 *g = q;
327 *b = v;
328 break;
329 case 4:
330 *r = t;
331 *g = p;
332 *b = v;
333 break;
334 case 5:
335 default:
336 *r = v;
337 *g = p;
338 *b = q;
339 break;
340 }
341 }
342
rgb_to_xyz(double r,double g,double b,double * x,double * y,double * z)343 void rgb_to_xyz(double r, double g, double b, double *x, double *y, double *z) {
344 r = (r <= 0.04045) ? r / 12.92 : pow(((r + 0.055) / 1.055), 2.4);
345 g = (g <= 0.04045) ? g / 12.92 : pow(((g + 0.055) / 1.055), 2.4);
346 b = (b <= 0.04045) ? b / 12.92 : pow(((b + 0.055) / 1.055), 2.4);
347
348 r *= 100;
349 g *= 100;
350 b *= 100;
351
352 *x = 0.412453 * r + 0.357580 * g + 0.180423 * b;
353 *y = 0.212671 * r + 0.715160 * g + 0.072169 * b;
354 *z = 0.019334 * r + 0.119193 * g + 0.950227 * b;
355 }
356
xyz_to_LAB(double x,double y,double z,double * L,double * a,double * b)357 void xyz_to_LAB(double x, double y, double z, double *L, double *a, double *b) {
358 x /= 95.047;
359 y /= 100.000;
360 z /= 108.883;
361
362 x = (x > 0.008856452) ? pow(x, 1 / 3.0) : (7.787037037 * x) + (16. / 116.);
363 y = (y > 0.008856452) ? pow(y, 1 / 3.0) : (7.787037037 * y) + (16. / 116.);
364 z = (z > 0.008856452) ? pow(z, 1 / 3.0) : (7.787037037 * z) + (16. / 116.);
365
366 *L = (116.0 * y) - 16.0;
367 *a = 500.0 * (x - y);
368 *b = 200.0 * (y - z);
369 }
370
LAB_to_xyz(double L,double a,double b,double * x,double * y,double * z)371 void LAB_to_xyz(double L, double a, double b, double *x, double *y, double *z) {
372 *y = (L + 16.0) / 116.0;
373 *x = a / 500.0 + (*y);
374 *z = *y - b / 200.0;
375
376 double x3, y3, z3;
377 x3 = (*x) * (*x) * (*x);
378 y3 = (*y) * (*y) * (*y);
379 z3 = (*z) * (*z) * (*z);
380
381 *x = (x3 > 0.008856452) ? x3 : (*x - 16. / 116.) / 7.787037037;
382 *y = (y3 > 0.008856452) ? y3 : (*y - 16. / 116.) / 7.787037037;
383 *z = (z3 > 0.008856452) ? z3 : (*z - 16. / 116.) / 7.787037037;
384
385 *x *= 95.047;
386 *y *= 100.000;
387 *z *= 108.883;
388 }
389
xyz_to_rgb(double x,double y,double z,double * r,double * g,double * b)390 void xyz_to_rgb(double x, double y, double z, double *r, double *g, double *b) {
391 x /= 100.0;
392 y /= 100.0;
393 z /= 100.0;
394
395 *r = 3.240479 * x - 1.537150 * y - 0.498535 * z;
396 *g = -0.969256 * x + 1.875992 * y + 0.041556 * z;
397 *b = 0.055648 * x - 0.204043 * y + 1.057311 * z;
398
399 *r = (*r > 0.0031308) ? 1.055 * (pow(*r, (1 / 2.4))) - 0.055 : 12.92 * (*r);
400 *g = (*g > 0.0031308) ? 1.055 * (pow(*g, (1 / 2.4))) - 0.055 : 12.92 * (*g);
401 *b = (*b > 0.0031308) ? 1.055 * (pow(*b, (1 / 2.4))) - 0.055 : 12.92 * (*b);
402 }
403
404 // color index to temperature in kelvin
BV_to_T(double BV)405 double BV_to_T(double BV) {
406 double T;
407
408 // make sure BV is within its bounds [-0.4, 2] otherwise the math doesnt work
409 if (BV < -0.4) {
410 BV = -0.4;
411 } else if (BV > 2) {
412 BV = 2;
413 }
414
415 // http://www.wikiwand.com/en/Color_index
416 T = 4600 * ((1 / ((0.92 * BV) + 1.7)) + (1 / ((0.92 * BV) + 0.62)));
417
418 return T;
419 }
420
equalize_cfa_fit_with_coeffs(fits * fit,float coeff1,float coeff2,int config)421 int equalize_cfa_fit_with_coeffs(fits *fit, float coeff1, float coeff2, int config) {
422 unsigned int row, col;
423 float tmp1, tmp2;
424 if (fit->type == DATA_USHORT) {
425 WORD *data = fit->data;
426 for (row = 0; row < fit->ry - 1; row += 2) {
427 for (col = 0; col < fit->rx - 1; col += 2) {
428 if (config == 0) {
429 tmp1 = (float)data[1 + col + row * fit->rx] / coeff1;
430 data[1 + col + row * fit->rx] = round_to_WORD(tmp1);
431
432 tmp2 = (float)data[col + (1 + row) * fit->rx] / coeff2;
433 data[col + (1 + row) * fit->rx] = round_to_WORD(tmp2);
434
435 } else {
436 tmp1 = (float)data[col + row * fit->rx] / coeff1;
437 data[col + row * fit->rx] = round_to_WORD(tmp1);
438
439 tmp2 = (float)data[1 + col + (1 + row) * fit->rx] / coeff2;
440 data[1 + col + (1 + row) * fit->rx] = round_to_WORD(tmp2);
441
442 }
443 }
444 }
445 }
446 else if (fit->type == DATA_FLOAT) {
447 float *data = fit->fdata;
448 for (row = 0; row < fit->ry - 1; row += 2) {
449 for (col = 0; col < fit->rx - 1; col += 2) {
450 if (config == 0) {
451 tmp1 = data[1 + col + row * fit->rx] / coeff1;
452 data[1 + col + row * fit->rx] = tmp1;
453
454 tmp2 = data[col + (1 + row) * fit->rx] / coeff2;
455 data[col + (1 + row) * fit->rx] = tmp2;
456
457 } else {
458 tmp1 = data[col + row * fit->rx] / coeff1;
459 data[col + row * fit->rx] = tmp1;
460
461 tmp2 = data[1 + col + (1 + row) * fit->rx] / coeff2;
462 data[1 + col + (1 + row) * fit->rx] = tmp2;
463
464 }
465 }
466 }
467 }
468 else return 1;
469 return 0;
470 }
471
472 // idle function executed at the end of the extract_channels processing
end_extract_channels(gpointer p)473 static gboolean end_extract_channels(gpointer p) {
474 struct extract_channels_data *args = (struct extract_channels_data *) p;
475 stop_processing_thread();
476 free(args->channel[0]);
477 free(args->channel[1]);
478 free(args->channel[2]);
479 free(args);
480 set_cursor_waiting(FALSE);
481
482 return FALSE;
483 }
484
extract_channels_ushort(gpointer p)485 static gpointer extract_channels_ushort(gpointer p) {
486 struct extract_channels_data *args = (struct extract_channels_data *) p;
487 WORD *buf[3] = { args->fit->pdata[RLAYER], args->fit->pdata[GLAYER],
488 args->fit->pdata[BLAYER] };
489 size_t n = args->fit->naxes[0] * args->fit->naxes[1];
490 struct timeval t_start, t_end;
491 args->process = TRUE;
492
493 if (args->fit->naxes[2] != 3) {
494 siril_log_message(
495 _("Siril cannot extract layers. Make sure your image is in RGB mode.\n"));
496 args->process = FALSE;
497 clearfits(args->fit);
498 siril_add_idle(end_extract_channels, args);
499 return GINT_TO_POINTER(1);
500 }
501
502 siril_log_color_message(_("%s channel extraction: processing...\n"), "green",
503 args->str_type);
504 gettimeofday(&t_start, NULL);
505
506 switch (args->type) {
507 /* RGB space: nothing to do */
508 case 0:
509 break;
510 /* HSL space */
511 case 1:
512 #ifdef _OPENMP
513 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
514 #endif
515 for (size_t i = 0; i < n; i++) {
516 double h, s, l;
517 double r = (double) buf[RLAYER][i] / USHRT_MAX_DOUBLE;
518 double g = (double) buf[GLAYER][i] / USHRT_MAX_DOUBLE;
519 double b = (double) buf[BLAYER][i] / USHRT_MAX_DOUBLE;
520 rgb_to_hsl(r, g, b, &h, &s, &l);
521 buf[RLAYER][i] = round_to_WORD(h * 360.0); // TODO: what's that?
522 buf[GLAYER][i] = round_to_WORD(s * USHRT_MAX_DOUBLE);
523 buf[BLAYER][i] = round_to_WORD(l * USHRT_MAX_DOUBLE);
524 }
525 break;
526 /* HSV space */
527 case 2:
528 #ifdef _OPENMP
529 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
530 #endif
531 for (size_t i = 0; i < n; i++) {
532 double h, s, v;
533 double r = (double) buf[RLAYER][i] / USHRT_MAX_DOUBLE;
534 double g = (double) buf[GLAYER][i] / USHRT_MAX_DOUBLE;
535 double b = (double) buf[BLAYER][i] / USHRT_MAX_DOUBLE;
536 rgb_to_hsv(r, g, b, &h, &s, &v);
537 buf[RLAYER][i] = round_to_WORD(h * 360.0);
538 buf[GLAYER][i] = round_to_WORD(s * USHRT_MAX_DOUBLE);
539 buf[BLAYER][i] = round_to_WORD(v * USHRT_MAX_DOUBLE);
540 }
541 break;
542 /* CIE L*a*b */
543 case 3:
544 #ifdef _OPENMP
545 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
546 #endif
547 for (size_t i = 0; i < n; i++) {
548 double x, y, z, L, a, b;
549 double red = (double) buf[RLAYER][i] / USHRT_MAX_DOUBLE;
550 double green = (double) buf[GLAYER][i] / USHRT_MAX_DOUBLE;
551 double blue = (double) buf[BLAYER][i] / USHRT_MAX_DOUBLE;
552 rgb_to_xyz(red, green, blue, &x, &y, &z);
553 xyz_to_LAB(x, y, z, &L, &a, &b);
554 buf[RLAYER][i] = round_to_WORD(L / 100. * USHRT_MAX_DOUBLE);// 0 < L < 100
555 buf[GLAYER][i] = round_to_WORD(
556 ((a + 128) / 255.) * USHRT_MAX_DOUBLE); // -128 < a < 127
557 buf[BLAYER][i] = round_to_WORD(
558 ((b + 128) / 255.) * USHRT_MAX_DOUBLE); // -128 < b < 127
559 }
560
561 }
562 for (int i = 0; i < 3; i++)
563 save1fits16(args->channel[i], args->fit, i);
564 clearfits(args->fit);
565 gettimeofday(&t_end, NULL);
566 show_time(t_start, t_end);
567 siril_add_idle(end_extract_channels, args);
568
569 return GINT_TO_POINTER(0);
570 }
571
extract_channels_float(gpointer p)572 static gpointer extract_channels_float(gpointer p) {
573 struct extract_channels_data *args = (struct extract_channels_data *) p;
574 float *buf[3] = { args->fit->fpdata[RLAYER], args->fit->fpdata[GLAYER],
575 args->fit->fpdata[BLAYER] };
576 struct timeval t_start, t_end;
577 size_t n = args->fit->naxes[0] * args->fit->naxes[1];
578 args->process = TRUE;
579
580 if (args->fit->naxes[2] != 3) {
581 siril_log_message(
582 _("Siril cannot extract layers. Make sure your image is in RGB mode.\n"));
583 args->process = FALSE;
584 clearfits(args->fit);
585 siril_add_idle(end_extract_channels, args);
586 return GINT_TO_POINTER(1);
587 }
588
589 siril_log_color_message(_("%s channel extraction: processing...\n"), "green",
590 args->str_type);
591 gettimeofday(&t_start, NULL);
592
593 switch (args->type) {
594 /* RGB space: nothing to do */
595 case 0:
596 break;
597 /* HSL space */
598 case 1:
599 #ifdef _OPENMP
600 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
601 #endif
602 for (size_t i = 0; i < n; i++) {
603 double h, s, l;
604 double r = (double) buf[RLAYER][i];
605 double g = (double) buf[GLAYER][i];
606 double b = (double) buf[BLAYER][i];
607 rgb_to_hsl(r, g, b, &h, &s, &l);
608 buf[RLAYER][i] = (float) h;
609 buf[GLAYER][i] = (float) s;
610 buf[BLAYER][i] = (float) l;
611 }
612 break;
613 /* HSV space */
614 case 2:
615 #ifdef _OPENMP
616 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
617 #endif
618 for (size_t i = 0; i < n; i++) {
619 double h, s, v;
620 double r = (double) buf[RLAYER][i];
621 double g = (double) buf[GLAYER][i];
622 double b = (double) buf[BLAYER][i];
623 rgb_to_hsv(r, g, b, &h, &s, &v);
624 buf[RLAYER][i] = (float) h;
625 buf[GLAYER][i] = (float) s;
626 buf[BLAYER][i] = (float) v;
627 }
628 break;
629 /* CIE L*a*b */
630 case 3:
631 #ifdef _OPENMP
632 #pragma omp parallel for num_threads(com.max_thread) schedule(static)
633 #endif
634 for (size_t i = 0; i < n; i++) {
635 double x, y, z, L, a, b;
636 double red = (double) buf[RLAYER][i];
637 double green = (double) buf[GLAYER][i];
638 double blue = (double) buf[BLAYER][i];
639 rgb_to_xyz(red, green, blue, &x, &y, &z);
640 xyz_to_LAB(x, y, z, &L, &a, &b);
641 buf[RLAYER][i] = (float) (L / 100.); // 0 < L < 100
642 buf[GLAYER][i] = (float) ((a + 128.) / 255.); // -128 < a < 127
643 buf[BLAYER][i] = (float) ((b + 128.) / 255.); // -128 < b < 127
644 }
645
646 }
647 for (int i = 0; i < 3; i++)
648 save1fits32(args->channel[i], args->fit, i);
649 clearfits(args->fit);
650 gettimeofday(&t_end, NULL);
651 show_time(t_start, t_end);
652 siril_add_idle(end_extract_channels, args);
653
654 return GINT_TO_POINTER(0);
655 }
656
extract_channels(gpointer p)657 gpointer extract_channels(gpointer p) {
658 struct extract_channels_data *args = (struct extract_channels_data *)p;
659 if (args->fit->type == DATA_USHORT)
660 return extract_channels_ushort(p);
661 if (args->fit->type == DATA_FLOAT)
662 return extract_channels_float(p);
663 siril_add_idle(end_extract_channels, args);
664 return GINT_TO_POINTER(1);
665 }
666
667 /****************** Color calibration ************************/
on_button_bkg_selection_clicked(GtkButton * button,gpointer user_data)668 void on_button_bkg_selection_clicked(GtkButton *button, gpointer user_data) {
669 static GtkSpinButton *selection_black_value[4] = { NULL, NULL, NULL, NULL };
670
671 if ((!com.selection.h) || (!com.selection.w)) {
672 siril_message_dialog(GTK_MESSAGE_WARNING, _("There is no selection"),
673 _("Make a selection of the background area"));
674 return;
675 }
676
677 if (!selection_black_value[0]) {
678 selection_black_value[0] = GTK_SPIN_BUTTON(
679 gtk_builder_get_object(builder, "spin_bkg_x"));
680 selection_black_value[1] = GTK_SPIN_BUTTON(
681 gtk_builder_get_object(builder, "spin_bkg_y"));
682 selection_black_value[2] = GTK_SPIN_BUTTON(
683 gtk_builder_get_object(builder, "spin_bkg_w"));
684 selection_black_value[3] = GTK_SPIN_BUTTON(
685 gtk_builder_get_object(builder, "spin_bkg_h"));
686 }
687
688 gtk_spin_button_set_value(selection_black_value[0], com.selection.x);
689 gtk_spin_button_set_value(selection_black_value[1], com.selection.y);
690 gtk_spin_button_set_value(selection_black_value[2], com.selection.w);
691 gtk_spin_button_set_value(selection_black_value[3], com.selection.h);
692 }
693
initialize_calibration_interface()694 void initialize_calibration_interface() {
695 static GtkAdjustment *selection_black_adjustment[4] = { NULL, NULL, NULL,
696 NULL };
697 static GtkAdjustment *selection_white_adjustment[4] = { NULL, NULL, NULL,
698 NULL };
699
700 if (!selection_black_adjustment[0]) {
701 selection_black_adjustment[0] = GTK_ADJUSTMENT(
702 gtk_builder_get_object(builder, "adjustment_bkg_x"));
703 selection_black_adjustment[1] = GTK_ADJUSTMENT(
704 gtk_builder_get_object(builder, "adjustment_bkg_y"));
705 selection_black_adjustment[2] = GTK_ADJUSTMENT(
706 gtk_builder_get_object(builder, "adjustment_bkg_w"));
707 selection_black_adjustment[3] = GTK_ADJUSTMENT(
708 gtk_builder_get_object(builder, "adjustment_bkg_h"));
709 }
710 if (!selection_white_adjustment[0]) {
711 selection_white_adjustment[0] = GTK_ADJUSTMENT(
712 gtk_builder_get_object(builder, "adjustment_white_x"));
713 selection_white_adjustment[1] = GTK_ADJUSTMENT(
714 gtk_builder_get_object(builder, "adjustment_white_y"));
715 selection_white_adjustment[2] = GTK_ADJUSTMENT(
716 gtk_builder_get_object(builder, "adjustment_white_w"));
717 selection_white_adjustment[3] = GTK_ADJUSTMENT(
718 gtk_builder_get_object(builder, "adjustment_white_h"));
719 }
720 gtk_adjustment_set_upper(selection_black_adjustment[0], gfit.rx);
721 gtk_adjustment_set_upper(selection_black_adjustment[1], gfit.ry);
722 gtk_adjustment_set_upper(selection_black_adjustment[2], gfit.rx);
723 gtk_adjustment_set_upper(selection_black_adjustment[3], gfit.ry);
724 gtk_adjustment_set_value(selection_black_adjustment[0], 0);
725 gtk_adjustment_set_value(selection_black_adjustment[1], 0);
726 gtk_adjustment_set_value(selection_black_adjustment[2], 0);
727 gtk_adjustment_set_value(selection_black_adjustment[3], 0);
728
729 gtk_adjustment_set_upper(selection_white_adjustment[0], gfit.rx);
730 gtk_adjustment_set_upper(selection_white_adjustment[1], gfit.ry);
731 gtk_adjustment_set_upper(selection_white_adjustment[2], gfit.rx);
732 gtk_adjustment_set_upper(selection_white_adjustment[3], gfit.ry);
733 gtk_adjustment_set_value(selection_white_adjustment[0], 0);
734 gtk_adjustment_set_value(selection_white_adjustment[1], 0);
735 gtk_adjustment_set_value(selection_white_adjustment[2], 0);
736 gtk_adjustment_set_value(selection_white_adjustment[3], 0);
737 }
738
739 /* This function equalize the background by giving equal value for all layers */
background_neutralize(fits * fit,rectangle black_selection)740 static void background_neutralize(fits* fit, rectangle black_selection) {
741 int chan;
742 size_t i, n = fit->naxes[0] * fit->naxes[1];
743 imstats* stats[3];
744 double ref = 0;
745
746 assert(fit->naxes[2] == 3);
747
748 for (chan = 0; chan < 3; chan++) {
749 stats[chan] = statistics(NULL, -1, fit, chan, &black_selection, STATS_BASIC, TRUE);
750 if (!stats[chan]) {
751 siril_log_message(_("Error: statistics computation failed.\n"));
752 return;
753 }
754 ref += stats[chan]->median;
755 }
756 ref /= 3.0;
757
758 if (fit->type == DATA_USHORT) {
759 for (chan = 0; chan < 3; chan++) {
760 double offset = stats[chan]->mean - ref;
761 WORD *buf = fit->pdata[chan];
762 for (i = 0; i < n; i++) {
763 buf[i] = round_to_WORD((double)buf[i] - offset);
764 }
765 free_stats(stats[chan]);
766 }
767 }
768 else if (fit->type == DATA_FLOAT) {
769 for (chan = 0; chan < 3; chan++) {
770 float offset = stats[chan]->mean - ref;
771 float *buf = fit->fpdata[chan];
772 for (i = 0; i < n; i++) {
773 buf[i] = buf[i] - offset;
774 }
775 free_stats(stats[chan]);
776 }
777 }
778
779 invalidate_stats_from_fit(fit);
780 }
781
on_button_bkg_neutralization_clicked(GtkButton * button,gpointer user_data)782 void on_button_bkg_neutralization_clicked(GtkButton *button, gpointer user_data) {
783 static GtkSpinButton *selection_black_value[4] = { NULL, NULL, NULL, NULL };
784 rectangle black_selection;
785 int width, height;
786
787 if (!selection_black_value[0]) {
788 selection_black_value[0] = GTK_SPIN_BUTTON(
789 gtk_builder_get_object(builder, "spin_bkg_x"));
790 selection_black_value[1] = GTK_SPIN_BUTTON(
791 gtk_builder_get_object(builder, "spin_bkg_y"));
792 selection_black_value[2] = GTK_SPIN_BUTTON(
793 gtk_builder_get_object(builder, "spin_bkg_w"));
794 selection_black_value[3] = GTK_SPIN_BUTTON(
795 gtk_builder_get_object(builder, "spin_bkg_h"));
796 }
797 width = (int) gtk_spin_button_get_value(selection_black_value[2]);
798 height = (int) gtk_spin_button_get_value(selection_black_value[3]);
799
800 if ((!width) || (!height)) {
801 siril_message_dialog( GTK_MESSAGE_WARNING, _("There is no selection"),
802 _("Make a selection of the background area"));
803 return;
804 }
805 black_selection.x = gtk_spin_button_get_value(selection_black_value[0]);
806 black_selection.y = gtk_spin_button_get_value(selection_black_value[1]);
807 black_selection.w = gtk_spin_button_get_value(selection_black_value[2]);
808 black_selection.h = gtk_spin_button_get_value(selection_black_value[3]);
809
810 undo_save_state(&gfit, _("Background neutralization"));
811
812 set_cursor_waiting(TRUE);
813 background_neutralize(&gfit, black_selection);
814 delete_selected_area();
815
816 redraw(com.cvport, REMAP_ALL);
817 redraw_previews();
818 update_gfit_histogram_if_needed();
819 set_cursor_waiting(FALSE);
820 }
821
on_button_white_selection_clicked(GtkButton * button,gpointer user_data)822 void on_button_white_selection_clicked(GtkButton *button, gpointer user_data) {
823 static GtkSpinButton *selection_white_value[4] = { NULL, NULL, NULL, NULL };
824
825 if (!selection_white_value[0]) {
826 selection_white_value[0] = GTK_SPIN_BUTTON(
827 gtk_builder_get_object(builder, "spin_white_x"));
828 selection_white_value[1] = GTK_SPIN_BUTTON(
829 gtk_builder_get_object(builder, "spin_white_y"));
830 selection_white_value[2] = GTK_SPIN_BUTTON(
831 gtk_builder_get_object(builder, "spin_white_w"));
832 selection_white_value[3] = GTK_SPIN_BUTTON(
833 gtk_builder_get_object(builder, "spin_white_h"));
834 }
835
836 if ((!com.selection.h) || (!com.selection.w)) {
837 siril_message_dialog( GTK_MESSAGE_WARNING, _("There is no selection"),
838 _("Make a selection of the white reference area"));
839 return;
840 }
841
842 gtk_spin_button_set_value(selection_white_value[0], com.selection.x);
843 gtk_spin_button_set_value(selection_white_value[1], com.selection.y);
844 gtk_spin_button_set_value(selection_white_value[2], com.selection.w);
845 gtk_spin_button_set_value(selection_white_value[3], com.selection.h);
846 }
847
get_coeff_for_wb(fits * fit,rectangle white,rectangle black,double kw[],double bg[],double norm,double low,double high)848 static void get_coeff_for_wb(fits *fit, rectangle white, rectangle black,
849 double kw[], double bg[], double norm, double low, double high) {
850 int chan, i, j, n;
851 double tmp[3] = { 0.0, 0.0, 0.0 };
852
853 assert(fit->naxes[2] == 3);
854
855 if (fit->type == DATA_USHORT) {
856 WORD lo = round_to_WORD(low * (norm));
857 WORD hi = round_to_WORD(high * (norm));
858
859 for (chan = 0; chan < 3; chan++) {
860 n = 0;
861 WORD *from = fit->pdata[chan] + (fit->ry - white.y - white.h) * fit->rx
862 + white.x;
863 int stridefrom = fit->rx - white.w;
864
865 for (i = 0; i < white.h; i++) {
866 for (j = 0; j < white.w; j++) {
867 if (*from > lo && *from < hi ) {
868 kw[chan] += (double)*from / norm;
869 n++;
870 }
871 from++;
872 }
873 from += stridefrom;
874 }
875 if (n > 0)
876 kw[chan] /= (double)n;
877 }
878 }
879 else if (fit->type == DATA_FLOAT) {
880 for (chan = 0; chan < 3; chan++) {
881 n = 0;
882 float *from = fit->fpdata[chan] + (fit->ry - white.y - white.h) * fit->rx
883 + white.x;
884 int stridefrom = fit->rx - white.w;
885
886 for (i = 0; i < white.h; i++) {
887 for (j = 0; j < white.w; j++) {
888 double f = (double)*from;
889 if (f > low && f < high) {
890 kw[chan] += f;
891 n++;
892 }
893 from++;
894 }
895 from += stridefrom;
896 }
897 if (n > 0)
898 kw[chan] /= (double)n;
899 }
900 }
901 else return;
902
903 siril_log_message(_("Background reference:\n"));
904 for (chan = 0; chan < 3; chan++) {
905 imstats *stat = statistics(NULL, -1, fit, chan, &black, STATS_BASIC, TRUE);
906 if (!stat) {
907 siril_log_message(_("Error: statistics computation failed.\n"));
908 return;
909 }
910 bg[chan] = stat->median / stat->normValue;
911 siril_log_message("B%d: %.5e\n", chan, bg[chan]);
912 free_stats(stat);
913
914 }
915
916 siril_log_message(_("White reference:\n"));
917 for (chan = 0; chan < 3; chan++) {
918 siril_log_message("W%d: %.5e\n", chan, kw[chan]);
919 kw[chan] = fabs(kw[chan] - bg[chan]);
920 }
921
922 int rc = (kw[0] > kw[1]) ? ((kw[0] > kw[2]) ? 0 : 2) :
923 ((kw[1] > kw[2]) ? 1 : 2);
924 for (chan = 0; chan < 3; chan++) {
925 if (chan == rc)
926 tmp[chan] = 1.0;
927 else
928 tmp[chan] = kw[rc] / kw[chan];
929 }
930
931 siril_log_message(_("Color calibration factors:\n"));
932 for (chan = 0; chan < 3; chan++) {
933 kw[chan] = tmp[chan];
934 siril_log_message("K%d: %5.3lf\n", chan, kw[chan]);
935 }
936 }
937
calibrate(fits * fit,int layer,double kw,double bg,double norm)938 static int calibrate(fits *fit, int layer, double kw, double bg, double norm) {
939 size_t i, n = fit->naxes[0] * fit->naxes[1];
940 if (fit->type == DATA_USHORT) {
941 double bgNorm = bg * norm;
942 WORD *buf = fit->pdata[layer];
943 for (i = 0; i < n; ++i) {
944 buf[i] = round_to_WORD((buf[i] - bgNorm) * kw + bgNorm);
945 }
946 }
947 else if (fit->type == DATA_FLOAT) {
948 float *buf = fit->fpdata[layer];
949 for (i = 0; i < n; ++i) {
950 buf[i] = (float)(((double)buf[i] - bg) * kw + bg);
951 }
952 }
953 else return 1;
954 return 0;
955 }
956
white_balance(fits * fit,gboolean is_manual,rectangle white_selection,rectangle black_selection)957 static void white_balance(fits *fit, gboolean is_manual, rectangle white_selection,
958 rectangle black_selection) {
959 int chan;
960 double norm, low, high;
961 double kw[3] = { 0.0, 0.0, 0.0 };
962 double bg[3] = { 0.0, 0.0, 0.0 };
963 static GtkRange *scale_white_balance[3] = { NULL, NULL, NULL };
964 static GtkRange *scaleLimit[2] = { NULL, NULL };
965
966 if (scale_white_balance[RLAYER] == NULL) {
967 scale_white_balance[RLAYER] = GTK_RANGE(lookup_widget("scale_r"));
968 scale_white_balance[GLAYER] = GTK_RANGE(lookup_widget("scale_g"));
969 scale_white_balance[BLAYER] = GTK_RANGE(lookup_widget("scale_b"));
970
971 scaleLimit[0] = GTK_RANGE(lookup_widget("lowWhiteColorCalibScale"));
972 scaleLimit[1] = GTK_RANGE(lookup_widget("upWhiteColorCalibScale"));
973 }
974
975 assert(fit->naxes[2] == 3);
976 norm = get_normalized_value(fit);
977
978 if (is_manual) {
979 kw[RLAYER] = gtk_range_get_value(scale_white_balance[RLAYER]);
980 kw[GLAYER] = gtk_range_get_value(scale_white_balance[GLAYER]);
981 kw[BLAYER] = gtk_range_get_value(scale_white_balance[BLAYER]);
982
983 } else {
984 low = gtk_range_get_value(scaleLimit[0]);
985 high = gtk_range_get_value(scaleLimit[1]);
986 get_coeff_for_wb(fit, white_selection, black_selection, kw, bg, norm, low, high);
987 }
988 #ifdef _OPENMP
989 #pragma omp parallel for num_threads(com.max_thread) private(chan) schedule(static)
990 #endif
991 for (chan = 0; chan < 3; chan++) {
992 if (kw[chan] == 1.0) continue;
993 calibrate(fit, chan, kw[chan], bg[chan], norm);
994 }
995
996 invalidate_stats_from_fit(fit);
997 }
998
on_calibration_apply_button_clicked(GtkButton * button,gpointer user_data)999 void on_calibration_apply_button_clicked(GtkButton *button, gpointer user_data) {
1000 rectangle black_selection, white_selection;
1001 static GtkSpinButton *selection_black_value[4] = { NULL, NULL, NULL, NULL };
1002 static GtkSpinButton *selection_white_value[4] = { NULL, NULL, NULL, NULL };
1003 struct timeval t_start, t_end;
1004
1005 siril_log_color_message(_("Color Calibration: processing...\n"), "green");
1006 gettimeofday(&t_start, NULL);
1007
1008 GtkToggleButton *manual = GTK_TOGGLE_BUTTON(lookup_widget("checkbutton_manual_calibration"));
1009 gboolean is_manual = gtk_toggle_button_get_active(manual);
1010
1011 if (!selection_black_value[0]) {
1012 selection_black_value[0] = GTK_SPIN_BUTTON(lookup_widget("spin_bkg_x"));
1013 selection_black_value[1] = GTK_SPIN_BUTTON(lookup_widget("spin_bkg_y"));
1014 selection_black_value[2] = GTK_SPIN_BUTTON(lookup_widget("spin_bkg_w"));
1015 selection_black_value[3] = GTK_SPIN_BUTTON(lookup_widget("spin_bkg_h"));
1016 }
1017
1018 if (!selection_white_value[0]) {
1019 selection_white_value[0] = GTK_SPIN_BUTTON(lookup_widget("spin_white_x"));
1020 selection_white_value[1] = GTK_SPIN_BUTTON(lookup_widget("spin_white_y"));
1021 selection_white_value[2] = GTK_SPIN_BUTTON(lookup_widget("spin_white_w"));
1022 selection_white_value[3] = GTK_SPIN_BUTTON(lookup_widget("spin_white_h"));
1023 }
1024
1025 black_selection.x = gtk_spin_button_get_value(selection_black_value[0]);
1026 black_selection.y = gtk_spin_button_get_value(selection_black_value[1]);
1027 black_selection.w = gtk_spin_button_get_value(selection_black_value[2]);
1028 black_selection.h = gtk_spin_button_get_value(selection_black_value[3]);
1029
1030 if ((!black_selection.w || !black_selection.h) && !is_manual) {
1031 siril_message_dialog( GTK_MESSAGE_WARNING, _("There is no selection"),
1032 _("Make a selection of the background area"));
1033 return;
1034 }
1035
1036 white_selection.x = gtk_spin_button_get_value(selection_white_value[0]);
1037 white_selection.y = gtk_spin_button_get_value(selection_white_value[1]);
1038 white_selection.w = gtk_spin_button_get_value(selection_white_value[2]);
1039 white_selection.h = gtk_spin_button_get_value(selection_white_value[3]);
1040
1041 if ((!white_selection.w || !white_selection.h) && !is_manual) {
1042 siril_message_dialog( GTK_MESSAGE_WARNING, _("There is no selection"),
1043 _("Make a selection of the white reference area"));
1044 return;
1045 }
1046
1047 set_cursor_waiting(TRUE);
1048 undo_save_state(&gfit, _("Color Calibration"));
1049 white_balance(&gfit, is_manual, white_selection, black_selection);
1050
1051 gettimeofday(&t_end, NULL);
1052
1053 show_time(t_start, t_end);
1054
1055 delete_selected_area();
1056
1057 redraw(com.cvport, REMAP_ALL);
1058 redraw_previews();
1059 update_gfit_histogram_if_needed();
1060 set_cursor_waiting(FALSE);
1061 }
1062
on_calibration_close_button_clicked(GtkButton * button,gpointer user_data)1063 void on_calibration_close_button_clicked(GtkButton *button, gpointer user_data) {
1064 siril_close_dialog("color_calibration");
1065 }
1066
on_checkbutton_manual_calibration_toggled(GtkToggleButton * togglebutton,gpointer user_data)1067 void on_checkbutton_manual_calibration_toggled(GtkToggleButton *togglebutton,
1068 gpointer user_data) {
1069 GtkWidget *cc_box_red = lookup_widget("cc_box_red");
1070 GtkWidget *scale_r = lookup_widget("scale_r");
1071 GtkWidget *cc_box_green = lookup_widget("cc_box_green");
1072 GtkWidget *scale_g = lookup_widget("scale_g");
1073 GtkWidget *cc_box_blue = lookup_widget("cc_box_blue");
1074 GtkWidget *scale_b = lookup_widget("scale_b");
1075 gtk_widget_set_sensitive(cc_box_red, gtk_toggle_button_get_active(togglebutton));
1076 gtk_widget_set_sensitive(scale_r, gtk_toggle_button_get_active(togglebutton));
1077 gtk_widget_set_sensitive(cc_box_green, gtk_toggle_button_get_active(togglebutton));
1078 gtk_widget_set_sensitive(scale_g, gtk_toggle_button_get_active(togglebutton));
1079 gtk_widget_set_sensitive(cc_box_blue, gtk_toggle_button_get_active(togglebutton));
1080 gtk_widget_set_sensitive(scale_b, gtk_toggle_button_get_active(togglebutton));
1081 }
1082
pos_to_neg(fits * fit)1083 int pos_to_neg(fits *fit) {
1084 size_t i, n = fit->naxes[0] * fit->naxes[1] * fit->naxes[2];
1085 if (fit->type == DATA_USHORT) {
1086 WORD norm = (WORD)get_normalized_value(fit);
1087 #ifdef _OPENMP
1088 #pragma omp parallel for num_threads(com.max_thread)
1089 #endif
1090 for (i = 0; i < n; i++) {
1091 fit->data[i] = norm - fit->data[i];
1092 }
1093 }
1094 else if (fit->type == DATA_FLOAT) {
1095 #ifdef _OPENMP
1096 #pragma omp parallel for num_threads(com.max_thread)
1097 #endif
1098 for (i = 0; i < n; i++) {
1099 fit->fdata[i] = 1.0f - fit->fdata[i];
1100 }
1101 }
1102 else return 1;
1103
1104 return 0;
1105 }
1106
negative_processing()1107 void negative_processing() {
1108 set_cursor_waiting(TRUE);
1109 undo_save_state(&gfit, _("Negative Transformation"));
1110 pos_to_neg(&gfit);
1111 invalidate_stats_from_fit(&gfit);
1112 invalidate_gfit_histogram();
1113 update_gfit_histogram_if_needed();
1114 redraw(com.cvport, REMAP_ALL);
1115 redraw_previews();
1116 set_cursor_waiting(FALSE);
1117 }
1118
1119 /**********************************************************************/
1120
on_extract_channel_button_close_clicked(GtkButton * button,gpointer user_data)1121 void on_extract_channel_button_close_clicked(GtkButton *button,
1122 gpointer user_data) {
1123 siril_close_dialog("extract_channel_dialog");
1124 }
1125
on_combo_extract_colors_changed(GtkComboBox * box,gpointer user_data)1126 void on_combo_extract_colors_changed(GtkComboBox *box, gpointer user_data) {
1127 switch(gtk_combo_box_get_active(box)) {
1128 default:
1129 case 0: // RGB
1130 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c1")), _("Red: "));
1131 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c2")), _("Green: "));
1132 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c3")), _("Blue: "));
1133 break;
1134 case 1: // HSL
1135 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c1")), _("Hue: "));
1136 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c2")), _("Saturation: "));
1137 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c3")), _("Lightness: "));
1138 break;
1139 case 2: // HSV
1140 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c1")), _("Hue: "));
1141 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c2")), _("Saturation: "));
1142 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c3")), _("Value: "));
1143 break;
1144 case 3: // CIE L*a*b*
1145 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c1")), "L*: ");
1146 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c2")), "a*: ");
1147 gtk_label_set_text(GTK_LABEL(lookup_widget("label_extract_c3")), "b*: ");
1148 }
1149 }
1150
on_extract_channel_button_ok_clicked(GtkButton * button,gpointer user_data)1151 void on_extract_channel_button_ok_clicked(GtkButton *button, gpointer user_data) {
1152 static GtkEntry *channel_extract_entry[3] = { NULL, NULL, NULL };
1153 static GtkComboBox *combo_extract_channel = NULL;
1154
1155 if (get_thread_run()) {
1156 PRINT_ANOTHER_THREAD_RUNNING;
1157 return;
1158 }
1159
1160 struct extract_channels_data *args = malloc(sizeof(struct extract_channels_data));
1161 if (!args) {
1162 PRINT_ALLOC_ERR;
1163 return;
1164 }
1165
1166 if (combo_extract_channel == NULL) {
1167 combo_extract_channel = GTK_COMBO_BOX(
1168 lookup_widget("combo_extract_colors"));
1169 channel_extract_entry[0] = GTK_ENTRY(
1170 lookup_widget("Ch1_extract_channel_entry"));
1171 channel_extract_entry[1] = GTK_ENTRY(
1172 lookup_widget("Ch2_extract_channel_entry"));
1173 channel_extract_entry[2] = GTK_ENTRY(
1174 lookup_widget("Ch3_extract_channel_entry"));
1175 }
1176
1177 args->type = gtk_combo_box_get_active(combo_extract_channel);
1178 args->str_type = gtk_combo_box_get_active_id(combo_extract_channel);
1179
1180 args->channel[0] = g_strdup_printf("%s%s", gtk_entry_get_text(channel_extract_entry[0]), com.pref.ext);
1181 args->channel[1] = g_strdup_printf("%s%s", gtk_entry_get_text(channel_extract_entry[1]), com.pref.ext);
1182 args->channel[2] = g_strdup_printf("%s%s", gtk_entry_get_text(channel_extract_entry[2]), com.pref.ext);
1183
1184 if ((args->channel[0][0] != '\0') && (args->channel[1][0] != '\0')
1185 && (args->channel[2][0] != '\0')) {
1186 args->fit = calloc(1, sizeof(fits));
1187 set_cursor_waiting(TRUE);
1188 if (copyfits(&gfit, args->fit, CP_ALLOC | CP_COPYA | CP_FORMAT, -1)) {
1189 siril_log_message(_("Could not copy the input image, aborting.\n"));
1190 free(args->fit);
1191 free(args->channel[0]);
1192 free(args->channel[1]);
1193 free(args->channel[2]);
1194 free(args);
1195 } else {
1196 copy_fits_metadata(&gfit, args->fit);
1197 start_in_new_thread(extract_channels, args);
1198 }
1199 }
1200 else {
1201 free(args->channel[0]);
1202 free(args->channel[1]);
1203 free(args->channel[2]);
1204 free(args);
1205 }
1206 }
1207