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