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 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <dirent.h>
26 #include <math.h>
27 #include <string.h>
28 #include <gsl/gsl_histogram.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <opencv2/core/version.hpp>
32 #include <glib.h>
33 #include <libgen.h>
34
35 #include "core/siril.h"
36 #include "core/proto.h"
37 #include "core/arithm.h"
38 #include "core/undo.h"
39 #include "core/initfile.h"
40 #include "core/preprocess.h"
41 #include "core/processing.h"
42 #include "core/sequence_filtering.h"
43 #include "core/OS_utils.h"
44 #include "io/conversion.h"
45 #include "io/image_format_fits.h"
46 #include "io/sequence.h"
47 #include "io/single_image.h"
48 #include "gui/utils.h"
49 #include "gui/callbacks.h"
50 #include "gui/PSF_list.h"
51 #include "gui/histogram.h"
52 #include "gui/plot.h"
53 #include "gui/progress_and_log.h"
54 #include "gui/image_display.h"
55 #include "gui/image_interactions.h"
56 #include "gui/linear_match.h"
57 #include "gui/sequence_list.h"
58 #include "gui/siril_preview.h"
59 #include "gui/script_menu.h"
60 #include "filters/asinh.h"
61 #include "filters/banding.h"
62 #include "filters/clahe.h"
63 #include "filters/cosmetic_correction.h"
64 #include "filters/deconv.h"
65 #include "filters/median.h"
66 #include "filters/fft.h"
67 #include "filters/rgradient.h"
68 #include "filters/saturation.h"
69 #include "filters/scnr.h"
70 #include "filters/wavelets.h"
71 #include "algos/PSF.h"
72 #include "algos/star_finder.h"
73 #include "algos/Def_Math.h"
74 #include "algos/Def_Wavelet.h"
75 #include "algos/background_extraction.h"
76 #include "algos/demosaicing.h"
77 #include "algos/extraction.h"
78 #include "algos/colors.h"
79 #include "algos/quality.h"
80 #include "algos/noise.h"
81 #include "algos/statistics.h"
82 #include "algos/sorting.h"
83 #include "algos/siril_wcs.h"
84 #include "algos/geometry.h"
85 #include "opencv/opencv.h"
86 #include "stacking/stacking.h"
87 #include "stacking/sum.h"
88 #include "registration/registration.h"
89 #include "registration/matching/match.h"
90 #include "algos/fix_xtrans_af.h"
91 #include "algos/annotate.h"
92
93 #include "command.h"
94 #include "command_def.h"
95 #include "command_list.h"
96 #include "command_line_processor.h"
97
98 #define PRINT_NOT_FOR_SEQUENCE siril_log_message(_("This command cannot be applied on a sequence.\n"))
99 #define PRINT_NOT_FOR_SINGLE siril_log_message(_("This command can only be used when a sequence is loaded.\n"))
100
101 char *word[MAX_COMMAND_WORDS]; // NULL terminated
102
process_load(int nb)103 int process_load(int nb){
104 char filename[256];
105
106 strncpy(filename, word[1], 250);
107 filename[250] = '\0';
108
109 for (int i = 1; i < nb - 1; ++i) {
110 strcat(filename, " ");
111 strcat(filename, word[i + 1]);
112 }
113 expand_home_in_filename(filename, 256);
114 int retval = open_single_image(filename);
115 return (retval < 0);
116 }
117
process_satu(int nb)118 int process_satu(int nb){
119 if (get_thread_run()) {
120 PRINT_ANOTHER_THREAD_RUNNING;
121 return 1;
122 }
123 if (!single_image_is_loaded()) {
124 PRINT_NOT_FOR_SEQUENCE;
125 return 1;
126 }
127
128 struct enhance_saturation_data *args = malloc(sizeof(struct enhance_saturation_data));
129
130 args->coeff = g_ascii_strtod(word[1], NULL);
131
132 args->input = &gfit;
133 args->output = &gfit;
134 args->h_min = 0.0;
135 args->h_max = 360.0;
136 args->background_factor = 1.0;
137
138 enhance_saturation(args);
139
140 adjust_cutoff_from_updated_gfit();
141 redraw(com.cvport, REMAP_ALL);
142 redraw_previews();
143
144 return 0;
145 }
146
process_save(int nb)147 int process_save(int nb){
148 if (sequence_is_loaded() && !single_image_is_loaded()) {
149 gfit.hi = com.seq.layers[RLAYER].hi;
150 gfit.lo = com.seq.layers[RLAYER].lo;
151 }
152 else if (single_image_is_loaded()) {
153 gfit.hi = com.uniq->layers[RLAYER].hi;
154 gfit.lo = com.uniq->layers[RLAYER].lo;
155 } else {
156 return 1;
157 }
158
159 gchar *filename = g_strdup(word[1]);
160 set_cursor_waiting(TRUE);
161 int retval = savefits(filename, &(gfit));
162 set_precision_switch();
163 set_cursor_waiting(FALSE);
164 g_free(filename);
165 return retval;
166 }
167
process_savebmp(int nb)168 int process_savebmp(int nb){
169 if (!single_image_is_loaded()) {
170 PRINT_NOT_FOR_SEQUENCE;
171 return 1;
172 }
173
174 gchar *filename = g_strdup_printf("%s.bmp", word[1]);
175
176 set_cursor_waiting(TRUE);
177 savebmp(filename, &(gfit));
178 set_cursor_waiting(FALSE);
179 g_free(filename);
180 return 0;
181 }
182
183 #ifdef HAVE_LIBJPEG
process_savejpg(int nb)184 int process_savejpg(int nb){
185 if (!single_image_is_loaded()) {
186 PRINT_NOT_FOR_SEQUENCE;
187 return 1;
188 }
189
190 int quality = 100;
191
192 if ((nb == 3) && g_ascii_strtoull(word[2], NULL, 10) <= 100 && g_ascii_strtoull(word[2], NULL, 10) > 0)
193 quality = g_ascii_strtoull(word[2], NULL, 10);
194
195 gchar *filename = g_strdup_printf("%s.jpg", word[1]);
196
197 set_cursor_waiting(TRUE);
198 savejpg(filename, &gfit, quality);
199 set_cursor_waiting(FALSE);
200 g_free(filename);
201 return 0;
202 }
203 #endif
204
205 #ifdef HAVE_LIBPNG
process_savepng(int nb)206 int process_savepng(int nb){
207
208 if (!single_image_is_loaded()) {
209 PRINT_NOT_FOR_SEQUENCE;
210 return 1;
211 }
212
213 gchar *filename = g_strdup_printf("%s.png", word[1]);
214
215 set_cursor_waiting(TRUE);
216 uint32_t bytes_per_sample = gfit.orig_bitpix != BYTE_IMG ? 2 : 1;
217 savepng(filename, &gfit, bytes_per_sample, gfit.naxes[2] == 3);
218 set_cursor_waiting(FALSE);
219 g_free(filename);
220 return 0;
221 }
222 #endif
223
224 #ifdef HAVE_LIBTIFF
process_savetif(int nb)225 int process_savetif(int nb){
226 uint16_t bitspersample = 16;
227
228 if (!single_image_is_loaded()) {
229 PRINT_NOT_FOR_SEQUENCE;
230 return 1;
231 }
232
233 if (strcasecmp(word[0], "savetif8") == 0)
234 bitspersample = 8;
235 else if (strcasecmp(word[0], "savetif32") == 0)
236 bitspersample = 32;
237 gchar *filename = g_strdup_printf("%s.tif", word[1]);
238 set_cursor_waiting(TRUE);
239 savetif(filename, &gfit, bitspersample);
240 set_cursor_waiting(FALSE);
241 g_free(filename);
242 return 0;
243 }
244 #endif
245
process_savepnm(int nb)246 int process_savepnm(int nb){
247 if (!single_image_is_loaded()) {
248 PRINT_NOT_FOR_SEQUENCE;
249 return 1;
250 }
251
252 saveNetPBM(word[1], &gfit);
253 return 0;
254 }
255
process_imoper(int nb)256 int process_imoper(int nb){
257 fits fit = { 0 };
258 if (!single_image_is_loaded()) return 1;
259 if (readfits(word[1], &fit, NULL, !com.pref.force_to_16bit)) return -1;
260
261 image_operator oper;
262 switch (word[0][1]) {
263 case 'a':
264 case 'A':
265 oper = OPER_ADD;
266 break;
267 case 's':
268 case 'S':
269 oper = OPER_SUB;
270 break;
271 case 'm':
272 case 'M':
273 oper = OPER_MUL;
274 break;
275 case 'd':
276 case 'D':
277 oper = OPER_DIV;
278 break;
279 default:
280 siril_log_color_message(_("Could not understand the requested operator\n"), "red");
281 clearfits(&fit);
282 return 1;
283 }
284 int retval = imoper(&gfit, &fit, oper, !com.pref.force_to_16bit);
285
286 clearfits(&fit);
287 adjust_cutoff_from_updated_gfit();
288 redraw(com.cvport, REMAP_ALL);
289 redraw_previews();
290 return retval;
291 }
292
process_addmax(int nb)293 int process_addmax(int nb){
294 fits fit = { 0 };
295
296 if (!single_image_is_loaded()) {
297 PRINT_NOT_FOR_SEQUENCE;
298 return 1;
299 }
300
301 if (readfits(word[1], &fit, NULL, gfit.type == DATA_FLOAT))
302 return -1;
303 if (addmax(&gfit, &fit) == 0) {
304 adjust_cutoff_from_updated_gfit();
305 redraw(com.cvport, REMAP_ALL);
306 redraw_previews();
307 }
308 clearfits(&fit);
309 return 0;
310 }
311
process_fdiv(int nb)312 int process_fdiv(int nb){
313 // combines an image division and a scalar multiplication.
314 fits fit = { 0 };
315 if (!single_image_is_loaded()) {
316 PRINT_NOT_FOR_SEQUENCE;
317 return 1;
318 }
319
320 float norm = g_ascii_strtod(word[2], NULL);
321 if (readfits(word[1], &fit, NULL, !com.pref.force_to_16bit)) return -1;
322 siril_fdiv(&gfit, &fit, norm, TRUE);
323
324 clearfits(&fit);
325 adjust_cutoff_from_updated_gfit();
326 redraw(com.cvport, REMAP_ALL);
327 redraw_previews();
328 return 0;
329 }
330
process_fmul(int nb)331 int process_fmul(int nb){
332 if (!single_image_is_loaded()) {
333 PRINT_NOT_FOR_SEQUENCE;
334 return 1;
335 }
336
337 float coeff = g_ascii_strtod(word[1], NULL);
338 if (coeff <= 0.f) {
339 siril_log_message(_("Multiplying by a coefficient less than or equal to 0 is not possible.\n"));
340 return 1;
341 }
342 soper(&gfit, coeff, OPER_MUL, TRUE);
343
344 adjust_cutoff_from_updated_gfit();
345 redraw(com.cvport, REMAP_ALL);
346 redraw_previews();
347 return 0;
348 }
349
process_entropy(int nb)350 int process_entropy(int nb){
351 rectangle area;
352 float e;
353
354 if (!single_image_is_loaded()) {
355 PRINT_NOT_FOR_SEQUENCE;
356 return 1;
357 }
358
359 if (com.selection.w > 0 && com.selection.h > 0) {
360 memcpy(&area, &com.selection, sizeof(rectangle));
361 e = entropy(&gfit, com.cvport, &area, NULL);
362 }
363 else {
364 e = entropy(&gfit, com.cvport, NULL, NULL);
365 }
366 siril_log_message(_("Entropy: %.3f\n"), e);
367 return 0;
368 }
369
process_gauss(int nb)370 int process_gauss(int nb){
371 if (!single_image_is_loaded()) {
372 PRINT_NOT_FOR_SEQUENCE;
373 return 1;
374 }
375
376 unsharp(&(gfit), g_ascii_strtod(word[1], NULL), 0.0, TRUE);
377 adjust_cutoff_from_updated_gfit();
378 redraw(com.cvport, REMAP_ALL);
379 redraw_previews();
380 return 0;
381 }
382
process_grey_flat(int nb)383 int process_grey_flat(int nb) {
384 if (!single_image_is_loaded()) {
385 PRINT_NOT_FOR_SEQUENCE;
386 return 1;
387 }
388
389 compute_grey_flat(&gfit);
390 adjust_cutoff_from_updated_gfit();
391 redraw(com.cvport, REMAP_ALL);
392 redraw_previews();
393
394 return 0;
395 }
396
process_rl(int nb)397 int process_rl(int nb) {
398 double sigma, corner;
399 int iter;
400
401 if (!single_image_is_loaded()) {
402 PRINT_NOT_FOR_SEQUENCE;
403 return 1;
404 }
405
406 if (!com.script)
407 control_window_switch_to_tab(OUTPUT_LOGS);
408 sigma = g_ascii_strtod(word[1], NULL);
409 corner = g_ascii_strtod(word[2], NULL);
410 iter = g_ascii_strtoull(word[3], NULL, 10);
411
412 if (sigma < 0.4 || sigma > 2.0) {
413 siril_log_message(_("Sigma must be between [0.4, 2.0]\n"));
414 return 1;
415 }
416
417 if (corner < -0.5 || corner > 0.5) {
418 siril_log_message(_("Corner radius boost must be between [0.5, 0.5]\n"));
419 return 1;
420 }
421
422 if (iter <= 0) {
423 siril_log_message(_("Number of iterations must be > 0.\n"));
424 return 1;
425 }
426
427 if (get_thread_run()) {
428 PRINT_ANOTHER_THREAD_RUNNING;
429 return 1;
430 }
431
432 struct deconv_data *args = malloc(sizeof(struct deconv_data));
433
434 args->fit = &gfit;
435 if (args->fit->type == DATA_USHORT) {
436 args->clip = (args->fit->maxi <= 0) ? USHRT_MAX_DOUBLE : args->fit->maxi;
437 } else {
438 args->clip = (args->fit->maxi <= 0) ? USHRT_MAX_DOUBLE : args->fit->maxi * USHRT_MAX_DOUBLE;
439 }
440 args->auto_contrast_threshold = TRUE;
441 args->sigma = sigma;
442 args->corner_radius = corner;
443 args->iterations = (size_t)iter;
444 args->auto_limit = TRUE;
445
446 set_cursor_waiting(TRUE);
447
448 start_in_new_thread(RTdeconv, args);
449
450 return 0;
451 }
452
process_unsharp(int nb)453 int process_unsharp(int nb) {
454 if (!single_image_is_loaded()) {
455 PRINT_NOT_FOR_SEQUENCE;
456 return 1;
457 }
458
459 unsharp(&(gfit), g_ascii_strtod(word[1], NULL), g_ascii_strtod(word[2], NULL), TRUE);
460 adjust_cutoff_from_updated_gfit();
461 redraw(com.cvport, REMAP_ALL);
462 redraw_previews();
463 return 0;
464 }
465
process_crop(int nb)466 int process_crop(int nb) {
467 if (!single_image_is_loaded()) {
468 PRINT_NOT_FOR_SEQUENCE;
469 return 1;
470 }
471 if (is_preview_active()) {
472 siril_log_message(_("It is impossible to crop the image when a filter with preview session is active. "
473 "Please consider to close the filter dialog first.\n"));
474 return 1;
475 }
476
477
478 rectangle area;
479 if ((!com.selection.h) || (!com.selection.w)) {
480 if (nb == 5) {
481 if (g_ascii_strtoull(word[1], NULL, 10) < 0 || g_ascii_strtoull(word[2], NULL, 10) < 0) {
482 siril_log_message(_("Crop: x and y must be positive values.\n"));
483 return 1;
484 }
485 if (g_ascii_strtoull(word[3], NULL, 10) <= 0 || g_ascii_strtoull(word[4], NULL, 10) <= 0) {
486 siril_log_message(_("Crop: width and height must be greater than 0.\n"));
487 return 1;
488 }
489 if (g_ascii_strtoull(word[1], NULL, 10) + g_ascii_strtoull(word[3], NULL, 10) > gfit.rx || g_ascii_strtoull(word[2], NULL, 10) + g_ascii_strtoull(word[4], NULL, 10) > gfit.ry) {
490 siril_log_message(_("Crop: width and height, respectively, must be less than %d and %d.\n"), gfit.rx,gfit.ry);
491 return 1;
492 }
493 area.x = g_ascii_strtoull(word[1], NULL, 10);
494 area.y = g_ascii_strtoull(word[2], NULL, 10);
495 area.w = g_ascii_strtoull(word[3], NULL, 10);
496 area.h = g_ascii_strtoull(word[4], NULL, 10);
497 }
498 else {
499 siril_log_message(_("Crop: select a region or provide x, y, width, height\n"));
500 return 1;
501 }
502 } else {
503 memcpy(&area, &com.selection, sizeof(rectangle));
504 }
505
506 crop(&gfit, &area);
507 delete_selected_area();
508 reset_display_offset();
509 adjust_cutoff_from_updated_gfit();
510 redraw(com.cvport, REMAP_ALL);
511 redraw_previews();
512
513 return 0;
514 }
515
process_cd(int nb)516 int process_cd(int nb) {
517 char filename[256];
518 int retval;
519
520 g_strlcpy(filename, word[1], 250);
521
522 expand_home_in_filename(filename, 256);
523 retval = siril_change_dir(filename, NULL);
524 if (!retval) {
525 writeinitfile();
526 if (!com.script) {
527 set_GUI_CWD();
528 }
529 }
530 return retval;
531 }
532
process_wrecons(int nb)533 int process_wrecons(int nb) {
534 int i;
535 float coef[7];
536 char *File_Name_Transform[3] = { "r_rawdata.wave", "g_rawdata.wave",
537 "b_rawdata.wave" }, *dir[3];
538 const char *tmpdir;
539 int nb_chan;
540
541 if (!single_image_is_loaded()) {
542 PRINT_NOT_FOR_SEQUENCE;
543 return 1;
544 }
545
546 nb_chan = gfit.naxes[2];
547
548 g_assert(nb_chan == 1 || nb_chan == 3);
549
550 tmpdir = g_get_tmp_dir();
551
552 for (i = 0; i < nb - 1; ++i) {
553 coef[i] = g_ascii_strtod(word[i + 1], NULL);
554 }
555
556 for (i = 0; i < nb_chan; i++) {
557 dir[i] = g_build_filename(tmpdir, File_Name_Transform[i], NULL);
558 if (gfit.type == DATA_USHORT) {
559 wavelet_reconstruct_file(dir[i], coef, gfit.pdata[i]);
560 } else if (gfit.type == DATA_FLOAT) {
561 wavelet_reconstruct_file_float(dir[i], coef, gfit.fpdata[i]);
562 }
563 else return 1;
564 g_free(dir[i]);
565 }
566
567 adjust_cutoff_from_updated_gfit();
568 redraw(com.cvport, REMAP_ALL);
569 redraw_previews();
570 return 0;
571 }
572
process_wavelet(int nb)573 int process_wavelet(int nb) {
574 char *File_Name_Transform[3] = { "r_rawdata.wave", "g_rawdata.wave",
575 "b_rawdata.wave" }, *dir[3];
576 const char* tmpdir;
577 int Type_Transform, Nbr_Plan, maxplan, mins, chan, nb_chan;
578
579 if (!single_image_is_loaded()) {
580 PRINT_NOT_FOR_SEQUENCE;
581 return 1;
582 }
583
584 tmpdir = g_get_tmp_dir();
585
586 Nbr_Plan = g_ascii_strtoull(word[1], NULL, 10);
587 Type_Transform = g_ascii_strtoull(word[2], NULL, 10);
588
589 nb_chan = gfit.naxes[2];
590 g_assert(nb_chan <= 3);
591
592 mins = min (gfit.rx, gfit.ry);
593 maxplan = log(mins) / log(2) - 2;
594
595 if ( Nbr_Plan > maxplan ){
596 siril_log_message(_("Wavelet: maximum number of plans for this image size is %d\n"),
597 maxplan);
598 return 1;
599 }
600
601 if(Type_Transform != TO_PAVE_LINEAR && Type_Transform != TO_PAVE_BSPLINE){
602 siril_log_message(_("Wavelet: type must be %d or %d\n"), TO_PAVE_LINEAR, TO_PAVE_BSPLINE);
603 return 1;
604 }
605
606 if (gfit.type == DATA_USHORT) {
607 float *Imag = f_vector_alloc(gfit.rx * gfit.ry);
608 if (!Imag) {
609 PRINT_ALLOC_ERR;
610 return 1;
611 }
612
613 for (chan = 0; chan < nb_chan; chan++) {
614 dir[chan] = g_build_filename(tmpdir, File_Name_Transform[chan], NULL);
615 wavelet_transform_file(Imag, gfit.ry, gfit.rx, dir[chan],
616 Type_Transform, Nbr_Plan, gfit.pdata[chan]);
617 g_free(dir[chan]);
618 }
619
620 free(Imag);
621 } else if (gfit.type == DATA_FLOAT) {
622 for (chan = 0; chan < nb_chan; chan++) {
623 dir[chan] = g_build_filename(tmpdir, File_Name_Transform[chan], NULL);
624 wavelet_transform_file_float(gfit.fpdata[chan], gfit.ry, gfit.rx, dir[chan],
625 Type_Transform, Nbr_Plan);
626 g_free(dir[chan]);
627 }
628 }
629 else return 1;
630 return 0;
631 }
632
process_log(int nb)633 int process_log(int nb){
634 if (!single_image_is_loaded()) {
635 PRINT_NOT_FOR_SEQUENCE;
636 return 1;
637 }
638
639 loglut(&gfit);
640 adjust_cutoff_from_updated_gfit();
641 redraw(com.cvport, REMAP_ALL);
642 redraw_previews();
643 return 0;
644 }
645
process_linear_match(int nb)646 int process_linear_match(int nb) {
647 if (!single_image_is_loaded()) {
648 PRINT_NOT_FOR_SEQUENCE;
649 return 1;
650 }
651 fits ref = { 0 };
652 double a[3] = { 0.0 }, b[3] = { 0.0 };
653 double low = g_ascii_strtod(word[2], NULL);
654 double high = g_ascii_strtod(word[3], NULL);
655 if (readfits(word[1], &ref, NULL, gfit.type == DATA_FLOAT))
656 return 1;
657 set_cursor_waiting(TRUE);
658 if (!find_linear_coeff(&gfit, &ref, low, high, a, b, NULL)) {
659 apply_linear_to_fits(&gfit, a, b);
660
661 adjust_cutoff_from_updated_gfit();
662 redraw(com.cvport, REMAP_ALL);
663 redraw_previews();
664 }
665 clearfits(&ref);
666 set_cursor_waiting(FALSE);
667 return 0;
668 }
669
process_asinh(int nb)670 int process_asinh(int nb) {
671 if (!single_image_is_loaded()) {
672 PRINT_NOT_FOR_SEQUENCE;
673 return 1;
674 }
675
676 double beta = g_ascii_strtod(word[1], NULL);
677
678 asinhlut(&gfit, beta, 0, FALSE);
679 adjust_cutoff_from_updated_gfit();
680 redraw(com.cvport, REMAP_ALL);
681 redraw_previews();
682 return 0;
683 }
684
process_clahe(int nb)685 int process_clahe(int nb) {
686 double clip_limit;
687 int size;
688
689 if (CV_MAJOR_VERSION < 3) {
690 siril_log_message(_("Your version of opencv is "
691 "too old for this feature. Please upgrade your system."));
692 return 1;
693 }
694
695 if (!single_image_is_loaded()) {
696 PRINT_NOT_FOR_SEQUENCE;
697 return 1;
698 }
699
700 if (!com.script)
701 control_window_switch_to_tab(OUTPUT_LOGS);
702 clip_limit = g_ascii_strtod(word[1], NULL);
703
704 if (clip_limit <= 0.0) {
705 siril_log_message(_("Clip limit must be > 0.\n"));
706 return 1;
707 }
708
709 size = g_ascii_strtoull(word[2], NULL, 10);
710
711 if (size <= 0.0) {
712 siril_log_message(_("Tile size must be > 0.\n"));
713 return 1;
714 }
715
716 if (get_thread_run()) {
717 PRINT_ANOTHER_THREAD_RUNNING;
718 return 1;
719 }
720
721 struct CLAHE_data *args = malloc(sizeof(struct CLAHE_data));
722
723 args->fit = &gfit;
724 args->clip = clip_limit;
725 args->tileSize = size;
726
727 set_cursor_waiting(TRUE);
728
729 start_in_new_thread(clahe, args);
730 adjust_cutoff_from_updated_gfit();
731 redraw(com.cvport, REMAP_ALL);
732 redraw_previews();
733
734 return 0;
735 }
736
737 #ifndef _WIN32
process_ls(int nb)738 int process_ls(int nb){
739 struct dirent **list;
740 gchar *path = NULL;
741
742 /* If a path is given in argument */
743 if (nb > 1) {
744 if (word[1][0] != '\0') {
745 /* Absolute path */
746 if (word[1][0] == G_DIR_SEPARATOR || word[1][0] == '~') {
747 char filename[256];
748
749 g_strlcpy(filename, word[1], 250);
750 filename[250] = '\0';
751 expand_home_in_filename(filename, 256);
752 path = g_build_filename(filename, NULL);
753 }
754 /* Relative path */
755 else {
756 path = g_build_filename(com.wd, word[1], NULL);
757 }
758 }
759 /* Should not happen */
760 else {
761 printf("Cannot list files in %s\n", word[1]);
762 return 1;
763 }
764 }
765 /* No paths are given in argument */
766 else {
767 if (!com.wd) {
768 siril_log_message(_("Cannot list files, set working directory first.\n"));
769 return 1;
770 }
771 path = g_strdup(com.wd);
772 }
773 if (path == NULL) {
774 siril_log_message(_("Siril cannot open the directory.\n"));
775 return 1;
776 }
777
778 int i, n;
779
780 n = scandir(path, &list, 0, alphasort);
781 if (n < 0) {
782 perror("scandir");
783 siril_log_message(_("Siril cannot open the directory.\n"));
784 g_free(path);
785 return 1;
786 }
787
788 /* List the entries */
789 for (i = 0; i < n; ++i) {
790 GStatBuf entrystat;
791 gchar *filename;
792 const char *ext;
793 if (list[i]->d_name[0] == '.')
794 continue; /* no hidden files */
795
796 filename = g_build_filename(path, list[i]->d_name, NULL);
797
798 if (g_lstat(filename, &entrystat)) {
799 perror("stat");
800 g_free(filename);
801 break;
802 }
803 g_free(filename);
804 if (S_ISLNK(entrystat.st_mode)) {
805 siril_log_color_message(_("Link: %s\n"), "bold", list[i]->d_name);
806 continue;
807 }
808 if (S_ISDIR(entrystat.st_mode)) {
809 siril_log_color_message(_("Directory: %s\n"), "green",
810 list[i]->d_name);
811 continue;
812 }
813 ext = get_filename_ext(list[i]->d_name);
814 if (!ext)
815 continue;
816 image_type type = get_type_for_extension(ext);
817 if (type != TYPEUNDEF) {
818 if (type == TYPEAVI || type == TYPESER)
819 siril_log_color_message(_("Sequence: %s\n"), "salmon",
820 list[i]->d_name);
821 else if (type == TYPEFITS)
822 siril_log_color_message(_("Image: %s\n"), "plum", list[i]->d_name);
823 else
824 siril_log_color_message(_("Image: %s\n"), "red", list[i]->d_name);
825 } else if (!strncmp(ext, "seq", 3))
826 siril_log_color_message(_("Sequence: %s\n"), "blue", list[i]->d_name);
827 }
828 for (i = 0; i < n; i++)
829 free(list[i]);
830 free(list);
831 siril_log_message(_("********* END OF THE LIST *********\n"));
832 g_free(path);
833
834 return 0;
835 }
836 #endif
837
process_merge(int nb)838 int process_merge(int nb) {
839 int retval = 0, nb_seq = nb-2;
840 if (!com.wd) {
841 siril_log_message(_("Merge: no working directory set.\n"));
842 set_cursor_waiting(FALSE);
843 return 1;
844 }
845 char *dest_dir = strdup(com.wd);
846 sequence **seqs = calloc(nb_seq, sizeof(sequence *));
847 GList *list = NULL;
848 for (int i = 0; i < nb_seq; i++) {
849 char *seqpath1 = strdup(word[i + 1]), *seqpath2 = strdup(word[i + 1]);
850 char *dir = g_path_get_dirname(seqpath1);
851 char *seqname = g_path_get_basename(seqpath2);
852 #ifdef _WIN32
853 gchar **token = g_strsplit(dir, "/", -1);
854 g_free(dir);
855 dir = g_strjoinv(G_DIR_SEPARATOR_S, token);
856 g_strfreev(token);
857 #endif
858 if (dir[0] != '\0' && !(dir[0] == '.' && dir[1] == '\0'))
859 siril_change_dir(dir, NULL);
860 if (!(seqs[i] = load_sequence(seqname, NULL))) {
861 siril_log_message(_("Could not open sequence `%s' for merging\n"), word[i + 1]);
862 retval = 1;
863 free(seqpath1); free(seqpath2); g_free(seqname); g_free(dir);
864 goto merge_clean_up;
865 }
866 g_free(seqname);
867 if (seq_check_basic_data(seqs[i], FALSE) < 0) {
868 siril_log_message(_("Sequence `%s' is invalid, could not merge\n"), word[i + 1]);
869 retval = 1;
870 free(seqpath1); free(seqpath2); g_free(dir);
871 goto merge_clean_up;
872 }
873
874 if (i != 0 && (seqs[i]->rx != seqs[0]->rx ||
875 seqs[i]->ry != seqs[0]->ry ||
876 seqs[i]->nb_layers != seqs[0]->nb_layers ||
877 seqs[i]->bitpix != seqs[0]->bitpix ||
878 seqs[i]->type != seqs[0]->type)) {
879 siril_log_message(_("All sequences must be the same format for merging. Sequence `%s' is different\n"), word[i + 1]);
880 retval = 1;
881 free(seqpath1); free(seqpath2); g_free(dir);
882 goto merge_clean_up;
883 }
884
885 if (seqs[i]->type == SEQ_REGULAR) {
886 // we need to build the list of files
887 char filename[256];
888 for (int image = 0; image < seqs[i]->number; image++) {
889 fit_sequence_get_image_filename(seqs[i], image, filename, TRUE);
890 list = g_list_append(list, g_build_filename(dir, filename, NULL));
891 }
892 }
893 free(seqpath1); free(seqpath2); g_free(dir);
894 siril_change_dir(dest_dir, NULL); // they're all relative to this one
895 }
896
897 char *outseq_name;
898 struct ser_struct out_ser;
899 struct _convert_data *args;
900 fitseq out_fitseq;
901 switch (seqs[0]->type) {
902 case SEQ_REGULAR:
903 // use the conversion, it makes symbolic links or copies as a fallback
904 args = malloc(sizeof(struct _convert_data));
905 args->start = 0;
906 args->total = 0; // init to get it from glist_to_array()
907 args->list = glist_to_array(list, &args->total);
908 args->destroot = format_basename(word[nb - 1], FALSE);
909 args->input_has_a_seq = FALSE;
910 args->input_has_a_film = FALSE;
911 args->debayer = FALSE;
912 args->multiple_output = FALSE;
913 args->output_type = SEQ_REGULAR;
914 args->make_link = TRUE;
915 gettimeofday(&(args->t_start), NULL);
916 start_in_new_thread(convert_thread_worker, args);
917 break;
918
919 case SEQ_SER:
920 if (g_str_has_suffix(word[nb - 1], ".ser"))
921 outseq_name = g_strdup(word[nb - 1]);
922 else outseq_name = g_strdup_printf("%s.ser", word[nb - 1]);
923 if (ser_create_file(outseq_name, &out_ser, TRUE, seqs[0]->ser_file)) {
924 siril_log_message(_("Failed to create the output SER file `%s'\n"), word[nb - 1]);
925 retval = 1;
926 goto merge_clean_up;
927 }
928 free(outseq_name);
929 seqwriter_set_max_active_blocks(2);
930 int written_frames = 0;
931 for (int i = 0; i < nb_seq; i++) {
932 for (unsigned int frame = 0; frame < seqs[i]->number; frame++) {
933 seqwriter_wait_for_memory();
934 fits *fit = calloc(1, sizeof(fits));
935 if (ser_read_frame(seqs[i]->ser_file, frame, fit, FALSE, com.pref.debayer.open_debayer)) {
936 siril_log_message(_("Failed to read frame %d from input sequence `%s'\n"), frame, word[i + 1]);
937 retval = 1;
938 seqwriter_release_memory();
939 ser_close_and_delete_file(&out_ser);
940 goto merge_clean_up;
941 }
942
943 if (ser_write_frame_from_fit(&out_ser, fit, written_frames)) {
944 siril_log_message(_("Failed to write frame %d in merged sequence\n"), written_frames);
945 retval = 1;
946 seqwriter_release_memory();
947 ser_close_and_delete_file(&out_ser);
948 goto merge_clean_up;
949 }
950 written_frames++;
951 }
952 }
953 if (ser_write_and_close(&out_ser)) {
954 siril_log_message(_("Error while finalizing the merged sequence\n"));
955 retval = 1;
956 }
957 break;
958
959 case SEQ_FITSEQ:
960 if (g_str_has_suffix(word[nb - 1], com.pref.ext))
961 outseq_name = g_strdup(word[nb - 1]);
962 else outseq_name = g_strdup_printf("%s%s", word[nb - 1], com.pref.ext);
963 if (fitseq_create_file(outseq_name, &out_fitseq, -1)) {
964 siril_log_message(_("Failed to create the output SER file `%s'\n"), word[nb - 1]);
965 retval = 1;
966 goto merge_clean_up;
967 }
968 free(outseq_name);
969 seqwriter_set_max_active_blocks(2);
970 written_frames = 0;
971 for (int i = 0; i < nb_seq; i++) {
972 for (unsigned int frame = 0; frame < seqs[i]->number; frame++) {
973 seqwriter_wait_for_memory();
974 fits *fit = calloc(1, sizeof(fits));
975 if (fitseq_read_frame(seqs[i]->fitseq_file, frame, fit, FALSE, -1)) {
976 siril_log_message(_("Failed to read frame %d from input sequence `%s'\n"), frame, word[i + 1]);
977 retval = 1;
978 seqwriter_release_memory();
979 fitseq_close_and_delete_file(&out_fitseq);
980 goto merge_clean_up;
981 }
982
983 if (fitseq_write_image(&out_fitseq, fit, written_frames)) {
984 siril_log_message(_("Failed to write frame %d in merged sequence\n"), written_frames);
985 retval = 1;
986 seqwriter_release_memory();
987 fitseq_close_and_delete_file(&out_fitseq);
988 goto merge_clean_up;
989 }
990 written_frames++;
991 }
992 }
993 if (fitseq_close_file(&out_fitseq)) {
994 siril_log_message(_("Error while finalizing the merged sequence\n"));
995 retval = 1;
996 }
997 break;
998 default:
999 siril_log_message(_("This type of sequence cannot be created by Siril, aborting the merge\n"));
1000 retval = 1;
1001 }
1002
1003 merge_clean_up:
1004 for (int i = 0; i < nb_seq; i++) {
1005 if (seqs[i])
1006 free_sequence(seqs[i], TRUE);
1007 }
1008 free(seqs);
1009 siril_change_dir(dest_dir, NULL);
1010 free(dest_dir);
1011 return retval;
1012 }
1013
process_mirrorx(int nb)1014 int process_mirrorx(int nb){
1015 if (!single_image_is_loaded()) {
1016 PRINT_NOT_FOR_SEQUENCE;
1017 return 1;
1018 }
1019
1020 mirrorx(&gfit, TRUE);
1021 redraw(com.cvport, REMAP_ALL);
1022 redraw_previews();
1023 return 0;
1024 }
1025
process_mirrory(int nb)1026 int process_mirrory(int nb){
1027 if (!single_image_is_loaded()) {
1028 PRINT_NOT_FOR_SEQUENCE;
1029 return 1;
1030 }
1031
1032 mirrory(&gfit, TRUE);
1033 redraw(com.cvport, REMAP_ALL);
1034 redraw_previews();
1035 return 0;
1036 }
1037
process_mtf(int nb)1038 int process_mtf(int nb) {
1039 float lo = g_ascii_strtod(word[1], NULL);
1040 float mid = g_ascii_strtod(word[2], NULL);
1041 float hi = g_ascii_strtod(word[3], NULL);
1042
1043 mtf_with_parameters(&gfit, lo, mid, hi);
1044
1045 adjust_cutoff_from_updated_gfit();
1046 redraw(com.cvport, REMAP_ALL);
1047 redraw_previews();
1048 return 0;
1049 }
1050
process_resample(int nb)1051 int process_resample(int nb) {
1052 if (!single_image_is_loaded()) {
1053 PRINT_NOT_FOR_SEQUENCE;
1054 return 1;
1055 }
1056
1057 double factor = g_ascii_strtod(word[1], NULL);
1058 if (factor > 5.0) {
1059 siril_log_message(_("The scaling factor must be less than 5.0\n"));
1060 return 1;
1061 }
1062 int toX = round_to_int(factor * gfit.rx);
1063 int toY = round_to_int(factor * gfit.ry);
1064
1065 set_cursor_waiting(TRUE);
1066 verbose_resize_gaussian(&gfit, toX, toY, OPENCV_AREA);
1067
1068 redraw(com.cvport, REMAP_ALL);
1069 redraw_previews();
1070 set_cursor_waiting(FALSE);
1071 return 0;
1072 }
1073
process_rgradient(int nb)1074 int process_rgradient(int nb) {
1075 if (get_thread_run()) {
1076 PRINT_ANOTHER_THREAD_RUNNING;
1077 return 1;
1078 }
1079
1080 if (!single_image_is_loaded()) {
1081 PRINT_NOT_FOR_SEQUENCE;
1082 return 1;
1083 }
1084
1085 struct rgradient_filter_data *args = malloc(sizeof(struct rgradient_filter_data));
1086 args->xc = g_ascii_strtod(word[1], NULL);
1087 args->yc = g_ascii_strtod(word[2], NULL);
1088 args->dR = g_ascii_strtod(word[3], NULL);
1089 args->da = g_ascii_strtod(word[4], NULL);
1090 args->fit = &gfit;
1091
1092 if ((args->xc >= args->fit->rx) || (args->yc >= args->fit->ry)) {
1093 siril_log_message(_("The coordinates cannot be greater than the size of the image. "
1094 "Please change their values and retry.\n"));
1095 } else {
1096
1097 set_cursor_waiting(TRUE);
1098
1099 start_in_new_thread(rgradient_filter, args);
1100 }
1101 return 0;
1102 }
1103
process_rotate(int nb)1104 int process_rotate(int nb) {
1105 double degree;
1106 int crop = 1;
1107
1108 if (!single_image_is_loaded()) {
1109 PRINT_NOT_FOR_SEQUENCE;
1110 return 1;
1111 }
1112
1113 set_cursor_waiting(TRUE);
1114
1115 degree = g_ascii_strtod(word[1], NULL);
1116
1117 /* check for options */
1118 if (word[2] && (!strcmp(word[2], "-nocrop"))) {
1119 crop = 0;
1120 }
1121
1122 verbose_rotate_image(&gfit, degree, OPENCV_AREA, crop);
1123 redraw(com.cvport, REMAP_ALL);
1124 redraw_previews();
1125 set_cursor_waiting(FALSE);
1126 return 0;
1127 }
1128
process_rotatepi(int nb)1129 int process_rotatepi(int nb){
1130 if (!single_image_is_loaded()) {
1131 PRINT_NOT_FOR_SEQUENCE;
1132 return 1;
1133 }
1134
1135 verbose_rotate_image(&gfit, 180.0, OPENCV_AREA, 1);
1136
1137 redraw(com.cvport, REMAP_ALL);
1138 redraw_previews();
1139 return 0;
1140 }
1141
process_set_mag(int nb)1142 int process_set_mag(int nb) {
1143 if (!single_image_is_loaded()) {
1144 PRINT_NOT_FOR_SEQUENCE;
1145 return 1;
1146 }
1147
1148 int layer = match_drawing_area_widget(com.vport[com.cvport], FALSE);
1149 double mag = g_ascii_strtod(word[1], NULL);
1150
1151 if (layer != -1) {
1152
1153 if (com.selection.w > 300 || com.selection.h > 300){
1154 siril_log_message(_("Current selection is too large. To determine the PSF, please make a selection around a single star.\n"));
1155 return 1;
1156 }
1157 if (com.selection.w <= 0 || com.selection.h <= 0){
1158 siril_log_message(_("Select an area first\n"));
1159 return 1;
1160 }
1161 fitted_PSF *result = psf_get_minimisation(&gfit, layer, &com.selection, TRUE, TRUE, TRUE);
1162 if (result) {
1163 com.magOffset = mag - result->mag;
1164 siril_log_message(_("Relative magnitude: %.3lf, "
1165 "True reduced magnitude: %.3lf, "
1166 "Offset: %.3lf\n"), result->mag, mag, com.magOffset);
1167 free(result);
1168 }
1169 }
1170 return 0;
1171 }
1172
process_set_ref(int nb)1173 int process_set_ref(int nb) {
1174 sequence *seq = load_sequence(word[1], NULL);
1175 if (!seq)
1176 return 1;
1177
1178 int n = g_ascii_strtoull(word[2], NULL, 10) - 1;
1179 if (n < 0 || n > seq->number) {
1180 siril_log_message(_("The reference image must be set between 1 and %d\n"), seq->number);
1181 return 1;
1182 }
1183
1184 seq->reference_image = n;
1185 // a reference image should not be excluded to avoid confusion
1186 if (!seq->imgparam[seq->current].incl) {
1187 seq->imgparam[seq->current].incl = TRUE;
1188 }
1189
1190 writeseqfile(seq);
1191
1192 return 0;
1193 }
1194
process_unset_mag(int nb)1195 int process_unset_mag(int nb) {
1196 com.magOffset = 0.0;
1197 return 0;
1198 }
1199
process_set_mag_seq(int nb)1200 int process_set_mag_seq(int nb) {
1201 if (!sequence_is_loaded()) {
1202 PRINT_NOT_FOR_SINGLE;
1203 return 1;
1204 }
1205 double mag = g_ascii_strtod(word[1], NULL);
1206 int i;
1207 for (i = 0; i < MAX_SEQPSF && com.seq.photometry[i]; i++);
1208 com.seq.reference_star = i - 1;
1209 if (i == 0) {
1210 siril_log_message(_("Run a PSF for the sequence first (see seqpsf)\n"));
1211 return 1;
1212 }
1213 com.seq.reference_mag = mag;
1214 siril_log_message(_("Reference magnitude has been set for star %d to %f and will be computed for each image\n"), i - 1, mag);
1215 drawPlot();
1216 return 0;
1217 }
1218
process_set_ext(int nb)1219 int process_set_ext(int nb) {
1220 if (word[1]) {
1221 GString *str = NULL;
1222
1223 if ((g_ascii_strncasecmp(word[1], "fit", 3))
1224 && (g_ascii_strncasecmp(word[1], "fts", 3))
1225 && (g_ascii_strncasecmp(word[1], "fits", 4))) {
1226 siril_log_message(_("FITS extension unknown: %s\n"), word[1]);
1227 }
1228
1229 free(com.pref.ext);
1230 str = g_string_new(".");
1231 str = g_string_append(str, word[1]);
1232 str = g_string_ascii_down(str);
1233 com.pref.ext = g_string_free(str, FALSE);
1234 writeinitfile();
1235 }
1236
1237 return 0;
1238 }
1239
process_set_findstar(int nb)1240 int process_set_findstar(int nb) {
1241 double sigma = g_ascii_strtod(word[1], NULL);
1242 double roundness = g_ascii_strtod(word[2], NULL);
1243 int retval = 0;
1244
1245 if (sigma >= 0.05 && roundness >= 0 && roundness <= 0.9) {
1246 com.starfinder_conf.sigma = sigma;
1247 com.starfinder_conf.roundness = roundness;
1248 } else {
1249 siril_log_message(_("Wrong parameter values. Sigma must be >= 0.05 and roundness between 0 and 0.9.\n"));
1250 retval = 1;
1251 }
1252 return retval;
1253 }
1254
process_unset_mag_seq(int nb)1255 int process_unset_mag_seq(int nb) {
1256 if (!sequence_is_loaded()) {
1257 PRINT_NOT_FOR_SINGLE;
1258 return 1;
1259 }
1260 com.seq.reference_star = -1;
1261 com.seq.reference_mag = -1001.0;
1262 siril_log_message(_("Reference magnitude unset for sequence\n"));
1263 drawPlot();
1264 return 0;
1265 }
1266
process_psf(int nb)1267 int process_psf(int nb){
1268 if (!single_image_is_loaded()) {
1269 PRINT_NOT_FOR_SEQUENCE;
1270 return 1;
1271 }
1272
1273 int layer = match_drawing_area_widget(com.vport[com.cvport], FALSE);
1274 if (layer != -1) {
1275
1276 if (com.selection.w > 300 || com.selection.h > 300){
1277 siril_log_message(_("Current selection is too large. To determine the PSF, please make a selection around a single star.\n"));
1278 return 1;
1279 }
1280 if (com.selection.w <= 0 || com.selection.h <= 0){
1281 siril_log_message(_("Select an area first\n"));
1282 return 1;
1283 }
1284 fitted_PSF *result = psf_get_minimisation(&gfit, layer, &com.selection, TRUE, TRUE, TRUE);
1285 if (result) {
1286 psf_display_result(result, &com.selection);
1287 free(result);
1288 }
1289 }
1290 return 0;
1291 }
1292
process_seq_psf(int nb)1293 int process_seq_psf(int nb) {
1294 if (get_thread_run()) {
1295 PRINT_ANOTHER_THREAD_RUNNING;
1296 return 1;
1297 }
1298 if (com.selection.w > 300 || com.selection.h > 300){
1299 siril_log_message(_("Current selection is too large. To determine the PSF, please make a selection around a single star.\n"));
1300 return 1;
1301 }
1302 if (com.selection.w <= 0 || com.selection.h <= 0){
1303 siril_log_message(_("Select an area first\n"));
1304 return 1;
1305 }
1306
1307 int layer = match_drawing_area_widget(com.vport[com.cvport], FALSE);
1308 if (sequence_is_loaded() && layer != -1) {
1309 framing_mode framing = REGISTERED_FRAME;
1310 if (framing == REGISTERED_FRAME && !com.seq.regparam[layer])
1311 framing = ORIGINAL_FRAME;
1312 if (framing == ORIGINAL_FRAME) {
1313 GtkToggleButton *follow = GTK_TOGGLE_BUTTON(lookup_widget("followStarCheckButton"));
1314 if (gtk_toggle_button_get_active(follow))
1315 framing = FOLLOW_STAR_FRAME;
1316 }
1317 siril_log_message(_("Running the PSF on the loaded sequence, layer %d\n"), layer);
1318 seqpsf(&com.seq, layer, FALSE, FALSE, framing, TRUE);
1319 return 0;
1320 }
1321 else {
1322 PRINT_NOT_FOR_SINGLE;
1323 return 1;
1324 }
1325 }
1326
process_seq_crop(int nb)1327 int process_seq_crop(int nb) {
1328 if (!sequence_is_loaded()) {
1329 PRINT_NOT_FOR_SINGLE;
1330 return 1;
1331 }
1332 if (get_thread_run()) {
1333 PRINT_ANOTHER_THREAD_RUNNING;
1334 return 1;
1335 }
1336
1337 rectangle area;
1338
1339 int startoptargs = 5;
1340
1341 if ((!com.selection.h) || (!com.selection.w)) {
1342 if (nb >= startoptargs) {
1343 if (g_ascii_strtoull(word[1], NULL, 10) < 0 || g_ascii_strtoull(word[2], NULL, 10) < 0) {
1344 siril_log_message(_("Crop: x and y must be positive values.\n"));
1345 return 1;
1346 }
1347 if (g_ascii_strtoull(word[3], NULL, 10) <= 0 || g_ascii_strtoull(word[4], NULL, 10) <= 0) {
1348 siril_log_message(_("Crop: width and height must be greater than 0.\n"));
1349 return 1;
1350 }
1351 area.x = g_ascii_strtoull(word[1], NULL, 10);
1352 area.y = g_ascii_strtoull(word[2], NULL, 10);
1353 area.w = g_ascii_strtoull(word[3], NULL, 10);
1354 area.h = g_ascii_strtoull(word[4], NULL, 10);
1355 } else {
1356 siril_log_message(_("Crop: select a region or provide x, y, width, height\n"));
1357 return 1;
1358 }
1359 } else {
1360 memcpy(&area, &com.selection, sizeof(rectangle));
1361 }
1362
1363
1364 if (g_ascii_strtoull(word[3], NULL, 10) > com.seq.rx || g_ascii_strtoull(word[4], NULL, 10) > com.seq.ry) {
1365 siril_log_message(_("Crop: width and height, respectively, must be less than %d and %d.\n"),
1366 com.seq.rx, com.seq.ry);
1367 return 1;
1368 }
1369
1370 struct crop_sequence_data *args = malloc(sizeof(struct crop_sequence_data));
1371
1372 args->seq = &com.seq;
1373 args->area = area;
1374 args->prefix = "cropped_";
1375
1376 if (nb > startoptargs) {
1377 for (int i = startoptargs; i < nb; i++) {
1378 if (word[i]) {
1379 if (g_str_has_prefix(word[i], "-prefix=")) {
1380 char *current = word[i], *value;
1381 value = current + 8;
1382 if (value[0] == '\0') {
1383 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
1384 return 1;
1385 }
1386 args->prefix = strdup(value);
1387 }
1388 }
1389 }
1390 }
1391
1392 set_cursor_waiting(TRUE);
1393
1394 crop_sequence(args);
1395 return 0;
1396 }
1397
process_bg(int nb)1398 int process_bg(int nb){
1399 if (!(single_image_is_loaded() || sequence_is_loaded())) return 1;
1400 WORD us_bg;
1401
1402 for (int layer = 0; layer < gfit.naxes[2]; layer++) {
1403 double bg = background(&gfit, layer, &com.selection, TRUE);
1404 if (gfit.type == DATA_USHORT) {
1405 us_bg = round_to_WORD(bg);
1406 bg = bg / get_normalized_value(&gfit);
1407 } else if (gfit.type == DATA_FLOAT) {
1408 us_bg = float_to_ushort_range(bg);
1409 } else return 1;
1410 siril_log_message(_("Background value (channel: #%d): %d (%.3e)\n"), layer, us_bg, bg);
1411 }
1412 return 0;
1413 }
1414
process_bgnoise(int nb)1415 int process_bgnoise(int nb){
1416 if (get_thread_run()) {
1417 PRINT_ANOTHER_THREAD_RUNNING;
1418 return 1;
1419 }
1420
1421 if (!(single_image_is_loaded() || sequence_is_loaded())) return 1;
1422
1423 struct noise_data *args = malloc(sizeof(struct noise_data));
1424
1425 if (!com.script) {
1426 control_window_switch_to_tab(OUTPUT_LOGS);
1427 }
1428
1429 args->fit = &gfit;
1430 args->verbose = TRUE;
1431 args->use_idle = TRUE;
1432 memset(args->bgnoise, 0.0, sizeof(double[3]));
1433 set_cursor_waiting(TRUE);
1434
1435 start_in_new_thread(noise, args);
1436 return 0;
1437 }
1438
process_histo(int nb)1439 int process_histo(int nb){
1440 GError *error = NULL;
1441 int nlayer = g_ascii_strtoull(word[1], NULL, 10);
1442 const gchar* clayer;
1443
1444 if (!single_image_is_loaded()) {
1445 PRINT_NOT_FOR_SEQUENCE;
1446 return 1;
1447 }
1448
1449 if (nlayer>3 || nlayer <0)
1450 return 1;
1451 gsl_histogram *histo = computeHisto(&gfit, nlayer);
1452 if (!isrgb(&gfit))
1453 clayer = "bw"; //if B&W
1454 else
1455 clayer = vport_number_to_name(nlayer);
1456 gchar *filename = g_strdup_printf("histo_%s.dat", clayer);
1457
1458 GFile *file = g_file_new_for_path(filename);
1459 g_free(filename);
1460
1461 GOutputStream *output_stream = (GOutputStream*) g_file_replace(file, NULL, FALSE,
1462 G_FILE_CREATE_NONE, NULL, &error);
1463
1464 if (output_stream == NULL) {
1465 if (error != NULL) {
1466 g_warning("%s\n", error->message);
1467 g_clear_error(&error);
1468 fprintf(stderr, "Cannot save histo\n");
1469 }
1470 g_object_unref(file);
1471 return 1;
1472 }
1473 for (size_t i = 0; i < USHRT_MAX + 1; i++) {
1474 gchar *buffer = g_strdup_printf("%zu %d\n", i, (int) gsl_histogram_get (histo, i));
1475
1476 if (!g_output_stream_write_all(output_stream, buffer, strlen(buffer), NULL, NULL, &error)) {
1477 g_warning("%s\n", error->message);
1478 g_free(buffer);
1479 g_clear_error(&error);
1480 g_object_unref(output_stream);
1481 g_object_unref(file);
1482 return 1;
1483 }
1484 g_free(buffer);
1485 }
1486
1487 siril_log_message(_("The file %s has been created for the %s layer.\n"), g_file_peek_path(file), clayer);
1488
1489 g_object_unref(output_stream);
1490 g_object_unref(file);
1491 gsl_histogram_free(histo);
1492 return 0;
1493 }
1494
process_thresh(int nb)1495 int process_thresh(int nb){
1496 int lo, hi;
1497
1498 if (!single_image_is_loaded()) {
1499 PRINT_NOT_FOR_SEQUENCE;
1500 return 1;
1501 }
1502 int maxlevel = (gfit.orig_bitpix == BYTE_IMG) ? UCHAR_MAX : USHRT_MAX;
1503 lo = g_ascii_strtoull(word[1], NULL, 10);
1504 if (lo < 0 || lo > maxlevel) {
1505 siril_log_message(_("replacement value is out of range (0 - %d)\n"), maxlevel);
1506 return 1;
1507 }
1508 hi = g_ascii_strtoull(word[2], NULL, 10);
1509 if (hi < 0 || hi > maxlevel) {
1510 siril_log_message(_("replacement value is out of range (0 - %d)\n"), maxlevel);
1511 return 1;
1512 }
1513 if (lo >= hi) {
1514 siril_log_message(_("lo must be strictly smaller than hi\n"));
1515 return 1;
1516 }
1517 threshlo(&gfit, lo);
1518 threshhi(&gfit, hi);
1519 adjust_cutoff_from_updated_gfit();
1520 redraw(com.cvport, REMAP_ALL);
1521 redraw_previews();
1522 return 0;
1523 }
1524
process_threshlo(int nb)1525 int process_threshlo(int nb){
1526 int lo;
1527
1528 if (!single_image_is_loaded()) {
1529 PRINT_NOT_FOR_SEQUENCE;
1530 return 1;
1531 }
1532 int maxlevel = (gfit.orig_bitpix == BYTE_IMG) ? UCHAR_MAX : USHRT_MAX;
1533 lo = g_ascii_strtoull(word[1], NULL, 10);
1534 if (lo < 0 || lo > maxlevel) {
1535 siril_log_message(_("replacement value is out of range (0 - %d)\n"), maxlevel);
1536 return 1;
1537 }
1538 threshlo(&gfit, lo);
1539 adjust_cutoff_from_updated_gfit();
1540 redraw(com.cvport, REMAP_ALL);
1541 redraw_previews();
1542 return 0;
1543 }
1544
process_threshhi(int nb)1545 int process_threshhi(int nb){
1546 int hi;
1547
1548 if (!single_image_is_loaded()) {
1549 PRINT_NOT_FOR_SEQUENCE;
1550 return 1;
1551 }
1552 int maxlevel = (gfit.orig_bitpix == BYTE_IMG) ? UCHAR_MAX : USHRT_MAX;
1553 hi = g_ascii_strtoull(word[1], NULL, 10);
1554 if (hi < 0 || hi > maxlevel) {
1555 siril_log_message(_("replacement value is out of range (0 - %d)\n"), maxlevel);
1556 return 1;
1557 }
1558 threshhi(&gfit, hi);
1559 adjust_cutoff_from_updated_gfit();
1560 redraw(com.cvport, REMAP_ALL);
1561 redraw_previews();
1562 return 0;
1563 }
1564
process_neg(int nb)1565 int process_neg(int nb) {
1566 set_cursor_waiting(TRUE);
1567 pos_to_neg(&gfit);
1568 update_gfit_histogram_if_needed();
1569 invalidate_stats_from_fit(&gfit);
1570 redraw(com.cvport, REMAP_ALL);
1571 redraw_previews();
1572 set_cursor_waiting(FALSE);
1573 return 0;
1574 }
1575
process_nozero(int nb)1576 int process_nozero(int nb){
1577 int level;
1578
1579 if (!single_image_is_loaded()) {
1580 PRINT_NOT_FOR_SEQUENCE;
1581 return 1;
1582 }
1583
1584 level = g_ascii_strtoull(word[1], NULL, 10);
1585 int maxlevel = (gfit.orig_bitpix == BYTE_IMG) ? UCHAR_MAX : USHRT_MAX;
1586 if (level < 0 || level > maxlevel) {
1587 siril_log_message(_("replacement value is out of range (0 - %d)\n"), maxlevel);
1588 return 1;
1589 }
1590 nozero(&gfit, (WORD)level);
1591 adjust_cutoff_from_updated_gfit();
1592 redraw(com.cvport, REMAP_ALL);
1593 redraw_previews();
1594 return 0;
1595 }
1596
process_ddp(int nb)1597 int process_ddp(int nb){
1598 // combines an image division and a scalar multiplication.
1599 float coeff, sigma;
1600 unsigned level;
1601
1602 if (!single_image_is_loaded()) {
1603 PRINT_NOT_FOR_SEQUENCE;
1604 return 1;
1605 }
1606
1607 level = g_ascii_strtoull(word[1], NULL, 10);
1608 coeff = g_ascii_strtod(word[2], NULL);
1609 sigma = g_ascii_strtod(word[3], NULL);
1610 ddp(&gfit, level, coeff, sigma);
1611 adjust_cutoff_from_updated_gfit();
1612 redraw(com.cvport, REMAP_ALL);
1613 redraw_previews();
1614 return 0;
1615 }
1616
process_new(int nb)1617 int process_new(int nb){
1618 int width, height, layers;
1619
1620 width = g_ascii_strtod(word[1], NULL);
1621 height = g_ascii_strtod(word[2], NULL);
1622 layers = g_ascii_strtoull(word[3], NULL, 10);
1623 if (layers != 1 && layers != 3) {
1624 siril_log_message(_("Number of layers MUST be 1 or 3\n"));
1625 return 1;
1626 }
1627 if (!height || !width) return 1;
1628
1629 close_single_image();
1630 close_sequence(FALSE);
1631
1632 fits *fit = &gfit;
1633 if (new_fit_image(&fit, width, height, layers, DATA_FLOAT))
1634 return 1;
1635 memset(gfit.fdata, 0, width * height * layers * sizeof(float));
1636
1637 com.seq.current = UNRELATED_IMAGE;
1638 com.uniq = calloc(1, sizeof(single));
1639 com.uniq->filename = strdup(_("new empty image"));
1640 com.uniq->fileexist = FALSE;
1641 com.uniq->nb_layers = gfit.naxes[2];
1642 com.uniq->layers = calloc(com.uniq->nb_layers, sizeof(layer_info));
1643 com.uniq->fit = &gfit;
1644
1645 open_single_image_from_gfit();
1646 return 0;
1647 }
1648
process_visu(int nb)1649 int process_visu(int nb){
1650 int low, high;
1651
1652 if (!single_image_is_loaded()) {
1653 PRINT_NOT_FOR_SEQUENCE;
1654 return 1;
1655 }
1656
1657 low = g_ascii_strtoull(word[1], NULL, 10);
1658 high = g_ascii_strtoull(word[2], NULL, 10);
1659 if ((high > USHRT_MAX) || (low < 0)) {
1660 siril_log_message(_("Values must be positive and less than %d.\n"), USHRT_MAX);
1661 return 1;
1662 }
1663 visu(&gfit, low, high);
1664 return 0;
1665 }
1666
process_fill2(int nb)1667 int process_fill2(int nb){
1668 int level = g_ascii_strtoull(word[1], NULL, 10);
1669 rectangle area;
1670
1671 if (!single_image_is_loaded()) {
1672 PRINT_NOT_FOR_SEQUENCE;
1673 return 1;
1674 }
1675
1676 if ((!com.selection.h) || (!com.selection.w)) {
1677 if (nb == 6) {
1678 area.x = g_ascii_strtoull(word[2], NULL, 10);
1679 area.y = g_ascii_strtoull(word[3], NULL, 10);
1680 area.w = g_ascii_strtoull(word[4], NULL, 10);
1681 area.h = g_ascii_strtoull(word[5], NULL, 10);
1682 if ((area.w + area.x > gfit.rx) || (area.h + area.y > gfit.ry)) {
1683 siril_log_message(_("Wrong parameters.\n"));
1684 return 1;
1685 }
1686 }
1687 else {
1688 siril_log_message(_("Fill2: select a region or provide x, y, width, height\n"));
1689 return 1;
1690 }
1691 } else {
1692 memcpy(&area, &com.selection, sizeof(rectangle));
1693 }
1694 int retval = fill(&gfit, level, &area);
1695 if (retval) {
1696 siril_log_message(_("Wrong parameters.\n"));
1697 return 1;
1698 }
1699 area.x = gfit.rx - area.x - area.w;
1700 area.y = gfit.ry - area.y - area.h;
1701 fill(&gfit, level, &area);
1702 redraw(com.cvport, REMAP_ALL);
1703 return 0;
1704 }
1705
process_findstar(int nb)1706 int process_findstar(int nb){
1707 int nbstars = 0;
1708
1709 int layer = com.cvport == RGB_VPORT ? GLAYER : com.cvport;
1710
1711 delete_selected_area();
1712 com.stars = peaker(&gfit, layer, &com.starfinder_conf, &nbstars, NULL, TRUE);
1713 siril_log_message(_("Found %d stars in image, channel #%d\n"), nbstars, layer);
1714 if (com.stars)
1715 refresh_star_list(com.stars);
1716 return 0;
1717 }
1718
process_findhot(int nb)1719 int process_findhot(int nb){
1720 GError *error = NULL;
1721 long icold, ihot;
1722 gchar type;
1723
1724 if (!single_image_is_loaded()) {
1725 PRINT_NOT_FOR_SEQUENCE;
1726 return 1;
1727 }
1728
1729 if (gfit.naxes[2] != 1) {
1730 siril_log_message(_("find_hot must be applied on an one-channel master-dark frame"));
1731 return 1;
1732 }
1733 double sig[2];
1734 sig[0] = g_ascii_strtod(word[2], NULL);
1735 sig[1] = g_ascii_strtod(word[3], NULL);
1736
1737 deviant_pixel *dev = find_deviant_pixels(&gfit, sig, &icold, &ihot, FALSE);
1738 siril_log_message(_("%ld cold and %ld hot pixels\n"), icold, ihot);
1739
1740 gchar *filename = g_strdup_printf("%s.lst", word[1]);
1741 GFile *file = g_file_new_for_path(filename);
1742 g_free(filename);
1743
1744 GOutputStream *output_stream = (GOutputStream*) g_file_replace(file, NULL, FALSE,
1745 G_FILE_CREATE_NONE, NULL, &error);
1746
1747 if (output_stream == NULL) {
1748 if (error != NULL) {
1749 g_warning("%s\n", error->message);
1750 g_clear_error(&error);
1751 fprintf(stderr, "Cannot open file: %s\n", filename);
1752 }
1753 g_object_unref(file);
1754 return 1;
1755 }
1756
1757 for (int i = 0; i < icold + ihot; i++) {
1758 int y = gfit.ry - (int) dev[i].p.y - 1; /* FITS is stored bottom to top */
1759 if (dev[i].type == HOT_PIXEL)
1760 type = 'H';
1761 else
1762 type = 'C';
1763 gchar *buffer = g_strdup_printf("P %d %d %c\n", (int) dev[i].p.x, y, type);
1764 if (!g_output_stream_write_all(output_stream, buffer, strlen(buffer), NULL, NULL, &error)) {
1765 g_warning("%s\n", error->message);
1766 g_free(buffer);
1767 g_clear_error(&error);
1768 g_object_unref(output_stream);
1769 g_object_unref(file);
1770 return 1;
1771 }
1772 g_free(buffer);
1773 }
1774
1775 free(dev);
1776 g_object_unref(output_stream);
1777 g_object_unref(file);
1778
1779 return 0;
1780 }
1781
process_fix_xtrans(int nb)1782 int process_fix_xtrans(int nb) {
1783 if (!(single_image_is_loaded() || sequence_is_loaded())) return 1;
1784
1785 fix_xtrans_ac(&gfit);
1786 adjust_cutoff_from_updated_gfit();
1787 redraw(com.cvport, REMAP_ALL);
1788 return 0;
1789 }
1790
process_cosme(int nb)1791 int process_cosme(int nb) {
1792 GError *error = NULL;
1793 deviant_pixel dev;
1794 gchar *filename;
1795 double dirty;
1796 int is_cfa, i = 0, retval = 0;
1797 int nb_tokens;
1798 gchar *line;
1799 char type;
1800
1801 if (!single_image_is_loaded()) {
1802 PRINT_NOT_FOR_SEQUENCE;
1803 return 1;
1804 }
1805
1806 if (!g_str_has_suffix(word[1], ".lst")) {
1807 filename = g_strdup_printf("%s.lst", word[1]);
1808 } else {
1809 filename = g_strdup(word[1]);
1810 }
1811 GFile *file = g_file_new_for_path(filename);
1812 g_free(filename);
1813
1814 GInputStream *input_stream = (GInputStream *)g_file_read(file, NULL, &error);
1815
1816 if (input_stream == NULL) {
1817 if (error != NULL) {
1818 g_clear_error(&error);
1819 siril_log_message(_("File [%s] does not exist\n"), filename);
1820 }
1821
1822 g_object_unref(file);
1823 return 1;
1824 }
1825
1826 is_cfa = (word[0][5] == '_') ? 1 : 0;
1827
1828 GDataInputStream *data_input = g_data_input_stream_new(input_stream);
1829 while ((line = g_data_input_stream_read_line_utf8(data_input, NULL,
1830 NULL, NULL))) {
1831 ++i;
1832 switch (line[0]) {
1833 case '#': // comments.
1834 g_free(line);
1835 continue;
1836 break;
1837 case 'P':
1838 nb_tokens = sscanf(line + 2, "%lf %lf %c", &dev.p.x, &dev.p.y, &type);
1839 if (nb_tokens != 2 && nb_tokens != 3) {
1840 fprintf(stderr, "cosmetic correction: "
1841 "cosme file format error at line %d: %s", i, line);
1842 retval = 1;
1843 g_free(line);
1844 continue;
1845 }
1846 if (nb_tokens == 2) {
1847 type = 'H';
1848 }
1849 if (type == 'H')
1850 dev.type = HOT_PIXEL;
1851 else
1852 dev.type = COLD_PIXEL;
1853 dev.p.y = gfit.ry - dev.p.y - 1; /* FITS are stored bottom to top */
1854 cosmeticCorrOnePoint(&gfit, dev, is_cfa);
1855 break;
1856 case 'L':
1857 nb_tokens = sscanf(line + 2, "%lf %lf %c", &dev.p.y, &dirty, &type);
1858 if (nb_tokens != 2 && nb_tokens != 3) {
1859 fprintf(stderr, "cosmetic correction: "
1860 "cosme file format error at line %d: %s\n", i, line);
1861 retval = 1;
1862 g_free(line);
1863 continue;
1864 }
1865 dev.type = HOT_PIXEL; // we force it
1866 dev.p.y = gfit.ry - dev.p.y - 1; /* FITS are stored bottom to top */
1867 cosmeticCorrOneLine(&gfit, dev, is_cfa);
1868 break;
1869 case 'C':
1870 nb_tokens = sscanf(line + 2, "%lf %lf %c", &dev.p.y, &dirty, &type);
1871 if (nb_tokens != 2 && nb_tokens != 3) {
1872 fprintf(stderr, "cosmetic correction: "
1873 "cosme file format error at line %d: %s\n", i, line);
1874 retval = 1;
1875 g_free(line);
1876 continue;
1877 }
1878 point center = {gfit.rx / 2.0, gfit.ry / 2.0};
1879 dev.type = HOT_PIXEL; // we force it
1880 dev.p.y = gfit.rx - dev.p.y - 1; /* FITS are stored bottom to top */
1881 cvRotateImage(&gfit, center, 90.0, -1, OPENCV_AREA);
1882 cosmeticCorrOneLine(&gfit, dev, is_cfa);
1883 cvRotateImage(&gfit, center, -90.0, -1, OPENCV_AREA);
1884
1885 break;
1886 default:
1887 fprintf(stderr, _("cosmetic correction: "
1888 "cosme file format error at line %d: %s\n"), i, line);
1889 retval = 1;
1890 }
1891 g_free(line);
1892 }
1893
1894 g_object_unref(input_stream);
1895 g_object_unref(file);
1896 if (retval)
1897 siril_log_color_message(_("There were some errors, please check your input file.\n"), "salmon");
1898
1899 invalidate_stats_from_fit(&gfit);
1900 adjust_cutoff_from_updated_gfit();
1901 redraw(com.cvport, REMAP_ALL);
1902 redraw_previews();
1903 return 0;
1904 }
1905
process_fmedian(int nb)1906 int process_fmedian(int nb){
1907 if (get_thread_run()) {
1908 PRINT_ANOTHER_THREAD_RUNNING;
1909 return 1;
1910 }
1911
1912 if (!single_image_is_loaded()) {
1913 PRINT_NOT_FOR_SEQUENCE;
1914 return 1;
1915 }
1916
1917 struct median_filter_data *args = malloc(sizeof(struct median_filter_data));
1918 args->ksize = g_ascii_strtoull(word[1], NULL, 10);
1919 args->amount = g_ascii_strtod(word[2], NULL);
1920 args->iterations = 1;
1921
1922 if (!(args->ksize & 1) || args->ksize < 2 || args->ksize > 15) {
1923 siril_log_message(_("The size of the kernel MUST be odd and in the range [3, 15].\n"));
1924 free(args);
1925 return 1;
1926 }
1927 if (args->amount < 0.0 || args->amount > 1.0) {
1928 siril_log_message(_("Modulation value MUST be between 0 and 1\n"));
1929 free(args);
1930 return 1;
1931 }
1932 args->fit = &gfit;
1933
1934 set_cursor_waiting(TRUE);
1935
1936 start_in_new_thread(median_filter, args);
1937
1938 return 0;
1939 }
1940
1941 /* The name of this command should be COG in english but this choice
1942 * was done to be consistent with IRIS
1943 */
process_cdg(int nb)1944 int process_cdg(int nb) {
1945 float x_avg, y_avg;
1946
1947 if (!single_image_is_loaded()) {
1948 PRINT_NOT_FOR_SEQUENCE;
1949 return 1;
1950 }
1951
1952 if (!FindCentre(&gfit, &x_avg, &y_avg)) {
1953 siril_log_message(_("Center of gravity coordinates are (%.3lf, %.3lf)\n"), x_avg, y_avg);
1954 return 0;
1955 }
1956 return 1;
1957 }
1958
process_clear(int nb)1959 int process_clear(int nb) {
1960 if (com.script) return 0;
1961 GtkTextView *text = GTK_TEXT_VIEW(lookup_widget("output"));
1962 GtkTextBuffer *tbuf = gtk_text_view_get_buffer(text);
1963 GtkTextIter start_iter, end_iter;
1964 gtk_text_buffer_get_start_iter(tbuf, &start_iter);
1965 gtk_text_buffer_get_end_iter(tbuf, &end_iter);
1966 gtk_text_buffer_delete(tbuf, &start_iter, &end_iter);
1967 return 0;
1968 }
1969
process_clearstar(int nb)1970 int process_clearstar(int nb){
1971 clear_stars_list();
1972 adjust_cutoff_from_updated_gfit();
1973 redraw(com.cvport, REMAP_NONE);
1974 redraw_previews();
1975 return 0;
1976 }
1977
process_close(int nb)1978 int process_close(int nb) {
1979 close_sequence(FALSE);
1980 close_single_image();
1981 if (!com.script) {
1982 update_MenuItem();
1983 reset_plot(); // reset all plots
1984 close_tab(); //close Green and Blue Tab if a 1-layer sequence is loaded
1985
1986 }
1987 return 0;
1988 }
1989
process_fill(int nb)1990 int process_fill(int nb){
1991 int level;
1992 rectangle area;
1993
1994 if (!single_image_is_loaded()) {
1995 PRINT_NOT_FOR_SEQUENCE;
1996 return 1;
1997 }
1998
1999 if ((!com.selection.h) || (!com.selection.w)) {
2000 if (nb == 6) {
2001 area.x = g_ascii_strtoull(word[2], NULL, 10);
2002 area.y = g_ascii_strtoull(word[3], NULL, 10);
2003 area.w = g_ascii_strtoull(word[4], NULL, 10);
2004 area.h = g_ascii_strtoull(word[5], NULL, 10);
2005 if ((area.w + area.x > gfit.rx) || (area.h + area.y > gfit.ry)) {
2006 siril_log_message(_("Wrong parameters.\n"));
2007 return 1;
2008 }
2009 }
2010 else {
2011 area.w = gfit.rx; area.h = gfit.ry;
2012 area.x = 0; area.y = 0;
2013 }
2014 } else {
2015 memcpy(&area, &com.selection, sizeof(rectangle));
2016 }
2017 level = g_ascii_strtoull(word[1], NULL, 10);
2018 int retval = fill(&gfit, level, &area);
2019 if (retval) {
2020 siril_log_message(_("Wrong parameters.\n"));
2021 return 1;
2022 }
2023 redraw(com.cvport, REMAP_ALL);
2024 return 0;
2025 }
2026
process_offset(int nb)2027 int process_offset(int nb){
2028 int level;
2029
2030 if (!single_image_is_loaded()) {
2031 PRINT_NOT_FOR_SEQUENCE;
2032 return 1;
2033 }
2034
2035 level = g_ascii_strtod(word[1], NULL);
2036 off(&gfit, level);
2037 adjust_cutoff_from_updated_gfit();
2038 redraw(com.cvport, REMAP_ALL);
2039 redraw_previews();
2040 return 0;
2041 }
2042
2043 /* The version in command line is a minimal version
2044 * Only neutral type are available (no amount needed),
2045 * then we always preserve the lightness */
process_scnr(int nb)2046 int process_scnr(int nb){
2047 if (get_thread_run()) {
2048 PRINT_ANOTHER_THREAD_RUNNING;
2049 return 1;
2050 }
2051
2052 if (!single_image_is_loaded()) {
2053 PRINT_NOT_FOR_SEQUENCE;
2054 return 1;
2055 }
2056 if (gfit.naxes[2] == 1) return 1;
2057
2058 struct scnr_data *args = malloc(sizeof(struct scnr_data));
2059
2060 args->type = g_ascii_strtoull(word[1], NULL, 10);
2061 args->fit = &gfit;
2062 args->amount = 0.0;
2063 args->preserve = TRUE;
2064
2065 set_cursor_waiting(TRUE);
2066
2067 start_in_new_thread(scnr, args);
2068
2069 return 0;
2070 }
2071
process_fft(int nb)2072 int process_fft(int nb){
2073 if (get_thread_run()) {
2074 PRINT_ANOTHER_THREAD_RUNNING;
2075 return 1;
2076 }
2077
2078 if (sequence_is_loaded()) {
2079 PRINT_NOT_FOR_SEQUENCE;
2080 return 1;
2081 }
2082
2083 if (!single_image_is_loaded()) {
2084 PRINT_NOT_FOR_SEQUENCE;
2085 return 1;
2086 }
2087
2088 struct fft_data *args = malloc(sizeof(struct fft_data));
2089
2090 args->fit = &gfit;
2091 args->type = strdup(word[0]);
2092 args->modulus = strdup(word[1]);
2093 args->phase = strdup(word[2]);
2094 args->type_order = 0;
2095
2096 set_cursor_waiting(TRUE);
2097
2098 start_in_new_thread(fourier_transform, args);
2099
2100 return 0;
2101 }
2102
process_fixbanding(int nb)2103 int process_fixbanding(int nb) {
2104 if (get_thread_run()) {
2105 PRINT_ANOTHER_THREAD_RUNNING;
2106 return 1;
2107 }
2108
2109 if (!single_image_is_loaded()) {
2110 PRINT_NOT_FOR_SEQUENCE;
2111 return 1;
2112 }
2113
2114 struct banding_data *args = malloc(sizeof(struct banding_data));
2115
2116 args->amount = g_ascii_strtod(word[1], NULL);
2117 args->sigma = g_ascii_strtod(word[2], NULL);
2118 args->protect_highlights = TRUE;
2119 args->fit = &gfit;
2120
2121 set_cursor_waiting(TRUE);
2122
2123 start_in_new_thread(BandingEngineThreaded, args);
2124
2125 return 0;
2126 }
2127
2128
process_subsky(int nb)2129 int process_subsky(int nb) {
2130 gboolean is_sequence;
2131 sequence *seq = NULL;
2132 int degree = 0;
2133
2134 if (get_thread_run()) {
2135 PRINT_ANOTHER_THREAD_RUNNING;
2136 return 1;
2137 }
2138
2139 is_sequence = (word[0][2] == 'q');
2140
2141 if (is_sequence) {
2142 seq = load_sequence(word[1], NULL);
2143 if (!seq)
2144 return 1;
2145 degree = g_ascii_strtoull(word[2], NULL, 10);
2146 } else {
2147 if (!single_image_is_loaded()) return 1;
2148 degree = g_ascii_strtoull(word[1], NULL, 10);
2149 }
2150
2151 if (degree < 1 || degree > 4) {
2152 siril_log_message(_("Polynomial degree order must be within the [1, 4] range.\n"));
2153 return 1;
2154 }
2155
2156 set_cursor_waiting(TRUE);
2157
2158 if (is_sequence) {
2159 struct background_data *args = malloc(sizeof(struct background_data));
2160
2161 args->seq = seq;
2162 args->nb_of_samples = 20;
2163 args->tolerance = 1.0;
2164 args->correction = 0; //subtraction
2165 args->seqEntry = "bkg_";
2166 args->degree = (poly_order) (degree - 1);
2167 args->dither = TRUE;
2168
2169 int startoptargs = 3;
2170 if (nb > startoptargs) {
2171 for (int i = startoptargs; i < nb; i++) {
2172 if (word[i]) {
2173 if (g_str_has_prefix(word[i], "-prefix=")) {
2174 char *current = word[i], *value;
2175 value = current + 8;
2176 if (value[0] == '\0') {
2177 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2178 return 1;
2179 }
2180 args->seqEntry = strdup(value);
2181 }
2182 }
2183 }
2184 }
2185
2186
2187 apply_background_extraction_to_sequence(args);
2188 } else {
2189 generate_background_samples(20, 1.0);
2190 remove_gradient_from_image(0, (poly_order) (degree - 1));
2191 free_background_sample_list(com.grad_samples);
2192 com.grad_samples = NULL;
2193
2194 adjust_cutoff_from_updated_gfit();
2195 redraw(com.cvport, REMAP_ALL);
2196 }
2197 set_cursor_waiting(FALSE);
2198
2199 return 0;
2200 }
2201
2202
process_findcosme(int nb)2203 int process_findcosme(int nb) {
2204 gboolean is_sequence;
2205 sequence *seq = NULL;
2206 int i = 0;
2207
2208 if (get_thread_run()) {
2209 PRINT_ANOTHER_THREAD_RUNNING;
2210 return 1;
2211 }
2212
2213 is_sequence = (word[0][0] == 's');
2214
2215 if (is_sequence) {
2216 seq = load_sequence(word[1], NULL);
2217 if (!seq)
2218 return 1;
2219 i++;
2220 } else {
2221 if (!single_image_is_loaded()) return 1;
2222 }
2223
2224 struct cosmetic_data *args = malloc(sizeof(struct cosmetic_data));
2225
2226 args->seq = seq;
2227 args->sigma[0] = g_ascii_strtod(word[1 + i], NULL);
2228 args->sigma[1] = g_ascii_strtod(word[2 + i], NULL);
2229 args->is_cfa = (word[0][10] == '_' || word[0][13] == '_'); // find_cosme_cfa or seqfind_cosme_cfa
2230 args->amount = 1.0;
2231 args->fit = &gfit;
2232
2233 set_cursor_waiting(TRUE);
2234
2235 if (is_sequence) {
2236 args->seqEntry = "cc_";
2237 args->multithread = FALSE;
2238
2239 int startoptargs = i + 3;
2240 int nb_command_max = i + 4;
2241 if (nb > startoptargs) {
2242 for (int j = startoptargs; j < nb_command_max; j++) {
2243 if (word[j]) {
2244 if (g_str_has_prefix(word[j], "-prefix=")) {
2245 char *current = word[j], *value;
2246 value = current + 8;
2247 if (value[0] == '\0') {
2248 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2249 return 1;
2250 }
2251 args->seqEntry = strdup(value);
2252 }
2253 }
2254 }
2255 }
2256 apply_cosmetic_to_sequence(args);
2257 } else {
2258 args->multithread = TRUE;
2259 start_in_new_thread(autoDetectThreaded, args);
2260 }
2261
2262 return 0;
2263 }
2264
select_unselect(gboolean select)2265 int select_unselect(gboolean select) {
2266 if (!sequence_is_loaded()) {
2267 siril_log_message(_("Use this command to select images in a sequence, load a sequence first.\n"));
2268 return 1;
2269 }
2270 int from = g_ascii_strtoull(word[1], NULL, 10);
2271 int to = g_ascii_strtoull(word[2], NULL, 10);
2272 if (from < 0 || from >= com.seq.number) {
2273 siril_log_message(_("The first argument must be between 0 and the number of images minus one.\n"));
2274 return 1;
2275 }
2276 gboolean current_updated = FALSE;
2277 for (int i = from; i <= to; i++) {
2278 if (i >= com.seq.number) break;
2279 if (com.seq.imgparam[i].incl != select) {
2280 com.seq.imgparam[i].incl = select;
2281 if (!com.headless)
2282 sequence_list_change_selection_index(i, i);
2283 if (select)
2284 com.seq.selnum++;
2285 else com.seq.selnum--;
2286 if (i == com.seq.current)
2287 current_updated = TRUE;
2288 }
2289 if (!select && com.seq.reference_image == i) {
2290 com.seq.reference_image = -1;
2291 if (!com.headless) {
2292 sequence_list_change_reference();
2293 adjust_refimage(com.seq.current);
2294 }
2295 }
2296 }
2297
2298 if (!com.headless) {
2299 if (current_updated) {
2300 redraw(com.cvport, REMAP_NONE);
2301 drawPlot();
2302 adjust_sellabel();
2303 }
2304 update_reg_interface(FALSE);
2305 adjust_sellabel();
2306 }
2307 writeseqfile(&com.seq);
2308 siril_log_message(_("Selection update finished, %d images are selected in the sequence\n"), com.seq.selnum);
2309
2310 return 0;
2311 }
2312
process_select(int nb)2313 int process_select(int nb){
2314 return select_unselect(TRUE);
2315 }
2316
process_unselect(int nb)2317 int process_unselect(int nb){
2318 return select_unselect(FALSE);
2319 }
2320
process_split(int nb)2321 int process_split(int nb){
2322 if (!single_image_is_loaded()) {
2323 PRINT_NOT_FOR_SEQUENCE;
2324 return 1;
2325 }
2326
2327 if (!isrgb(&gfit)) {
2328 siril_log_message(_("Siril cannot split layers. Make sure your image is in RGB mode.\n"));
2329 return 1;
2330 }
2331
2332 if (get_thread_run()) {
2333 PRINT_ANOTHER_THREAD_RUNNING;
2334 return 1;
2335 }
2336
2337 struct extract_channels_data *args = malloc(sizeof(struct extract_channels_data));
2338 if (!args) {
2339 PRINT_ALLOC_ERR;
2340 return 1;
2341 }
2342
2343 args->type = 0;
2344 args->str_type = _("RGB");
2345
2346 args->channel[0] = g_strdup_printf("%s%s", word[1], com.pref.ext);
2347 args->channel[1] = g_strdup_printf("%s%s", word[2], com.pref.ext);
2348 args->channel[2] = g_strdup_printf("%s%s", word[3], com.pref.ext);
2349
2350 args->fit = calloc(1, sizeof(fits));
2351 set_cursor_waiting(TRUE);
2352 if (copyfits(&gfit, args->fit, CP_ALLOC | CP_COPYA | CP_FORMAT, -1)) {
2353 siril_log_message(_("Could not copy the input image, aborting.\n"));
2354 free(args->fit);
2355 free(args->channel[0]);
2356 free(args->channel[1]);
2357 free(args->channel[2]);
2358 free(args);
2359 return 1;
2360 }
2361 copy_fits_metadata(&gfit, args->fit);
2362 start_in_new_thread(extract_channels, args);
2363 return 0;
2364 }
2365
process_split_cfa(int nb)2366 int process_split_cfa(int nb) {
2367 if (isrgb(&gfit)) {
2368 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2369 return 1;
2370 }
2371 char *filename = NULL;
2372 int ret = 1;
2373
2374 fits f_cfa0 = { 0 }, f_cfa1 = { 0 }, f_cfa2 = { 0 }, f_cfa3 = { 0 };
2375
2376 if (sequence_is_loaded() && !single_image_is_loaded()) {
2377 filename = g_path_get_basename(com.seq.seqname);
2378 }
2379 else {
2380 if (com.uniq->filename != NULL) {
2381 char *tmp = remove_ext_from_filename(com.uniq->filename);
2382 filename = g_path_get_basename(tmp);
2383 free(tmp);
2384 }
2385 }
2386
2387 gchar *cfa0 = g_strdup_printf("CFA0_%s%s", filename, com.pref.ext);
2388 gchar *cfa1 = g_strdup_printf("CFA1_%s%s", filename, com.pref.ext);
2389 gchar *cfa2 = g_strdup_printf("CFA2_%s%s", filename, com.pref.ext);
2390 gchar *cfa3 = g_strdup_printf("CFA3_%s%s", filename, com.pref.ext);
2391
2392 if (gfit.type == DATA_USHORT) {
2393 if (!(ret = split_cfa_ushort(&gfit, &f_cfa0, &f_cfa1, &f_cfa2, &f_cfa3))) {
2394 ret = save1fits16(cfa0, &f_cfa0, 0) ||
2395 save1fits16(cfa1, &f_cfa1, 0) ||
2396 save1fits16(cfa2, &f_cfa2, 0) ||
2397 save1fits16(cfa3, &f_cfa3, 0);
2398 }
2399 }
2400 else if (gfit.type == DATA_FLOAT) {
2401 if (!(ret = split_cfa_float(&gfit, &f_cfa0, &f_cfa1, &f_cfa2, &f_cfa3))) {
2402 ret = save1fits32(cfa0, &f_cfa0, 0) ||
2403 save1fits32(cfa1, &f_cfa1, 0) ||
2404 save1fits32(cfa2, &f_cfa2, 0) ||
2405 save1fits32(cfa3, &f_cfa3, 0);
2406 }
2407 }
2408
2409 g_free(cfa0); g_free(cfa1);
2410 g_free(cfa2); g_free(cfa3);
2411 clearfits(&f_cfa0); clearfits(&f_cfa1);
2412 clearfits(&f_cfa2); clearfits(&f_cfa3);
2413 free(filename);
2414 return ret;
2415 }
2416
process_extractGreen(int nb)2417 int process_extractGreen(int nb) {
2418 if (isrgb(&gfit)) {
2419 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2420 return 1;
2421 }
2422 char *filename = NULL;
2423 int ret = 1;
2424
2425 fits f_green = { 0 };
2426
2427 if (sequence_is_loaded() && !single_image_is_loaded()) {
2428 filename = g_path_get_basename(com.seq.seqname);
2429 }
2430 else {
2431 if (com.uniq->filename != NULL) {
2432 char *tmp = remove_ext_from_filename(com.uniq->filename);
2433 filename = g_path_get_basename(tmp);
2434 free(tmp);
2435 }
2436 }
2437
2438 sensor_pattern pattern = get_bayer_pattern(&gfit);
2439
2440 gchar *green = g_strdup_printf("Green_%s%s", filename, com.pref.ext);
2441 if (gfit.type == DATA_USHORT) {
2442 if (!(ret = extractGreen_ushort(&gfit, &f_green, pattern))) {
2443 ret = save1fits16(green, &f_green, 0);
2444 }
2445 }
2446 else if (gfit.type == DATA_FLOAT) {
2447 if (!(ret = extractGreen_float(&gfit, &f_green, pattern))) {
2448 ret = save1fits32(green, &f_green, 0);
2449 }
2450 } else return 1;
2451
2452 g_free(green);
2453 clearfits(&f_green);
2454 free(filename);
2455 return ret;
2456
2457 }
2458
process_extractHa(int nb)2459 int process_extractHa(int nb) {
2460 if (isrgb(&gfit)) {
2461 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2462 return 1;
2463 }
2464 char *filename = NULL;
2465 int ret = 1;
2466
2467 fits f_Ha = { 0 };
2468
2469 if (sequence_is_loaded() && !single_image_is_loaded()) {
2470 filename = g_path_get_basename(com.seq.seqname);
2471 }
2472 else {
2473 if (com.uniq->filename != NULL) {
2474 char *tmp = remove_ext_from_filename(com.uniq->filename);
2475 filename = g_path_get_basename(tmp);
2476 free(tmp);
2477 }
2478 }
2479
2480 sensor_pattern pattern = get_bayer_pattern(&gfit);
2481
2482 gchar *Ha = g_strdup_printf("Ha_%s%s", filename, com.pref.ext);
2483 if (gfit.type == DATA_USHORT) {
2484 if (!(ret = extractHa_ushort(&gfit, &f_Ha, pattern))) {
2485 ret = save1fits16(Ha, &f_Ha, 0);
2486 }
2487 }
2488 else if (gfit.type == DATA_FLOAT) {
2489 if (!(ret = extractHa_float(&gfit, &f_Ha, pattern))) {
2490 ret = save1fits32(Ha, &f_Ha, 0);
2491 }
2492 } else return 1;
2493
2494 g_free(Ha);
2495 clearfits(&f_Ha);
2496 free(filename);
2497 return ret;
2498 }
2499
process_extractHaOIII(int nb)2500 int process_extractHaOIII(int nb) {
2501 if (isrgb(&gfit)) {
2502 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2503 return 1;
2504 }
2505 char *filename = NULL;
2506 int ret = 1;
2507
2508 fits f_Ha = { 0 }, f_OIII = { 0 };
2509
2510 if (sequence_is_loaded() && !single_image_is_loaded()) {
2511 filename = g_path_get_basename(com.seq.seqname);
2512 }
2513 else {
2514 if (com.uniq->filename != NULL) {
2515 char *tmp = remove_ext_from_filename(com.uniq->filename);
2516 filename = g_path_get_basename(tmp);
2517 free(tmp);
2518 }
2519 }
2520
2521 sensor_pattern pattern = get_bayer_pattern(&gfit);
2522
2523 gchar *Ha = g_strdup_printf("Ha_%s%s", filename, com.pref.ext);
2524 gchar *OIII = g_strdup_printf("OIII_%s%s", filename, com.pref.ext);
2525 if (gfit.type == DATA_USHORT) {
2526 if (!(ret = extractHaOIII_ushort(&gfit, &f_Ha, &f_OIII, pattern))) {
2527 ret = save1fits16(Ha, &f_Ha, 0) ||
2528 save1fits16(OIII, &f_OIII, 0);
2529 }
2530 }
2531 else if (gfit.type == DATA_FLOAT) {
2532 if (!(ret = extractHaOIII_float(&gfit, &f_Ha, &f_OIII, pattern))) {
2533 ret = save1fits32(Ha, &f_Ha, 0) ||
2534 save1fits16(OIII, &f_OIII, 0);
2535 }
2536 } else return 1;
2537
2538 g_free(Ha);
2539 g_free(OIII);
2540 clearfits(&f_Ha);
2541 clearfits(&f_OIII);
2542 free(filename);
2543 return ret;
2544 }
2545
process_seq_mtf(int nb)2546 int process_seq_mtf(int nb) {
2547 if (get_thread_run()) {
2548 PRINT_ANOTHER_THREAD_RUNNING;
2549 return 1;
2550 }
2551 sequence *seq = load_sequence(word[1], NULL);
2552 if (!seq)
2553 return 1;
2554
2555 struct mtf_data *args = malloc(sizeof(struct mtf_data));
2556
2557 args->seq = seq;
2558 args->fit = &gfit;
2559 args->seqEntry = "mtf_";
2560 args->lo = g_ascii_strtod(word[2], NULL);
2561 args->mid = g_ascii_strtod(word[3], NULL);
2562 args->hi = g_ascii_strtod(word[4], NULL);
2563
2564 int startoptargs = 5;
2565 if (nb > startoptargs) {
2566 for (int i = startoptargs; i < nb; i++) {
2567 if (word[i]) {
2568 if (g_str_has_prefix(word[i], "-prefix=")) {
2569 char *current = word[i], *value;
2570 value = current + 8;
2571 if (value[0] == '\0') {
2572 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2573 return 1;
2574 }
2575 args->seqEntry = strdup(value);
2576 }
2577 }
2578 }
2579 }
2580
2581 set_cursor_waiting(TRUE);
2582
2583 apply_mtf_to_sequence(args);
2584
2585 return 0;
2586 }
2587
process_seq_split_cfa(int nb)2588 int process_seq_split_cfa(int nb) {
2589 if (get_thread_run()) {
2590 PRINT_ANOTHER_THREAD_RUNNING;
2591 return 1;
2592 }
2593
2594 sequence *seq = load_sequence(word[1], NULL);
2595 if (!seq)
2596 return 1;
2597
2598 if (seq->nb_layers > 1) {
2599 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2600 return 1;
2601 }
2602
2603 struct split_cfa_data *args = calloc(1, sizeof(struct split_cfa_data));
2604
2605 args->seq = seq;
2606 args->fit = &gfit;
2607 args->seqEntry = "CFA_"; // propose to default to "CFA" for consistency of output names with single image split_cfa
2608
2609 int startoptargs = 2;
2610 if (nb > startoptargs) {
2611 for (int i = startoptargs; i < nb; i++) {
2612 if (word[i]) {
2613 if (g_str_has_prefix(word[i], "-prefix=")) {
2614 char *current = word[i], *value;
2615 value = current + 8;
2616 if (value[0] == '\0') {
2617 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2618 return 1;
2619 }
2620 args->seqEntry = strdup(value);
2621 }
2622 }
2623 }
2624 }
2625
2626 set_cursor_waiting(TRUE);
2627
2628 apply_split_cfa_to_sequence(args);
2629
2630 return 0;
2631 }
2632
process_seq_extractHa(int nb)2633 int process_seq_extractHa(int nb) {
2634 if (get_thread_run()) {
2635 PRINT_ANOTHER_THREAD_RUNNING;
2636 return 1;
2637 }
2638
2639 sequence *seq = load_sequence(word[1], NULL);
2640 if (!seq)
2641 return 1;
2642
2643 if (seq->nb_layers > 1) {
2644 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2645 return 1;
2646 }
2647
2648 struct split_cfa_data *args = calloc(1, sizeof(struct split_cfa_data));
2649
2650 args->seq = seq;
2651 args->seqEntry = "Ha_";
2652
2653 int startoptargs = 2;
2654 if (nb > startoptargs) {
2655 for (int i = startoptargs; i < nb; i++) {
2656 if (word[i]) {
2657 if (g_str_has_prefix(word[i], "-prefix=")) {
2658 char *current = word[i], *value;
2659 value = current + 8;
2660 if (value[0] == '\0') {
2661 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2662 return 1;
2663 }
2664 args->seqEntry = strdup(value);
2665 }
2666 }
2667 }
2668 }
2669
2670 set_cursor_waiting(TRUE);
2671
2672 apply_extractHa_to_sequence(args);
2673
2674 return 0;
2675 }
2676
process_seq_extractGreen(int nb)2677 int process_seq_extractGreen(int nb) {
2678 if (get_thread_run()) {
2679 PRINT_ANOTHER_THREAD_RUNNING;
2680 return 1;
2681 }
2682
2683 sequence *seq = load_sequence(word[1], NULL);
2684 if (!seq)
2685 return 1;
2686
2687 if (seq->nb_layers > 1) {
2688 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2689 return 1;
2690 }
2691
2692 struct split_cfa_data *args = calloc(1, sizeof(struct split_cfa_data));
2693
2694 args->seq = seq;
2695 args->seqEntry = "Green_";
2696
2697 int startoptargs = 2;
2698 if (nb > startoptargs) {
2699 for (int i = startoptargs; i < nb; i++) {
2700 if (word[i]) {
2701 if (g_str_has_prefix(word[i], "-prefix=")) {
2702 char *current = word[i], *value;
2703 value = current + 8;
2704 if (value[0] == '\0') {
2705 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2706 return 1;
2707 }
2708 args->seqEntry = strdup(value);
2709 }
2710 }
2711 }
2712 }
2713
2714 set_cursor_waiting(TRUE);
2715
2716 apply_extractGreen_to_sequence(args);
2717
2718 return 0;
2719 }
2720
process_seq_extractHaOIII(int nb)2721 int process_seq_extractHaOIII(int nb) {
2722 if (get_thread_run()) {
2723 PRINT_ANOTHER_THREAD_RUNNING;
2724 return 1;
2725 }
2726
2727 sequence *seq = load_sequence(word[1], NULL);
2728 if (!seq)
2729 return 1;
2730
2731 if (seq->nb_layers > 1) {
2732 siril_log_message(_("Siril cannot split CFA channel. Make sure your image is in CFA mode.\n"));
2733 return 1;
2734 }
2735
2736 struct split_cfa_data *args = calloc(1, sizeof(struct split_cfa_data));
2737
2738 args->seq = seq;
2739 args->seqEntry = ""; // not used
2740
2741 set_cursor_waiting(TRUE);
2742
2743 apply_extractHaOIII_to_sequence(args);
2744
2745 return 0;
2746 }
2747
process_stat(int nb)2748 int process_stat(int nb){
2749 int nplane;
2750 int layer;
2751 char layername[6];
2752
2753 nplane = gfit.naxes[2];
2754
2755 for (layer = 0; layer < nplane; layer++) {
2756 imstats* stat = statistics(NULL, -1, &gfit, layer, &com.selection, STATS_MAIN, TRUE);
2757 if (!stat) {
2758 siril_log_message(_("Error: statistics computation failed.\n"));
2759 return 1;
2760 }
2761
2762 switch (layer) {
2763 case 0:
2764 if (nplane == 1)
2765 strcpy(layername, "B&W");
2766 else
2767 strcpy(layername, "Red");
2768 break;
2769 case 1:
2770 strcpy(layername, "Green");
2771 break;
2772 case 2:
2773 strcpy(layername, "Blue");
2774 break;
2775 }
2776
2777 if (gfit.type == DATA_USHORT) {
2778 siril_log_message(
2779 _("%s layer: Mean: %0.1lf, Median: %0.1lf, Sigma: %0.1lf, "
2780 "AvgDev: %0.1lf, Min: %0.1lf, Max: %0.1lf\n"),
2781 layername, stat->mean, stat->median, stat->sigma,
2782 stat->avgDev, stat->min, stat->max);
2783 } else {
2784 siril_log_message(
2785 _("%s layer: Mean: %0.1lf, Median: %0.1lf, Sigma: %0.1lf, "
2786 "AvgDev: %0.1lf, Min: %0.1lf, Max: %0.1lf\n"),
2787 layername, stat->mean * USHRT_MAX_DOUBLE,
2788 stat->median * USHRT_MAX_DOUBLE,
2789 stat->sigma * USHRT_MAX_DOUBLE,
2790 stat->avgDev * USHRT_MAX_DOUBLE,
2791 stat->min * USHRT_MAX_DOUBLE, stat->max * USHRT_MAX_DOUBLE);
2792 }
2793 free_stats(stat);
2794 }
2795 return 0;
2796 }
2797
process_seq_stat(int nb)2798 int process_seq_stat(int nb) {
2799 if (get_thread_run()) {
2800 PRINT_ANOTHER_THREAD_RUNNING;
2801 return 1;
2802 }
2803
2804 sequence *seq = load_sequence(word[1], NULL);
2805 if (!seq)
2806 return 1;
2807
2808 struct stat_data *args = calloc(1, sizeof(struct stat_data));
2809
2810 args->seq = seq;
2811 args->seqEntry = ""; // not used
2812 args->csv_name = g_strdup(word[2]);
2813
2814 if (word[3] && !g_strcmp0(word[3], "main")) {
2815 args->option = STATS_MAIN;
2816 } else {
2817 args->option = STATS_BASIC;
2818 }
2819
2820 set_cursor_waiting(TRUE);
2821
2822 apply_stats_to_sequence(args);
2823
2824 return 0;
2825 }
2826
process_convertraw(int nb)2827 int process_convertraw(int nb) {
2828 GDir *dir;
2829 GError *error = NULL;
2830 const gchar *file;
2831 GList *list = NULL;
2832 int idx = 1;
2833 gchar *destroot = g_strdup(word[1]);
2834 sequence_type output = SEQ_REGULAR;
2835 gboolean debayer = FALSE;
2836
2837 if (get_thread_run()) {
2838 PRINT_ANOTHER_THREAD_RUNNING;
2839 return 1;
2840 }
2841
2842 if (!com.wd) {
2843 siril_log_message(_("Conversion: no working directory set.\n"));
2844 return 1;
2845 }
2846
2847 for (int i = 2; i < nb; i++) {
2848 char *current = word[i], *value;
2849 if (!strcmp(current, "-debayer")) {
2850 debayer = TRUE;
2851 } else if (!strcmp(current, "-fitseq")) {
2852 output = SEQ_FITSEQ;
2853 if (!g_str_has_suffix(destroot, com.pref.ext))
2854 str_append(&destroot, com.pref.ext);
2855 } else if (!strcmp(current, "-ser")) {
2856 output = SEQ_SER;
2857 if (!g_str_has_suffix(destroot, ".ser"))
2858 str_append(&destroot, ".ser");
2859 } else if (g_str_has_prefix(current, "-start=")) {
2860 value = current + 7;
2861 idx = (g_ascii_strtoull(value, NULL, 10) <= 0 || g_ascii_strtoull(value, NULL, 10) >= INDEX_MAX) ? 1 : g_ascii_strtoull(value, NULL, 10);
2862 } else if (g_str_has_prefix(current, "-out=")) {
2863 value = current + 5;
2864 if (value[0] == '\0') {
2865 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2866 return 1;
2867 }
2868 if (!g_file_test(value, G_FILE_TEST_EXISTS)) {
2869 if (g_mkdir_with_parents(value, 0755) < 0) {
2870 siril_log_color_message(_("Cannot create output folder: %s\n"), "red", value);
2871 return 1;
2872 }
2873 }
2874 gchar *filename = g_build_filename(value, destroot, NULL);
2875 g_free(destroot);
2876 destroot = filename;
2877 }
2878 }
2879
2880 if ((dir = g_dir_open(com.wd, 0, &error)) == NULL){
2881 siril_log_message(_("Conversion: error opening working directory %s.\n"), com.wd);
2882 fprintf (stderr, "Conversion: %s\n", error->message);
2883 g_clear_error(&error);
2884 return 1;
2885 }
2886
2887 int count = 0;
2888 while ((file = g_dir_read_name(dir)) != NULL) {
2889 const char *ext = get_filename_ext(file);
2890 if (!ext)
2891 continue;
2892 image_type type = get_type_for_extension(ext);
2893 if (type == TYPERAW) {
2894 if (output == SEQ_SER && !g_ascii_strcasecmp(ext, "raf") && !debayer) {
2895 siril_log_message(_("FujiFilm XTRANS sensors are not supported by SER v2 (CFA-style) standard. You may use FITS sequences instead."));
2896 g_list_free_full(list, g_free);
2897 return 1;
2898 }
2899 list = g_list_append(list, g_build_filename(com.wd, file, NULL));
2900 count++;
2901 }
2902 }
2903 g_dir_close(dir);
2904 if (!count) {
2905 siril_log_message(_("No RAW files were found for conversion\n"));
2906 g_list_free_full(list, g_free);
2907 return 1;
2908 }
2909 /* sort list */
2910 list = g_list_sort(list, (GCompareFunc) strcompare);
2911 /* convert the list to an array for parallel processing */
2912 char **files_to_convert = glist_to_array(list, &count);
2913
2914 siril_log_color_message(_("Conversion: processing %d RAW files...\n"), "green", count);
2915
2916 set_cursor_waiting(TRUE);
2917 if (!com.script)
2918 control_window_switch_to_tab(OUTPUT_LOGS);
2919
2920 struct _convert_data *args = malloc(sizeof(struct _convert_data));
2921 args->start = idx;
2922 args->list = files_to_convert;
2923 args->total = count;
2924 if (output == SEQ_REGULAR)
2925 args->destroot = format_basename(destroot, TRUE);
2926 else
2927 args->destroot = destroot;
2928 args->input_has_a_seq = FALSE;
2929 args->input_has_a_film = FALSE;
2930 args->debayer = debayer;
2931 args->output_type = output;
2932 args->multiple_output = FALSE;
2933 args->make_link = FALSE;
2934 gettimeofday(&(args->t_start), NULL);
2935 start_in_new_thread(convert_thread_worker, args);
2936 return 0;
2937 }
2938
process_link(int nb)2939 int process_link(int nb) {
2940 GDir *dir;
2941 GError *error = NULL;
2942 const gchar *file;
2943 GList *list = NULL;
2944 int idx = 1;
2945 gchar *destroot = g_strdup(word[1]);
2946
2947 if (get_thread_run()) {
2948 PRINT_ANOTHER_THREAD_RUNNING;
2949 return 1;
2950 }
2951
2952 for (int i = 2; i < nb; i++) {
2953 char *current = word[i], *value;
2954 if (g_str_has_prefix(current, "-start=")) {
2955 value = current + 7;
2956 idx = (g_ascii_strtoull(value, NULL, 10) <= 0 ||
2957 g_ascii_strtoull(value, NULL, 10) >= INDEX_MAX) ?
2958 1 : g_ascii_strtoull(value, NULL, 10);
2959 } else if (g_str_has_prefix(current, "-out=")) {
2960 value = current + 5;
2961 if (value[0] == '\0') {
2962 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
2963 return 1;
2964 }
2965 if (!g_file_test(value, G_FILE_TEST_EXISTS)) {
2966 if (g_mkdir_with_parents(value, 0755) < 0) {
2967 siril_log_color_message(_("Cannot create output folder: %s\n"), "red", value);
2968 return 1;
2969 }
2970 }
2971 gchar *filename = g_build_filename(value, destroot, NULL);
2972 g_free(destroot);
2973 destroot = filename;
2974 }
2975 }
2976
2977 if ((dir = g_dir_open(com.wd, 0, &error)) == NULL){
2978 siril_log_message(_("Link: error opening working directory %s.\n"), com.wd);
2979 fprintf (stderr, "Link: %s\n", error->message);
2980 g_clear_error(&error);
2981 set_cursor_waiting(FALSE);
2982 return 1;
2983 }
2984
2985 int count = 0;
2986 while ((file = g_dir_read_name(dir)) != NULL) {
2987 const char *ext = get_filename_ext(file);
2988 if (!ext)
2989 continue;
2990 image_type type = get_type_for_extension(ext);
2991 if (type == TYPEFITS) {
2992 list = g_list_append(list, g_build_filename(com.wd, file, NULL));
2993 count++;
2994 }
2995 }
2996 g_dir_close(dir);
2997 if (!count) {
2998 siril_log_message(_("No FITS files were found for link\n"));
2999 return 1;
3000 }
3001 /* sort list */
3002 list = g_list_sort(list, (GCompareFunc) strcompare);
3003 /* convert the list to an array for parallel processing */
3004 char **files_to_link = glist_to_array(list, &count);
3005
3006 gchar *str = ngettext("Link: processing %d FITS file...\n", "Link: processing %d FITS files...\n", count);
3007 str = g_strdup_printf(str, count);
3008 siril_log_color_message(str, "green");
3009 g_free(str);
3010
3011 set_cursor_waiting(TRUE);
3012 if (!com.script)
3013 control_window_switch_to_tab(OUTPUT_LOGS);
3014
3015 if (!com.wd) {
3016 siril_log_message(_("Link: no working directory set.\n"));
3017 set_cursor_waiting(FALSE);
3018 return 1;
3019 }
3020
3021 struct _convert_data *args = malloc(sizeof(struct _convert_data));
3022 args->start = idx;
3023 args->list = files_to_link;
3024 args->total = count;
3025 args->destroot = format_basename(destroot, TRUE);
3026 args->input_has_a_seq = FALSE;
3027 args->input_has_a_film = FALSE;
3028 args->debayer = FALSE;
3029 args->multiple_output = FALSE;
3030 args->output_type = SEQ_REGULAR; // fallback if symlink does not work
3031 args->make_link = TRUE;
3032 gettimeofday(&(args->t_start), NULL);
3033 start_in_new_thread(convert_thread_worker, args);
3034
3035 return 0;
3036 }
3037
process_convert(int nb)3038 int process_convert(int nb) {
3039 GDir *dir;
3040 GError *error = NULL;
3041 const gchar *file;
3042 GList *list = NULL;
3043 int idx = 1;
3044 gboolean debayer = FALSE;
3045 gboolean make_link = TRUE;
3046 sequence_type output = SEQ_REGULAR;
3047 gchar *destroot = g_strdup(word[1]);
3048
3049 if (get_thread_run()) {
3050 PRINT_ANOTHER_THREAD_RUNNING;
3051 return 1;
3052 }
3053
3054 for (int i = 2; i < nb; i++) {
3055 char *current = word[i], *value;
3056 if (!strcmp(current, "-debayer")) {
3057 debayer = TRUE;
3058 make_link = FALSE;
3059 } else if (!strcmp(current, "-fitseq")) {
3060 output = SEQ_FITSEQ;
3061 if (!g_str_has_suffix(destroot, com.pref.ext))
3062 str_append(&destroot, com.pref.ext);
3063 } else if (!strcmp(current, "-ser")) {
3064 output = SEQ_SER;
3065 if (!g_str_has_suffix(destroot, ".ser"))
3066 str_append(&destroot, ".ser");
3067 } else if (g_str_has_prefix(current, "-start=")) {
3068 value = current + 7;
3069 idx = (g_ascii_strtoull(value, NULL, 10) <= 0 || g_ascii_strtoull(value, NULL, 10) >= INDEX_MAX) ?
3070 1 : g_ascii_strtoull(value, NULL, 10);
3071 } else if (g_str_has_prefix(current, "-out=")) {
3072 value = current + 5;
3073 if (value[0] == '\0') {
3074 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3075 return 1;
3076 }
3077 if (!g_file_test(value, G_FILE_TEST_EXISTS)) {
3078 if (g_mkdir_with_parents(value, 0755) < 0) {
3079 siril_log_color_message(_("Cannot create output folder: %s\n"), "red", value);
3080 return 1;
3081 }
3082 }
3083 gchar *filename = g_build_filename(value, destroot, NULL);
3084 g_free(destroot);
3085 destroot = filename;
3086 }
3087 }
3088
3089 if ((dir = g_dir_open(com.wd, 0, &error)) == NULL){
3090 siril_log_message(_("Convert: error opening working directory %s.\n"), com.wd);
3091 fprintf (stderr, "Convert: %s\n", error->message);
3092 g_clear_error(&error);
3093 set_cursor_waiting(FALSE);
3094 return 1;
3095 }
3096
3097 int count = 0;
3098 while ((file = g_dir_read_name(dir)) != NULL) {
3099 const char *ext = get_filename_ext(file);
3100 if (!ext)
3101 continue;
3102 image_type type = get_type_for_extension(ext);
3103 if (type != TYPEUNDEF && type != TYPEAVI && type != TYPESER) {
3104 list = g_list_append(list, g_build_filename(com.wd, file, NULL));
3105 count++;
3106 }
3107 }
3108 g_dir_close(dir);
3109 if (!count) {
3110 siril_log_message(_("No files were found for convert\n"));
3111 return 1;
3112 }
3113 /* sort list */
3114 list = g_list_sort(list, (GCompareFunc) strcompare);
3115 /* convert the list to an array for parallel processing */
3116 char **files_to_link = glist_to_array(list, &count);
3117
3118 gchar *str = ngettext("Convert: processing %d FITS file...\n", "Convert: processing %d FITS files...\n", count);
3119 str = g_strdup_printf(str, count);
3120 siril_log_color_message(str, "green");
3121 g_free(str);
3122
3123 set_cursor_waiting(TRUE);
3124 if (!com.script)
3125 control_window_switch_to_tab(OUTPUT_LOGS);
3126
3127 if (!com.wd) {
3128 siril_log_message(_("Convert: no working directory set.\n"));
3129 set_cursor_waiting(FALSE);
3130 return 1;
3131 }
3132
3133 struct _convert_data *args = malloc(sizeof(struct _convert_data));
3134 args->start = idx;
3135 args->list = files_to_link;
3136 args->total = count;
3137 if (output == SEQ_REGULAR)
3138 args->destroot = format_basename(destroot, TRUE);
3139 else
3140 args->destroot = destroot;
3141 args->input_has_a_seq = FALSE;
3142 args->input_has_a_film = FALSE;
3143 args->debayer = debayer;
3144 args->multiple_output = FALSE;
3145 args->output_type = output;
3146 args->make_link = make_link;
3147 gettimeofday(&(args->t_start), NULL);
3148 start_in_new_thread(convert_thread_worker, args);
3149
3150 return 0;
3151 }
3152
process_register(int nb)3153 int process_register(int nb) {
3154 struct registration_args *reg_args;
3155 struct registration_method *method;
3156 char *msg;
3157 int i;
3158
3159 if (get_thread_run()) {
3160 PRINT_ANOTHER_THREAD_RUNNING;
3161 return 1;
3162 }
3163 sequence *seq = load_sequence(word[1], NULL);
3164 if (!seq)
3165 return 1;
3166
3167 /* getting the selected registration method */
3168 method = malloc(sizeof(struct registration_method));
3169 method->name = strdup(_("Global Star Alignment (deep-sky)"));
3170 method->method_ptr = ®ister_star_alignment;
3171 method->sel = REQUIRES_NO_SELECTION;
3172 method->type = REGTYPE_DEEPSKY;
3173
3174 reg_args = calloc(1, sizeof(struct registration_args));
3175
3176 if (!com.script)
3177 control_window_switch_to_tab(OUTPUT_LOGS);
3178
3179 /* filling the arguments for registration */
3180 reg_args->func = method->method_ptr;
3181 reg_args->seq = seq;
3182 reg_args->reference_image = sequence_find_refimage(seq);
3183 reg_args->process_all_frames = TRUE;
3184 reg_args->follow_star = FALSE;
3185 reg_args->matchSelection = FALSE;
3186 reg_args->translation_only = FALSE;
3187 reg_args->x2upscale = FALSE;
3188 reg_args->prefix = "r_";
3189 reg_args->min_pairs = AT_MATCH_MINPAIRS;
3190
3191 /* check for options */
3192 for (i = 2; i < nb; i++) {
3193 if (word[i]) {
3194 if (!strcmp(word[i], "-drizzle")) {
3195 reg_args->x2upscale = TRUE;
3196 } else if (!strcmp(word[i], "-norot")) {
3197 reg_args->translation_only = TRUE;
3198 } else if (g_str_has_prefix(word[i], "-prefix=")) {
3199 char *current = word[i], *value;
3200 value = current + 8;
3201 if (value[0] == '\0') {
3202 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3203 return 1;
3204 }
3205 reg_args->prefix = strdup(value);
3206 } else if (g_str_has_prefix(word[i], "-minpairs=")) {
3207 char *current = word[i], *value;
3208 value = current + 10;
3209 if (value[0] == '\0') {
3210 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3211 return 1;
3212 }
3213 if (g_ascii_strtoull(value, NULL, 10) < AT_MATCH_MINPAIRS) {
3214 gchar *str = ngettext("%d smaller than minimum allowable star pairs: %d, aborting.\n", "%d smaller than minimum allowable star pairs: %d, aborting.\n",
3215 g_ascii_strtoull(value, NULL, 10));
3216 str = g_strdup_printf(str, g_ascii_strtoull(value, NULL, 10), AT_MATCH_MINPAIRS);
3217 siril_log_message(str);
3218 g_free(str);
3219
3220 return 1;
3221 }
3222 reg_args->min_pairs = g_ascii_strtoull(value, NULL, 10);
3223 }
3224 }
3225 }
3226
3227 // testing free space
3228 if (reg_args->x2upscale ||
3229 (method->method_ptr == register_star_alignment &&
3230 !reg_args->translation_only)) {
3231 // first, remove the files that we are about to create
3232 remove_prefixed_sequence_files(reg_args->seq, reg_args->prefix);
3233
3234 int nb_frames = reg_args->process_all_frames ? reg_args->seq->number : reg_args->seq->selnum;
3235 int64_t size = seq_compute_size(reg_args->seq, nb_frames, get_data_type(seq->bitpix));
3236 if (reg_args->x2upscale)
3237 size *= 4;
3238 if (test_available_space(size) > 0) {
3239 free(reg_args);
3240 free(method);
3241 return 1;
3242 }
3243 }
3244
3245 /* getting the selected registration layer from the combo box. The value is the index
3246 * of the selected line, and they are in the same order than layers so there should be
3247 * an exact matching between the two */
3248 reg_args->layer = (reg_args->seq->nb_layers == 3) ? 1 : 0;
3249 reg_args->interpolation = OPENCV_AREA;
3250 get_the_registration_area(reg_args, method); // sets selection
3251 reg_args->run_in_thread = TRUE;
3252 reg_args->load_new_sequence = FALSE; // don't load it for command line execution
3253
3254 msg = siril_log_color_message(
3255 _("Registration: processing using method: %s\n"), "green",
3256 method->name);
3257 free(method);
3258 msg[strlen(msg) - 1] = '\0';
3259 set_progress_bar_data(msg, PROGRESS_RESET);
3260
3261 set_cursor_waiting(TRUE);
3262
3263 start_in_new_thread(register_thread_func, reg_args);
3264 return 0;
3265 }
3266
3267 // parse normalization and filters from the stack command line, starting at word `first'
parse_stack_command_line(struct stacking_configuration * arg,int first,gboolean norm_allowed,gboolean out_allowed)3268 static int parse_stack_command_line(struct stacking_configuration *arg, int first, gboolean norm_allowed, gboolean out_allowed) {
3269 while (word[first]) {
3270 char *current = word[first], *value;
3271 if (!strcmp(current, "-nonorm") || !strcmp(current, "-no_norm"))
3272 arg->force_no_norm = TRUE;
3273 else if (!strcmp(current, "-output_norm")) {
3274 arg->output_norm = TRUE;
3275 } else if (!strcmp(current, "-weighted")) {
3276 if (arg->method != stack_mean_with_rejection) {
3277 siril_log_message(_("Weighting is allowed only with average stacking, ignoring.\n"));
3278 } else if (arg->norm == NO_NORM) {
3279 siril_log_message(_("Weighting is allowed only if normalization has been activated, ignoring.\n"));
3280 } else{
3281 arg->apply_weight = TRUE;
3282 }
3283 } else if (g_str_has_prefix(current, "-norm=")) {
3284 if (!norm_allowed) {
3285 siril_log_message(_("Normalization options are not allowed in this context, ignoring.\n"));
3286 } else {
3287 value = current + 6;
3288 if (!strcmp(value, "add"))
3289 arg->norm = ADDITIVE;
3290 else if (!strcmp(value, "addscale"))
3291 arg->norm = ADDITIVE_SCALING;
3292 else if (!strcmp(value, "mul"))
3293 arg->norm = MULTIPLICATIVE;
3294 else if (!strcmp(value, "mulscale"))
3295 arg->norm = MULTIPLICATIVE_SCALING;
3296 }
3297 } else if (g_str_has_prefix(current, "-filter-fwhm=")) {
3298 value = strchr(current, '=') + 1;
3299 if (value[0] != '\0') {
3300 char *end;
3301 float val = strtof(value, &end);
3302 if (end == value) {
3303 siril_log_message(_("Could not parse argument `%s' to the filter `%s', aborting.\n"), value, current);
3304 return 1;
3305 }
3306 if (*end == '%')
3307 arg->f_fwhm_p = val;
3308 else arg->f_fwhm = val;
3309 } else {
3310 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3311 return 1;
3312 }
3313 } else if (g_str_has_prefix(current, "-filter-wfwhm=")) {
3314 value = strchr(current, '=') + 1;
3315 if (value[0] != '\0') {
3316 char *end;
3317 float val = strtof(value, &end);
3318 if (end == value) {
3319 siril_log_message(_("Could not parse argument `%s' to the filter `%s', aborting.\n"), value, current);
3320 return 1;
3321 }
3322 if (*end == '%')
3323 arg->f_wfwhm_p = val;
3324 else arg->f_wfwhm = val;
3325 } else {
3326 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3327 return 1;
3328 }
3329 } else if (g_str_has_prefix(current, "-filter-round=") ||
3330 g_str_has_prefix(current, "-filter-roundness=")) {
3331 value = strchr(current, '=') + 1;
3332 if (value[0] != '\0') {
3333 char *end;
3334 float val = strtof(value, &end);
3335 if (end == value) {
3336 siril_log_message(_("Could not parse argument `%s' to the filter `%s', aborting.\n"), value, current);
3337 return 1;
3338 }
3339 if (*end == '%')
3340 arg->f_round_p = val;
3341 else arg->f_round = val;
3342 } else {
3343 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3344 return 1;
3345 }
3346 } else if (g_str_has_prefix(current, "-filter-qual=") ||
3347 g_str_has_prefix(current, "-filter-quality=")) {
3348 value = strchr(current, '=') + 1;
3349 if (value[0] != '\0') {
3350 char *end;
3351 float val = strtof(value, &end);
3352 if (end == value) {
3353 siril_log_message(_("Could not parse argument `%s' to the filter `%s', aborting.\n"), value, current);
3354 return 1;
3355 }
3356 if (*end == '%')
3357 arg->f_quality_p = val;
3358 else arg->f_quality = val;
3359 } else {
3360 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3361 return 1;
3362 }
3363 } else if (g_str_has_prefix(current, "-filter-incl") ||
3364 g_str_has_prefix(current, "-filter-included")) {
3365 arg->filter_included = TRUE;
3366 } else if (g_str_has_prefix(current, "-out=")) {
3367 if (out_allowed) {
3368 value = current + 5;
3369 if (value[0] == '\0') {
3370 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3371 return 1;
3372 }
3373 arg->result_file = strdup(value);
3374 }
3375 else {
3376 siril_log_message(_("Output filename option is not allowed in this context, ignoring.\n"));
3377 }
3378 }
3379 else {
3380 siril_log_message(_("Unexpected argument to stacking `%s', aborting.\n"), current);
3381 return 1;
3382 }
3383 first++;
3384 }
3385 return 0;
3386 }
3387
stack_one_seq(struct stacking_configuration * arg)3388 static int stack_one_seq(struct stacking_configuration *arg) {
3389 int retval = -1;
3390 sequence *seq = readseqfile(arg->seqfile);
3391 if (seq != NULL) {
3392 struct stacking_args args = { 0 };
3393 gchar *error = NULL;
3394 if (seq_check_basic_data(seq, FALSE) == -1) {
3395 free(seq);
3396 return 1;
3397 }
3398 siril_log_message(_("Stacking sequence %s\n"), seq->seqname);
3399 args.seq = seq;
3400 args.ref_image = sequence_find_refimage(seq);
3401 // the three below: used only if method is average w/ rejection
3402 if (arg->method == stack_mean_with_rejection && (arg->sig[0] != 0.0 || arg->sig[1] != 0.0)) {
3403 args.sig[0] = arg->sig[0];
3404 args.sig[1] = arg->sig[1];
3405 args.type_of_rejection = arg->type_of_rejection;
3406 } else {
3407 args.type_of_rejection = NO_REJEC;
3408 siril_log_message(_("Not using rejection for stacking\n"));
3409 }
3410 args.coeff.offset = NULL;
3411 args.coeff.mul = NULL;
3412 args.coeff.scale = NULL;
3413 if (!arg->force_no_norm &&
3414 (arg->method == stack_median || arg->method == stack_mean_with_rejection))
3415 args.normalize = arg->norm;
3416 else args.normalize = NO_NORM;
3417 args.method = arg->method;
3418 args.force_norm = FALSE;
3419 args.output_norm = arg->output_norm;
3420 args.reglayer = args.seq->nb_layers == 1 ? 0 : 1;
3421 args.apply_weight = arg->apply_weight;
3422
3423 // manage filters
3424 if (convert_stack_data_to_filter(arg, &args) ||
3425 setup_filtered_data(&args)) {
3426 free_sequence(seq, TRUE);
3427 return 1;
3428 }
3429 args.description = describe_filter(seq, args.filtering_criterion, args.filtering_parameter);
3430 args.use_32bit_output = evaluate_stacking_should_output_32bits(args.method,
3431 args.seq, args.nb_images_to_stack, &error);
3432 if (error) {
3433 siril_log_color_message(error, "red");
3434 free_sequence(seq, TRUE);
3435 return 1;
3436 }
3437
3438 if (!arg->result_file) {
3439 char filename[256];
3440 char *suffix = g_str_has_suffix(seq->seqname, "_") ||
3441 g_str_has_suffix(seq->seqname, "-") ? "" : "_";
3442 snprintf(filename, 256, "%s%sstacked%s",
3443 seq->seqname, suffix, com.pref.ext);
3444 arg->result_file = strdup(filename);
3445 }
3446
3447 main_stack(&args);
3448
3449 retval = args.retval;
3450 clean_end_stacking(&args);
3451 free_sequence(seq, TRUE);
3452 free(args.image_indices);
3453 free(args.description);
3454
3455 if (!retval) {
3456 struct noise_data noise_args = { .fit = &gfit, .verbose = FALSE, .use_idle = FALSE };
3457 noise(&noise_args);
3458 if (savefits(arg->result_file, &gfit))
3459 siril_log_color_message(_("Could not save the stacking result %s\n"),
3460 "red", arg->result_file);
3461 //clearfits(&gfit);
3462 ++arg->number_of_loaded_sequences;
3463 }
3464 else if (!get_thread_run()) return -1;
3465
3466 } else {
3467 siril_log_message(_("No sequence `%s' found.\n"), arg->seqfile);
3468 }
3469 return retval;
3470 }
3471
stackall_worker(gpointer garg)3472 static gpointer stackall_worker(gpointer garg) {
3473 GDir *dir;
3474 GError *error = NULL;
3475 const gchar *file;
3476 struct timeval t_end;
3477 struct stacking_configuration *arg = (struct stacking_configuration *)garg;
3478 gboolean was_in_script = com.script;
3479 com.script = TRUE;
3480
3481 siril_log_message(_("Looking for sequences in current working directory...\n"));
3482 if (check_seq(FALSE) || (dir = g_dir_open(com.wd, 0, &error)) == NULL) {
3483 siril_log_message(_("Error while searching sequences or opening the directory.\n"));
3484 if (error) {
3485 fprintf(stderr, "stackall: %s\n", error->message);
3486 g_clear_error(&error);
3487 }
3488 siril_add_idle(end_generic, NULL);
3489 return NULL;
3490 }
3491
3492 siril_log_message(_("Starting stacking of found sequences...\n"));
3493 while ((file = g_dir_read_name(dir)) != NULL) {
3494 if (g_str_has_suffix(file, ".seq")) {
3495 arg->seqfile = strdup(file);
3496 stack_one_seq(arg);
3497
3498 g_free(arg->result_file);
3499 arg->result_file = NULL;
3500 g_free(arg->seqfile);
3501 }
3502 }
3503
3504 siril_log_message(_("Stacked %d sequences successfully.\n"), arg->number_of_loaded_sequences);
3505 gettimeofday(&t_end, NULL);
3506 show_time(arg->t_start, t_end);
3507 g_dir_close(dir);
3508 free(arg);
3509 com.script = was_in_script;
3510 siril_add_idle(end_generic, NULL);
3511 return NULL;
3512 }
3513
process_stackall(int nb)3514 int process_stackall(int nb) {
3515 struct stacking_configuration *arg;
3516
3517 arg = calloc(1, sizeof(struct stacking_configuration));
3518 arg->f_fwhm = -1.f; arg->f_fwhm_p = -1.f; arg->f_round = -1.f;
3519 arg->f_round_p = -1.f; arg->f_quality = -1.f; arg->f_quality_p = -1.f;
3520 arg->filter_included = FALSE; arg->norm = NO_NORM; arg->force_no_norm = FALSE;
3521 arg->apply_weight = FALSE;
3522
3523 // stackall { sum | min | max } [-filter-fwhm=value[%]] [-filter-wfwhm=value[%]] [-filter-round=value[%]] [-filter-quality=value[%]] [-filter-incl[uded]]
3524 // stackall { med | median } [-nonorm, norm=] [-filter-incl[uded]]
3525 // stackall { rej | mean } sigma_low sigma_high [-nonorm, norm=] [-filter-fwhm=value[%]] [-filter-round=value[%]] [-filter-quality=value[%]] [-filter-incl[uded]] [-weighted]
3526 if (!word[1]) {
3527 arg->method = stack_summing_generic;
3528 } else {
3529 int start_arg_opt = 2;
3530 gboolean allow_norm = FALSE;
3531 if (!strcmp(word[1], "sum")) {
3532 arg->method = stack_summing_generic;
3533 } else if (!strcmp(word[1], "max")) {
3534 arg->method = stack_addmax;
3535 } else if (!strcmp(word[1], "min")) {
3536 arg->method = stack_addmin;
3537 } else if (!strcmp(word[1], "med") || !strcmp(word[1], "median")) {
3538 arg->method = stack_median;
3539 allow_norm = TRUE;
3540 } else if (!strcmp(word[1], "rej") || !strcmp(word[1], "mean")) {
3541 int shift = 1;
3542 if (!strcmp(word[3], "p") || !strcmp(word[3], "percentile")) {
3543 arg->type_of_rejection = PERCENTILE;
3544 } else if (!strcmp(word[3], "s") || !strcmp(word[3], "sigma")) {
3545 arg->type_of_rejection = SIGMA;
3546 } else if (!strcmp(word[3], "a") || !strcmp(word[3], "mad")) {
3547 arg->type_of_rejection = MAD;
3548 } else if (!strcmp(word[3], "m") || !strcmp(word[3], "median")) {
3549 arg->type_of_rejection = SIGMEDIAN;
3550 } else if (!strcmp(word[3], "l") || !strcmp(word[3], "linear")) {
3551 arg->type_of_rejection = LINEARFIT;
3552 } else if (!strcmp(word[3], "w") || !strcmp(word[3], "winsorized")) {
3553 arg->type_of_rejection = WINSORIZED;
3554 } else if (!strcmp(word[3], "g") || !strcmp(word[3], "generalized")) {
3555 arg->type_of_rejection = GESDT;
3556 } else {
3557 arg->type_of_rejection = WINSORIZED;
3558 shift = 0;
3559 }
3560 if (!word[2 + shift] || !word[3 + shift] || (arg->sig[0] = g_ascii_strtod(word[2 + shift], NULL)) < 0.0
3561 || (arg->sig[1] = g_ascii_strtod(word[3 + shift], NULL)) < 0.0) {
3562 siril_log_color_message(_("The average stacking with rejection requires two extra arguments: sigma low and high.\n"), "red");
3563 goto failure;
3564 }
3565 if (((arg->type_of_rejection == GESDT)
3566 || (arg->type_of_rejection == PERCENTILE))
3567 && (arg->sig[0] > 1.0 || (arg->sig[1] > 1.0))) {
3568 siril_log_color_message(_("Extra parameters of this rejection algorithm must be between 0 and 1.\n"), "red");
3569 goto failure;
3570 }
3571 arg->method = stack_mean_with_rejection;
3572 start_arg_opt = 4 + shift;
3573 allow_norm = TRUE;
3574 }
3575 else {
3576 siril_log_color_message(_("Stacking method type '%s' is invalid\n"), "red", word[2]);
3577 goto failure;
3578 }
3579 if (parse_stack_command_line(arg, start_arg_opt, allow_norm, FALSE))
3580 goto failure;
3581 }
3582 set_cursor_waiting(TRUE);
3583 if (!com.headless)
3584 control_window_switch_to_tab(OUTPUT_LOGS);
3585 gettimeofday(&arg->t_start, NULL);
3586
3587 start_in_new_thread(stackall_worker, arg);
3588 return 0;
3589
3590 failure:
3591 g_free(arg->result_file);
3592 g_free(arg->seqfile);
3593 free(arg);
3594 return 1;
3595 }
3596
stackone_worker(gpointer garg)3597 static gpointer stackone_worker(gpointer garg) {
3598 int retval = 0;
3599 struct timeval t_end;
3600 struct stacking_configuration *arg = (struct stacking_configuration *)garg;
3601 gboolean was_in_script = com.script;
3602 com.script = TRUE;
3603
3604 retval = stack_one_seq(arg);
3605
3606 if (retval) {
3607 if (retval == ST_ALLOC_ERROR) {
3608 siril_log_message(_("It looks like there is a memory allocation error, change memory settings and try to fix it.\n"));
3609 }
3610 } else {
3611 siril_log_message(_("Stacked sequence successfully.\n"));
3612 }
3613
3614 gettimeofday(&t_end, NULL);
3615 show_time(arg->t_start, t_end);
3616
3617 g_free(arg->result_file);
3618 g_free(arg->seqfile);
3619 free(arg);
3620 com.script = was_in_script;
3621 siril_add_idle(end_generic, NULL);
3622 return GINT_TO_POINTER(retval);
3623 }
3624
process_stackone(int nb)3625 int process_stackone(int nb) {
3626 struct stacking_configuration *arg;
3627
3628 arg = calloc(1, sizeof(struct stacking_configuration));
3629 arg->f_fwhm = -1.f; arg->f_fwhm_p = -1.f; arg->f_round = -1.f;
3630 arg->f_round_p = -1.f; arg->f_quality = -1.f; arg->f_quality_p = -1.f;
3631 arg->filter_included = FALSE; arg->norm = NO_NORM; arg->force_no_norm = FALSE;
3632 arg->apply_weight = FALSE;
3633
3634 sequence *seq = load_sequence(word[1], &arg->seqfile);
3635 if (!seq)
3636 goto failure;
3637
3638 // stack seqfilename { sum | min | max } [-filter-fwhm=value[%]] [-filter-wfwhm=value[%]] [-filter-round=value[%]] [-filter-quality=value[%]] [-filter-incl[uded]] -out=result_filename
3639 // stack seqfilename { med | median } [-nonorm, norm=] [-filter-incl[uded]] -out=result_filename
3640 // stack seqfilename { rej | mean } sigma_low sigma_high [-nonorm, norm=] [-filter-fwhm=value[%]] [-filter-round=value[%]] [-filter-quality=value[%]] [-filter-incl[uded]] [-weighted] -out=result_filename
3641 if (!word[2]) {
3642 arg->method = stack_summing_generic;
3643 } else {
3644 int start_arg_opt = 3;
3645 gboolean allow_norm = FALSE;
3646 if (!strcmp(word[2], "sum")) {
3647 arg->method = stack_summing_generic;
3648 } else if (!strcmp(word[2], "max")) {
3649 arg->method = stack_addmax;
3650 } else if (!strcmp(word[2], "min")) {
3651 arg->method = stack_addmin;
3652 } else if (!strcmp(word[2], "med") || !strcmp(word[2], "median")) {
3653 arg->method = stack_median;
3654 allow_norm = TRUE;
3655 } else if (!strcmp(word[2], "rej") || !strcmp(word[2], "mean")) {
3656 int shift = 1;
3657 if (!strcmp(word[3], "p") || !strcmp(word[3], "percentile")) {
3658 arg->type_of_rejection = PERCENTILE;
3659 } else if (!strcmp(word[3], "s") || !strcmp(word[3], "sigma")) {
3660 arg->type_of_rejection = SIGMA;
3661 } else if (!strcmp(word[3], "a") || !strcmp(word[3], "mad")) {
3662 arg->type_of_rejection = MAD;
3663 } else if (!strcmp(word[3], "m") || !strcmp(word[3], "median")) {
3664 arg->type_of_rejection = SIGMEDIAN;
3665 } else if (!strcmp(word[3], "l") || !strcmp(word[3], "linear")) {
3666 arg->type_of_rejection = LINEARFIT;
3667 } else if (!strcmp(word[3], "w") || !strcmp(word[3], "winsorized")) {
3668 arg->type_of_rejection = WINSORIZED;
3669 } else if (!strcmp(word[3], "g") || !strcmp(word[3], "generalized")) {
3670 arg->type_of_rejection = GESDT;
3671 } else {
3672 arg->type_of_rejection = WINSORIZED;
3673 shift = 0;
3674 }
3675 if (!word[3 + shift] || !word[4 + shift] || (arg->sig[0] = g_ascii_strtod(word[3 + shift], NULL)) < 0.0
3676 || (arg->sig[1] = g_ascii_strtod(word[4 + shift], NULL)) < 0.0) {
3677 siril_log_color_message(_("The average stacking with rejection requires two extra arguments: sigma low and high.\n"), "red");
3678 goto failure;
3679 }
3680 arg->method = stack_mean_with_rejection;
3681 start_arg_opt = 5 + shift;
3682 allow_norm = TRUE;
3683 }
3684 else {
3685 siril_log_color_message(_("Stacking method type '%s' is invalid\n"), "red", word[2]);
3686 goto failure;
3687 }
3688 if (parse_stack_command_line(arg, start_arg_opt, allow_norm, TRUE))
3689 goto failure;
3690 }
3691 set_cursor_waiting(TRUE);
3692 gettimeofday(&arg->t_start, NULL);
3693 if (!com.headless)
3694 control_window_switch_to_tab(OUTPUT_LOGS);
3695
3696 start_in_new_thread(stackone_worker, arg);
3697 return 0;
3698
3699 failure:
3700 g_free(arg->result_file);
3701 g_free(arg->seqfile);
3702 free(arg);
3703 return 1;
3704 }
3705
process_preprocess(int nb)3706 int process_preprocess(int nb) {
3707 struct preprocessing_data *args;
3708 int i, retvalue = 0;
3709
3710 if (word[1][0] == '\0') {
3711 return -1;
3712 }
3713
3714 sequence *seq = load_sequence(word[1], NULL);
3715 if (!seq)
3716 return 1;
3717
3718 args = calloc(1, sizeof(struct preprocessing_data));
3719 args->ppprefix = "pp_";
3720 args->bias_level = FLT_MAX;
3721 if (seq->type == SEQ_SER) args->output_seqtype = SEQ_SER; // to be able to check allow_32bit_output. Overiden by -fitseq if required
3722
3723 /* checking for options */
3724 for (i = 2; i < nb; i++) {
3725 if (word[i]) {
3726 if (g_str_has_prefix(word[i], "-bias=")) {
3727 gchar *expression = g_shell_unquote(word[i] + 6, NULL);
3728 if (expression[0] == '=') {
3729 int offsetlevel = evaluateoffsetlevel(expression+1);
3730 if (!offsetlevel) {
3731 siril_log_message(_("The offset value could not be parsed from expression: %s, aborting.\n"), expression +1);
3732 g_free(expression);
3733 retvalue = 1;
3734 break;
3735 } else {
3736 g_free(expression);
3737 siril_log_message(_("Synthetic offset: Level = %d\n"), offsetlevel);
3738 int maxlevel = (gfit.orig_bitpix == BYTE_IMG) ? UCHAR_MAX : USHRT_MAX;
3739 if ((offsetlevel > maxlevel) || (offsetlevel < -maxlevel) ) { // not excluding all neg values here to allow defining a pedestal
3740 siril_log_message(_("The offset value is out of allowable bounds [-%d,%d], aborting.\n"), maxlevel, maxlevel);
3741 retvalue = 1;
3742 break;
3743 } else {
3744 args->bias_level = (float)offsetlevel * INV_USHRT_MAX_SINGLE; //converting to [0 1] to use with soper
3745 args->use_bias = TRUE;
3746 }
3747 }
3748 } else {
3749 g_free(expression);
3750 args->bias = calloc(1, sizeof(fits));
3751 if (!readfits(word[i] + 6, args->bias, NULL, !com.pref.force_to_16bit)) {
3752 args->use_bias = TRUE;
3753 } else {
3754 retvalue = 1;
3755 free(args->bias);
3756 break;
3757 }
3758 }
3759 } else if (g_str_has_prefix(word[i], "-dark=")) {
3760 args->dark = calloc(1, sizeof(fits));
3761 if (!readfits(word[i] + 6, args->dark, NULL, !com.pref.force_to_16bit)) {
3762 args->use_dark = TRUE;
3763 args->use_cosmetic_correction = TRUE;
3764 } else {
3765 retvalue = 1;
3766 free(args->dark);
3767 break;
3768 }
3769 } else if (g_str_has_prefix(word[i], "-flat=")) {
3770 args->flat = calloc(1, sizeof(fits));
3771 if (!readfits(word[i] + 6, args->flat, NULL, !com.pref.force_to_16bit)) {
3772 args->use_flat = TRUE;
3773 } else {
3774 retvalue = 1;
3775 free(args->flat);
3776 break;
3777 }
3778 } else if (g_str_has_prefix(word[i], "-prefix=")) {
3779 char *current = word[i], *value;
3780 value = current + 8;
3781 if (value[0] == '\0') {
3782 siril_log_message(_("Missing argument to %s, aborting.\n"), current);
3783 retvalue = 1;
3784 break;
3785 }
3786 args->ppprefix = strdup(value);
3787 } else if (!strcmp(word[i], "-opt")) {
3788 args->use_dark_optim = TRUE;
3789 } else if (!strcmp(word[i], "-fix_xtrans")) {
3790 args->fix_xtrans = TRUE;
3791 } else if (!strcmp(word[i], "-cfa")) {
3792 args->is_cfa = TRUE;
3793 } else if (!strcmp(word[i], "-debayer")) {
3794 args->debayer = TRUE;
3795 } else if (!strcmp(word[i], "-stretch")) {
3796 siril_log_message(_("-stretch option is now deprecated.\n")); // TODO. Should we keep it only for compatibility?
3797 } else if (!strcmp(word[i], "-flip")) {
3798 siril_log_message(_("-flip option is now deprecated.\n")); // TODO. Should we keep it only for compatibility?
3799 } else if (!strcmp(word[i], "-equalize_cfa")) {
3800 args->equalize_cfa = TRUE;
3801 } else if (!strcmp(word[i], "-fitseq")) {
3802 args->output_seqtype = SEQ_FITSEQ;
3803 }
3804 }
3805 }
3806
3807 if (retvalue) {
3808 free(args);
3809 return -1;
3810 }
3811
3812 siril_log_color_message(_("Preprocessing...\n"), "green");
3813 gettimeofday(&args->t_start, NULL);
3814 args->seq = seq;
3815 args->is_sequence = TRUE;
3816 args->autolevel = TRUE;
3817 args->normalisation = 1.0f; // will be updated anyway
3818 args->sigma[0] = -1.00; /* cold pixels: it is better to deactivate it */
3819 args->sigma[1] = 3.00; /* hot pixels */
3820 args->allow_32bit_output = (args->output_seqtype == SEQ_REGULAR
3821 || args->output_seqtype == SEQ_FITSEQ) && !com.pref.force_to_16bit;
3822
3823 // start preprocessing
3824 set_cursor_waiting(TRUE);
3825
3826 start_sequence_preprocessing(args);
3827 return 0;
3828 }
3829
process_set_32bits(int nb)3830 int process_set_32bits(int nb) {
3831 com.pref.force_to_16bit = word[0][3] == '1';
3832 if (com.pref.force_to_16bit)
3833 siril_log_message(_("16-bit per channel in processed images mode is active\n"));
3834 else siril_log_message(_("32-bit per channel in processed images mode is active\n"));
3835 writeinitfile();
3836 if (!com.headless)
3837 set_GUI_misc();
3838 return 0;
3839 }
3840
process_set_compress(int nb)3841 int process_set_compress(int nb) {
3842 gboolean compress = g_ascii_strtoull(word[1], NULL, 10) == 1;
3843 int method = 0;
3844 double q = 16.0, hscale= 4.0;
3845
3846 if (compress) {
3847 if (!word[2] || !word[3] || (!g_str_has_prefix(word[2], "-type="))) {
3848 siril_log_message(_("Please specify the type of compression and quantization value.\n"));
3849 return 1;
3850 }
3851 gchar *comp = NULL;
3852 if (!g_ascii_strncasecmp(word[2] + 6, "rice", 4)) {
3853 method = RICE_COMP;
3854 comp = g_strdup("rice");
3855 } else if (!g_ascii_strncasecmp(word[2] + 6, "gzip1", 5)) {
3856 method = GZIP1_COMP;
3857 comp = g_strdup("GZIP1");
3858 } else if (!g_ascii_strncasecmp(word[2] + 6, "gzip2", 5)) {
3859 method = GZIP2_COMP;
3860 comp = g_strdup("GZIP2");
3861 } /*else if (!g_ascii_strncasecmp(word[2] + 6, "hcompress", 9)) {
3862 method = HCOMPRESS_COMP;
3863 if (!word[4]) {
3864 siril_log_message(_("Please specify the value of hcompress scale factor.\n"));
3865 g_free(comp);
3866 return 1;
3867 }
3868 hscale = g_ascii_strtod(word[4], NULL);
3869 comp = g_strdup_printf("hcompress (scale factor = %.2lf) ", hscale);
3870 }*/ else {
3871 // siril_log_message(_("Wrong type of compression. Choices are rice, gzip1, gzip2 or hcompress\n"));
3872 siril_log_message(_("Wrong type of compression. Choices are rice, gzip1, gzip2\n"));
3873 return 1;
3874 }
3875 if (!word[3]) {
3876 siril_log_message(_("Please specify the value of quantization.\n"));
3877 g_free(comp);
3878 return 1;
3879 }
3880 q = g_ascii_strtod(word[3], NULL);
3881 if (q == 0.0 && (method == RICE_COMP || (method == HCOMPRESS_COMP))) {
3882 siril_log_message(_("Quantization can only be equal to 0 for GZIP1 and GZIP2 algorithms.\n"));
3883 return 1;
3884 }
3885 siril_log_message(_("Compression enabled with the %s algorithm and a quantization value of %.2lf\n"), comp, q);
3886 g_free(comp);
3887 } else {
3888 siril_log_message(_("No compression enabled.\n"));
3889 }
3890 com.pref.comp.fits_enabled = compress;
3891 com.pref.comp.fits_method = method;
3892 com.pref.comp.fits_quantization = q;
3893 com.pref.comp.fits_hcompress_scale = hscale;
3894 if (!com.headless)
3895 set_GUI_compression();
3896 writeinitfile();
3897 return 0;
3898 }
3899
3900 #ifdef _OPENMP
process_set_cpu(int nb)3901 int process_set_cpu(int nb){
3902 int proc_in, proc_out, proc_max;
3903
3904 proc_in = g_ascii_strtoull(word[1], NULL, 10);
3905 proc_max = omp_get_num_procs();
3906 if (proc_in > proc_max || proc_in < 1) {
3907 siril_log_message(_("Number of logical processors MUST be greater "
3908 "than 0 and lower or equal to %d.\n"), proc_max);
3909 return 1;
3910 }
3911 omp_set_num_threads(proc_in);
3912
3913 #pragma omp parallel
3914 {
3915 proc_out = omp_get_num_threads();
3916 }
3917
3918 gchar *str = ngettext("Using now %d logical processor\n", "Using now %d logical processors\n", proc_out);
3919 str = g_strdup_printf(str, proc_out);
3920 siril_log_message(str);
3921 g_free(str);
3922
3923 com.max_thread = proc_out;
3924 if (!com.headless)
3925 update_spinCPU(0);
3926
3927 return 0;
3928 }
3929 #endif
3930
process_set_mem(int nb)3931 int process_set_mem(int nb){
3932 double ratio = g_ascii_strtod(word[1], NULL);
3933 if (ratio < 0.05 || ratio > 4.0) {
3934 siril_log_message(_("The accepted range for the ratio of memory used for stacking is [0.05, 4], with values below the available memory recommended\n"));
3935 return 1;
3936 }
3937 if (ratio > 1.0) {
3938 siril_log_message(_("Setting the ratio of memory used for stacking above 1 will require the use of on-disk memory, which can be very slow and is unrecommended (%g requested)\n"), ratio);
3939 }
3940 com.pref.stack.memory_ratio = ratio;
3941 writeinitfile();
3942 siril_log_message(_("Usable memory for stacking changed to %g\n"), ratio);
3943 if (!com.headless)
3944 set_GUI_misc();
3945 return 0;
3946 }
3947
process_help(int nb)3948 int process_help(int nb){
3949 control_window_switch_to_tab(OUTPUT_LOGS);
3950
3951 command *current = commands;
3952 siril_log_message(_("********* LIST OF AVAILABLE COMMANDS *********\n"));
3953 while (current->process) {
3954 siril_log_message("%s\n", current->usage);
3955 current++;
3956 }
3957 siril_log_message(_("********* END OF THE LIST *********\n"));
3958 return 0;
3959 }
3960
process_exit(int nb)3961 int process_exit(int nb){
3962 gtk_main_quit();
3963 return 0;
3964 }
3965
process_extract(int nb)3966 int process_extract(int nb) {
3967 int Nbr_Plan, maxplan, mins;
3968
3969 if (!single_image_is_loaded()) return 1;
3970
3971 Nbr_Plan = g_ascii_strtoull(word[1], NULL, 10);
3972
3973 mins = min (gfit.rx, gfit.ry);
3974 maxplan = log(mins) / log(2) - 2;
3975
3976 if ( Nbr_Plan > maxplan ){
3977 siril_log_message(_("Wavelet: maximum number of plans for this image size is %d\n"),
3978 maxplan);
3979 return 1;
3980 }
3981
3982 struct wavelets_filter_data *args = malloc(sizeof(struct wavelets_filter_data));
3983
3984 args->Type = TO_PAVE_BSPLINE;
3985 args->Nbr_Plan = Nbr_Plan;
3986 args->fit = &gfit;
3987 start_in_new_thread(extract_plans, args);
3988
3989 return 0;
3990 }
3991
process_reloadscripts(int nb)3992 int process_reloadscripts(int nb){
3993 return refresh_scripts(FALSE, NULL);
3994 }
3995
3996
process_requires(int nb)3997 int process_requires(int nb) {
3998 gchar **version, **required;
3999 gint major, minor, micro;
4000 gint req_major, req_minor, req_micro;
4001
4002 version = g_strsplit(PACKAGE_VERSION, ".", 3);
4003 required = g_strsplit(word[1], ".", 3);
4004
4005 if (g_strv_length(required) != 3) {
4006 siril_log_color_message(_("Required version is not correct.\n"), "red");
4007
4008 g_strfreev(version);
4009 g_strfreev(required);
4010 return 1;
4011 }
4012
4013 major = g_ascii_strtoull(version[0], NULL, 10);
4014 minor = g_ascii_strtoull(version[1], NULL, 10);
4015 micro = g_ascii_strtoull(version[2], NULL, 10);
4016
4017 req_major = g_ascii_strtoull(required[0], NULL, 10);
4018 req_minor = g_ascii_strtoull(required[1], NULL, 10);
4019 req_micro = g_ascii_strtoull(required[2], NULL, 10);
4020
4021 g_strfreev(version);
4022 g_strfreev(required);
4023
4024 if ((major > req_major || (major == req_major && minor > req_minor)
4025 || (major == req_major && minor == req_minor && micro >= req_micro))) {
4026 // no need to output something in script conditions
4027 if (!com.script) {
4028 siril_log_message(_("The required version of Siril is ok.\n"));
4029 }
4030 return 0;
4031 } else {
4032 if (!com.script) {
4033 siril_log_color_message(_("A newer version of Siril is required, please update your version.\n"), "red");
4034 } else {
4035 siril_log_color_message(_("The script you are executing requires a newer version of Siril to run (%s), aborting.\n"), "red", word[1]);
4036 }
4037 return 1;
4038 }
4039 }
4040