Commit 2a86b81d authored by Cyril Richard's avatar Cyril Richard
Browse files

Cherry-pick from 53fe9c71 and...

Cherry-pick from 53fe9c71 and 1067768e
parent 86d469c9
Pipeline #497231436 passed with stages
in 22 minutes and 40 seconds
......@@ -393,35 +393,35 @@ struct ffit {
* */
/* data obtained from the FITS file */
char *header; // entire header of the FITS file. NULL for non-FITS file.
WORD lo; // MIPS-LO key in FITS file, which is "Lower visualization cutoff"
WORD hi; // MIPS-HI key in FITS file, which is "Upper visualization cutoff"
double data_max; // used to check if 32b float is in the [0, 1] range
char *header; // entire header of the FITS file. NULL for non-FITS file.
WORD lo; // MIPS-LO key in FITS file, "Lower visualization cutoff"
WORD hi; // MIPS-HI key in FITS file, "Upper visualization cutoff"
double data_max; // used to check if 32b float is in the [0, 1] range
float pixel_size_x, pixel_size_y; // XPIXSZ and YPIXSZ keys
unsigned int binning_x, binning_y; // XBINNING and YBINNING keys
unsigned int binning_x, binning_y; // XBINNING and YBINNING keys
gboolean unbinned;
char row_order[FLEN_VALUE];
GDateTime *date, *date_obs;
double expstart, expend;
char filter[FLEN_VALUE]; // FILTER key
char image_type[FLEN_VALUE]; // IMAGETYP key
char image_type[FLEN_VALUE]; // IMAGETYP key
char object[FLEN_VALUE]; // OBJECT key
char instrume[FLEN_VALUE]; // INSTRUME key
char telescop[FLEN_VALUE]; // TELESCOP key
char observer[FLEN_VALUE]; // OBSERVER key
char bayer_pattern[FLEN_VALUE]; // BAYERPAT key Bayer Pattern if available
char bayer_pattern[FLEN_VALUE]; // BAYERPAT key Bayer Pattern if available
int bayer_xoffset, bayer_yoffset;
/* data obtained from FITS or RAW files */
double focal_length, iso_speed, exposure, aperture, ccd_temp;
double livetime; // total exposure
guint stacknt; // number of stacked frame
double cvf; // Conversion factor (e-/adu)
int key_gain, key_offset; // Gain, Offset values read in camera headers.
double livetime; // total exposure
guint stackcnt; // number of stacked frame
double cvf; // Conversion factor (e-/adu)
int key_gain, key_offset; // Gain, Offset values read in camera headers.
/* Plate Solving data */
wcs_info wcsdata; // data from the header
wcs_info wcsdata; // data from the header
#ifdef HAVE_WCSLIB
struct wcsprm *wcslib; // struct of the lib
struct wcsprm *wcslib; // struct of the lib
#endif
/* data used in the Fourier space */
......@@ -570,18 +570,18 @@ struct pref_struct {
gint combo_theme; // value of the combobox theme
gdouble font_scale; // font scale
gboolean icon_symbolic; // icon style
gboolean icon_symbolic; // icon style
gchar *combo_lang; // string value of the combobox lang
gchar *ext; // FITS extension used in SIRIL
gchar *swap_dir; // swap directory
gchar *swap_dir; // swap directory
GSList *script_path; // script path directories
gdouble focal; // focal length saved in config file
gdouble pitch; // pixel pitch saved in config file
gdouble focal; // focal length saved in config file
gdouble pitch; // pixel pitch saved in config file
libraw raw_set; // the libraw settings
libraw raw_set; // the libraw settings
struct debayer_config debayer; // debayer settings
phot phot_set; // photometry settings
gboolean catalog[7]; // Yet 6 catalogs and 1 user catalog
......@@ -677,16 +677,16 @@ struct guiinf {
/* The global data structure of siril core */
struct cominf {
preferences pref; // saved variable in preferences
preferences pref; // saved variable in preferences
gchar *wd; // working directory, where images and sequences are
gchar *initfile; // the path of the init file
gchar *initfile; // the path of the init file
int reg_settings; // Use to save registration method in the init file
gboolean cache_upscaled; // keep up-scaled files for 'drizzle' (only used by developers)
/* history of operations */
historic *history; // the history of all operations
historic *history; // the history of all operations
int hist_size; // allocated size
int hist_current; // current index
int hist_display; // displayed index
......
......@@ -65,6 +65,7 @@ static char *FILTER[] = {"FILTER", "FILT-1", NULL };
static char *CVF[] = { "CVF", "EGAIN", NULL };
static char *IMAGETYP[] = { "IMAGETYP", "FRAMETYP", NULL };
static char *OFFSETLEVEL[] = { "OFFSET", "BLKLEVEL", NULL }; //Used for synthetic offset
static char *NB_STACKED[] = { "STACKCNT", "NCOMBINE", NULL };
static int CompressionMethods[] = { RICE_1, GZIP_1, GZIP_2, HCOMPRESS_1};
......@@ -344,9 +345,7 @@ void read_fits_header(fits *fit) {
__tryToFindKeywords(fit->fptr, TDOUBLE, CCD_TEMP, &fit->ccd_temp);
__tryToFindKeywords(fit->fptr, TDOUBLE, EXPOSURE, &fit->exposure);
status = 0;
fits_read_key(fit->fptr, TUINT, "STACKCNT", &(fit->stacknt), NULL, &status);
__tryToFindKeywords(fit->fptr, TUINT, NB_STACKED, &fit->stackcnt);
status = 0;
fits_read_key(fit->fptr, TDOUBLE, "LIVETIME", &(fit->livetime), NULL, &status);
......@@ -547,7 +546,7 @@ int fits_parse_header_string(fits *fit, gchar *header) {
} else if (siril_str_has_prefix(card, EXPOSURE)) {
fit->exposure = g_ascii_strtod(value, NULL);
} else if (g_str_has_prefix(card, "STACKCNT=")) {
fit->stacknt = g_ascii_strtoull(value, NULL, 10);
fit->stackcnt = g_ascii_strtoull(value, NULL, 10);
} else if (g_str_has_prefix(card, "LIVETIME=")) {
fit->livetime = g_ascii_strtod(value, NULL);
} else if (siril_str_has_prefix(card, FILTER)) {
......@@ -1315,8 +1314,8 @@ void save_fits_header(fits *fit) {
}
status = 0;
if (fit->stacknt > 0)
fits_update_key(fit->fptr, TUINT, "STACKCNT", &(fit->stacknt),
if (fit->stackcnt > 0)
fits_update_key(fit->fptr, TUINT, "STACKCNT", &(fit->stackcnt),
"Stack frames", &status);
status = 0;
......@@ -1500,15 +1499,27 @@ void save_fits_header(fits *fit) {
/********************** public functions ************************************/
void get_date_data_from_fitsfile(fitsfile *fptr, GDateTime **dt, double *exposure) {
char date_obs[FLEN_VALUE];
/* gets specific data from the header (for stacking).
* if exposure is not found, it defaults to 0
* if livetime is not found, exposure is assigned to it
* if stack_count is not found, it defaults to 1
*/
void get_date_data_from_fitsfile(fitsfile *fptr, GDateTime **dt, double *exposure, double *livetime, unsigned int *stack_count) {
*exposure = 0.0;
*stack_count = 1;
*dt = NULL;
__tryToFindKeywords(fptr, TDOUBLE, EXPOSURE, exposure);
__tryToFindKeywords(fptr, TUINT, NB_STACKED, stack_count);
int status = 0;
fits_read_key(fptr, TSTRING, "DATE-OBS", &date_obs, NULL, &status);
if (!status) {
if (fits_read_key(fptr, TDOUBLE, "LIVETIME", livetime, NULL, &status))
*livetime = *exposure;
char date_obs[FLEN_VALUE];
status = 0;
if (!fits_read_key(fptr, TSTRING, "DATE-OBS", &date_obs, NULL, &status))
*dt = FITS_date_to_date_time(date_obs);
}
}
int import_metadata_from_fitsfile(fitsfile *fptr, fits *to) {
......@@ -1516,6 +1527,7 @@ int import_metadata_from_fitsfile(fitsfile *fptr, fits *to) {
from.fptr = fptr;
read_fits_header(&from);
copy_fits_metadata(&from, to);
clearfits(&from);
return 0;
}
......@@ -2319,8 +2331,6 @@ int copy_fits_metadata(fits *from, fits *to) {
strncpy(to->instrume, from->instrume, FLEN_VALUE);
strncpy(to->telescop, from->telescop, FLEN_VALUE);
strncpy(to->observer, from->observer, FLEN_VALUE);
strncpy(to->dft.type, from->dft.type, FLEN_VALUE);
strncpy(to->dft.ord, from->dft.ord, FLEN_VALUE);
strncpy(to->bayer_pattern, from->bayer_pattern, FLEN_VALUE);
strncpy(to->row_order, from->row_order, FLEN_VALUE);
......@@ -2332,16 +2342,19 @@ int copy_fits_metadata(fits *from, fits *to) {
to->expstart = from->expstart;
to->expend = from->expend;
to->livetime = from->livetime;
to->stackcnt = from->stackcnt;
to->aperture = from->aperture;
to->ccd_temp = from->ccd_temp;
to->cvf = from->cvf;
to->key_gain = from->key_gain;
to->key_offset = from->key_offset;
to->dft.norm[0] = from->dft.norm[0];
to->dft.norm[1] = from->dft.norm[1];
to->dft.norm[2] = from->dft.norm[2];
memcpy(&to->dft, &from->dft, sizeof(dft_info));
memcpy(&to->wcsdata, &from->wcsdata, sizeof(wcs_info));
#ifdef HAVE_WCSLIB
//wcssub()?
#endif
// copy from->history?
return 0;
}
......
......@@ -11,7 +11,7 @@ char *copy_header(fits *fit);
data_type get_data_type(int bitpix);
void fit_get_photometry_data(fits *fit);
int readfits(const char *filename, fits *fit, char *realname, gboolean force_float);
void get_date_data_from_fitsfile(fitsfile *fptr, GDateTime **dt, double *exposure);
void get_date_data_from_fitsfile(fitsfile *fptr, GDateTime **dt, double *exposure, double *livetime, unsigned int *stack_count);
int import_metadata_from_fitsfile(fitsfile *fptr, fits *to);
void clearfits(fits*);
int readfits_partial(const char *filename, int layer, fits *fit,
......
......@@ -1275,6 +1275,8 @@ int import_metadata_from_serfile(struct ser_struct *ser_file, fits *to) {
strncpy(to->instrume, ser_file->instrument, FLEN_VALUE);
strncpy(to->observer, ser_file->observer, FLEN_VALUE);
strncpy(to->telescop, ser_file->telescope, FLEN_VALUE);
if (ser_file->fps > 0.0)
to->exposure = 1.0 / ser_file->fps;
return 0;
}
......
......@@ -42,19 +42,15 @@ typedef struct {
} DateEvent;
static void free_list_date(gpointer data) {
DateEvent *item = data;
DateEvent *item = (DateEvent *)data;
g_date_time_unref(item->date_obs);
g_slice_free(DateEvent, item);
}
static DateEvent* new_item(GDateTime *dt, gdouble exposure) {
DateEvent *item;
item = g_slice_new(DateEvent);
static DateEvent* new_date_item(GDateTime *dt, gdouble exposure) {
DateEvent *item = g_slice_new(DateEvent);
item->exposure = exposure;
item->date_obs = dt;
return item;
}
......@@ -68,7 +64,7 @@ static gint list_date_compare(gconstpointer *a, gconstpointer *b) {
static int stack_mean_or_median(struct stacking_args *args, gboolean is_mean);
/*************************** MEDIAN AND MEAN STACKING **************************
* Median and mean stacking requires all images to be in memory, so we don't
* Median and mean stacking require all images to be in memory, so we don't
* use the generic readfits() but directly the cfitsio routines to open them
* and seq_opened_read_region() to read data randomly from them.
* Since all data of all images cannot fit in memory, a divide and conqueer
......@@ -85,85 +81,88 @@ static int stack_mean_or_median(struct stacking_args *args, gboolean is_mean);
* remaining ones is used.
* ****************************************************************************/
int stack_open_all_files(struct stacking_args *args, int *bitpix, int *naxis, long *naxes, GList **list_date, fits *fit) {
int status, nb_frames = args->nb_images_to_stack;
int stack_open_all_files(struct stacking_args *args, int *bitpix, int *naxis, long *naxes,
GList **list_date, fits *fit) {
int nb_frames = args->nb_images_to_stack;
guint stackcnt = 0;
double livetime = 0.0;
*bitpix = 0;
*naxis = 0;
*naxes = 0;
set_progress_bar_data(_("Opening images for stacking"), PROGRESS_NONE);
if (args->seq->type == SEQ_REGULAR || args->seq->type == SEQ_FITSEQ) {
if (args->apply_weight) {
int nb_frames = args->nb_images_to_stack;
int nb_layers = args->seq->nb_layers;
args->weights = malloc(nb_layers * nb_frames * sizeof(double));
}
if (args->seq->type == SEQ_FITSEQ) {
g_assert(args->seq->fitseq_file);
/* we assume that all images have the same dimensions and bitpix */
memcpy(naxes, args->seq->fitseq_file->naxes, sizeof args->seq->fitseq_file->naxes);
*naxis = naxes[2] == 3 ? 3 : 2;
*bitpix = args->seq->fitseq_file->bitpix;
}
if (args->seq->type == SEQ_REGULAR) {
for (int i = 0; i < nb_frames; ++i) {
long oldnaxes[3] = { 0 };
int oldbitpix = 0, oldnaxis = -1;
char msg[256], filename[256];
int image_index = args->image_indices[i]; // image index in sequence
if (!get_thread_run()) {
int image_index = args->image_indices[i]; // image index in sequence
if (!get_thread_run())
return ST_GENERIC_ERROR;
}
if (!fit_sequence_get_image_filename(args->seq, image_index, filename, TRUE))
continue;
snprintf(msg, 255, _("Opening image %s for stacking"), filename);
msg[255] = '\0';
set_progress_bar_data(msg, PROGRESS_NONE);
if (i % 20 == 0)
set_progress_bar_data(NULL, PROGRESS_PULSATE);
/* open input images */
if (seq_open_image(args->seq, image_index)) {
siril_log_message(_("Opening image %s failed\n"), filename);
return ST_SEQUENCE_ERROR;
}
/* here we use the internal data of sequences, it's quite ugly, we should
* consider moving these tests in seq_open_image() or wrapping them in a
* sequence function */
status = 0;
fits_get_img_param(args->seq->fptr[image_index], 3, bitpix, naxis, naxes, &status);
if (status) {
siril_log_message(_("Opening image %s failed\n"), filename);
fits_report_error(stderr, status); /* print error message */
return ST_SEQUENCE_ERROR;
}
if (*naxis > 3) {
siril_log_message(_("Stacking error: images with > 3 dimensions "
"are not supported\n"));
return ST_SEQUENCE_ERROR;
}
if (oldnaxis > 0) {
if (*naxis != oldnaxis ||
oldnaxes[0] != naxes[0] ||
oldnaxes[1] != naxes[1] ||
oldnaxes[2] != naxes[2]) {
siril_log_message(_("Stacking error: input images have "
"different sizes\n"));
fitsfile *fptr;
if (args->seq->type == SEQ_REGULAR) {
if (seq_open_image(args->seq, image_index)) {
siril_log_message(_("Opening image %d failed\n"), image_index);
return ST_SEQUENCE_ERROR;
}
} else {
oldnaxis = *naxis;
oldnaxes[0] = naxes[0];
oldnaxes[1] = naxes[1];
oldnaxes[2] = naxes[2];
}
if (oldbitpix > 0) {
if (*bitpix != oldbitpix) {
siril_log_message(_("Stacking error: input images have "
"different precision\n"));
fptr = args->seq->fptr[image_index];
if (check_fits_params(fptr, bitpix, naxis, naxes)) {
siril_log_message(_("Opening image %d failed\n"), image_index);
return ST_SEQUENCE_ERROR;
}
} else {
oldbitpix = *bitpix;
if (fitseq_set_current_frame(args->seq->fitseq_file, image_index)) {
siril_log_color_message(_("There was an error opening frame %d for stacking\n"), "red", image_index);
return ST_SEQUENCE_ERROR;
}
fptr = args->seq->fitseq_file->fptr;
}
gdouble current_exp;
/* we get some metadata at the same time: date, exposure ... */
gdouble current_exp, current_livetime;
unsigned int stack_count;
GDateTime *dt = NULL;
get_date_data_from_fitsfile(args->seq->fptr[image_index], &dt, &current_exp);
get_date_data_from_fitsfile(fptr, &dt, &current_exp, &current_livetime, &stack_count);
if (dt)
*list_date = g_list_prepend(*list_date, new_item(dt, current_exp));
*list_date = g_list_prepend(*list_date, new_date_item(dt, current_exp));
livetime += current_livetime;
stackcnt += stack_count;
/* We copy metadata from reference to the final fit */
if (image_index == args->ref_image)
import_metadata_from_fitsfile(args->seq->fptr[image_index], fit);
import_metadata_from_fitsfile(fptr, fit);
if (args->apply_weight) {
int nb_layers = args->seq->nb_layers;
double weight = (double)stack_count;
siril_debug_print("weight for image %d: %d\n", i, stack_count);
args->weights[i] = weight;
if (nb_layers > 1) {
args->weights[nb_frames + i] = weight;
args->weights[nb_frames * 2 + i] = weight;
}
}
}
if (stackcnt <= 0)
stackcnt = nb_frames;
fit->stackcnt = stackcnt;
fit->livetime = livetime;
// keeping exposure of the reference frame
if (naxes[2] == 0)
naxes[2] = 1;
......@@ -186,39 +185,22 @@ int stack_open_all_files(struct stacking_args *args, int *bitpix, int *naxis, lo
}
import_metadata_from_serfile(args->seq->ser_file, fit);
for (int frame = 0; frame < args->seq->number; frame++) {
GDateTime *dt = ser_read_frame_date(args->seq->ser_file, frame);
if (dt)
*list_date = g_list_prepend(*list_date, new_item(dt, 0.0));
}
}
else if (args->seq->type == SEQ_FITSEQ) {
g_assert(args->seq->fitseq_file);
memcpy(naxes, args->seq->fitseq_file->naxes, sizeof args->seq->fitseq_file->naxes);
*naxis = naxes[2] == 3 ? 3 : 2;
*bitpix = args->seq->fitseq_file->bitpix;
for (int frame = 0; frame < args->seq->number; frame++) {
if (fitseq_set_current_frame(args->seq->fitseq_file, frame)) {
siril_log_color_message(_("There was an error opening frame %d for stacking\n"), "red", frame);
return ST_SEQUENCE_ERROR;
}
gdouble current_exp;
GDateTime *dt = NULL;
get_date_data_from_fitsfile(args->seq->fitseq_file->fptr, &dt, &current_exp);
for (int i = 0; i < nb_frames; ++i) {
int image_index = args->image_indices[i]; // image index in sequence
GDateTime *dt = ser_read_frame_date(args->seq->ser_file, image_index);
if (dt)
*list_date = g_list_prepend(*list_date, new_item(dt, current_exp));
/* We copy metadata from reference to the final fit */
if (frame == args->ref_image)
import_metadata_from_fitsfile(args->seq->fitseq_file->fptr, fit);
*list_date = g_list_prepend(*list_date, new_date_item(dt, 0.0));
}
fit->stackcnt = nb_frames;
fit->livetime = fit->exposure * nb_frames;
// keeping the fallacious exposure based on fps from the header
} else {
siril_log_message(_("Rejection stacking is only supported for FITS images/sequences and SER sequences.\nUse \"Sum Stacking\" instead.\n"));
return ST_SEQUENCE_ERROR;
}
set_progress_bar_data(NULL, PROGRESS_DONE);
siril_debug_print("stack count: %u, livetime: %f\n", fit->stackcnt, fit->livetime);
return ST_OK;
}
......@@ -638,7 +620,7 @@ static int apply_rejection_ushort(struct _data_block *data, int nb_frames, struc
}
for (int frame = 0; frame < N; frame++) {
if (N - r <= 4) {
// no more rejections
// no more rejections
rejected[frame] = 0;
} else {
rejected[frame] = sigma_clipping(stack[frame], args->sig, var, median, crej);
......@@ -685,7 +667,8 @@ static int apply_rejection_ushort(struct _data_block *data, int nb_frames, struc
else firstloop = 0;
memcpy(w_stack, stack, N * sizeof(WORD));
do {
Winsorize(w_stack, roundf_to_WORD(median - 1.5f * sigma), roundf_to_WORD(median + 1.5f * sigma), N);
Winsorize(w_stack, roundf_to_WORD(median - 1.5f * sigma),
roundf_to_WORD(median + 1.5f * sigma), N);
sigma0 = sigma;
sigma = 1.134f * args->sd_calculator(w_stack, N);
} while (fabs(sigma - sigma0) > sigma0 * 0.0005f);
......@@ -694,7 +677,8 @@ static int apply_rejection_ushort(struct _data_block *data, int nb_frames, struc
// no more rejections
rejected[frame] = 0;
} else {
rejected[frame] = sigma_clipping(stack[frame], args->sig, sigma, median, crej);
rejected[frame] = sigma_clipping(stack[frame],
args->sig, sigma, median, crej);
if (rejected[frame] != 0)
r++;
}
......@@ -905,43 +889,39 @@ static int compute_weights(struct stacking_args *args) {
return ST_OK;
}
/* computes the observation date (beginning) and the start of exposure (same
* but in julian date) and end of exposure (start of last shot + exposure time)
*/
static void compute_date_time_keywords(GList *list_date, fits *fit) {
if (list_date) {
gdouble exposure = 0.0;
GDateTime *date_obs;
gdouble start, end;
GList *list;
/* First we want to sort the list */
list_date = g_list_sort(list_date, (GCompareFunc) list_date_compare);
/* Then we compute the sum of exposure */
for (list = list_date; list; list = list->next) {
exposure += ((DateEvent *)list->data)->exposure;
}
if (!list_date)
return;
GDateTime *date_obs;
gdouble start, end;
/* First we want to sort the list */
list_date = g_list_sort(list_date, (GCompareFunc) list_date_compare);
/* go to the first stacked image and get needed values */
list_date = g_list_first(list_date);
date_obs = g_date_time_ref(((DateEvent *)list_date->data)->date_obs);
start = date_time_to_Julian(((DateEvent *)list_date->data)->date_obs);
/* go to the last stacked image and get needed values
* This time we need to add the exposure to the date_obs
* to exactly retrieve the end of the exposure
*/
list_date = g_list_last(list_date);
gdouble last_exp = ((DateEvent *)list_date->data)->exposure;
GDateTime *last_date = ((DateEvent *)list_date->data)->date_obs;
GDateTime *corrected_last_date = g_date_time_add_seconds(last_date, (gdouble) last_exp);
/* go to the first stacked image and get needed values */
list_date = g_list_first(list_date);
date_obs = g_date_time_ref(((DateEvent *)list_date->data)->date_obs);
start = date_time_to_Julian(((DateEvent *)list_date->data)->date_obs);
/* go to the last stacked image and get needed values
* This time we need to add the exposure to the date_obs
* to exactly retrieve the end of the exposure
*/
list_date = g_list_last(list_date);
gdouble last_exp = ((DateEvent *)list_date->data)->exposure;
GDateTime *last_date = ((DateEvent *)list_date->data)->date_obs;
GDateTime *corrected_last_date = g_date_time_add_seconds(last_date, (gdouble) last_exp);
end = date_time_to_Julian(corrected_last_date);
g_date_time_unref(corrected_last_date);
/* we address the computed values to the keywords */
fit->livetime = exposure;
fit->date_obs = date_obs;
fit->expstart = start;
fit->expend = end;
}
end = date_time_to_Julian(corrected_last_date);
g_date_time_unref(corrected_last_date);
/* we address the computed values to the keywords */
fit->date_obs = date_obs;
fit->expstart = start;
fit->expend = end;
}
/* How many rows fit in memory, based on image size, number and available memory.
......@@ -1147,7 +1127,7 @@ static int stack_mean_or_median(struct stacking_args *args, gboolean is_mean) {
const float dx = j - data_pool[i].m_x;
data_pool[i].xf[j] = 1.f / (j + 1);
data_pool[i].m_dx2 += (dx * dx - data_pool[i].m_dx2)
* data_pool[i].xf[j];
* data_pool[i].xf[j];
}
data_pool[i].m_dx2 = 1.f / data_pool[i].m_dx2;
}
......@@ -1398,8 +1378,6 @@ static int stack_mean_or_median(struct stacking_args *args, gboolean is_mean) {
}
compute_date_time_keywords(list_date, &gfit);
/* report he number of stacked image */
gfit.stacknt = args->nb_images_to_stack;
free_and_close:
fprintf(stdout, "free and close (%d)\n", retval);
......
......@@ -900,3 +900,49 @@ static void stacking_args_deep_free(struct stacking_args *args) {
free(args->critical_value);
free(args);
}
/* verify that the parameters of the image pointed by fptr are the same as some reference values */
int check_fits_params(fitsfile *fptr, int *oldbitpix, int *oldnaxis, long *oldnaxes) {
int status = 0;
long naxes[3] = { 0 };
int bitpix = 0, naxis = -1;
fits_get_img_param(fptr, 3, &bitpix, &naxis, naxes, &status);
if (status) {
siril_log_message(_("Opening image failed\n"));
fits_report_error(stderr, status); /* print error message */
return -1;
}
if (naxis > 3) {
siril_log_message(_("Stacking error: images with > 3 dimensions "
"are not supported\n"));
return -1;
}