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