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 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <gtk/gtk.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <time.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <sys/time.h>
34 #include <ctype.h>
35 #include <assert.h>
36 #include <math.h>
37 #include <libgen.h>
38 
39 #include "core/siril.h"
40 #include "core/proto.h"
41 #include "core/siril_date.h"
42 #include "core/OS_utils.h"
43 #include "core/initfile.h"
44 #include "core/undo.h"
45 #include "io/conversion.h"
46 #include "gui/utils.h"
47 #include "gui/callbacks.h"
48 #include "gui/message_dialog.h"
49 #include "gui/plot.h"
50 #include "ser.h"
51 #include "fits_sequence.h"
52 #ifdef HAVE_FFMS2
53 #include "films.h"
54 #endif
55 #include "avi_pipp/avi_writer.h"
56 #include "single_image.h"
57 #include "image_format_fits.h"
58 #include "gui/histogram.h"
59 #include "gui/image_display.h"
60 #include "gui/progress_and_log.h"
61 #include "gui/PSF_list.h"	// clear_stars_list
62 #include "gui/sequence_list.h"
63 #include "gui/preferences.h"
64 #include "algos/PSF.h"
65 #include "algos/quality.h"
66 #include "algos/statistics.h"
67 #include "algos/geometry.h"
68 #include "registration/registration.h"
69 #include "stacking/stacking.h"	// for update_stack_interface
70 
71 #include "sequence.h"
72 
73 
74 /* com.seq is a static struct containing the sequence currently selected by the
75  * user from the interface. It may change to be a pointer to any sequence
76  * someday, until then, the seqname is NULL when no sequence is loaded and the
77  * number of images in the sequence is also negative.
78  * com.uniq represents information about an image opened and displayed outside
79  * a sequence, for example from the load command, the open menu, or the result
80  * of a stacking operation.
81  * com.seq.number is used to provide a relationship between a possibly loaded
82  * sequence and the single image. A single image can be loaded without
83  * unloading the sequence. This information could as well be moved to
84  * com.status if com.seq becomes a pointer. Three constants have been declared
85  * in siril.h to explicit this relationship: RESULT_IMAGE, UNRELATED_IMAGE and
86  * SCALED_IMAGE. They are mostly used to understand what to do to display
87  * single images when a sequence is loaded or not.
88  */
89 
fillSeqAviExport()90 static void fillSeqAviExport() {
91 	char width[6], height[6], fps[7];
92 	GtkEntry *heightEntry = GTK_ENTRY(lookup_widget("entryAviHeight"));
93 	GtkEntry *widthEntry = GTK_ENTRY(lookup_widget("entryAviWidth"));
94 
95 	g_snprintf(width, sizeof(width), "%d", com.seq.rx);
96 	g_snprintf(height, sizeof(width), "%d", com.seq.ry);
97 	gtk_entry_set_text(widthEntry, width);
98 	gtk_entry_set_text(heightEntry, height);
99 	if (com.seq.type == SEQ_SER) {
100 		GtkEntry *entryAviFps = GTK_ENTRY(lookup_widget("entryAviFps"));
101 
102 		if (com.seq.ser_file != NULL) {
103 			if (com.seq.ser_file->fps <= 0.0) {
104 				g_snprintf(fps, sizeof(fps), "25.000");
105 			} else {
106 				g_snprintf(fps, sizeof(fps), "%2.3lf", com.seq.ser_file->fps);
107 			}
108 			gtk_entry_set_text(entryAviFps, fps);
109 		}
110 	}
111 }
112 
113 static sequence *check_seq_one_file(const char* name);
114 static int seq_read_frame_metadata(sequence *seq, int index, fits *dest);
115 
populate_seqcombo(const gchar * realname)116 void populate_seqcombo(const gchar *realname) {
117 	control_window_switch_to_tab(IMAGE_SEQ);
118 	GtkComboBoxText *combo_box_text = GTK_COMBO_BOX_TEXT(lookup_widget("sequence_list_combobox"));
119 	gtk_combo_box_text_remove_all(combo_box_text);
120 	gchar *rname = g_path_get_basename(realname);
121 	gtk_combo_box_text_append(combo_box_text, 0, rname);
122 	g_signal_handlers_block_by_func(GTK_COMBO_BOX(combo_box_text), on_seqproc_entry_changed, NULL);
123 	gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box_text), 0);
124 	g_signal_handlers_unblock_by_func(GTK_COMBO_BOX(combo_box_text), on_seqproc_entry_changed, NULL);
125 	g_free(rname);
126 }
127 
128 /* when opening a file outside the main sequence loading system and that file
129  * is a sequence (SER/AVI), this function is called to load this sequence. */
read_single_sequence(char * realname,image_type imagetype)130 int read_single_sequence(char *realname, image_type imagetype) {
131 	int retval = 0, len;
132 	gchar *dirname = g_path_get_dirname(realname);
133 	if (!siril_change_dir(dirname, NULL)) {
134 		writeinitfile();
135 		if (!com.script) {
136 			set_GUI_CWD();
137 		}
138 	}
139 	g_free(dirname);
140 
141 	sequence *new_seq = check_seq_one_file(realname); // it's not the real .seq read
142 	if (!new_seq)
143 		return 1;
144 	free_sequence(new_seq, TRUE);
145 
146 	char *name = strdup(realname);
147 	const char *ext;
148 #ifdef HAVE_FFMS2
149 	const char *film_ext;
150 #endif
151 	switch (imagetype) {
152 	case TYPESER:
153 		name[strlen(name) - 1] = 'q';
154 		break;
155 	case TYPEFITS:
156 		ext = get_filename_ext(realname);
157 		assert(ext);
158 		len = strlen(ext);
159 		strncpy(name + strlen(name) - len, "seq", len);
160 		break;
161 #ifdef HAVE_FFMS2
162 	case TYPEAVI:
163 		film_ext = get_filename_ext(realname);
164 		assert(film_ext);
165 		len = strlen(film_ext);
166 		strncpy(name + strlen(name) - len, "seq", len);
167 		break;
168 #endif
169 		default:
170 			retval = 1;
171 	}
172 	gchar *fname = g_path_get_basename(name);
173 	if (!set_seq(fname)) {
174 		/* if it loads, make it selected and only element in the list of sequences */
175 		populate_seqcombo(realname);
176 	}
177 	else retval = 1;
178 	g_free(fname);
179 	free(name);
180 	return retval;
181 }
182 
183 /* Find sequences in CWD and create .seq files.
184  * In the current working directory, looks for sequences of fits files or files
185  * already representing sequences like SER and AVI formats and builds the
186  * corresponding sequence files.
187  * Called when changing wd with name == NULL or when an explicit root name is
188  * given in the GUI or when searching for sequences.
189  * force clears the stats in the seqfile.
190  */
check_seq(int recompute_stats)191 int check_seq(int recompute_stats) {
192 	char *basename;
193 	int curidx, fixed;
194 	GDir *dir;
195 	GError *error = NULL;
196 	const gchar *file;
197 	sequence **sequences;
198 	int i, nb_seq = 0, max_seq = 10;
199 
200 	if (!com.wd) {
201 		siril_log_message(_("Current working directory is not set, aborting.\n"));
202 		return 1;
203 	}
204 	if ((dir = g_dir_open(com.wd, 0, &error)) == NULL) {
205 		fprintf (stderr, "check_seq: %s\n", error->message);
206 		g_clear_error(&error);
207 		g_free(com.wd);
208 		com.wd = NULL;
209 		return 1;
210 	}
211 
212 	sequences = malloc(sizeof(sequence *) * max_seq);
213 	if (!sequences) {
214 		PRINT_ALLOC_ERR;
215 		g_dir_close(dir);
216 		return 1;
217 	}
218 	set_progress_bar_data(NULL, PROGRESS_PULSATE);
219 
220 	while ((file = g_dir_read_name(dir)) != NULL) {
221 		sequence *new_seq;
222 		int fnlen = strlen(file);
223 		if (fnlen < 4) continue;
224 		const char *ext = get_filename_ext(file);
225 		if (!ext) continue;
226 
227 		if ((new_seq = check_seq_one_file(file))) {
228 			sequences[nb_seq] = new_seq;
229 			nb_seq++;
230 		} else if (!strcasecmp(ext, com.pref.ext + 1)) {
231 			if (!get_index_and_basename(file, &basename, &curidx, &fixed)) {
232 				int current_seq = -1;
233 				/* search in known sequences if we already have it */
234 				for (i = 0; i < nb_seq; i++) {
235 					if (!strcmp(sequences[i]->seqname, basename)) {
236 						current_seq = i;
237 					}
238 				}
239 				/* not found */
240 				if (current_seq == -1) {
241 					new_seq = calloc(1, sizeof(sequence));
242 					initialize_sequence(new_seq, TRUE);
243 					new_seq->seqname = basename;
244 					new_seq->beg = INT_MAX;
245 					new_seq->end = 0;
246 					new_seq->fixed = fixed;
247 					sequences[nb_seq] = new_seq;
248 					current_seq = nb_seq;
249 					nb_seq++;
250 					siril_debug_print("Found a sequence (number %d) with base name"
251 							" \"%s\", looking for first and last indexes.\n",
252 							nb_seq, basename);
253 				}
254 				if (curidx < sequences[current_seq]->beg)
255 					sequences[current_seq]->beg = curidx;
256 				if (curidx > sequences[current_seq]->end)
257 					sequences[current_seq]->end = curidx;
258 				if (fixed > sequences[current_seq]->fixed)
259 					sequences[current_seq]->fixed = fixed;
260 			}
261 		}
262 		if (nb_seq == max_seq) {
263 			max_seq *= 2;
264 			sequence **tmp = realloc(sequences, sizeof(sequence *) * max_seq);
265 			if (tmp)
266 				sequences = tmp;
267 			else {
268 				PRINT_ALLOC_ERR;
269 				break;
270 			}
271 		}
272 	}
273 	set_progress_bar_data(NULL, PROGRESS_DONE);
274 	g_dir_close(dir);
275 
276 	if (nb_seq > 0) {
277 		int retval = 1;
278 		for (i = 0; i < nb_seq; i++) {
279 			if (sequences[i]->beg != sequences[i]->end) {
280 				siril_debug_print(_("sequence %d, found: %d to %d\n"),
281 						i + 1, sequences[i]->beg, sequences[i]->end);
282 				if (!buildseqfile(sequences[i], recompute_stats) && retval)
283 					retval = 0;	// at least one succeeded to be created
284 			}
285 			free_sequence(sequences[i], TRUE);
286 		}
287 		free(sequences);
288 		return retval;
289 	}
290 	free(sequences);
291 	siril_log_message(_("No sequence found, verify working directory or "
292 				"change FITS extension in settings (current is %s)\n"), com.pref.ext);
293 	return 1;	// no sequence found
294 }
295 
296 /* Creates a .seq file for one-file sequence passed in argument */
check_seq_one_file(const char * name)297 static sequence *check_seq_one_file(const char* name) {
298 	if (!com.wd) {
299 		siril_log_message(_("Current working directory is not set, aborting.\n"));
300 		return NULL;
301 	}
302 	int fnlen = strlen(name);
303 	const char *ext = get_filename_ext(name);
304 	sequence *new_seq = NULL;
305 
306 	if (!strcasecmp(ext, "ser")) {
307 		struct ser_struct *ser_file = malloc(sizeof(struct ser_struct));
308 		ser_init_struct(ser_file);
309 		if (ser_open_file(name, ser_file)) {
310 			return NULL;
311 		}
312 
313 		new_seq = calloc(1, sizeof(sequence));
314 		initialize_sequence(new_seq, TRUE);
315 		new_seq->seqname = g_strndup(name, fnlen - 4);
316 		new_seq->beg = 0;
317 		new_seq->end = ser_file->frame_count - 1;
318 		new_seq->number = ser_file->frame_count;
319 		new_seq->type = SEQ_SER;
320 		new_seq->ser_file = ser_file;
321 		siril_debug_print("Found a SER sequence\n");
322 	}
323 #ifdef HAVE_FFMS2
324 	else if (!check_for_film_extensions(ext)) {
325 		struct film_struct *film_file = malloc(sizeof(struct film_struct));
326 		if (film_open_file(name, film_file)) {
327 			free(film_file);
328 			return NULL;
329 		}
330 		new_seq = calloc(1, sizeof(sequence));
331 		initialize_sequence(new_seq, TRUE);
332 		int len = strlen(ext);
333 		new_seq->seqname = g_strndup(name, fnlen-len-1);
334 		new_seq->beg = 0;
335 		new_seq->end = film_file->frame_count-1;
336 		new_seq->number = film_file->frame_count;
337 		new_seq->type = SEQ_AVI;
338 		new_seq->film_file = film_file;
339 		siril_debug_print("Found a AVI sequence\n");
340 	}
341 #endif
342 	else if (!strcasecmp(ext, com.pref.ext + 1) && fitseq_is_fitseq(name, NULL)) {
343 		fitseq *fitseq_file = malloc(sizeof(fitseq));
344 		fitseq_init_struct(fitseq_file);
345 		if (fitseq_open(name, fitseq_file)) {
346 			free(fitseq_file);
347 			return NULL;
348 		}
349 		new_seq = calloc(1, sizeof(sequence));
350 		initialize_sequence(new_seq, TRUE);
351 		new_seq->seqname = g_strndup(name, fnlen-strlen(com.pref.ext));
352 		new_seq->beg = 0;
353 		new_seq->end = fitseq_file->frame_count - 1;
354 		new_seq->number = fitseq_file->frame_count;
355 		new_seq->type = SEQ_FITSEQ;
356 		new_seq->fitseq_file = fitseq_file;
357 		siril_debug_print("Found a FITS sequence\n");
358 	}
359 
360 	if (new_seq && new_seq->beg != new_seq->end) {
361 		if (buildseqfile(new_seq, 0)) {
362 			free_sequence(new_seq, TRUE);
363 			new_seq = NULL;
364 		}
365 	}
366 	return new_seq;
367 }
368 
369 // get the number of layers and image size for a new sequence
370 // if load_ref_into_gfit is true, the image is kept into gfit if data loading was
371 // required, and 1 is returned when it required loading
seq_check_basic_data(sequence * seq,gboolean load_ref_into_gfit)372 int seq_check_basic_data(sequence *seq, gboolean load_ref_into_gfit) {
373 	if (seq->nb_layers == -1 || seq->rx == 0) {	// not init yet, first loading of the sequence
374 		int image_to_load = sequence_find_refimage(seq);
375 		fits tmpfit = { 0 }, *fit;
376 
377 		if (load_ref_into_gfit) {
378 			clearfits(&gfit);
379 			fit = &gfit;
380 		} else {
381 			fit = &tmpfit;
382 			memset(fit, 0, sizeof(fits));
383 		}
384 
385 		if (load_ref_into_gfit) {
386 			if (seq_read_frame(seq, image_to_load, fit, FALSE, -1)) {
387 				fprintf(stderr, "could not load first image from sequence\n");
388 				return -1;
389 			}
390 		} else {
391 			if (seq_read_frame_metadata(seq, image_to_load, fit)) {
392 				fprintf(stderr, "could not load first image from sequence\n");
393 				return -1;
394 			}
395 		}
396 
397 		/* initialize sequence-related runtime data */
398 		seq->rx = fit->rx;
399 		seq->ry = fit->ry;
400 		seq->bitpix = fit->orig_bitpix;	// for partial read
401 		fprintf(stdout, "bitpix for the sequence is set as %d\n", seq->bitpix);
402 		if (seq->nb_layers == -1) {
403 			seq->nb_layers = fit->naxes[2];
404 			seq->regparam = calloc(seq->nb_layers, sizeof(regdata*));
405 			seq->layers = calloc(seq->nb_layers, sizeof(layer_info));
406 			if (!seq->regparam || !seq->layers) {
407 				PRINT_ALLOC_ERR;
408 				clearfits(fit);
409 				return 1;
410 			}
411 		}
412 		seq->needs_saving = TRUE;
413 
414 		if (load_ref_into_gfit) {
415 			seq->current = image_to_load;
416 		} else {
417 			clearfits(fit);
418 		}
419 		return 1;
420 	}
421 	return 0;
422 }
423 
free_cbbt_layers()424 static void free_cbbt_layers() {
425 	GtkComboBoxText *cbbt_layers = GTK_COMBO_BOX_TEXT(lookup_widget("comboboxreglayer"));
426 	gtk_combo_box_text_remove_all(cbbt_layers);
427 }
428 
429 /* load a sequence and initializes everything that relates */
set_seq(const char * name)430 int set_seq(const char *name){
431 	sequence *seq = NULL;
432 	char *basename;
433 
434 	if ((seq = readseqfile(name)) == NULL) {
435 		fprintf(stderr, "could not load sequence %s\n", name);
436 		return 1;
437 	}
438 	free_image_data();
439 
440 #ifdef HAVE_FFMS2
441 	int convert = 0;
442 	if (seq->type == SEQ_AVI) {
443 		convert = siril_confirm_dialog(_("Deprecated sequence"),
444 				_("Film sequences are now deprecated in Siril: some features are disabled and others may crash."
445 						" We strongly encourage you to convert this sequence into a SER file."
446 						" SER file format is a simple image sequence format, similar to uncompressed films."), _("Convert to SER"));
447 	}
448 
449 	if (convert) {
450 		close_sequence(FALSE);
451 		convert_single_film_to_ser(seq);
452 	} else
453 #endif
454 	{
455 		int retval = seq_check_basic_data(seq, TRUE);
456 		if (retval == -1) {
457 			free(seq);
458 			return 1;
459 		}
460 		if (retval == 0) {
461 			int image_to_load = sequence_find_refimage(seq);
462 			if (seq_read_frame(seq, image_to_load, &gfit, FALSE, -1)) {
463 				fprintf(stderr, "could not load first image from sequence\n");
464 				free(seq);
465 				return 1;
466 			}
467 			seq->current = image_to_load;
468 		}
469 
470 		basename = g_path_get_basename(seq->seqname);
471 		siril_log_message(_("Sequence loaded: %s (%d->%d)\n"),
472 				basename, seq->beg, seq->end);
473 		g_free(basename);
474 
475 		/* Sequence is stored in com.seq for now */
476 		close_sequence(TRUE);
477 		memcpy(&com.seq, seq, sizeof(sequence));
478 
479 		init_layers_hi_and_lo_values(MIPSLOHI); // set some hi and lo values in seq->layers,
480 		set_cutoff_sliders_max_values();// update min and max values for contrast sliders
481 		set_cutoff_sliders_values();	// update values for contrast sliders for this image
482 		set_layers_for_assign();	// set default layers assign and populate combo box
483 		set_layers_for_registration();	// set layers in the combo box for registration
484 		update_seqlist();
485 		fill_sequence_list(seq, RLAYER, FALSE);// display list of files in the sequence
486 		set_output_filename_to_sequence_name();
487 		sliders_mode_set_state(com.sliders);
488 		initialize_display_mode();
489 		reset_plot(); // reset all plots
490 
491 		/* initialize image-related runtime data */
492 		set_display_mode();		// display the display mode in the combo box
493 		display_filename();		// display filename in gray window
494 		set_precision_switch(); // set precision on screen
495 		adjust_refimage(seq->current);	// check or uncheck reference image checkbox
496 		update_prepro_interface(seq->type == SEQ_REGULAR || seq->type == SEQ_FITSEQ); // enable or not the preprobutton
497 		update_reg_interface(FALSE);	// change the registration prereq message
498 	//	update_stack_interface(FALSE);	// get stacking info and enable the Go button, already done in set_layers_for_registration
499 		adjust_reginfo();		// change registration displayed/editable values
500 		update_gfit_histogram_if_needed();
501 		adjust_sellabel();
502 		fillSeqAviExport();	// fill GtkEntry of export box
503 
504 		/* update menus */
505 		update_MenuItem();
506 		/* update parameters */
507 		set_GUI_CAMERA();
508 		set_GUI_photometry();
509 
510 		/* redraw and display image */
511 		close_tab();	//close Green and Blue Tab if a 1-layer sequence is loaded
512 		redraw(com.cvport, REMAP_ALL);
513 		drawPlot();
514 	}
515 
516 	return 0;
517 }
518 
519 /* Load image number index from the sequence and display it.
520  * if load_it is true, dest is assumed to be gfit
521  * TODO: cut that method in two, with an internal func taking a filename and a fits
522  */
seq_load_image(sequence * seq,int index,gboolean load_it)523 int seq_load_image(sequence *seq, int index, gboolean load_it) {
524 	if (!single_image_is_loaded())
525 		save_stats_from_fit(&gfit, seq, seq->current);
526 	clear_stars_list();
527 	clear_histograms();
528 	undo_flush();
529 	close_single_image();
530 	clearfits(&gfit);
531 	if (seq->current == SCALED_IMAGE) {
532 		gfit.rx = seq->rx;
533 		gfit.ry = seq->ry;
534 	}
535 	seq->current = index;
536 
537 	if (load_it) {
538 		set_cursor_waiting(TRUE);
539 		if (seq_read_frame(seq, index, &gfit, FALSE, -1)) {
540 			set_cursor_waiting(FALSE);
541 			return 1;
542 		}
543 		set_fwhm_star_as_star_list(seq);// display the fwhm star if possible
544 		if (com.sliders != USER) {
545 			init_layers_hi_and_lo_values(com.sliders);
546 			sliders_mode_set_state(com.sliders);
547 			set_cutoff_sliders_max_values();// update min and max values for contrast sliders
548 			set_cutoff_sliders_values();	// update values for contrast sliders for this image
549 			set_display_mode();		// display the display mode in the combo box
550 		}
551 		if (copy_rendering_settings_when_chained(TRUE))
552 			redraw(com.cvport, REMAP_ALL);
553 		else
554 			redraw(com.cvport, REMAP_ONLY);
555 		redraw_previews();		// redraw registration preview areas
556 		display_filename();		// display filename in gray window
557 		set_precision_switch(); // set precision on screen
558 		adjust_reginfo();		// change registration displayed/editable values
559 		update_display_fwhm();
560 		update_gfit_histogram_if_needed();
561 		set_cursor_waiting(FALSE);
562 	}
563 
564 	update_MenuItem();		// initialize menu gui
565 	sequence_list_change_current();
566 	adjust_refimage(index);	// check or uncheck reference image checkbox
567 
568 	return 0;
569 }
570 
571 /**
572  * Computes size of an opened sequence in bytes for a passed number of frames.
573  * For SER or films, it returns the size of the file.
574  * For FITS sequences, the reference image's size is used as baseline.
575  * Unsupported for internal sequences.
576  * @param seq input sequence
577  * @param nb_frames number of frames to compute the size of the sequence of
578  * @return the size of the sequence in bytes, or -1 if an error happened.
579  */
seq_compute_size(sequence * seq,int nb_frames,data_type depth)580 int64_t seq_compute_size(sequence *seq, int nb_frames, data_type depth) {
581 	int64_t frame_size, size = -1LL;
582 #ifdef HAVE_FFMS2
583 	GStatBuf sts;
584 #endif
585 
586 	switch(seq->type) {
587 	case SEQ_SER:
588 		size = ser_compute_file_size(seq->ser_file, nb_frames);
589 		break;
590 	case SEQ_REGULAR:
591 	case SEQ_FITSEQ:
592 		frame_size = seq->rx * seq->ry * seq->nb_layers;
593 		if (depth == DATA_USHORT)
594 			frame_size *= sizeof(WORD);
595 		else if (depth == DATA_FLOAT)
596 			frame_size *= sizeof(float);
597 		frame_size += 5760; // FITS double HDU size
598 		size = frame_size * nb_frames;
599 		break;
600 #ifdef HAVE_FFMS2
601 	case SEQ_AVI:
602 		if (g_stat(seq->film_file->filename, &sts) == 0) {
603 			// this is a close approximation
604 			frame_size = sts.st_size / seq->film_file->frame_count;
605 			size = nb_frames * frame_size;
606 		}
607 		break;
608 #endif
609 	default:
610 		fprintf(stderr, "Failure: computing sequence size on internal sequence is unsupported\n");
611 	}
612 	return size;
613 }
614 
615 /**
616  * Check if a sequence with a basename 'name' or a full name 'name' already exists
617  * @param name either the base name of the sequence or its full name in case of
618  * single file sequence
619  * @param name_is_base TRUE is the name is a base name
620  * @return TRUE if the name already exists, FALSE otherwise
621  */
check_if_seq_exist(gchar * name,gboolean name_is_base)622 gboolean check_if_seq_exist(gchar *name, gboolean name_is_base) {
623 	gchar *path, *path_;
624 	if (name_is_base) {
625 		gchar *seq = g_strdup_printf("%s.seq", name);
626 		gchar *seq_ = g_strdup_printf("%s_.seq", name);
627 		path = g_build_filename(com.wd, seq, NULL);
628 		path_ = g_build_filename(com.wd, seq_, NULL);
629 		g_free(seq);
630 		gboolean retval = is_readable_file(path);
631 		if (!retval) {
632 			retval = is_readable_file(path_);
633 		}
634 		g_free(path);
635 		g_free(path_);
636 		return retval;
637 	} else {
638 		path = g_build_filename(com.wd, name, NULL);
639 		gboolean retval = is_readable_file(path);
640 		g_free(path);
641 		return retval;
642 	}
643 }
644 
645 /*****************************************************************************
646  *              SEQUENCE FUNCTIONS FOR NON-OPENED SEQUENCES                  *
647  * **************************************************************************/
648 
649 /* Get the filename of an image in a sequence.
650  * Return value is the same as the name_buf argument, which must be
651  * pre-allocated to at least 256 characters. If sequence has no file names, a
652  * description like image "42 from awesome_mars.ser" is made. */
seq_get_image_filename(sequence * seq,int index,char * name_buf)653 char *seq_get_image_filename(sequence *seq, int index, char *name_buf) {
654 	switch (seq->type) {
655 		case SEQ_REGULAR:
656 			return fit_sequence_get_image_filename(seq, index, name_buf, TRUE);
657 		case SEQ_SER:
658 			if (!name_buf || index < 0 || index > seq->end) {
659 				return NULL;
660 			}
661 			snprintf(name_buf, 255, "%s_%d.ser", seq->seqname,  index);
662 			return name_buf;
663 		case SEQ_FITSEQ:
664 			if (!name_buf || index < 0 || index > seq->end) {
665 				return NULL;
666 			}
667 			snprintf(name_buf, 255, "%s_%d%s", seq->seqname,  index, com.pref.ext);
668 			return name_buf;
669 #ifdef HAVE_FFMS2
670 		case SEQ_AVI:
671 			if (!name_buf || index < 0 || index > seq->end) {
672 				return NULL;
673 			}
674 			snprintf(name_buf, 255, "%s_%d", seq->seqname, index);
675 			return name_buf;
676 #endif
677 		case SEQ_INTERNAL:
678 			snprintf(name_buf, 255, "%s_%d", seq->seqname, index);
679 			return name_buf;
680 	}
681 	return NULL;
682 }
683 
684 /* Read an entire image from a sequence, inside a pre-allocated fits.
685  * Opens the file, reads data, closes the file.
686  */
seq_read_frame(sequence * seq,int index,fits * dest,gboolean force_float,int thread_id)687 int seq_read_frame(sequence *seq, int index, fits *dest, gboolean force_float, int thread_id) {
688 	char filename[256];
689 	assert(index < seq->number);
690 	switch (seq->type) {
691 		case SEQ_REGULAR:
692 			fit_sequence_get_image_filename(seq, index, filename, TRUE);
693 			if (readfits(filename, dest, NULL, force_float)) {
694 				siril_log_message(_("Could not load image %d from sequence %s\n"),
695 						index, seq->seqname);
696 				return 1;
697 			}
698 			break;
699 		case SEQ_SER:
700 			assert(seq->ser_file);
701 			if (ser_read_frame(seq->ser_file, index, dest, force_float, com.pref.debayer.open_debayer)) {
702 				siril_log_message(_("Could not load frame %d from SER sequence %s\n"),
703 						index, seq->seqname);
704 				return 1;
705 			}
706 			break;
707 		case SEQ_FITSEQ:
708 			assert(seq->fitseq_file);
709 			if (fitseq_read_frame(seq->fitseq_file, index, dest, force_float, thread_id)) {
710 				siril_log_message(_("Could not load frame %d from FITS sequence %s\n"),
711 						index, seq->seqname);
712 				return 1;
713 			}
714 			break;
715 
716 #ifdef HAVE_FFMS2
717 		case SEQ_AVI:
718 			assert(seq->film_file);
719 			if (film_read_frame(seq->film_file, index, dest)) {
720 				siril_log_message(_("Could not load frame %d from AVI sequence %s\n"),
721 						index, seq->seqname);
722 				return 1;
723 			}
724 			// should dest->maxi be set to 255 here?
725 			break;
726 #endif
727 		case SEQ_INTERNAL:
728 			assert(seq->internal_fits);
729 			copyfits(seq->internal_fits[index], dest, CP_FORMAT, -1);
730 			if (seq->internal_fits[index]->type == DATA_FLOAT) {
731 				dest->fdata = seq->internal_fits[index]->fdata;
732 				dest->fpdata[0] = seq->internal_fits[index]->fpdata[0];
733 				dest->fpdata[1] = seq->internal_fits[index]->fpdata[1];
734 				dest->fpdata[2] = seq->internal_fits[index]->fpdata[2];
735 			}
736 			else if (seq->internal_fits[index]->type == DATA_USHORT) {
737 				dest->data = seq->internal_fits[index]->data;
738 				dest->pdata[0] = seq->internal_fits[index]->pdata[0];
739 				dest->pdata[1] = seq->internal_fits[index]->pdata[1];
740 				dest->pdata[2] = seq->internal_fits[index]->pdata[2];
741 			}
742 			else return 1;
743 			break;
744 	}
745 	full_stats_invalidation_from_fit(dest);
746 	copy_seq_stats_to_fit(seq, index, dest);
747 	return 0;
748 }
749 
750 /* same as seq_read_frame above, but creates an image the size of the selection
751  * rectangle only. layer is set to the layer number in the read partial frame.
752  * The partial image result is only one-channel deep, so it cannot be used to
753  * have a partial RGB image. */
seq_read_frame_part(sequence * seq,int layer,int index,fits * dest,const rectangle * area,gboolean do_photometry,int thread_id)754 int seq_read_frame_part(sequence *seq, int layer, int index, fits *dest, const rectangle *area, gboolean do_photometry, int thread_id) {
755 	char filename[256];
756 #ifdef HAVE_FFMS2
757 	fits tmp_fit;
758 #endif
759 	switch (seq->type) {
760 		case SEQ_REGULAR:
761 			fit_sequence_get_image_filename(seq, index, filename, TRUE);
762 			if (readfits_partial(filename, layer, dest, area, do_photometry)) {
763 				siril_log_message(_("Could not load partial image %d from sequence %s\n"),
764 						index, seq->seqname);
765 				return 1;
766 			}
767 			break;
768 		case SEQ_SER:
769 			assert(seq->ser_file);
770 			if (ser_read_opened_partial_fits(seq->ser_file, layer, index, dest, area)) {
771 				siril_log_message(_("Could not load frame %d from SER sequence %s\n"),
772 						index, seq->seqname);
773 				return 1;
774 			}
775 			break;
776 		case SEQ_FITSEQ:
777 			assert(seq->fitseq_file);
778 			if (fitseq_read_partial_fits(seq->fitseq_file, layer, index, dest, area, do_photometry, thread_id)) {
779 				siril_log_message(_("Could not load partial image %d from sequence %s\n"),
780 						index, seq->seqname);
781 				return 1;
782 			}
783 			break;
784 
785 #ifdef HAVE_FFMS2
786 		case SEQ_AVI:
787 			assert(seq->film_file);
788 			memset(&tmp_fit, 0, sizeof(fits));
789 			if (film_read_frame(seq->film_file, index, &tmp_fit)) {
790 				siril_log_message(_("Could not load frame %d from AVI sequence %s\n"),
791 						index, seq->seqname);
792 				return 1;
793 			}
794 			extract_region_from_fits(&tmp_fit, layer, dest, area);
795 			clearfits(&tmp_fit);
796 			break;
797 #endif
798 		case SEQ_INTERNAL:
799 			assert(seq->internal_fits);
800 			extract_region_from_fits(seq->internal_fits[index], 0, dest, area);
801 			break;
802 	}
803 
804 	return 0;
805 }
806 
807 // not thread-safe
808 // gets image naxes and bitpix
seq_read_frame_metadata(sequence * seq,int index,fits * dest)809 static int seq_read_frame_metadata(sequence *seq, int index, fits *dest) {
810 	assert(index < seq->number);
811 	char filename[256];
812 	switch (seq->type) {
813 		case SEQ_REGULAR:
814 			fit_sequence_get_image_filename(seq, index, filename, TRUE);
815 			if (read_fits_metadata_from_path(filename, dest)) {
816 				siril_log_message(_("Could not load image %d from sequence %s\n"),
817 						index, seq->seqname);
818 				return 1;
819 			}
820 			break;
821 		case SEQ_SER:
822 			assert(seq->ser_file);
823 			if (ser_metadata_as_fits(seq->ser_file, dest)) {
824 				siril_log_message(_("Could not load frame %d from SER sequence %s\n"),
825 						index, seq->seqname);
826 				return 1;
827 			}
828 			break;
829 		case SEQ_FITSEQ:
830 			assert(seq->fitseq_file);
831 			dest->fptr = seq->fitseq_file->fptr;
832 			if (fitseq_set_current_frame(seq->fitseq_file, index) ||
833 					read_fits_metadata(dest)) {
834 				siril_log_message(_("Could not load frame %d from FITS sequence %s\n"),
835 						index, seq->seqname);
836 				return 1;
837 			}
838 			break;
839 
840 #ifdef HAVE_FFMS2
841 		case SEQ_AVI:
842 			assert(seq->film_file);
843 			// TODO: do a metadata-only read in films
844 			if (film_read_frame(seq->film_file, index, dest)) {
845 				siril_log_message(_("Could not load frame %d from AVI sequence %s\n"),
846 						index, seq->seqname);
847 				return 1;
848 			}
849 			// should dest->maxi be set to 255 here?
850 			break;
851 #endif
852 		case SEQ_INTERNAL:
853 			assert(seq->internal_fits);
854 			copyfits(seq->internal_fits[index], dest, CP_FORMAT, -1);
855 			break;
856 	}
857 	return 0;
858 }
859 
860 /*****************************************************************************
861  *                 SEQUENCE FUNCTIONS FOR OPENED SEQUENCES                   *
862  * **************************************************************************/
863 
864 /* locks cannot be probed to see if they are init or not, so we have to keep
865  * all of them in the same state, which is initialized if the array is non-nul. */
_allocate_sequence_locks(sequence * seq)866 static int _allocate_sequence_locks(sequence *seq) {
867 #ifdef _OPENMP
868 	if (!seq->fd_lock) {
869 		int i;
870 		seq->fd_lock = malloc(seq->number * sizeof(omp_lock_t));
871 		if (!seq->fd_lock) {
872 			PRINT_ALLOC_ERR;
873 			return 1;
874 		}
875 
876 		for (i=0; i<seq->number; i++)
877 			omp_init_lock(&seq->fd_lock[i]);
878 	}
879 #endif
880 	return 0;
881 }
882 
883 /* open image for future intensive operations (read only) */
seq_open_image(sequence * seq,int index)884 int seq_open_image(sequence *seq, int index) {
885 	int status = 0;
886 	char filename[256];
887 	switch (seq->type) {
888 		case SEQ_REGULAR:
889 			if (!seq->fptr) {
890 				seq->fptr = calloc(seq->number, sizeof(fitsfile *));
891 				if (!seq->fptr) {
892 					PRINT_ALLOC_ERR;
893 					return 1;
894 				}
895 			}
896 			if (_allocate_sequence_locks(seq))
897 				return 1;
898 
899 			fit_sequence_get_image_filename(seq, index, filename, TRUE);
900 			siril_fits_open_diskfile(&seq->fptr[index], filename, READONLY, &status);
901 			if (status) {
902 				fits_report_error(stderr, status);
903 				return status;
904 			}
905 			/* should we check image parameters here? such as bitpix or naxis */
906 			break;
907 		case SEQ_SER:
908 			assert(seq->ser_file->file == NULL);
909 			break;
910 		case SEQ_FITSEQ:
911 			assert(seq->fitseq_file->fptr == NULL);
912 			break;
913 #ifdef HAVE_FFMS2
914 		case SEQ_AVI:
915 			siril_log_message(_("This operation is not supported on AVI sequences (seq_open_image)\n"));
916 			return 1;
917 #endif
918 		case SEQ_INTERNAL:
919 			siril_log_message(_("This operation is not supported on internal sequences (seq_open_image)\n"));
920 			return 1;
921 	}
922 	return 0;
923 }
924 
925 /* close opened images, only useful for regular FITS sequences */
seq_close_image(sequence * seq,int index)926 void seq_close_image(sequence *seq, int index) {
927 	int status = 0;
928 	switch (seq->type) {
929 		case SEQ_REGULAR:
930 			if (seq->fptr && seq->fptr[index]) {
931 				fits_close_file(seq->fptr[index], &status);
932 				seq->fptr[index] = NULL;
933 			}
934 			break;
935 		default:
936 			break;
937 	}
938 }
939 
940 /* read a region in a layer of an opened file from a sequence.
941  * The buffer must have been allocated to the size of the area, with type of
942  * float if seq->bitpix is FLOAT_IMG, with 16-bit type otherwise
943  * Used only by median and rejection stacking.
944  */
seq_opened_read_region(sequence * seq,int layer,int index,void * buffer,const rectangle * area,int thread_id)945 int seq_opened_read_region(sequence *seq, int layer, int index, void *buffer, const rectangle *area, int thread_id) {
946 	switch (seq->type) {
947 		case SEQ_REGULAR:
948 			return read_opened_fits_partial(seq, layer, index, buffer, area);
949 		case SEQ_SER:
950 			return ser_read_opened_partial(seq->ser_file, layer, index, buffer, area);
951 		case SEQ_FITSEQ:
952 			return fitseq_read_partial(seq->fitseq_file, layer, index, buffer, area, thread_id);
953 		default:
954 			break;
955 	}
956 	return 0;
957 }
958 
959 
960 /*****************************************************************************
961  *                         SEQUENCE DATA MANAGEMENT                          *
962  * **************************************************************************/
963 
964 /* if FWHM was calculated on the sequence, a minimisation exists for all
965  * images, and when switching to a new image, it should be set as the only item
966  * in the star list, in order to be displayed.
967  * A special care is required in PSF_list.c:clear_stars_list(), to not free this data. */
set_fwhm_star_as_star_list_with_layer(sequence * seq,int layer)968 static void set_fwhm_star_as_star_list_with_layer(sequence *seq, int layer) {
969 	assert(seq->regparam);
970 	/* we chose here the first layer that has been allocated, which doesn't
971 	 * mean it contains data for all images. Handle with care. */
972 	if (seq->regparam && layer >= 0 && layer < seq->nb_layers
973 			&& seq->regparam[layer] && seq->current >= 0
974 			&& seq->regparam[layer][seq->current].fwhm_data && !com.stars) {
975 		com.stars = malloc(2 * sizeof(fitted_PSF *));
976 		com.stars[0] = seq->regparam[layer][seq->current].fwhm_data;
977 		com.stars[1] = NULL;
978 		// this is freed in PSF_list.c:clear_stars_list()
979 		com.star_is_seqdata = TRUE;
980 	}
981 }
982 
983 // cannot be called in the worker thread
set_fwhm_star_as_star_list(sequence * seq)984 void set_fwhm_star_as_star_list(sequence *seq) {
985 	int layer = get_registration_layer(seq);
986 	set_fwhm_star_as_star_list_with_layer(seq, layer);
987 }
988 
989 /* Rebuilds the file name of an image in a sequence.
990  * The file name is stored in name_buffer, which must be allocated 256 bytes
991  * The index is the index in the sequence, not the number appearing in the file name
992  * Return value: NULL on error, name_buffer on success.
993  */
fit_sequence_get_image_filename(sequence * seq,int index,char * name_buffer,gboolean add_fits_ext)994 char *fit_sequence_get_image_filename(sequence *seq, int index, char *name_buffer, gboolean add_fits_ext) {
995 	char format[20];
996 	if (index < 0 || index > seq->number || name_buffer == NULL)
997 		return NULL;
998 	if (seq->fixed <= 1) {
999 		sprintf(format, "%%s%%d");
1000 	} else {
1001 		sprintf(format, "%%s%%.%dd", seq->fixed);
1002 	}
1003 	if (add_fits_ext)
1004 		strcat(format, com.pref.ext);
1005 	snprintf(name_buffer, 255, format,
1006 			seq->seqname, seq->imgparam[index].filenum);
1007 	name_buffer[255] = '\0';
1008 	return name_buffer;
1009 }
1010 
fit_sequence_get_image_filename_prefixed(sequence * seq,const char * prefix,int index)1011 char *fit_sequence_get_image_filename_prefixed(sequence *seq, const char *prefix, int index) {
1012 	char format[16];
1013 	gchar *basename = g_path_get_basename(seq->seqname);
1014 	GString *str = g_string_sized_new(70);
1015 	sprintf(format, "%%s%%s%%0%dd%%s", seq->fixed);
1016 	g_string_printf(str, format, prefix,
1017 			basename, seq->imgparam[index].filenum,
1018 			com.pref.ext);
1019 	g_free(basename);
1020 	return g_string_free(str, FALSE);
1021 }
1022 
1023 /* Returns a filename for an image that could be in a sequence, but the sequence structure
1024  * has not been fully initialized yet. Only beg, end, fixed and seqname are used.
1025  */
get_possible_image_filename(sequence * seq,int image_number,char * name_buffer)1026 char *get_possible_image_filename(sequence *seq, int image_number, char *name_buffer) {
1027 	char format[20];
1028 	if (image_number < seq->beg || image_number > seq->end || name_buffer == NULL)
1029 		return NULL;
1030 	if (seq->fixed <= 1){
1031 		sprintf(format, "%%s%%d%s", com.pref.ext);
1032 	} else {
1033 		sprintf(format, "%%s%%.%dd%s", seq->fixed, com.pref.ext);
1034 	}
1035 	sprintf(name_buffer, format, seq->seqname, image_number);
1036 	return name_buffer;
1037 }
1038 
1039 /* splits a filename in a base name and an index number, if the file name ends with .fit
1040  * it also computes the fixed length if there are zeros in the index */
get_index_and_basename(const char * filename,char ** basename,int * index,int * fixed)1041 int	get_index_and_basename(const char *filename, char **basename, int *index, int *fixed){
1042 	char *buffer;
1043 	int i, fnlen, first_zero, digit_idx;
1044 
1045 	*index = -1;		// error values
1046 	*fixed = 0;
1047 	first_zero = -1;
1048 	*basename = NULL;
1049 	fnlen = strlen(filename);
1050 	if (fnlen < strlen(com.pref.ext)+2) return -1;
1051 	if (!g_str_has_suffix(filename, com.pref.ext)) return -1;
1052 	i = fnlen-strlen(com.pref.ext)-1;
1053 	if (!isdigit(filename[i])) return -1;
1054 	digit_idx = i;
1055 
1056 	buffer = strdup(filename);
1057 	buffer[fnlen - strlen(com.pref.ext)] = '\0';		// for g_ascii_strtoll()
1058 	do {
1059 		if (buffer[i] == '0' && first_zero < 0)
1060 			first_zero = i;
1061 		if (buffer[i] != '0' && first_zero > 0)
1062 			first_zero = -1;
1063 		i--;
1064 	} while (i >= 0 && isdigit(buffer[i]));
1065 	i++;
1066 	if (i == 0) {
1067 		free(buffer);
1068 		return -1;	// no base name, only number
1069 	}
1070 	if (first_zero >= 0)
1071 		*fixed = digit_idx - i + 1;
1072 	//else *fixed = 0;
1073 	*index = g_ascii_strtoll(buffer+i, NULL, 10);
1074 	if (*basename == NULL) {	// don't copy it if we already have it
1075 		*basename = malloc(i * sizeof(char) + 1);
1076 		strncpy(*basename, buffer, i);
1077 		(*basename)[i] = '\0';
1078 	}
1079 	//fprintf(stdout, "from filename %s, base name is %s, index is %d\n", filename, *basename, *index);
1080 	free(buffer);
1081 	return 0;
1082 }
1083 
remove_prefixed_sequence_files(sequence * seq,const char * prefix)1084 void remove_prefixed_sequence_files(sequence *seq, const char *prefix) {
1085 	int i, len;
1086 	gchar *basename, *seqname;
1087 	if (!prefix || prefix[0] == '\0')
1088 		return;
1089 	basename = g_path_get_basename(seq->seqname);
1090 	len = strlen(basename) + 5 + strlen(prefix);
1091 	seqname = malloc(len);
1092 	g_snprintf(seqname, len, "%s%s.seq", prefix, basename);
1093 	siril_debug_print("Removing %s\n", seqname);
1094 	g_unlink(seqname); // removing the seqfile
1095 	free(seqname);
1096 	g_free(basename);
1097 
1098 	switch (seq->type) {
1099 	default:
1100 	case SEQ_REGULAR:
1101 		for (i = 0; i < seq->number; i++) {
1102 			// TODO: use com.cache_upscaled and the current sequence
1103 			// filter to leave the images to be up-scaled.
1104 			char *filename = fit_sequence_get_image_filename_prefixed(
1105 					seq, prefix, i);
1106 			siril_debug_print("Removing %s\n", filename);
1107 			g_unlink(filename);
1108 			free(filename);
1109 		}
1110 		break;
1111 	case SEQ_SER:
1112 	case SEQ_FITSEQ:
1113 		if (seq->type == SEQ_SER)
1114 			basename = seq->ser_file->filename;
1115 		else basename = seq->fitseq_file->filename;
1116 		len = strlen(basename) + strlen(prefix) + 1;
1117 		seqname = malloc(len);
1118 		g_snprintf(seqname, len, "%s%s", prefix, basename);
1119 		siril_debug_print("Removing %s\n", seqname);
1120 		g_unlink(seqname);
1121 		break;
1122 	}
1123 }
1124 
1125 /* sets default values for the sequence */
initialize_sequence(sequence * seq,gboolean is_zeroed)1126 void initialize_sequence(sequence *seq, gboolean is_zeroed) {
1127 	int i;
1128 	if (!is_zeroed) {
1129 		memset(seq, 0, sizeof(sequence));
1130 	}
1131 	seq->nb_layers = -1;		// uninit value
1132 	seq->reference_image = -1;	// uninit value
1133 	seq->reference_star = -1;	// uninit value
1134 	seq->type = SEQ_REGULAR;
1135 	for (i=0; i<PREVIEW_NB; i++) {
1136 		seq->previewX[i] = -1;
1137 		seq->previewY[i] = -1;
1138 	}
1139 	seq->upscale_at_stacking = 1.0;
1140 }
1141 
1142 /* call this to close a sequence. Second arg must be FALSE for com.seq
1143  * WARNING: the data is not reset to NULL, if seq is to be reused,
1144  * initialize_sequence() must be called on it right after free_sequence()
1145  * (= do it for com.seq) */
free_sequence(sequence * seq,gboolean free_seq_too)1146 void free_sequence(sequence *seq, gboolean free_seq_too) {
1147 	int layer, j;
1148 
1149 	if (seq == NULL) return;
1150 	// free regparam
1151 	if (seq->nb_layers > 0 && seq->regparam) {
1152 		for (layer = 0; layer < seq->nb_layers; layer++) {
1153 			if (seq->regparam[layer]) {
1154 				for (j = 0; j < seq->number; j++) {
1155 					if (seq->regparam[layer][j].fwhm_data &&
1156 							(!seq->photometry[0] ||
1157 							 seq->regparam[layer][j].fwhm_data != seq->photometry[0][j])) // avoid double free
1158 						free(seq->regparam[layer][j].fwhm_data);
1159 				}
1160 				free(seq->regparam[layer]);
1161 			}
1162 		}
1163 		free(seq->regparam);
1164 	}
1165 	// free stats
1166 	if (seq->nb_layers > 0 && seq->stats) {
1167 		for (layer = 0; layer < seq->nb_layers; layer++) {
1168 			if (seq->stats[layer]) {
1169 				for (j = 0; j < seq->number; j++) {
1170 					if (seq->stats[layer][j])
1171 						free_stats(seq->stats[layer][j]);
1172 				}
1173 				free(seq->stats[layer]);
1174 			}
1175 		}
1176 		free(seq->stats);
1177 	}
1178 	// free backup regparam
1179 	if (seq->nb_layers > 0 && seq->regparam_bkp) {
1180 		for (layer = 0; layer < seq->nb_layers; layer++) {
1181 			if (seq->regparam_bkp[layer]) {
1182 				for (j = 0; j < seq->number; j++) {
1183 					if (seq->regparam_bkp[layer][j].fwhm_data &&
1184 							(!seq->photometry[0] ||
1185 							 seq->regparam_bkp[layer][j].fwhm_data != seq->photometry[0][j])) // avoid double free
1186 						free(seq->regparam_bkp[layer][j].fwhm_data);
1187 				}
1188 				free(seq->regparam_bkp[layer]);
1189 			}
1190 		}
1191 		free(seq->regparam_bkp);
1192 	}
1193 	// free backup stats
1194 	if (seq->nb_layers > 0 && seq->stats_bkp) {
1195 		for (layer = 0; layer < seq->nb_layers; layer++) {
1196 			if (seq->stats_bkp[layer]) {
1197 				for (j = 0; j < seq->number; j++) {
1198 					if (seq->stats_bkp[layer][j])
1199 						free_stats(seq->stats_bkp[layer][j]);
1200 				}
1201 				free(seq->stats_bkp[layer]);
1202 			}
1203 		}
1204 		free(seq->stats_bkp);
1205 	}
1206 
1207 	// free name of the layers
1208 	if (seq->nb_layers > 0) {
1209 		for (layer = 0; layer < seq->nb_layers; layer++) {
1210 			free(seq->layers[layer].name);
1211 		}
1212 	}
1213 
1214 	for (j = 0; j < seq->number; j++) {
1215 		if (seq->fptr && seq->fptr[j]) {
1216 			int status = 0;
1217 			fits_close_file(seq->fptr[j], &status);
1218 		}
1219 		if (seq->imgparam) {
1220 			if (seq->imgparam[j].date_obs) {
1221 				g_date_time_unref(seq->imgparam[j].date_obs);
1222 			}
1223 		}
1224 	}
1225 	if (seq->seqname)	free(seq->seqname);
1226 	if (seq->layers)	free(seq->layers);
1227 	if (seq->imgparam)	free(seq->imgparam);
1228 	if (seq->fptr)		free(seq->fptr);
1229 
1230 #ifdef _OPENMP
1231 	if (seq->fd_lock) {
1232 		for (j=0; j<seq->number; j++) {
1233 			omp_destroy_lock(&seq->fd_lock[j]);
1234 		}
1235 		free(seq->fd_lock);
1236 	}
1237 #endif
1238 
1239 	if (seq->ser_file) {
1240 		ser_close_file(seq->ser_file);	// frees the data too
1241 		free(seq->ser_file);
1242 	}
1243 #ifdef HAVE_FFMS2
1244 	if (seq->film_file) {
1245 		film_close_file(seq->film_file);	// frees the data too
1246 		free(seq->film_file);
1247 	}
1248 #endif
1249 	if (seq->fitseq_file) {
1250 		fitseq_close_file(seq->fitseq_file);
1251 		free(seq->fitseq_file);
1252 	}
1253 
1254 	if (seq->internal_fits) {
1255 		// Compositing still uses references to the images in the sequence
1256 		//for (j=0; j<seq->number; j++)
1257 		//	clearfits(seq->internal_fits[j]);
1258 		free(seq->internal_fits);
1259 	}
1260 	/* Here this is a bit tricky. An internal sequence is a single image. So some
1261 	 * processes like RGB alignment could free sequences and load it again: we need
1262 	 * to keep undo history.
1263 	 * In the case of a standard sequence, loading a new sequence MUST remove all
1264 	 * undo history.
1265 	 */
1266 	if (seq->type != SEQ_INTERNAL)
1267 		undo_flush();
1268 
1269 	for (j = 0; j < MAX_SEQPSF && seq->photometry[j]; j++) {
1270 		free_photometry_set(seq, j);
1271 	}
1272 
1273 	if (free_seq_too)	free(seq);
1274 }
1275 
sequence_is_loaded()1276 gboolean sequence_is_loaded() {
1277 	return (com.seq.seqname != NULL && com.seq.imgparam != NULL);
1278 }
1279 
1280 /* Close the com.seq sequence */
close_sequence(int loading_another)1281 void close_sequence(int loading_another) {
1282 	if (sequence_is_loaded()) {
1283 		fprintf(stdout, "MODE: closing sequence\n");
1284 		siril_log_message(_("Closing sequence %s\n"), com.seq.seqname);
1285 		if (!com.headless) {
1286 			free_cbbt_layers();
1287 			clear_sequence_list();
1288 		}
1289 		if (com.seq.needs_saving)
1290 			writeseqfile(&com.seq);
1291 		free_sequence(&com.seq, FALSE);
1292 		initialize_sequence(&com.seq, FALSE);
1293 		if (!com.headless) {
1294 			clear_stars_list();
1295 			update_stack_interface(TRUE);
1296 		}
1297 		if (!loading_another && !com.headless) {
1298 			// unselect the sequence in the sequence list
1299 			GtkComboBox *seqcombo = GTK_COMBO_BOX(lookup_widget("sequence_list_combobox"));
1300 			gtk_combo_box_set_active(seqcombo, -1);
1301 		}
1302 		adjust_sellabel();
1303 		update_seqlist();
1304 	}
1305 }
1306 
1307 /* if no reference image has been set, return the index of an image that is
1308  * selected in the sequence, the best of the first registration data found if
1309  * any, the first otherwise */
sequence_find_refimage(sequence * seq)1310 int sequence_find_refimage(sequence *seq) {
1311 	if (seq->reference_image != -1)
1312 		return seq->reference_image;
1313 	if (seq->type == SEQ_INTERNAL)
1314 		return 1; // green channel
1315 	int layer, image, best = -1;
1316 	for (layer = 0; layer < seq->nb_layers; layer++) {
1317 		if (seq->regparam[layer]) {
1318 			gboolean use_fwhm;
1319 			double best_val;
1320 			if (seq->regparam[layer][0].fwhm > 0.0) {
1321 				use_fwhm = TRUE;
1322 				best_val = 1000000.0;
1323 			} else if (seq->regparam[layer][0].quality > 0.0) {
1324 				use_fwhm = FALSE;
1325 				best_val = 0.0;
1326 			}
1327 			else continue;
1328 
1329 			for (image = 0; image < seq->number; image++) {
1330 				if (!seq->imgparam[image].incl)
1331 					continue;
1332 				if (use_fwhm && seq->regparam[layer][image].fwhm > 0 &&
1333 						seq->regparam[layer][image].fwhm < best_val) {
1334 					best_val = seq->regparam[layer][image].fwhm;
1335 					best = image;
1336 				} else if (seq->regparam[layer][image].quality > 0 &&
1337 						seq->regparam[layer][image].quality > best_val) {
1338 					best_val = seq->regparam[layer][image].quality;
1339 					best = image;
1340 				}
1341 			}
1342 		}
1343 	}
1344 
1345 	if (best == -1 && seq->selnum > 0) {
1346 		for (image = 0; image < seq->number; image++) {
1347 			if (seq->imgparam[image].incl) {
1348 				best = image;
1349 				break;
1350 			}
1351 		}
1352 	}
1353 
1354 	if (best == -1) best = 0;	// the first anyway if no regdata and no selected
1355 
1356 	return best;
1357 }
1358 
1359 /* requires seq->nb_layers and seq->number to be already set */
check_or_allocate_regparam(sequence * seq,int layer)1360 void check_or_allocate_regparam(sequence *seq, int layer) {
1361 	assert(layer < seq->nb_layers);
1362 	if (!seq->regparam && seq->nb_layers > 0) {
1363 		seq->regparam = calloc(seq->nb_layers, sizeof(regdata*));
1364 		seq->layers = calloc(seq->nb_layers, sizeof(layer_info));
1365 	}
1366 	if (seq->regparam && !seq->regparam[layer] && seq->number > 0) {
1367 		seq->regparam[layer] = calloc(seq->number, sizeof(regdata));
1368 	}
1369 }
1370 
1371 /* assign shift values for registration data of a sequence, depending on its type and sign */
set_shifts(sequence * seq,int frame,int layer,float shiftx,float shifty,gboolean data_is_top_down)1372 void set_shifts(sequence *seq, int frame, int layer, float shiftx, float shifty, gboolean data_is_top_down) {
1373 	if (seq->regparam[layer]) {
1374 		seq->regparam[layer][frame].shiftx = shiftx;
1375 		seq->regparam[layer][frame].shifty = data_is_top_down ? -shifty : shifty;
1376 	}
1377 }
1378 
1379 /* internal sequence are a set of 1-layer images already loaded elsewhere, and
1380  * directly referenced as fits *.
1381  * This is used in LRGV composition.
1382  * The returned sequence does not contain any reference to files, and thus has
1383  * to be populated with internal_sequence_set() */
create_internal_sequence(int size)1384 sequence *create_internal_sequence(int size) {
1385 	int i;
1386 	sequence *seq = calloc(1, sizeof(sequence));
1387 	initialize_sequence(seq, TRUE);
1388 	seq->type = SEQ_INTERNAL;
1389 	seq->number = size;
1390 	seq->selnum = size;
1391 	seq->nb_layers = 1;
1392 	seq->internal_fits = calloc(size, sizeof(fits *));
1393 	seq->seqname = strdup(_("internal sequence"));
1394 	seq->imgparam = calloc(size, sizeof(imgdata));
1395 	for (i = 0; i < size; i++) {
1396 		seq->imgparam[i].filenum = i;
1397 		seq->imgparam[i].incl = 1;
1398 		seq->imgparam[i].date_obs = NULL;
1399 	}
1400 	check_or_allocate_regparam(seq, 0);
1401 	return seq;
1402 }
1403 
internal_sequence_set(sequence * seq,int index,fits * fit)1404 void internal_sequence_set(sequence *seq, int index, fits *fit) {
1405 	assert(seq);
1406 	assert(seq->internal_fits);
1407 	assert(index < seq->number);
1408 	seq->internal_fits[index] = fit;
1409 }
1410 
internal_sequence_get(sequence * seq,int index)1411 fits *internal_sequence_get(sequence *seq, int index) {
1412 	if (index > seq->number)
1413 		return NULL;
1414 	return seq->internal_fits[index];
1415 }
1416 
1417 // find index of the fit argument in the sequence
internal_sequence_find_index(sequence * seq,fits * fit)1418 int internal_sequence_find_index(sequence *seq, fits *fit) {
1419 	int i;
1420 	assert(seq);
1421 	assert(seq->internal_fits);
1422 	for (i = 0; i < seq->number; i++) {
1423 		if (fit == seq->internal_fits[i])
1424 			return i;
1425 	}
1426 	return -1;
1427 }
1428 
1429 // check if the passed sequence is used as a color sequence. It can be a CFA
1430 // sequence explicitly demoisaiced too, which returns true.
sequence_is_rgb(sequence * seq)1431 gboolean sequence_is_rgb(sequence *seq) {
1432 	switch (seq->type) {
1433 		case SEQ_REGULAR:
1434 			return seq->nb_layers == 3;
1435 		case SEQ_SER:
1436 			return (seq->ser_file->color_id != SER_MONO && com.pref.debayer.open_debayer) ||
1437 				seq->ser_file->color_id == SER_RGB ||
1438 				seq->ser_file->color_id == SER_BGR;
1439 		case SEQ_FITSEQ:
1440 			return seq->fitseq_file->naxes[2] == 3;
1441 		default:
1442 			return TRUE;
1443 	}
1444 }
1445 
1446 /* Ensures that an area does not derive off-image.
1447  * Verifies coordinates of the center and moves it inside the image if the area crosses the bounds.
1448  */
enforce_area_in_image(rectangle * area,sequence * seq)1449 void enforce_area_in_image(rectangle *area, sequence *seq) {
1450 	if (area->x < 0) area->x = 0;
1451 	if (area->y < 0) area->y = 0;
1452 	if (area->x + area->w > seq->rx)
1453 		area->x = seq->rx - area->w;
1454 	if (area->y + area->h > seq->ry)
1455 		area->y = seq->ry - area->h;
1456 }
1457 
1458 /********************************************************************
1459  *                                             __                   *
1460  *                   ___  ___  __ _ _ __  ___ / _|                  *
1461  *                  / __|/ _ \/ _` | '_ \/ __| |_                   *
1462  *                  \__ \  __/ (_| | |_) \__ \  _|                  *
1463  *                  |___/\___|\__, | .__/|___/_|                    *
1464  *                               |_|_|                              *
1465  ********************************************************************/
1466 
1467 /* Computes FWHM for a sequence image.
1468  * area is the area from which fit was extracted from the full frame.
1469  * when the framing is set to follow star, args->area is centered on the found star
1470  */
seqpsf_image_hook(struct generic_seq_args * args,int out_index,int index,fits * fit,rectangle * area)1471 int seqpsf_image_hook(struct generic_seq_args *args, int out_index, int index, fits *fit, rectangle *area) {
1472 	struct seqpsf_args *spsfargs = (struct seqpsf_args *)args->user;
1473 	struct seqpsf_data *data = malloc(sizeof(struct seqpsf_data));
1474 	if (!data) {
1475 		PRINT_ALLOC_ERR;
1476 		return -1;
1477 	}
1478 	data->image_index = index;
1479 
1480 	rectangle psfarea = { .x = 0, .y = 0, .w = fit->rx, .h = fit->ry };
1481 	data->psf = psf_get_minimisation(fit, 0, &psfarea, !spsfargs->for_registration, TRUE, FALSE);
1482 	if (data->psf) {
1483 		data->psf->xpos = data->psf->x0 + area->x;
1484 		if (fit->top_down)
1485 			data->psf->ypos = data->psf->y0 + area->y;
1486 		else data->psf->ypos = area->y + area->h - data->psf->y0;
1487 
1488 		/* let's move args->area to center it on the star */
1489 		if (spsfargs->framing == FOLLOW_STAR_FRAME) {
1490 			args->area.x = round_to_int(data->psf->xpos - args->area.w*0.5);
1491 			args->area.y = round_to_int(data->psf->ypos - args->area.h*0.5);
1492 			//fprintf(stdout, "moving area to %d, %d\n", args->area.x, args->area.y);
1493 		}
1494 
1495 		if (!args->seq->imgparam[index].date_obs && fit->date_obs) {
1496 			args->seq->imgparam[index].date_obs = g_date_time_ref(fit->date_obs);
1497 		}
1498 		data->exposure = fit->exposure;
1499 	}
1500 	else {
1501 		if (spsfargs->framing == FOLLOW_STAR_FRAME) {
1502 			siril_log_color_message(_("No star found in the area image %d around %d,%d"
1503 						" (use a larger area?)\n"),
1504 					"red", index, area->x, area->y);
1505 		} else {
1506 			siril_log_color_message(_("No star found in the area image %d around %d,%d"
1507 					" (use 'follow star' option?)\n"),
1508 				"red", index, area->x, area->y);
1509 		}
1510 	}
1511 
1512 #ifdef _OPENMP
1513 	omp_set_lock(&args->lock);
1514 #endif
1515 	spsfargs->list = g_slist_prepend(spsfargs->list, data);
1516 #ifdef _OPENMP
1517 	omp_unset_lock(&args->lock);
1518 #endif
1519 	return !data->psf;
1520 }
1521 
end_seqpsf(gpointer p)1522 gboolean end_seqpsf(gpointer p) {
1523 	struct generic_seq_args *args = (struct generic_seq_args *)p;
1524 	struct seqpsf_args *spsfargs = (struct seqpsf_args *)args->user;
1525 	sequence *seq = args->seq;
1526 	int layer = args->layer_for_partial;
1527 	int photometry_index = 0;
1528 	gboolean displayed_warning = FALSE, write_to_regdata = FALSE;
1529 	gboolean duplicate_for_regdata = FALSE, dont_stop_thread;
1530 
1531 	if (args->retval)
1532 		goto proper_ending;
1533 
1534 	if (spsfargs->for_registration || !seq->regparam[layer]) {
1535 		if (!spsfargs->for_registration && !seq->regparam[layer])
1536 			duplicate_for_regdata = TRUE;
1537 		check_or_allocate_regparam(seq, layer);
1538 		write_to_regdata = TRUE;
1539 		seq->needs_saving = TRUE;
1540 	}
1541 	if (!spsfargs->for_registration) {
1542 		int i;
1543 		for (i = 0; i < MAX_SEQPSF && seq->photometry[i]; i++);
1544 		if (i == MAX_SEQPSF) {
1545 			free_photometry_set(seq, 0);
1546 		       	i = 0;
1547 		}
1548 		seq->photometry[i] = calloc(seq->number, sizeof(fitted_PSF *));
1549 		photometry_index = i;
1550 	}
1551 
1552 	GSList *iterator;
1553 	for (iterator = spsfargs->list; iterator; iterator = iterator->next) {
1554 		struct seqpsf_data *data = iterator->data;
1555 		/* check exposure consistency (only obtained when !for_registration) */
1556 		if (!spsfargs->for_registration && seq->exposure > 0.0 &&
1557 				seq->exposure != data->exposure && !displayed_warning) {
1558 			siril_log_color_message(_("Star analysis does not give consistent results when exposure changes across the sequence.\n"), "red");
1559 			displayed_warning = TRUE;
1560 		}
1561 		seq->exposure = data->exposure;
1562 
1563 		if (write_to_regdata) {
1564 			seq->regparam[layer][data->image_index].fwhm_data =
1565 				duplicate_for_regdata ? duplicate_psf(data->psf) : data->psf;
1566 			if (data->psf) {
1567 				seq->regparam[layer][data->image_index].fwhm = data->psf->fwhmx;
1568 				seq->regparam[layer][data->image_index].roundness =
1569 					data->psf->fwhmy / data->psf->fwhmx;
1570 			}
1571 		}
1572 
1573 		// for photometry use: store data in seq->photometry
1574 		if (!spsfargs->for_registration) {
1575 			seq->photometry[photometry_index][data->image_index] = data->psf;
1576 		}
1577 	}
1578 
1579 	if (com.seq.needs_saving)
1580 		writeseqfile(&com.seq);
1581 
1582 	set_fwhm_star_as_star_list_with_layer(seq, layer);
1583 
1584 	if (!args->already_in_a_thread) {
1585 		/* do here all GUI-related items, because it runs in the main thread.
1586 		 * Most of these things are already done in end_register_idle
1587 		 * in case seqpsf is called for registration. */
1588 		// update the list in the GUI
1589 		if (seq->type != SEQ_INTERNAL) {
1590 			update_seqlist();
1591 			fill_sequence_list(seq, layer, FALSE);
1592 		}
1593 		set_layers_for_registration();	// update display of available reg data
1594 		drawPlot();
1595 		notify_new_photometry();	// switch to and update plot tab
1596 	}
1597 
1598 proper_ending:
1599 	dont_stop_thread = args->already_in_a_thread;
1600 	if (spsfargs->list)
1601 		g_slist_free(spsfargs->list);
1602 	free(spsfargs);
1603 	adjust_sellabel();
1604 
1605 	if (dont_stop_thread) {
1606 		// we must not call stop_processing_thread() here
1607 		return FALSE;
1608 	} else {
1609 		free(args);
1610 		return end_generic(NULL);
1611 	}
1612 }
1613 
1614 /* process PSF for the given sequence, on the given layer, the area of the
1615  * image selection (com.selection), as a threaded operation or not.
1616  */
seqpsf(sequence * seq,int layer,gboolean for_registration,gboolean regall,framing_mode framing,gboolean run_in_thread)1617 int seqpsf(sequence *seq, int layer, gboolean for_registration, gboolean regall,
1618 		framing_mode framing, gboolean run_in_thread) {
1619 
1620 	if (framing == REGISTERED_FRAME && !seq->regparam[layer])
1621 		framing = ORIGINAL_FRAME;
1622 
1623 	if (com.selection.w <= 0 || com.selection.h <= 0){
1624 		siril_log_message(_("Select an area first\n"));
1625 		return 1;
1626 	}
1627 	if (framing == FOLLOW_STAR_FRAME)
1628 		siril_log_color_message(_("The sequence analysis of the PSF will use a sliding selection area centred on the previous found star; this disables parallel processing.\n"), "salmon");
1629 	else if (framing == REGISTERED_FRAME)
1630 		siril_log_color_message(_("The sequence analysis of the PSF will use registration data to move the selection area for each image; this is compatible with parallel processing.\n"), "salmon");
1631 
1632 	struct generic_seq_args *args = create_default_seqargs(seq);
1633 	struct seqpsf_args *spsfargs = malloc(sizeof(struct seqpsf_args));
1634 
1635 	spsfargs->for_registration = for_registration;
1636 	spsfargs->framing = framing;
1637 	spsfargs->list = NULL;	// GSList init is NULL
1638 
1639 	args->partial_image = TRUE;
1640 	memcpy(&args->area, &com.selection, sizeof(rectangle));
1641 	if (seq->regparam[layer] && seq->current >= 0) {
1642 		args->area.x += seq->regparam[layer][seq->current].shiftx;
1643 		args->area.y -= seq->regparam[layer][seq->current].shifty;
1644 		if (args->area.x < 0 || args-> area.x > seq->rx - args->area.w ||
1645 				args->area.y < 0 || args->area.y > seq->ry - args->area.h) {
1646 			siril_log_color_message(_("This area is outside of the reference image. Please select the reference image to select another star.\n"), "red");
1647 			free(args);
1648 			free(spsfargs);
1649 			return 1;
1650 		}
1651 	}
1652 	args->layer_for_partial = layer;
1653 	args->regdata_for_partial = framing == REGISTERED_FRAME;
1654 	args->get_photometry_data_for_partial = !for_registration;
1655 	if (!regall) {
1656 		args->filtering_criterion = seq_filter_included;
1657 		args->nb_filtered_images = seq->selnum;
1658 	}
1659 	args->image_hook = seqpsf_image_hook;
1660 	args->idle_function = end_seqpsf;
1661 	args->stop_on_error = FALSE;
1662 	args->description = _("PSF on area");
1663 	args->user = spsfargs;
1664 	args->already_in_a_thread = !run_in_thread;
1665 	args->parallel = framing != FOLLOW_STAR_FRAME;
1666 
1667 	if (run_in_thread) {
1668 		start_in_new_thread(generic_sequence_worker, args);
1669 		return 0;
1670 	} else {
1671 		generic_sequence_worker(args);
1672 		int retval = args->retval;
1673 		free(args);
1674 		return retval;
1675 	}
1676 }
1677 
free_reference_image()1678 void free_reference_image() {
1679 	fprintf(stdout, "Purging previously saved reference frame data.\n");
1680 	if (com.refimage_regbuffer) {
1681 		free(com.refimage_regbuffer);
1682 		com.refimage_regbuffer = NULL;
1683 	}
1684 	if (com.refimage_surface) {
1685 		cairo_surface_destroy(com.refimage_surface);
1686 		com.refimage_surface = NULL;
1687 	}
1688 	if (com.seq.reference_image == -1)
1689 		enable_view_reference_checkbox(FALSE);
1690 }
1691 
1692 /* returns the number of images of the sequence that can fit into memory based
1693  * on the configured memory ratio */
compute_nb_images_fit_memory(sequence * seq,double factor,gboolean force_float,unsigned int * MB_per_orig_image,unsigned int * MB_per_scaled_image,unsigned int * max_mem_MB)1694 int compute_nb_images_fit_memory(sequence *seq, double factor, gboolean force_float, unsigned int *MB_per_orig_image, unsigned int *MB_per_scaled_image, unsigned int *max_mem_MB) {
1695 	int max_memory_MB = get_max_memory_in_MB();
1696 	if (factor < 1.0 || factor > 2.0) {
1697 		fprintf(stderr, "############ FACTOR UNINIT (set to 1) ############\n");
1698 		factor = 1.0;
1699 	}
1700 	uint64_t newx = round_to_int((double)seq->rx * factor);
1701 	uint64_t newy = round_to_int((double)seq->ry * factor);
1702 	uint64_t memory_per_orig_image = seq->rx * seq->ry * seq->nb_layers;
1703 	uint64_t memory_per_scaled_image = newx * newy * seq->nb_layers;
1704 	if (force_float || get_data_type(seq->bitpix) == DATA_FLOAT) {
1705 		memory_per_orig_image *= sizeof(float);
1706 		memory_per_scaled_image *= sizeof(float);
1707 	} else {
1708 		memory_per_orig_image *= sizeof(WORD);
1709 		memory_per_scaled_image *= sizeof(WORD);
1710 	}
1711 	unsigned int memory_per_orig_image_MB = memory_per_orig_image / BYTES_IN_A_MB;
1712 	unsigned int memory_per_scaled_image_MB = memory_per_scaled_image / BYTES_IN_A_MB;
1713 	if (memory_per_scaled_image_MB == 0)
1714 		memory_per_scaled_image_MB = 1;
1715 	fprintf(stdout, "Memory per image: %u MB. Max memory: %d MB\n", memory_per_scaled_image_MB, max_memory_MB);
1716 	if (MB_per_orig_image)
1717 		*MB_per_orig_image = memory_per_orig_image_MB;
1718 	if (MB_per_scaled_image)
1719 		*MB_per_scaled_image = memory_per_scaled_image_MB;
1720 	if (max_mem_MB)
1721 		*max_mem_MB = max_memory_MB;
1722 	return max_memory_MB / memory_per_scaled_image_MB;
1723 }
1724 
fix_selnum(sequence * seq,gboolean warn)1725 void fix_selnum(sequence *seq, gboolean warn) {
1726 	int nbsel = 0;
1727 	for (int i = 0; i < seq->number; i++)
1728 		if (seq->imgparam[i].incl)
1729 			nbsel++;
1730 
1731 	if (nbsel != seq->selnum) {
1732 		if (warn)
1733 			siril_log_message(_("Fixing the selection number in the .seq file (%d) to the actual value (%d) (not saved)\n"), seq->selnum, nbsel);
1734 		seq->selnum = nbsel;
1735 	}
1736 }
1737 
1738