1 #include <string.h>
2 #include "core/siril.h"
3 #include "core/proto.h"
4 #include "core/processing.h"
5 #include "core/command.h"
6 #include "algos/demosaicing.h"
7 #include "io/image_format_fits.h"
8 #include "io/sequence.h"
9 #include "gui/dialogs.h"
10 #include "gui/utils.h"
11 #include "gui/progress_and_log.h"
12 #include "extraction.h"
13
update_sampling_information(fits * fit)14 static void update_sampling_information(fits *fit) {
15 clear_Bayer_information(fit);
16
17 fit->pixel_size_x *= 2;
18 fit->pixel_size_y *= 2;
19 }
20
extractHa_ushort(fits * in,fits * Ha,sensor_pattern pattern)21 int extractHa_ushort(fits *in, fits *Ha, sensor_pattern pattern) {
22 int width = in->rx / 2, height = in->ry / 2;
23
24 if (strlen(in->bayer_pattern) > 4) {
25 siril_log_message(_("Extract_Ha does not work on non-Bayer filter camera images!\n"));
26 return 1;
27 }
28 if (new_fit_image(&Ha, width, height, 1, DATA_USHORT)) {
29 return 1;
30 }
31
32 int j = 0;
33
34 for (int row = 0; row < in->ry - 1; row += 2) {
35 for (int col = 0; col < in->rx - 1; col += 2) {
36 WORD c0 = in->data[col + row * in->rx];
37 WORD c1 = in->data[1 + col + row * in->rx];
38 WORD c2 = in->data[col + (1 + row) * in->rx];
39 WORD c3 = in->data[1 + col + (1 + row) * in->rx];
40
41 switch(pattern) {
42 case BAYER_FILTER_RGGB:
43 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c0) : c0;
44 break;
45 case BAYER_FILTER_BGGR:
46 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c3) : c3;
47 break;
48 case BAYER_FILTER_GRBG:
49 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c1) : c1;
50 break;
51 case BAYER_FILTER_GBRG:
52 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c2) : c2;
53 break;
54 default:
55 printf("Should not happen.\n");
56 return 1;
57 }
58 j++;
59 }
60 }
61
62 /* We update FITS keywords */
63 copy_fits_metadata(in, Ha);
64 update_sampling_information(Ha);
65
66 return 0;
67 }
68
extractHa_float(fits * in,fits * Ha,sensor_pattern pattern)69 int extractHa_float(fits *in, fits *Ha, sensor_pattern pattern) {
70 int width = in->rx / 2, height = in->ry / 2;
71
72 if (strlen(in->bayer_pattern) > 4) {
73 siril_log_message(_("Extract_Ha does not work on non-Bayer filter camera images!\n"));
74 return 1;
75 }
76 if (new_fit_image(&Ha, width, height, 1, DATA_FLOAT)) {
77 return 1;
78 }
79
80 int j = 0;
81
82 for (int row = 0; row < in->ry - 1; row += 2) {
83 for (int col = 0; col < in->rx - 1; col += 2) {
84 float c0 = in->fdata[col + row * in->rx];
85 float c1 = in->fdata[1 + col + row * in->rx];
86 float c2 = in->fdata[col + (1 + row) * in->rx];
87 float c3 = in->fdata[1 + col + (1 + row) * in->rx];
88
89 switch(pattern) {
90 case BAYER_FILTER_RGGB:
91 Ha->fdata[j] = c0;
92 break;
93 case BAYER_FILTER_BGGR:
94 Ha->fdata[j] = c3;
95 break;
96 case BAYER_FILTER_GRBG:
97 Ha->fdata[j] = c1;
98 break;
99 case BAYER_FILTER_GBRG:
100 Ha->fdata[j] = c2;
101 break;
102 default:
103 printf("Should not happen.\n");
104 return 1;
105 }
106 j++;
107 }
108 }
109
110 /* We update FITS keywords */
111 copy_fits_metadata(in, Ha);
112 update_sampling_information(Ha);
113
114 return 0;
115 }
116
get_bayer_pattern(fits * fit)117 sensor_pattern get_bayer_pattern(fits *fit) {
118 /* Get Bayer informations from header if available */
119 sensor_pattern tmp_pattern = com.pref.debayer.bayer_pattern;
120 if (com.pref.debayer.use_bayer_header) {
121 sensor_pattern bayer;
122 bayer = retrieveBayerPatternFromChar(fit->bayer_pattern);
123
124 if (bayer <= BAYER_FILTER_MAX) {
125 if (bayer != tmp_pattern) {
126 if (bayer == BAYER_FILTER_NONE) {
127 siril_log_color_message(_("No Bayer pattern found in the header file.\n"), "salmon");
128 }
129 else {
130 siril_log_color_message(_("Bayer pattern found in header (%s) is different"
131 " from Bayer pattern in settings (%s). Overriding settings.\n"),
132 "salmon", filter_pattern[bayer], filter_pattern[com.pref.debayer.bayer_pattern]);
133 tmp_pattern = bayer;
134 }
135 }
136 } else {
137 siril_log_message(_("XTRANS pattern not supported for this feature.\n"));
138 return 1;
139 }
140 }
141 if (tmp_pattern >= BAYER_FILTER_MIN && tmp_pattern <= BAYER_FILTER_MAX) {
142 siril_log_message(_("Filter Pattern: %s\n"),
143 filter_pattern[tmp_pattern]);
144 }
145
146 retrieve_Bayer_pattern(fit, &tmp_pattern);
147 return tmp_pattern;
148 }
149
extractHa_image_hook(struct generic_seq_args * args,int o,int i,fits * fit,rectangle * _)150 int extractHa_image_hook(struct generic_seq_args *args, int o, int i, fits *fit, rectangle *_) {
151 int ret = 1;
152 fits f_Ha = { 0 };
153 sensor_pattern pattern = get_bayer_pattern(fit);
154
155 if (fit->type == DATA_USHORT)
156 ret = extractHa_ushort(fit, &f_Ha, pattern);
157 else if (fit->type == DATA_FLOAT)
158 ret = extractHa_float(fit, &f_Ha, pattern);
159 else return 1;
160 if (!ret) {
161 clearfits(fit);
162 memcpy(fit, &f_Ha, sizeof(fits));
163 }
164 return ret;
165 }
166
apply_extractHa_to_sequence(struct split_cfa_data * split_cfa_args)167 void apply_extractHa_to_sequence(struct split_cfa_data *split_cfa_args) {
168 struct generic_seq_args *args = create_default_seqargs(split_cfa_args->seq);
169 args->seq = split_cfa_args->seq;
170 args->filtering_criterion = seq_filter_included;
171 args->nb_filtered_images = split_cfa_args->seq->selnum;
172 args->prepare_hook = seq_prepare_hook;
173 args->finalize_hook = seq_finalize_hook;
174 args->image_hook = extractHa_image_hook;
175 args->description = _("Extract Ha");
176 args->has_output = TRUE;
177 args->new_seq_prefix = split_cfa_args->seqEntry;
178 args->load_new_sequence = TRUE;
179 args->force_ser_output = FALSE;
180 args->user = split_cfa_args;
181
182 split_cfa_args->fit = NULL; // not used here
183
184 start_in_new_thread(generic_sequence_worker, args);
185 }
186
extractGreen_ushort(fits * in,fits * green,sensor_pattern pattern)187 int extractGreen_ushort(fits *in, fits *green, sensor_pattern pattern) {
188 int width = in->rx / 2, height = in->ry / 2;
189
190 if (strlen(in->bayer_pattern) > 4) {
191 siril_log_message(_("Extract_Green does not work on non-Bayer filter camera images!\n"));
192 return 1;
193 }
194 if (new_fit_image(&green, width, height, 1, DATA_USHORT))
195 return 1;
196
197 int j = 0;
198
199 for (int row = 0; row < in->ry - 1; row += 2) {
200 for (int col = 0; col < in->rx - 1; col += 2) {
201 WORD c0, c1, c2, c3;
202 switch(pattern) {
203 case BAYER_FILTER_RGGB:
204 case BAYER_FILTER_BGGR:
205 c1 = in->data[1 + col + row * in->rx];
206 c2 = in->data[col + (1 + row) * in->rx];
207 green->data[j] = (c1 + c2) / 2;
208 break;
209 case BAYER_FILTER_GRBG:
210 case BAYER_FILTER_GBRG:
211 c0 = in->data[col + row * in->rx];
212 c3 = in->data[1 + col + (1 + row) * in->rx];
213 green->data[j] = (c0 + c3) / 2;
214 break;
215 default:
216 printf("Should not happen.\n");
217 return 1;
218 }
219 j++;
220 }
221 }
222
223 /* We update FITS keywords */
224 copy_fits_metadata(in, green);
225 update_sampling_information(green);
226
227 return 0;
228 }
229
extractGreen_float(fits * in,fits * green,sensor_pattern pattern)230 int extractGreen_float(fits *in, fits *green, sensor_pattern pattern) {
231 int width = in->rx / 2, height = in->ry / 2;
232
233 if (strlen(in->bayer_pattern) > 4) {
234 siril_log_message(_("Extract_Green does not work on non-Bayer filter camera images!\n"));
235 return 1;
236 }
237 if (new_fit_image(&green, width, height, 1, DATA_FLOAT))
238 return 1;
239
240 int j = 0;
241
242 for (int row = 0; row < in->ry - 1; row += 2) {
243 for (int col = 0; col < in->rx - 1; col += 2) {
244 float c0, c1, c2, c3;
245 switch(pattern) {
246 case BAYER_FILTER_RGGB:
247 case BAYER_FILTER_BGGR:
248 c1 = in->fdata[1 + col + row * in->rx];
249 c2 = in->fdata[col + (1 + row) * in->rx];
250 green->fdata[j] = (c1 + c2) * 0.5f;
251 break;
252 case BAYER_FILTER_GRBG:
253 case BAYER_FILTER_GBRG:
254 c0 = in->fdata[col + row * in->rx];
255 c3 = in->fdata[1 + col + (1 + row) * in->rx];
256 green->fdata[j] = (c0 + c3) * 0.5f;
257 break;
258 default:
259 printf("Should not happen.\n");
260 return 1;
261 }
262 j++;
263 }
264 }
265
266 /* We update FITS keywords */
267 copy_fits_metadata(in, green);
268 update_sampling_information(green);
269
270 return 0;
271 }
272
extractGreen_image_hook(struct generic_seq_args * args,int o,int i,fits * fit,rectangle * _)273 int extractGreen_image_hook(struct generic_seq_args *args, int o, int i, fits *fit, rectangle *_) {
274 int ret = 1;
275 fits f_Ha = { 0 };
276 sensor_pattern pattern = get_bayer_pattern(fit);
277
278 if (fit->type == DATA_USHORT)
279 ret = extractGreen_ushort(fit, &f_Ha, pattern);
280 else if (fit->type == DATA_FLOAT)
281 ret = extractGreen_float(fit, &f_Ha, pattern);
282 else return 1;
283 if (!ret) {
284 clearfits(fit);
285 memcpy(fit, &f_Ha, sizeof(fits));
286 }
287 return ret;
288 }
289
apply_extractGreen_to_sequence(struct split_cfa_data * split_cfa_args)290 void apply_extractGreen_to_sequence(struct split_cfa_data *split_cfa_args) {
291 struct generic_seq_args *args = create_default_seqargs(split_cfa_args->seq);
292 args->seq = split_cfa_args->seq;
293 args->filtering_criterion = seq_filter_included;
294 args->nb_filtered_images = split_cfa_args->seq->selnum;
295 args->prepare_hook = seq_prepare_hook;
296 args->finalize_hook = seq_finalize_hook;
297 args->image_hook = extractGreen_image_hook;
298 args->description = _("Extract Green");
299 args->has_output = TRUE;
300 args->new_seq_prefix = split_cfa_args->seqEntry;
301 args->load_new_sequence = TRUE;
302 args->force_ser_output = FALSE;
303 args->user = split_cfa_args;
304
305 split_cfa_args->fit = NULL; // not used here
306
307 start_in_new_thread(generic_sequence_worker, args);
308 }
309
extractHaOIII_ushort(fits * in,fits * Ha,fits * OIII,sensor_pattern pattern)310 int extractHaOIII_ushort(fits *in, fits *Ha, fits *OIII, sensor_pattern pattern) {
311 int width = in->rx / 2, height = in->ry / 2;
312
313 if (strlen(in->bayer_pattern) > 4) {
314 siril_log_message(_("Extract_Ha does not work on non-Bayer filter camera images!\n"));
315 return 1;
316 }
317 if (new_fit_image(&Ha, width, height, 1, DATA_USHORT) ||
318 new_fit_image(&OIII, width, height, 1, DATA_USHORT)) {
319 return 1;
320 }
321
322 int j = 0;
323
324 for (int row = 0; row < in->ry - 1; row += 2) {
325 for (int col = 0; col < in->rx - 1; col += 2) {
326 WORD c0 = in->data[col + row * in->rx];
327 WORD c1 = in->data[1 + col + row * in->rx];
328 WORD c2 = in->data[col + (1 + row) * in->rx];
329 WORD c3 = in->data[1 + col + (1 + row) * in->rx];
330
331 switch(pattern) {
332 case BAYER_FILTER_RGGB:
333 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c0) : c0;
334 OIII->data[j] = (c1 + c2 + c3) / 3;
335 break;
336 case BAYER_FILTER_BGGR:
337 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c3) : c3;
338 OIII->data[j] = (c1 + c2 + c0) / 3;
339 break;
340 case BAYER_FILTER_GRBG:
341 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c1) : c1;
342 OIII->data[j] = (c0 + c2 + c3) / 3;
343 break;
344 case BAYER_FILTER_GBRG:
345 Ha->data[j] = (in->bitpix == 8) ? round_to_BYTE(c2) : c2;
346 OIII->data[j] = (c1 + c0 + c3) / 3;
347 break;
348 default:
349 printf("Should not happen.\n");
350 return 1;
351 }
352 j++;
353 }
354 }
355
356 /* We update FITS keywords */
357 copy_fits_metadata(in, Ha);
358 update_sampling_information(Ha);
359
360 copy_fits_metadata(in, OIII);
361 update_sampling_information(OIII);
362
363 return 0;
364 }
365
extractHaOIII_float(fits * in,fits * Ha,fits * OIII,sensor_pattern pattern)366 int extractHaOIII_float(fits *in, fits *Ha, fits *OIII, sensor_pattern pattern) {
367 int width = in->rx / 2, height = in->ry / 2;
368
369 if (strlen(in->bayer_pattern) > 4) {
370 siril_log_message(_("Extract_HaOIII does not work on non-Bayer filter camera images!\n"));
371 return 1;
372 }
373 if (new_fit_image(&Ha, width, height, 1, DATA_FLOAT) ||
374 new_fit_image(&OIII, width, height, 1, DATA_FLOAT)) {
375 return 1;
376 }
377
378 int j = 0;
379
380 for (int row = 0; row < in->ry - 1; row += 2) {
381 for (int col = 0; col < in->rx - 1; col += 2) {
382 float c0 = in->fdata[col + row * in->rx];
383 float c1 = in->fdata[1 + col + row * in->rx];
384 float c2 = in->fdata[col + (1 + row) * in->rx];
385 float c3 = in->fdata[1 + col + (1 + row) * in->rx];
386
387 switch(pattern) {
388 case BAYER_FILTER_RGGB:
389 Ha->fdata[j] = c0;
390 OIII->fdata[j] = (c1 + c2 + c3) / 3;
391 break;
392 case BAYER_FILTER_BGGR:
393 Ha->fdata[j] = c3;
394 OIII->fdata[j] = (c1 + c2 + c0) / 3;
395 break;
396 case BAYER_FILTER_GRBG:
397 Ha->fdata[j] = c1;
398 OIII->fdata[j] = (c0 + c2 + c3) / 3;
399 break;
400 case BAYER_FILTER_GBRG:
401 Ha->fdata[j] = c2;
402 OIII->fdata[j] = (c1 + c0 + c3) / 3;
403 break;
404 default:
405 printf("Should not happen.\n");
406 return 1;
407 }
408 j++;
409 }
410 }
411
412 /* We update FITS keywords */
413 copy_fits_metadata(in, Ha);
414 update_sampling_information(Ha);
415
416 copy_fits_metadata(in, OIII);
417 update_sampling_information(OIII);
418
419 return 0;
420 }
421
422 struct _double_split {
423 int index;
424 fits *ha;
425 fits *oiii;
426 };
427
extractHaOIII_image_hook(struct generic_seq_args * args,int o,int i,fits * fit,rectangle * _)428 int extractHaOIII_image_hook(struct generic_seq_args *args, int o, int i, fits *fit, rectangle *_) {
429 int ret = 1;
430 struct split_cfa_data *cfa_args = (struct split_cfa_data *) args->user;
431
432 sensor_pattern pattern = get_bayer_pattern(fit);
433
434 /* Demosaic and store images for write */
435 struct _double_split *double_data = malloc(sizeof(struct _double_split));
436 double_data->ha = calloc(1, sizeof(fits));
437 double_data->oiii = calloc(1, sizeof(fits));
438 double_data->index = o;
439
440 if (fit->type == DATA_USHORT) {
441 ret = extractHaOIII_ushort(fit, double_data->ha, double_data->oiii, pattern);
442 }
443 else if (fit->type == DATA_FLOAT) {
444 ret = extractHaOIII_float(fit, double_data->ha, double_data->oiii, pattern);
445 }
446
447 if (ret) {
448 clearfits(double_data->ha);
449 clearfits(double_data->oiii);
450 free(double_data);
451 } else {
452 #ifdef _OPENMP
453 omp_set_lock(&args->lock);
454 #endif
455 cfa_args->processed_images = g_list_append(cfa_args->processed_images, double_data);
456 #ifdef _OPENMP
457 omp_unset_lock(&args->lock);
458 #endif
459 siril_debug_print("Ha-OIII: processed images added to the save list (%d)\n", o);
460 }
461 return ret;
462 }
463
dual_prepare(struct generic_seq_args * args)464 static int dual_prepare(struct generic_seq_args *args) {
465 struct split_cfa_data *cfa_args = (struct split_cfa_data *) args->user;
466 // we call the generic prepare twice with different prefixes
467 args->new_seq_prefix = "Ha_";
468 if (seq_prepare_hook(args))
469 return 1;
470 // but we copy the result between each call
471 cfa_args->new_ser_ha = args->new_ser;
472 cfa_args->new_fitseq_ha = args->new_fitseq;
473
474 args->new_seq_prefix = "OIII_";
475 if (seq_prepare_hook(args))
476 return 1;
477 cfa_args->new_ser_oiii = args->new_ser;
478 cfa_args->new_fitseq_oiii = args->new_fitseq;
479
480 args->new_seq_prefix = NULL;
481 args->new_ser = NULL;
482 args->new_fitseq = NULL;
483
484 seqwriter_set_number_of_outputs(2);
485 return 0;
486 }
487
dual_finalize(struct generic_seq_args * args)488 static int dual_finalize(struct generic_seq_args *args) {
489 struct split_cfa_data *cfa_args = (struct split_cfa_data *) args->user;
490 args->new_ser = cfa_args->new_ser_ha;
491 args->new_fitseq = cfa_args->new_fitseq_ha;
492 int retval = seq_finalize_hook(args);
493 cfa_args->new_ser_ha = NULL;
494 cfa_args->new_fitseq_ha = NULL;
495
496 args->new_ser = cfa_args->new_ser_oiii;
497 args->new_fitseq = cfa_args->new_fitseq_oiii;
498 retval = seq_finalize_hook(args) || retval;
499 cfa_args->new_ser_oiii = NULL;
500 cfa_args->new_fitseq_oiii = NULL;
501 seqwriter_set_number_of_outputs(1);
502 return retval;
503 }
504
dual_save(struct generic_seq_args * args,int out_index,int in_index,fits * fit)505 static int dual_save(struct generic_seq_args *args, int out_index, int in_index, fits *fit) {
506 struct split_cfa_data *cfa_args = (struct split_cfa_data *) args->user;
507 struct _double_split *double_data = NULL;
508 // images are passed from the image_hook to the save in a list, because
509 // there are two, which is unsupported by the generic arguments
510 #ifdef _OPENMP
511 omp_set_lock(&args->lock);
512 #endif
513 GList *list = cfa_args->processed_images;
514 while (list) {
515 if (((struct _double_split *)list->data)->index == out_index) {
516 double_data = list->data;
517 break;
518 }
519 list = g_list_next(cfa_args->processed_images);
520 }
521 if (double_data)
522 cfa_args->processed_images = g_list_remove(cfa_args->processed_images, double_data);
523 #ifdef _OPENMP
524 omp_unset_lock(&args->lock);
525 #endif
526 if (!double_data) {
527 siril_log_color_message(_("Image %d not found for writing\n"), "red", in_index);
528 return 1;
529 }
530
531 siril_debug_print("Ha-OIII: images to be saved (%d)\n", out_index);
532 if (double_data->ha->naxes[0] == 0 || double_data->oiii->naxes[0] == 0) {
533 siril_debug_print("empty data\n");
534 return 1;
535 }
536
537 int retval1, retval2;
538 if (args->force_ser_output || args->seq->type == SEQ_SER) {
539 retval1 = ser_write_frame_from_fit(cfa_args->new_ser_ha, double_data->ha, out_index);
540 retval2 = ser_write_frame_from_fit(cfa_args->new_ser_oiii, double_data->oiii, out_index);
541 clearfits(double_data->ha);
542 clearfits(double_data->oiii);
543 } else if (args->force_fitseq_output || args->seq->type == SEQ_FITSEQ) {
544 retval1 = fitseq_write_image(cfa_args->new_fitseq_ha, double_data->ha, out_index);
545 retval2 = fitseq_write_image(cfa_args->new_fitseq_oiii, double_data->oiii, out_index);
546 // the two fits are freed by the writing thread
547 if (!retval1 && !retval2) {
548 /* special case because it's not done in the generic */
549 clearfits(fit);
550 free(fit);
551 }
552 } else {
553 char *dest = fit_sequence_get_image_filename_prefixed(args->seq, "Ha_", in_index);
554 if (fit->type == DATA_USHORT) {
555 retval1 = save1fits16(dest, double_data->ha, RLAYER);
556 } else {
557 retval1 = save1fits32(dest, double_data->ha, RLAYER);
558 }
559 free(dest);
560 dest = fit_sequence_get_image_filename_prefixed(args->seq, "OIII_", in_index);
561 if (fit->type == DATA_USHORT) {
562 retval2 = save1fits16(dest, double_data->oiii, RLAYER);
563 } else {
564 retval2 = save1fits32(dest, double_data->oiii, RLAYER);
565 }
566 free(dest);
567 clearfits(double_data->ha);
568 clearfits(double_data->oiii);
569 }
570 free(double_data);
571 return retval1 || retval2;
572 }
573
apply_extractHaOIII_to_sequence(struct split_cfa_data * split_cfa_args)574 void apply_extractHaOIII_to_sequence(struct split_cfa_data *split_cfa_args) {
575 struct generic_seq_args *args = create_default_seqargs(split_cfa_args->seq);
576 args->seq = split_cfa_args->seq;
577 args->filtering_criterion = seq_filter_included;
578 args->nb_filtered_images = split_cfa_args->seq->selnum;
579 args->prepare_hook = dual_prepare;
580 args->finalize_hook = dual_finalize;
581 args->save_hook = dual_save;
582 args->image_hook = extractHaOIII_image_hook;
583 args->description = _("Extract Ha and OIII");
584 args->has_output = TRUE;
585 args->output_type = get_data_type(args->seq->bitpix);
586 args->upscale_ratio = 1.23; // sqrt(1.5), for memory management
587 args->new_seq_prefix = NULL;
588 args->user = split_cfa_args;
589
590 split_cfa_args->fit = NULL; // not used here
591
592 start_in_new_thread(generic_sequence_worker, args);
593 }
594
split_cfa_ushort(fits * in,fits * cfa0,fits * cfa1,fits * cfa2,fits * cfa3)595 int split_cfa_ushort(fits *in, fits *cfa0, fits *cfa1, fits *cfa2, fits *cfa3) {
596 int width = in->rx / 2, height = in->ry / 2;
597
598 if (strlen(in->bayer_pattern) > 4) {
599 siril_log_message(_("Split CFA does not work on non-Bayer filter camera images!\n"));
600 return 1;
601 }
602 if (new_fit_image(&cfa0, width, height, 1, DATA_USHORT) ||
603 new_fit_image(&cfa1, width, height, 1, DATA_USHORT) ||
604 new_fit_image(&cfa2, width, height, 1, DATA_USHORT) ||
605 new_fit_image(&cfa3, width, height, 1, DATA_USHORT)) {
606 return 1;
607 }
608
609 int j = 0;
610
611 for (int row = 0; row < in->ry - 1; row += 2) {
612 for (int col = 0; col < in->rx - 1; col += 2) {
613 /* not c0, c1, c2 and c3 because of the read orientation */
614 WORD c1 = in->data[col + row * in->rx];
615 WORD c3 = in->data[1 + col + row * in->rx];
616 WORD c0 = in->data[col + (1 + row) * in->rx];
617 WORD c2 = in->data[1 + col + (1 + row) * in->rx];
618
619 cfa0->data[j] = (in->bitpix == 8) ? round_to_BYTE(c0) : c0;
620 cfa1->data[j] = (in->bitpix == 8) ? round_to_BYTE(c1) : c1;
621 cfa2->data[j] = (in->bitpix == 8) ? round_to_BYTE(c2) : c2;
622 cfa3->data[j] = (in->bitpix == 8) ? round_to_BYTE(c3) : c3;
623 j++;
624 }
625 }
626
627 copy_fits_metadata(in, cfa0);
628 copy_fits_metadata(in, cfa1);
629 copy_fits_metadata(in, cfa2);
630 copy_fits_metadata(in, cfa3);
631
632 /* we remove Bayer header because not needed now */
633 clear_Bayer_information(cfa0);
634 clear_Bayer_information(cfa1);
635 clear_Bayer_information(cfa2);
636 clear_Bayer_information(cfa3);
637
638 return 0;
639 }
640
split_cfa_float(fits * in,fits * cfa0,fits * cfa1,fits * cfa2,fits * cfa3)641 int split_cfa_float(fits *in, fits *cfa0, fits *cfa1, fits *cfa2, fits *cfa3) {
642 int width = in->rx / 2, height = in->ry / 2;
643
644 if (strlen(in->bayer_pattern) > 4) {
645 siril_log_message(_("Split CFA does not work on non-Bayer filter camera images!\n"));
646 return 1;
647 }
648 if (new_fit_image(&cfa0, width, height, 1, DATA_FLOAT) ||
649 new_fit_image(&cfa1, width, height, 1, DATA_FLOAT) ||
650 new_fit_image(&cfa2, width, height, 1, DATA_FLOAT) ||
651 new_fit_image(&cfa3, width, height, 1, DATA_FLOAT)) {
652 return 1;
653 }
654
655 int j = 0;
656
657 for (int row = 0; row < in->ry - 1; row += 2) {
658 for (int col = 0; col < in->rx - 1; col += 2) {
659 /* not c0, c1, c2 and c3 because of the read orientation */
660 float c1 = in->fdata[col + row * in->rx];
661 float c3 = in->fdata[1 + col + row * in->rx];
662 float c0 = in->fdata[col + (1 + row) * in->rx];
663 float c2 = in->fdata[1 + col + (1 + row) * in->rx];
664
665 cfa0->fdata[j] = c0;
666 cfa1->fdata[j] = c1;
667 cfa2->fdata[j] = c2;
668 cfa3->fdata[j] = c3;
669 j++;
670 }
671 }
672
673 copy_fits_metadata(in, cfa0);
674 copy_fits_metadata(in, cfa1);
675 copy_fits_metadata(in, cfa2);
676 copy_fits_metadata(in, cfa3);
677
678 /* we remove Bayer header because not needed now */
679 clear_Bayer_information(cfa0);
680 clear_Bayer_information(cfa1);
681 clear_Bayer_information(cfa2);
682 clear_Bayer_information(cfa3);
683
684 return 0;
685 }
686
split_cfa_image_hook(struct generic_seq_args * args,int o,int i,fits * fit,rectangle * _)687 int split_cfa_image_hook(struct generic_seq_args *args, int o, int i, fits *fit, rectangle *_) {
688 int ret = 1;
689 struct split_cfa_data *cfa_args = (struct split_cfa_data *) args->user;
690
691 fits f_cfa0 = { 0 }, f_cfa1 = { 0 }, f_cfa2 = { 0 }, f_cfa3 = { 0 };
692
693 gchar *cfa0 = g_strdup_printf("%s0_%s%05d%s", cfa_args->seqEntry, cfa_args->seq->seqname, o, com.pref.ext);
694 gchar *cfa1 = g_strdup_printf("%s1_%s%05d%s", cfa_args->seqEntry, cfa_args->seq->seqname, o, com.pref.ext);
695 gchar *cfa2 = g_strdup_printf("%s2_%s%05d%s", cfa_args->seqEntry, cfa_args->seq->seqname, o, com.pref.ext);
696 gchar *cfa3 = g_strdup_printf("%s3_%s%05d%s", cfa_args->seqEntry, cfa_args->seq->seqname, o, com.pref.ext);
697
698 if (fit->type == DATA_USHORT) {
699 if (!(ret = split_cfa_ushort(fit, &f_cfa0, &f_cfa1, &f_cfa2, &f_cfa3))) {
700 ret = save1fits16(cfa0, &f_cfa0, RLAYER) ||
701 save1fits16(cfa1, &f_cfa1, RLAYER) ||
702 save1fits16(cfa2, &f_cfa2, RLAYER) ||
703 save1fits16(cfa3, &f_cfa3, RLAYER);
704 }
705 }
706 else if (fit->type == DATA_FLOAT) {
707 if (!(ret = split_cfa_float(fit, &f_cfa0, &f_cfa1, &f_cfa2, &f_cfa3))) {
708 ret = save1fits32(cfa0, &f_cfa0, RLAYER) ||
709 save1fits32(cfa1, &f_cfa1, RLAYER) ||
710 save1fits32(cfa2, &f_cfa2, RLAYER) ||
711 save1fits32(cfa3, &f_cfa3, RLAYER);
712 }
713 }
714
715 g_free(cfa0); g_free(cfa1);
716 g_free(cfa2); g_free(cfa3);
717 clearfits(&f_cfa0); clearfits(&f_cfa1);
718 clearfits(&f_cfa2); clearfits(&f_cfa3);
719 return ret;
720 }
721
apply_split_cfa_to_sequence(struct split_cfa_data * split_cfa_args)722 void apply_split_cfa_to_sequence(struct split_cfa_data *split_cfa_args) {
723 struct generic_seq_args *args = create_default_seqargs(split_cfa_args->seq);
724 args->filtering_criterion = seq_filter_included;
725 args->nb_filtered_images = split_cfa_args->seq->selnum;
726 args->image_hook = split_cfa_image_hook;
727 args->description = _("Split CFA");
728 args->new_seq_prefix = split_cfa_args->seqEntry;
729 args->user = split_cfa_args;
730
731 split_cfa_args->fit = NULL; // not used here
732
733 start_in_new_thread(generic_sequence_worker, args);
734 }
735
736 /******* SPLIT CFA ******************************/
737
on_split_cfa_close_clicked(GtkButton * button,gpointer user_data)738 void on_split_cfa_close_clicked(GtkButton *button, gpointer user_data) {
739 siril_close_dialog("split_cfa_dialog");
740 }
741
on_split_cfa_apply_clicked(GtkButton * button,gpointer user_data)742 void on_split_cfa_apply_clicked(GtkButton *button, gpointer user_data) {
743 GtkToggleButton *seq = GTK_TOGGLE_BUTTON(lookup_widget("checkSplitCFASeq"));
744 GtkEntry *entrySplitCFA = GTK_ENTRY(lookup_widget("entrySplitCFA"));
745 gint method = gtk_combo_box_get_active(GTK_COMBO_BOX(lookup_widget("combo_split_cfa_method")));
746
747 if (gtk_toggle_button_get_active(seq) && sequence_is_loaded()) {
748 struct split_cfa_data *args = calloc(1, sizeof(struct split_cfa_data));
749
750 set_cursor_waiting(TRUE);
751 args->seq = &com.seq;
752 args->seqEntry = gtk_entry_get_text(entrySplitCFA);
753 switch (method) {
754 case 0:
755 if (args->seqEntry && args->seqEntry[0] == '\0')
756 args->seqEntry = "CFA_";
757 apply_split_cfa_to_sequence(args);
758 break;
759 case 1:
760 if (args->seqEntry && args->seqEntry[0] == '\0')
761 args->seqEntry = "Ha_";
762 apply_extractHa_to_sequence(args);
763 break;
764 case 2:
765 apply_extractHaOIII_to_sequence(args);
766 break;
767 case 3:
768 if (args->seqEntry && args->seqEntry[0] == '\0')
769 args->seqEntry = "Green_";
770 apply_extractGreen_to_sequence(args);
771 break;
772 default:
773 fprintf(stderr, "unhandled case!\n");
774 }
775 } else {
776 switch (method) {
777 case 0:
778 process_split_cfa(0);
779 break;
780 case 1:
781 process_extractHa(0);
782 break;
783 case 2:
784 process_extractHaOIII(0);
785 break;
786 case 3:
787 process_extractGreen(0);
788 break;
789 default:
790 fprintf(stderr, "unhandled case!\n");
791 }
792 }
793 }
794
on_combo_split_cfa_method_changed(GtkComboBox * box,gpointer user_data)795 void on_combo_split_cfa_method_changed(GtkComboBox *box, gpointer user_data) {
796 GtkWidget *w = lookup_widget("label10");
797 GtkWidget *txt = lookup_widget("entrySplitCFA");
798 gint method = gtk_combo_box_get_active(box);
799
800 gtk_widget_set_sensitive(w, method != 2);
801 gtk_widget_set_sensitive(txt, method != 2);
802 switch (method) {
803 case 0:
804 gtk_entry_set_text(GTK_ENTRY(txt), "CFA_");
805 break;
806 case 1:
807 gtk_entry_set_text(GTK_ENTRY(txt), "Ha_");
808 break;
809 case 3:
810 gtk_entry_set_text(GTK_ENTRY(txt), "Green_");
811 break;
812 }
813 }
814
815