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 * WARNING: the code in this file and its header will not work properly
22 * on big endian systems.
23 */
24
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <math.h>
29 #ifdef _WIN32
30 #include <io.h>
31 #endif
32 #include "core/siril.h"
33 #include "core/proto.h"
34 #include "core/siril_date.h"
35 #include "gui/utils.h"
36 #include "gui/progress_and_log.h"
37 #include "algos/demosaicing.h"
38 #include "io/conversion.h"
39 #include "io/image_format_fits.h"
40 #include "ser.h"
41
42 static gboolean user_warned = FALSE;
43
44 static int ser_write_header(struct ser_struct *ser_file);
45 static int ser_write_image_for_writer(struct seqwriter_data *writer, fits *image, int index);
46 static int ser_write_frame_from_fit_internal(struct ser_struct *ser_file, fits *fit, int frame_no);
47
48
49 /* Output SER timestamp */
display_date(guint64 timestamp,char * txt)50 static int display_date(guint64 timestamp, char *txt) {
51 if (timestamp == 0)
52 return -1;
53
54 GDateTime *date = ser_timestamp_to_date_time(timestamp);
55 if (date) {
56 gchar *str = date_time_to_FITS_date(date);
57 fprintf(stdout, "%s%s\n", txt, str);
58 free(str);
59 g_date_time_unref(date);
60 }
61 return 0;
62 }
63
convert_color_id_to_char(ser_color color_id)64 static char *convert_color_id_to_char(ser_color color_id) {
65 switch (color_id) {
66 case SER_MONO:
67 return "MONO";
68 case SER_BAYER_RGGB:
69 return "RGGB";
70 case SER_BAYER_BGGR:
71 return "BGGR";
72 case SER_BAYER_GBRG:
73 return "GBRG";
74 case SER_BAYER_GRBG:
75 return "GRBG";
76 case SER_BAYER_CYYM:
77 return "CYYM";
78 case SER_BAYER_YCMY:
79 return "YCMY";
80 case SER_BAYER_YMCY:
81 return "YMCY";
82 case SER_BAYER_MYYC:
83 return "MYYC";
84 case SER_RGB:
85 return "RGB";
86 case SER_BGR:
87 return "BGR";
88 default:
89 return "";
90 }
91 }
92
93 /* reads timestamps from the trailer of the file and stores them in ser_file->ts */
ser_read_timestamp(struct ser_struct * ser_file)94 static int ser_read_timestamp(struct ser_struct *ser_file) {
95 int i;
96 gboolean timestamps_in_order = TRUE;
97 guint64 previous_ts = 0L;
98 gint64 frame_size;
99
100 ser_file->fps = -1.0; // will be calculated from the timestamps
101
102 if (!ser_file->frame_count || ser_file->image_width <= 0 ||
103 ser_file->image_height <= 0 || ser_file->byte_pixel_depth <= 0 ||
104 !ser_file->number_of_planes)
105 return 0;
106
107 frame_size = ser_file->image_width *
108 ser_file->image_height * ser_file->number_of_planes;
109 gint64 offset = SER_HEADER_LEN + frame_size *
110 (gint64)ser_file->byte_pixel_depth * (gint64)ser_file->frame_count;
111 /* Check if file is large enough to have timestamps */
112 if (ser_file->filesize >= offset + (8 * ser_file->frame_count)) {
113 ser_file->ts = calloc(8, ser_file->frame_count);
114 if (!ser_file->ts) {
115 PRINT_ALLOC_ERR;
116 return 0;
117 }
118 ser_file->ts_alloc = ser_file->frame_count;
119
120 // Seek to start of timestamps
121 for (i = 0; i < ser_file->frame_count; i++) {
122 if ((gint64) -1 == fseek64(ser_file->file, offset + (i * 8), SEEK_SET))
123 return -1;
124
125 if (8 != fread(&ser_file->ts[i], 1, 8, ser_file->file))
126 return 0;
127
128 ser_file->ts[i] = le64_to_cpu(ser_file->ts[i]);
129 }
130
131 /* Check order of Timestamps */
132 guint64 *ts_ptr = ser_file->ts;
133 guint64 min_ts = *ts_ptr;
134 guint64 max_ts = *ts_ptr;
135
136 for (i = 0; i < ser_file->frame_count; i++) {
137 if (*ts_ptr < previous_ts) {
138 // Timestamps are not in order
139 timestamps_in_order = FALSE;
140 }
141 previous_ts = *ts_ptr;
142 // Keep track of maximum timestamp value
143 if (*ts_ptr > max_ts) {
144 max_ts = *ts_ptr;
145 }
146 // Keep track of minimum timestamp value
147 if (*ts_ptr < min_ts) {
148 min_ts = *ts_ptr;
149 }
150 ts_ptr++;
151 }
152
153 if (timestamps_in_order) {
154 if (min_ts == max_ts)
155 fprintf(stdout, _("Warning: timestamps in the SER sequence are all identical.\n"));
156 else fprintf(stdout, _("Timestamps in the SER sequence are correctly ordered.\n"));
157 } else {
158 fprintf(stdout, _("Warning: timestamps in the SER sequence are not in the correct order.\n"));
159 }
160
161 ser_file->ts_min = min_ts;
162 ser_file->ts_max = max_ts;
163 double diff_ts = (ser_file->ts_max - ser_file->ts_min) / 1000.0;
164 // diff_ts now in units of 100 us or ten thousandths of a second
165 if (diff_ts > 0.0) {
166 // There is a positive time difference between first and last
167 // timestamps, we can calculate a frames per second value
168 ser_file->fps = (ser_file->frame_count - 1) * 10000.0 / diff_ts;
169 }
170 } else {
171 fprintf(stdout, _("Warning: no timestamps stored in the SER sequence.\n"));
172 }
173 return 0;
174 }
175
ser_recompute_frame_count(struct ser_struct * ser_file)176 static int ser_recompute_frame_count(struct ser_struct *ser_file) {
177 int frame_count_calculated;
178 gint64 filesize = ser_file->filesize;
179
180 siril_log_message(_("Trying to fix broken SER file...\n"));
181 gint64 frame_size = ser_file->image_width * ser_file->image_height;
182 if (frame_size == 0)
183 return 0;
184
185 if (ser_file->color_id == SER_RGB || ser_file->color_id == SER_BGR) {
186 frame_size *= 3; // Color images have twice as many samples
187 }
188
189 if (ser_file->bit_pixel_depth > 8) {
190 frame_size *= 2; // Greater than 8-bit data has 2 bytes per pixel rather than one
191 }
192
193 filesize -= SER_HEADER_LEN; // Remove header size from file size
194 frame_count_calculated = filesize / frame_size;
195
196 return frame_count_calculated;
197 }
198
ser_read_header(struct ser_struct * ser_file)199 static int ser_read_header(struct ser_struct *ser_file) {
200 char header[SER_HEADER_LEN];
201
202 if (!ser_file || ser_file->file == NULL)
203 return -1;
204
205 /* Get file size */
206 fseek64(ser_file->file, 0, SEEK_END);
207 ser_file->filesize = ftell64(ser_file->file);
208 fseek64(ser_file->file, 0, SEEK_SET);
209 if (ser_file->filesize == -1) {
210 perror("seek");
211 return -1;
212 }
213
214 /* Read header (size of 178) */
215 if (SER_HEADER_LEN != fread(header, 1, sizeof header, ser_file->file)) {
216 perror("fread");
217 return -1;
218 }
219
220 // modify this to support big endian
221 memcpy(&ser_file->lu_id, header + 14, 28); // read all integers
222
223 ser_file->lu_id = le32_to_cpu(ser_file->lu_id);
224 ser_file->color_id = le32_to_cpu(ser_file->color_id);
225 ser_file->little_endian = le32_to_cpu(ser_file->little_endian);
226 ser_file->image_width = le32_to_cpu(ser_file->image_width);
227 ser_file->image_height = le32_to_cpu(ser_file->image_height);
228 ser_file->bit_pixel_depth = le32_to_cpu(ser_file->bit_pixel_depth);
229 ser_file->frame_count = le32_to_cpu(ser_file->frame_count);
230
231 memcpy(&ser_file->date, header + 162, 8);
232 memcpy(&ser_file->date_utc, header + 170, 8);
233
234 ser_file->date = le64_to_cpu(ser_file->date);
235 ser_file->date_utc = le64_to_cpu(ser_file->date_utc);
236
237 // strings
238 ser_file->file_id = g_strndup(header, 14);
239
240 memcpy(ser_file->observer, header + 42, 40);
241 memcpy(ser_file->instrument, header + 82, 40);
242 memcpy(ser_file->telescope, header + 122, 40);
243
244 /* internal representations of header data */
245 if (ser_file->bit_pixel_depth <= 8)
246 ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_8;
247 else ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_16;
248
249 if (ser_file->color_id == SER_RGB || ser_file->color_id == SER_BGR)
250 ser_file->number_of_planes = 3;
251 else
252 ser_file->number_of_planes = 1;
253
254 /* In some cases, oacapture, firecapture, ... crash before writing
255 * frame_count data. Here we try to get the calculated frame count
256 * which has not been written in the header. Then we fix the SER file
257 */
258 if (ser_file->frame_count == 0) {
259 ser_file->frame_count = ser_recompute_frame_count(ser_file);
260
261 if (ser_file->frame_count > 0) {
262 if (ser_write_header(ser_file) == 0)
263 siril_log_message(_("SER file has been fixed...\n"));
264 }
265 }
266
267 ser_read_timestamp(ser_file);
268
269 return 0;
270 }
271
ser_write_timestamps(struct ser_struct * ser_file)272 static int ser_write_timestamps(struct ser_struct *ser_file) {
273 int i;
274 gint64 frame_size;
275
276 if (!ser_file->frame_count || ser_file->image_width <= 0 ||
277 ser_file->image_height <= 0 || ser_file->byte_pixel_depth <= 0 ||
278 !ser_file->number_of_planes)
279 return -1;
280
281 if (ser_file->ts) {
282 // Seek to start of timestamps
283 frame_size = ser_file->image_width * ser_file->image_height
284 * ser_file->number_of_planes;
285 gint64 offset = SER_HEADER_LEN + frame_size *
286 (gint64)ser_file->byte_pixel_depth * (gint64)ser_file->frame_count;
287
288 for (i = 0; i < ser_file->frame_count; i++) {
289 guint64 ts;
290
291 if (i >= ser_file->ts_alloc)
292 break;
293 if ((gint64)-1 == fseek64(ser_file->file, offset+(i*8), SEEK_SET)) {
294 return -1;
295 }
296
297 ts = cpu_to_le64(ser_file->ts[i]);
298
299 if (8 != fwrite(&ts, 1, 8, ser_file->file)) {
300 perror("write timestamps:");
301 return -1;
302 }
303 }
304 }
305 return 0;
306 }
307
308 /* (over)write the header of the opened file on the disk */
ser_write_header(struct ser_struct * ser_file)309 static int ser_write_header(struct ser_struct *ser_file) {
310 char header[SER_HEADER_LEN];
311 struct ser_struct ser_file_le;
312
313 if (!ser_file || ser_file->file == NULL)
314 return -1;
315 if ((gint64) -1 == fseek64(ser_file->file, 0, SEEK_SET)) {
316 perror("seek");
317 return -1;
318 }
319
320 memcpy(&ser_file_le, ser_file, sizeof(struct ser_struct));
321
322 ser_file_le.lu_id = cpu_to_le32(ser_file_le.lu_id);
323 ser_file_le.color_id = cpu_to_le32(ser_file_le.color_id);
324 ser_file_le.little_endian = cpu_to_le32(ser_file_le.little_endian);
325 ser_file_le.image_width = cpu_to_le32(ser_file_le.image_width);
326 ser_file_le.image_height = cpu_to_le32(ser_file_le.image_height);
327 ser_file_le.bit_pixel_depth = cpu_to_le32(ser_file_le.bit_pixel_depth);
328 ser_file_le.frame_count = cpu_to_le32(ser_file_le.frame_count);
329 ser_file_le.date = cpu_to_le64(ser_file_le.date);
330 ser_file_le.date_utc = cpu_to_le64(ser_file_le.date_utc);
331
332 memset(header, 0, sizeof(header));
333 memcpy(header, ser_file_le.file_id, 14);
334 memcpy(header + 14, &ser_file_le.lu_id, 28);
335 memcpy(header + 42, ser_file_le.observer, 40);
336 memcpy(header + 82, ser_file_le.instrument, 40);
337 memcpy(header + 122, ser_file_le.telescope, 40);
338 memcpy(header + 162, &ser_file_le.date, 8);
339 memcpy(header + 170, &ser_file_le.date_utc, 8);
340
341 if (sizeof(header) != fwrite(header, 1, sizeof(header), ser_file->file)) {
342 perror("write");
343 return 1;
344 }
345 return 0;
346 }
347
348 /* populate fields that are not already set in ser_create_file */
ser_write_header_from_fit(struct ser_struct * ser_file,fits * fit)349 static int ser_write_header_from_fit(struct ser_struct *ser_file, fits *fit) {
350 ser_file->image_width = fit->rx;
351 ser_file->image_height = fit->ry;
352 fprintf(stdout, "setting SER image size as %dx%d\n", fit->rx, fit->ry);
353 // already managed during creation for monochrome formats
354 if (fit->naxes[2] == 3) {
355 ser_file->color_id = SER_RGB;
356 }
357 if (ser_file->color_id == SER_RGB)
358 ser_file->number_of_planes = 3;
359 else {
360 if (!g_strcmp0(fit->bayer_pattern, "RGGB")) {
361 ser_file->color_id = SER_BAYER_RGGB;
362 } else if (!g_strcmp0(fit->bayer_pattern, "BGGR")) {
363 ser_file->color_id = SER_BAYER_BGGR;
364 } else if (!g_strcmp0(fit->bayer_pattern, "GBRG")) {
365 ser_file->color_id = SER_BAYER_GBRG;
366 } else if (!g_strcmp0(fit->bayer_pattern, "GRBG")) {
367 ser_file->color_id = SER_BAYER_GRBG;
368 }
369 ser_file->number_of_planes = 1;
370 }
371
372 if (fit->bitpix == BYTE_IMG) {
373 ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_8;
374 ser_file->bit_pixel_depth = 8;
375 } else if (fit->bitpix == USHORT_IMG || fit->bitpix == SHORT_IMG) {
376 ser_file->byte_pixel_depth = SER_PIXEL_DEPTH_16;
377 ser_file->bit_pixel_depth = 16;
378 } else {
379 siril_log_message(_("Writing a 32-bit image to SER files is not supported.\n"));
380 return 1;
381 }
382 if (fit->instrume[0] != 0) {
383 memset(ser_file->instrument, 0, 40);
384 memcpy(ser_file->instrument, fit->instrume, 40);
385 }
386 if (fit->observer[0] != 0) {
387 memset(ser_file->observer, 0, 40);
388 memcpy(ser_file->observer, fit->observer, 40);
389 }
390 if (fit->instrume[0] != 0) {
391 memset(ser_file->telescope, 0, 40);
392 memcpy(ser_file->telescope, fit->telescop, 40);
393 }
394
395 if (fit->date_obs)
396 ser_file->date = date_time_to_ser_timestamp(fit->date_obs);
397 return 0;
398 }
399
get_SER_Bayer_Pattern(ser_color pattern)400 static int get_SER_Bayer_Pattern(ser_color pattern) {
401 switch (pattern) {
402 case SER_BAYER_RGGB:
403 return BAYER_FILTER_RGGB;
404 case SER_BAYER_BGGR:
405 return BAYER_FILTER_BGGR;
406 case SER_BAYER_GBRG:
407 return BAYER_FILTER_GBRG;
408 case SER_BAYER_GRBG:
409 return BAYER_FILTER_GRBG;
410 default:
411 return BAYER_FILTER_NONE;
412 }
413 }
414
415 /* once a buffer (data) has been acquired from the file, with frame_size pixels
416 * read in it, depending on ser_file's endianess and pixel depth, data is
417 * reorganized to match Siril's data format . */
ser_manage_endianess_and_depth(struct ser_struct * ser_file,WORD * data,gint64 frame_size)418 static void ser_manage_endianess_and_depth(struct ser_struct *ser_file,
419 WORD *data, gint64 frame_size) {
420 int i;
421 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
422 // inline conversion to 16 bit
423 for (i = frame_size - 1; i >= 0; i--)
424 data[i] = (WORD) (((BYTE*)data)[i]);
425 } else if (ser_file->little_endian == SER_BIG_ENDIAN) {
426 // inline conversion
427 for (i = frame_size - 1; i >= 0; i--) {
428 data[i] = be16_to_cpu(data[i]);
429 }
430 } else if (ser_file->little_endian == SER_LITTLE_ENDIAN) {
431 // inline conversion
432 for (i = frame_size - 1; i >= 0; i--) {
433 data[i] = le16_to_cpu(data[i]);
434 }
435 }
436 }
437
ser_alloc_ts(struct ser_struct * ser_file,int frame_no)438 static int ser_alloc_ts(struct ser_struct *ser_file, int frame_no) {
439 int retval = 0;
440 #ifdef _OPENMP
441 omp_set_lock(&ser_file->ts_lock);
442 #endif
443 if (ser_file->ts_alloc <= frame_no) {
444 guint64 *new = realloc(ser_file->ts, (frame_no + 1) * 2 * sizeof(guint64));
445 if (!new) {
446 PRINT_ALLOC_ERR;
447 retval = 1;
448 } else {
449 ser_file->ts = new;
450 ser_file->ts_alloc = (frame_no + 1) * 2;
451 }
452 }
453 #ifdef _OPENMP
454 omp_unset_lock(&ser_file->ts_lock);
455 #endif
456 return retval;
457 }
458
459 /*
460 * Public functions
461 */
462
ser_is_cfa(struct ser_struct * ser_file)463 gboolean ser_is_cfa(struct ser_struct *ser_file) {
464 return ser_file && (ser_file->color_id == SER_BAYER_RGGB ||
465 ser_file->color_id == SER_BAYER_GRBG ||
466 ser_file->color_id == SER_BAYER_GBRG ||
467 ser_file->color_id == SER_BAYER_BGGR);
468 // SER_BAYER_CYYM SER_BAYER_YCMY SER_BAYER_YMCY SER_BAYER_MYYC are not
469 // supported yet so returning false for them here is good
470 }
471
472 /* set the timestamps of the ser_file using a list of timestamps in string form */
ser_convertTimeStamp(struct ser_struct * ser_file,GSList * timestamp)473 void ser_convertTimeStamp(struct ser_struct *ser_file, GSList *timestamp) {
474 int i = 0;
475 if (ser_file->ts)
476 free(ser_file->ts);
477 ser_file->ts = calloc(sizeof(guint64), ser_file->frame_count);
478 if (!ser_file->ts) {
479 PRINT_ALLOC_ERR;
480 return;
481 }
482 ser_file->ts_alloc = ser_file->frame_count;
483
484 GSList *t = timestamp;
485 while (t && i < ser_file->frame_count) {
486 guint64 utc = date_time_to_ser_timestamp((GDateTime *)t->data);
487 t = t->next;
488 memcpy(&ser_file->ts[i], &utc, sizeof(guint64));
489 i++;
490 }
491 }
492
ser_display_info(struct ser_struct * ser_file)493 void ser_display_info(struct ser_struct *ser_file) {
494 char *color = convert_color_id_to_char(ser_file->color_id);
495
496 fprintf(stdout, "=========== SER file info ==============\n");
497 fprintf(stdout, "file id: %s\n", ser_file->file_id);
498 fprintf(stdout, "lu id: %d\n", ser_file->lu_id);
499 fprintf(stdout, "little endian: %d\n", ser_file->little_endian);
500 fprintf(stdout, "sensor type: %s\n", color);
501 fprintf(stdout, "image size: %d x %d (%d bits)\n", ser_file->image_width,
502 ser_file->image_height, ser_file->bit_pixel_depth);
503 fprintf(stdout, "frame count: %u\n", ser_file->frame_count);
504 fprintf(stdout, "observer: %.40s\n", ser_file->observer);
505 fprintf(stdout, "instrument: %.40s\n", ser_file->instrument);
506 fprintf(stdout, "telescope: %.40s\n", ser_file->telescope);
507 display_date(ser_file->date, "local time: ");
508 display_date(ser_file->date_utc, "UTC time: ");
509 fprintf(stdout, "fps: %.3lf\n", ser_file->fps);
510 fprintf(stdout, "========================================\n");
511 }
512
ser_end_write(struct ser_struct * ser_file,gboolean abort)513 static int ser_end_write(struct ser_struct *ser_file, gboolean abort) {
514 int retval = 0;
515 if (ser_file->writer) {
516 retval = stop_writer(ser_file->writer, abort);
517 ser_file->frame_count = ser_file->writer->frame_count;
518 free(ser_file->writer);
519 ser_file->writer = NULL;
520 }
521 return retval;
522 }
523
ser_close_and_delete_file(struct ser_struct * ser_file)524 int ser_close_and_delete_file(struct ser_struct *ser_file) {
525 if (ser_file == NULL) return -1;
526 int retval = ser_end_write(ser_file, TRUE);
527 char *filename = ser_file->filename;
528 ser_file->filename = NULL;
529 ser_close_file(ser_file); // closes, frees and zeroes
530 siril_log_message(_("Removing failed SER file: %s\n"), filename);
531 g_unlink(filename);
532 free(filename);
533 return retval;
534 }
535
ser_write_and_close(struct ser_struct * ser_file)536 int ser_write_and_close(struct ser_struct *ser_file) {
537 if (ser_file == NULL) return -1;
538 int retval = ser_end_write(ser_file, FALSE);
539 if (!ser_file->frame_count) {
540 siril_log_color_message(_("The SER sequence is being created with no image in it.\n"), "red");
541 ser_close_and_delete_file(ser_file);
542 return -1;
543 }
544 ser_write_header(ser_file); // writes the header
545 ser_write_timestamps(ser_file); // writes the trailer
546 ser_close_file(ser_file);// closes, frees and zeroes
547 return retval;
548 }
549
550 /* ser_file must be allocated and initialised with ser_init_struct()
551 * the file is created with no image size, the first image added will set it. */
ser_create_file(const char * filename,struct ser_struct * ser_file,gboolean overwrite,struct ser_struct * copy_from)552 int ser_create_file(const char *filename, struct ser_struct *ser_file,
553 gboolean overwrite, struct ser_struct *copy_from) {
554 if (overwrite)
555 g_unlink(filename);
556 if ((ser_file->file = g_fopen(filename, "w+b")) == NULL) {
557 perror("open SER file for creation");
558 return 1;
559 }
560
561 ser_file->filename = strdup(filename);
562 ser_file->ts = NULL;
563 ser_file->ts_alloc = 0;
564 ser_file->fps = -1.0;
565 ser_file->frame_count = 0; // incremented on image add
566
567 if (copy_from) {
568 memcpy(&ser_file->lu_id, ©_from->lu_id, 12);
569 memset(&ser_file->image_width, 0, 16);
570 memcpy(&ser_file->date, ©_from->date, 8);
571 memcpy(&ser_file->date_utc, ©_from->date_utc, 8);
572 ser_file->file_id = strdup(copy_from->file_id);
573 memcpy(ser_file->observer, copy_from->observer, 40);
574 memcpy(ser_file->instrument, copy_from->instrument, 40);
575 memcpy(ser_file->telescope, copy_from->telescope, 40);
576 ser_file->byte_pixel_depth = copy_from->byte_pixel_depth;
577 ser_file->number_of_planes = 0; // used as an indicator of new SER
578
579 if (copy_from->ts && copy_from->frame_count > 0) {
580 ser_file->ts = calloc(8, copy_from->frame_count);
581 if (!ser_file->ts) {
582 PRINT_ALLOC_ERR;
583 }
584 else ser_file->ts_alloc = copy_from->frame_count;
585 }
586 /* we write the header now, but it should be written again
587 * before closing in case the number of the image in the new
588 * SER changes from the copied SER */
589 ser_write_header(ser_file);
590 } else { // new SER
591 ser_file->file_id = strdup("LUCAM-RECORDER");
592 ser_file->lu_id = 0;
593 ser_file->color_id = SER_MONO; // this is 0
594 ser_file->little_endian = SER_LITTLE_ENDIAN; // what will it do on big endian machine?
595 memset(ser_file->observer, 0, 40);
596 memset(ser_file->instrument, 0, 40);
597 memset(ser_file->telescope, 0, 40);
598 memset(&ser_file->date, 0, 8);
599 memset(&ser_file->date_utc, 0, 8);
600 ser_file->number_of_planes = 0; // used as an indicator of new SER
601 }
602 #ifdef _OPENMP
603 omp_init_lock(&ser_file->fd_lock);
604 omp_init_lock(&ser_file->ts_lock);
605 #endif
606 ser_file->writer = malloc(sizeof(struct seqwriter_data));
607 ser_file->writer->write_image_hook = ser_write_image_for_writer;
608 ser_file->writer->sequence = ser_file;
609
610 siril_log_message(_("Created SER file %s\n"), filename);
611 start_writer(ser_file->writer, ser_file->frame_count);
612 return 0;
613 }
614
ser_write_image_for_writer(struct seqwriter_data * writer,fits * image,int index)615 static int ser_write_image_for_writer(struct seqwriter_data *writer, fits *image, int index) {
616 struct ser_struct *ser_file = (struct ser_struct *)writer->sequence;
617
618 return ser_write_frame_from_fit_internal(ser_file, image, index);
619 }
620
ser_open_file(const char * filename,struct ser_struct * ser_file)621 int ser_open_file(const char *filename, struct ser_struct *ser_file) {
622 if (ser_file->file) {
623 fprintf(stderr, "SER: file already opened, or badly closed\n");
624 return -1;
625 }
626 ser_file->file = g_fopen(filename, "r+b"); // now we can fix broken file, so not O_RDONLY anymore
627 if (ser_file->file == NULL) {
628 perror("SER file open");
629 return -1;
630 }
631 if (ser_read_header(ser_file)) {
632 fprintf(stderr, "SER: reading header failed, closing file %s\n",
633 filename);
634 ser_close_file(ser_file);
635 return -1;
636 }
637 ser_file->filename = strdup(filename);
638
639 #ifdef _OPENMP
640 omp_init_lock(&ser_file->fd_lock);
641 omp_init_lock(&ser_file->ts_lock);
642 #endif
643 return 0;
644 }
645
ser_close_file(struct ser_struct * ser_file)646 int ser_close_file(struct ser_struct *ser_file) {
647 int retval = 0;
648 if (!ser_file)
649 return -1;
650 if (ser_file->file) {
651 retval = fclose(ser_file->file);
652 ser_file->file = NULL;
653 }
654 if (ser_file->file_id)
655 free(ser_file->file_id);
656 if (ser_file->ts)
657 free(ser_file->ts);
658 if (ser_file->filename)
659 free(ser_file->filename);
660 #ifdef _OPENMP
661 omp_destroy_lock(&ser_file->fd_lock);
662 omp_destroy_lock(&ser_file->ts_lock);
663 #endif
664 ser_init_struct(ser_file);
665 return retval;
666 }
667
ser_init_struct(struct ser_struct * ser_file)668 void ser_init_struct(struct ser_struct *ser_file) {
669 g_assert(ser_file);
670 memset(ser_file, 0, sizeof(struct ser_struct));
671 }
672
ser_metadata_as_fits(struct ser_struct * ser_file,fits * fit)673 int ser_metadata_as_fits(struct ser_struct *ser_file, fits *fit) {
674 ser_color type_ser = ser_file->color_id;
675 if (!com.pref.debayer.open_debayer && type_ser != SER_RGB && type_ser != SER_BGR) {
676 type_ser = SER_MONO;
677 }
678 switch (type_ser) {
679 case SER_MONO:
680 fit->naxis = 2;
681 fit->naxes[2] = 1;
682 break;
683 case SER_BAYER_RGGB:
684 case SER_BAYER_BGGR:
685 case SER_BAYER_GBRG:
686 case SER_BAYER_GRBG:
687 case SER_BGR:
688 case SER_RGB:
689 fit->naxis = 3;
690 fit->naxes[2] = 3;
691 break;
692 default:
693 return 1;
694 }
695 fit->naxes[0] = fit->rx = ser_file->image_width;
696 fit->naxes[1] = fit->ry = ser_file->image_height;
697 fit->bitpix = (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) ? BYTE_IMG : USHORT_IMG;
698 fit->orig_bitpix = fit->bitpix;
699 fit->binning_x = fit->binning_y = 1;
700 return 0;
701 }
702
703 /* reads a frame on an already opened SER sequence.
704 * frame number starts at 0 */
ser_read_frame(struct ser_struct * ser_file,int frame_no,fits * fit,gboolean force_float,gboolean open_debayer)705 int ser_read_frame(struct ser_struct *ser_file, int frame_no, fits *fit, gboolean force_float, gboolean open_debayer) {
706 int retval = 0, i, j, swap = 0;
707 gint64 offset, frame_size;
708 size_t read_size;
709 WORD *olddata, *tmp;
710 if (!ser_file || ser_file->file == NULL || !ser_file->number_of_planes ||
711 !fit || frame_no < 0 || frame_no >= ser_file->frame_count)
712 return -1;
713
714 frame_size = ser_file->image_width * ser_file->image_height *
715 ser_file->number_of_planes;
716 read_size = frame_size * ser_file->byte_pixel_depth;
717
718 olddata = fit->data;
719 if ((fit->data = realloc(fit->data, frame_size * sizeof(WORD))) == NULL) {
720 PRINT_ALLOC_ERR;
721 if (olddata)
722 free(olddata);
723 return -1;
724 }
725
726 offset = SER_HEADER_LEN + frame_size *
727 (gint64)ser_file->byte_pixel_depth * (gint64)frame_no;
728 /*fprintf(stdout, "offset is %lu (frame %d, %d pixels, %d-byte)\n", offset,
729 frame_no, frame_size, ser_file->pixel_bytedepth);*/
730 #ifdef _OPENMP
731 omp_set_lock(&ser_file->fd_lock);
732 #endif
733 if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
734 perror("fseek in SER");
735 retval = -1;
736 } else {
737 if (fread(fit->data, 1, read_size, ser_file->file) != read_size)
738 retval = -1;
739 }
740 #ifdef _OPENMP
741 omp_unset_lock(&ser_file->fd_lock);
742 #endif
743 if (retval)
744 return -1;
745
746 ser_manage_endianess_and_depth(ser_file, fit->data, frame_size);
747
748 fit->bitpix = (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) ? BYTE_IMG : USHORT_IMG;
749 fit->orig_bitpix = fit->bitpix;
750 fit->binning_x = fit->binning_y = 1;
751
752 /* If opening images debayered is not activated, read the image as CFA monochrome */
753 ser_color type_ser = ser_file->color_id;
754 if (!open_debayer && type_ser != SER_RGB && type_ser != SER_BGR) {
755 const gchar *pattern = NULL;
756 type_ser = SER_MONO;
757
758 if (com.pref.debayer.use_bayer_header) {
759 if (ser_file->color_id == SER_BAYER_RGGB)
760 pattern = "RGGB";
761 else if (ser_file->color_id == SER_BAYER_BGGR)
762 pattern = "BGGR";
763 else if (ser_file->color_id == SER_BAYER_GBRG)
764 pattern = "GBRG";
765 else if (ser_file->color_id == SER_BAYER_GRBG)
766 pattern = "GRBG";
767 } else {
768 pattern = filter_pattern[com.pref.debayer.bayer_pattern];
769 }
770 if (pattern)
771 strcpy(fit->bayer_pattern, pattern);
772 g_snprintf(fit->row_order, FLEN_VALUE, "%s", "BOTTOM-UP");
773 }
774
775 switch (type_ser) {
776 case SER_MONO:
777 fit->naxis = 2;
778 fit->naxes[0] = fit->rx = ser_file->image_width;
779 fit->naxes[1] = fit->ry = ser_file->image_height;
780 fit->naxes[2] = 1;
781 fit->pdata[RLAYER] = fit->data;
782 fit->pdata[GLAYER] = fit->data;
783 fit->pdata[BLAYER] = fit->data;
784 break;
785 case SER_BAYER_RGGB:
786 case SER_BAYER_BGGR:
787 case SER_BAYER_GBRG:
788 case SER_BAYER_GRBG:
789 fit->naxes[0] = fit->rx = ser_file->image_width;
790 fit->naxes[1] = fit->ry = ser_file->image_height;
791 fit->naxes[2] = 3;
792 /* Get Bayer informations from header if available */
793 sensor_pattern sensortmp;
794 sensortmp = com.pref.debayer.bayer_pattern;
795 if (com.pref.debayer.use_bayer_header) {
796 sensor_pattern bayer;
797 bayer = get_SER_Bayer_Pattern(type_ser);
798 if (bayer != com.pref.debayer.bayer_pattern) {
799 if (bayer == BAYER_FILTER_NONE && !user_warned) {
800 siril_log_color_message(_("No Bayer pattern found in the header file.\n"), "red");
801 }
802 else {
803 if (!user_warned) {
804 siril_log_color_message(_("Bayer pattern found in header (%s) is different"
805 " from Bayer pattern in settings (%s). Overriding settings.\n"),
806 "salmon", filter_pattern[bayer], filter_pattern[com.pref.debayer.bayer_pattern]);
807 }
808 com.pref.debayer.bayer_pattern = bayer;
809 }
810 user_warned = TRUE;
811 }
812 }
813 /* for performance consideration (and many others) we force the interpolation algorithm
814 * to be BAYER_BILINEAR
815 */
816 debayer(fit, BAYER_RCD, com.pref.debayer.bayer_pattern);
817 com.pref.debayer.bayer_pattern = sensortmp;
818 break;
819 case SER_BGR:
820 swap = 2;
821 /* no break */
822 case SER_RGB:
823 tmp = malloc(frame_size * sizeof(WORD));
824 if (!tmp) {
825 PRINT_ALLOC_ERR;
826 return -1;
827 }
828 memcpy(tmp, fit->data, sizeof(WORD) * frame_size);
829 fit->naxes[0] = fit->rx = ser_file->image_width;
830 fit->naxes[1] = fit->ry = ser_file->image_height;
831 fit->naxes[2] = 3;
832 fit->naxis = 3;
833 fit->pdata[RLAYER] = fit->data;
834 fit->pdata[GLAYER] = fit->data + fit->rx * fit->ry;
835 fit->pdata[BLAYER] = fit->data + fit->rx * fit->ry * 2;
836 for (i = 0, j = 0; j < fit->rx * fit->ry; i += 3, j++) {
837 fit->pdata[0 + swap][j] = tmp[i + RLAYER];
838 fit->pdata[1][j] = tmp[i + GLAYER];
839 fit->pdata[2 - swap][j] = tmp[i + BLAYER];
840 }
841 free(tmp);
842 break;
843 case SER_BAYER_CYYM:
844 case SER_BAYER_YCMY:
845 case SER_BAYER_YMCY:
846 case SER_BAYER_MYYC:
847 default:
848 siril_log_message(_("This type of Bayer pattern is not handled yet.\n"));
849 return -1;
850 }
851
852 /* copy the SER timestamp to the fits */
853 if (ser_file->ts) {
854 GDateTime *timestamp = ser_timestamp_to_date_time(ser_file->ts[frame_no]);
855 if (timestamp) {
856 if (fit->date_obs) {
857 g_date_time_unref(fit->date_obs);
858 }
859 fit->date_obs = timestamp;
860 }
861 }
862
863 if (force_float) {
864 float *newbuf;
865 size_t pixel_count = fit->naxes[0] * fit->naxes[1] * fit->naxes[2];
866 if (fit->bitpix == BYTE_IMG)
867 newbuf = ushort8_buffer_to_float(fit->data, pixel_count);
868 else newbuf = ushort_buffer_to_float(fit->data, pixel_count);
869 fit_replace_buffer(fit, newbuf, DATA_FLOAT);
870 }
871
872 fits_flip_top_to_bottom(fit);
873 fit->top_down = FALSE;
874
875 return 0;
876 }
877
878 /* multi-type cropping, works in constant space if needed */
879 #define crop_area_from_lines(BUFFER_TYPE) { \
880 int x, y, src, dst = 0; \
881 BUFFER_TYPE *inbuf = (BUFFER_TYPE *)read_buffer; \
882 BUFFER_TYPE *out = (BUFFER_TYPE *)outbuf; \
883 for (y = 0; y < area->h; y++) { \
884 src = y * ser_file->image_width + area->x; \
885 for (x = 0; x < area->w; x++) \
886 out[dst++] = inbuf[src++]; \
887 } \
888 }
889
890 /* multi-type RGB reordering, works in constant space if needed */
891 #define crop_area_from_color_lines(BUFFER_TYPE) { \
892 int x, y, src, dst = 0; \
893 BUFFER_TYPE *inbuf = (BUFFER_TYPE *)read_buffer; \
894 BUFFER_TYPE *out = (BUFFER_TYPE *)outbuf; \
895 int color_offset; \
896 if (ser_file->color_id == SER_BGR) { \
897 color_offset = 2 - layer; \
898 } else { \
899 color_offset = layer; \
900 } \
901 for (y = 0; y < area->h; y++) { \
902 src = (y * ser_file->image_width + area->x) * 3 + color_offset; \
903 for (x = 0; x < area->w; x++) { \
904 out[dst++] = inbuf[src]; \
905 src += 3; \
906 } \
907 } \
908 }
909
910 /* reading an area from a SER frame, for one layer only, either layer == -1 for
911 * monochrome and debayer, or 0-2 for color.
912 * the area is read in one read(2) call to limit the number of syscalls, for a
913 * full-width area of same height as requested, then cropped horizontally to
914 * get the requested area.
915 * This function is the first one of siril to handle two different data types
916 * (BYTE and WORD) for the same algorithm! This uses VIPS-style macros.
917 * */
read_area_from_image(struct ser_struct * ser_file,const int frame_no,WORD * outbuf,const rectangle * area,const int layer)918 static int read_area_from_image(struct ser_struct *ser_file, const int frame_no,
919 WORD *outbuf, const rectangle *area, const int layer) {
920 gint64 offset, frame_size;
921 int retval = 0;
922 WORD *read_buffer;
923 size_t read_size = ser_file->image_width * area->h * ser_file->byte_pixel_depth;
924 if (layer != -1) read_size *= 3;
925 if (layer != -1 || area->w != ser_file->image_width) {
926 // allocated space is probably not enough to
927 // store whole lines or RGB data
928 read_buffer = malloc(read_size);
929 if (!read_buffer) {
930 PRINT_ALLOC_ERR;
931 return -1;
932 }
933 }
934 else read_buffer = outbuf;
935
936 frame_size = ser_file->image_width * ser_file->image_height *
937 ser_file->number_of_planes * ser_file->byte_pixel_depth;
938
939 #ifdef _OPENMP
940 omp_set_lock(&ser_file->fd_lock);
941 #endif
942 // we read the full-stride rectangle that contains the requested area
943 offset = SER_HEADER_LEN + frame_size * frame_no + // requested frame
944 area->y * ser_file->image_width *
945 ser_file->byte_pixel_depth * (layer != -1 ? 3 : 1); // requested area
946
947 if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
948 perror("fseek in SER");
949 retval = -1;
950 } else {
951 if (fread(read_buffer, 1, read_size, ser_file->file) != read_size) {
952 retval = -1;
953 }
954 }
955 #ifdef _OPENMP
956 omp_unset_lock(&ser_file->fd_lock);
957 #endif
958 if (!retval) {
959 if (area->w != ser_file->image_width) {
960 // here we crop x-wise our area
961 if (layer != -1) {
962 /* reorder the RGBRGB to RRGGBB and crop */
963 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
964 crop_area_from_color_lines(BYTE);
965 } else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
966 crop_area_from_color_lines(WORD);
967 }
968 } else {
969 /* just crop */
970 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
971 crop_area_from_lines(BYTE);
972 } else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
973 crop_area_from_lines(WORD);
974 }
975 }
976 } else if (layer != -1) {
977 /* just reorder RGB data, the crop function works too */
978 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
979 crop_area_from_color_lines(BYTE);
980 } else if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_16) {
981 crop_area_from_color_lines(WORD);
982 }
983 }
984 }
985 if (layer != -1 || area->w != ser_file->image_width)
986 free(read_buffer);
987 return retval;
988 }
989
990 /* read an area of an image in an opened SER sequence */
ser_read_opened_partial(struct ser_struct * ser_file,int layer,int frame_no,WORD * buffer,const rectangle * area)991 int ser_read_opened_partial(struct ser_struct *ser_file, int layer,
992 int frame_no, WORD *buffer, const rectangle *area) {
993 int xoffset, yoffset, x, y;
994 ser_color type_ser;
995 WORD *rawbuf, *demosaiced_buf;
996 rectangle debayer_area, image_area;
997 sensor_pattern sensortmp;
998
999 if (!ser_file || ser_file->file == NULL || frame_no < 0
1000 || frame_no >= ser_file->frame_count)
1001 return -1;
1002
1003 type_ser = ser_file->color_id;
1004 if (!com.pref.debayer.open_debayer &&
1005 type_ser != SER_RGB && type_ser != SER_BGR)
1006 type_ser = SER_MONO;
1007
1008 switch (type_ser) {
1009 case SER_MONO:
1010 if (read_area_from_image(ser_file, frame_no, buffer, area, -1))
1011 return -1;
1012 ser_manage_endianess_and_depth(ser_file, buffer, area->w * area->h);
1013 break;
1014
1015 case SER_BAYER_RGGB:
1016 case SER_BAYER_BGGR:
1017 case SER_BAYER_GBRG:
1018 case SER_BAYER_GRBG:
1019 /* SER v2: RGB images obtained from demosaicing.
1020 * Original is monochrome, we demosaic it in an area slightly larger than the
1021 * requested area, giving 3 channels in form of RGBRGBRGB buffers, and finally
1022 * we extract one of the three channels and crop it to the requested area. */
1023
1024 /* Get Bayer informations from header if available */
1025 sensortmp = com.pref.debayer.bayer_pattern;
1026 if (com.pref.debayer.use_bayer_header) {
1027 sensor_pattern bayer;
1028 bayer = get_SER_Bayer_Pattern(type_ser);
1029 if (bayer != com.pref.debayer.bayer_pattern) {
1030 if (bayer == BAYER_FILTER_NONE && !user_warned) {
1031 siril_log_color_message(_("No Bayer pattern found in the header file.\n"), "red");
1032 }
1033 else {
1034 if (!user_warned) {
1035 siril_log_color_message(_("Bayer pattern found in header (%s) is different"
1036 " from Bayer pattern in settings (%s). Overriding settings.\n"),
1037 "salmon", filter_pattern[bayer], filter_pattern[com.pref.debayer.bayer_pattern]);
1038 }
1039 com.pref.debayer.bayer_pattern = bayer;
1040 }
1041 user_warned = TRUE;
1042 }
1043 }
1044 if (layer < 0 || layer >= 3) {
1045 siril_log_message(_("For a demosaiced image, layer has to be R, G or B (0 to 2).\n"));
1046 return -1;
1047 }
1048
1049 image_area = (rectangle) { .x = 0, .y = 0,
1050 .w = ser_file->image_width, .h = ser_file->image_height };
1051 get_debayer_area(area, &debayer_area, &image_area, &xoffset, &yoffset);
1052
1053 // allocating a buffer for WORD because it's going to be converted in-place
1054 rawbuf = malloc(debayer_area.w * debayer_area.h * sizeof(WORD));
1055 if (!rawbuf) {
1056 PRINT_ALLOC_ERR;
1057 return -1;
1058 }
1059 if (read_area_from_image(ser_file, frame_no, rawbuf, &debayer_area, -1)) {
1060 free(rawbuf);
1061 return -1;
1062 }
1063 ser_manage_endianess_and_depth(ser_file, rawbuf, debayer_area.w * debayer_area.h);
1064
1065 /* for performance consideration (and many others) we force the interpolation algorithm
1066 * to be BAYER_BILINEAR
1067 */
1068 demosaiced_buf = debayer_buffer(rawbuf, &debayer_area.w,
1069 &debayer_area.h, BAYER_BILINEAR, com.pref.debayer.bayer_pattern, ser_file->bit_pixel_depth);
1070 free(rawbuf);
1071 if (!demosaiced_buf)
1072 return -1;
1073
1074 /* area is the destination area.
1075 * debayer_area is the demosaiced buf area.
1076 * xoffset and yoffset are the x,y offsets of area in the debayer area.
1077 */
1078 const int nbpixels = debayer_area.w * debayer_area.h;
1079 for (y = 0; y < area->h; y++) {
1080 for (x = 0; x < area->w; x++) {
1081 buffer[y*area->w + x] = demosaiced_buf[layer * nbpixels + (yoffset+y)*debayer_area.w + xoffset+x];
1082 }
1083 }
1084
1085 free(demosaiced_buf);
1086 com.pref.debayer.bayer_pattern = sensortmp;
1087 break;
1088
1089 case SER_BGR:
1090 case SER_RGB:
1091 g_assert(ser_file->number_of_planes == 3);
1092 if (read_area_from_image(ser_file, frame_no, buffer, area, layer))
1093 return -1;
1094 ser_manage_endianess_and_depth(ser_file, buffer, area->w * area->h);
1095 break;
1096 default:
1097 siril_log_message(_("This type of Bayer pattern is not handled yet.\n"));
1098 return -1;
1099 }
1100
1101 return 0;
1102 }
1103
ser_read_opened_partial_fits(struct ser_struct * ser_file,int layer,int frame_no,fits * fit,const rectangle * area)1104 int ser_read_opened_partial_fits(struct ser_struct *ser_file, int layer,
1105 int frame_no, fits *fit, const rectangle *area) {
1106 if (new_fit_image(&fit, area->w, area->h, 1, DATA_USHORT))
1107 return -1;
1108 fit->top_down = TRUE;
1109 if (ser_file->ts) {
1110 GDateTime *timestamp = ser_timestamp_to_date_time(ser_file->ts[frame_no]);
1111 if (timestamp) {
1112 if (fit->date_obs) {
1113 g_date_time_unref(fit->date_obs);
1114 }
1115 fit->date_obs = timestamp;
1116 }
1117 }
1118 return ser_read_opened_partial(ser_file, layer, frame_no, fit->pdata[0], area);
1119 }
1120
1121 // public function for writing an image to the file, calls the writer
ser_write_frame_from_fit(struct ser_struct * ser_file,fits * fit,int frame_no)1122 int ser_write_frame_from_fit(struct ser_struct *ser_file, fits *fit, int frame_no) {
1123 return seqwriter_append_write(ser_file->writer, fit, frame_no);
1124 }
1125
1126 // internal function, called by the writer hook ser_write_image_for_writer()
1127 // frame_no should always be the next image, or frame_count
ser_write_frame_from_fit_internal(struct ser_struct * ser_file,fits * fit,int frame_no)1128 static int ser_write_frame_from_fit_internal(struct ser_struct *ser_file, fits *fit, int frame_no) {
1129 int pixel, plane, dest;
1130 int ret, retval = 0;
1131 gint64 offset, frame_size;
1132 BYTE *data8 = NULL; // for 8-bit files
1133 WORD *data16 = NULL; // for 16-bit files
1134
1135 if (!ser_file || ser_file->file == NULL || !fit)
1136 return -1;
1137 if (ser_file->number_of_planes == 0) {
1138 // adding first frame of a new sequence, use it to populate the header
1139 if (ser_write_header_from_fit(ser_file, fit)) {
1140 return 1;
1141 }
1142 }
1143 if (fit->rx != ser_file->image_width || fit->ry != ser_file->image_height) {
1144 siril_log_message(_("Trying to add an image of different size in a SER\n"));
1145 return 1;
1146 }
1147
1148 fits_flip_top_to_bottom(fit);
1149 frame_size = ser_file->image_width * ser_file->image_height *
1150 ser_file->number_of_planes;
1151
1152 offset = SER_HEADER_LEN + frame_size *
1153 (gint64)ser_file->byte_pixel_depth * (gint64)frame_no;
1154
1155 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
1156 data8 = malloc(frame_size * ser_file->byte_pixel_depth);
1157 if (!data8) {
1158 PRINT_ALLOC_ERR;
1159 return -1;
1160 }
1161 } else {
1162 data16 = malloc(frame_size * ser_file->byte_pixel_depth);
1163 if (!data16) {
1164 PRINT_ALLOC_ERR;
1165 return -1;
1166 }
1167 }
1168
1169 for (plane = 0; plane < ser_file->number_of_planes; plane++) {
1170 dest = plane;
1171 for (pixel = 0; pixel < ser_file->image_width * ser_file->image_height;
1172 pixel++) {
1173 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8)
1174 data8[dest] = round_to_BYTE(fit->pdata[plane][pixel]);
1175 else {
1176 if (ser_file->little_endian == SER_BIG_ENDIAN)
1177 data16[dest] = (fit->pdata[plane][pixel] >> 8 | fit->pdata[plane][pixel] << 8);
1178 else
1179 data16[dest] = fit->pdata[plane][pixel];
1180 }
1181 dest += ser_file->number_of_planes;
1182 }
1183 }
1184
1185 #ifdef _OPENMP
1186 omp_set_lock(&ser_file->fd_lock);
1187 #endif
1188 if ((gint64)-1 == fseek64(ser_file->file, offset, SEEK_SET)) {
1189 #ifdef _OPENMP
1190 omp_unset_lock(&ser_file->fd_lock);
1191 #endif
1192 perror("seek");
1193 retval = -1;
1194 goto free_and_quit;
1195 }
1196
1197 if (ser_file->byte_pixel_depth == SER_PIXEL_DEPTH_8) {
1198 ret = fwrite(data8, 1, frame_size * ser_file->byte_pixel_depth, ser_file->file);
1199 #ifdef _OPENMP
1200 omp_unset_lock(&ser_file->fd_lock);
1201 #endif
1202 if (ret != frame_size * ser_file->byte_pixel_depth) {
1203 perror("write image in SER");
1204 retval = 1;
1205 goto free_and_quit;
1206 }
1207 } else {
1208 ret = fwrite(data16, 1, frame_size * ser_file->byte_pixel_depth, ser_file->file);
1209 #ifdef _OPENMP
1210 omp_unset_lock(&ser_file->fd_lock);
1211 #endif
1212 if (ret != frame_size * ser_file->byte_pixel_depth) {
1213 perror("write image in SER");
1214 retval = 1;
1215 goto free_and_quit;
1216 }
1217 }
1218
1219 #ifdef _OPENMP
1220 #pragma omp atomic
1221 #endif
1222 ser_file->frame_count++;
1223
1224 if (fit->date_obs && !ser_alloc_ts(ser_file, frame_no)) {
1225 guint64 utc;
1226 utc = date_time_to_ser_timestamp(fit->date_obs);
1227 ser_file->ts[frame_no] = utc;
1228 }
1229
1230 free_and_quit:
1231 if (data8) free(data8);
1232 if (data16) free(data16);
1233 return retval;
1234 }
1235
ser_compute_file_size(struct ser_struct * ser_file,int nb_frames)1236 gint64 ser_compute_file_size(struct ser_struct *ser_file, int nb_frames) {
1237 gint64 frame_size, size = ser_file->filesize;
1238
1239 if (nb_frames != ser_file->frame_count) {
1240 frame_size = (size - SER_HEADER_LEN) / ser_file->frame_count;
1241 size = SER_HEADER_LEN + frame_size * nb_frames;
1242 }
1243 return size;
1244 }
1245
import_metadata_from_serfile(struct ser_struct * ser_file,fits * to)1246 int import_metadata_from_serfile(struct ser_struct *ser_file, fits *to) {
1247 strncpy(to->instrume, ser_file->instrument, FLEN_VALUE);
1248 strncpy(to->observer, ser_file->observer, FLEN_VALUE);
1249 strncpy(to->telescop, ser_file->telescope, FLEN_VALUE);
1250 return 0;
1251 }
1252
free_preview_data(guchar * pixels,gpointer data)1253 static GdkPixbufDestroyNotify free_preview_data(guchar *pixels, gpointer data) {
1254 free(pixels);
1255 free(data);
1256 return FALSE;
1257 }
1258
1259 /**
1260 * Create a monochrome preview (only the first channel is displayed) of a SER file in a GdkPixbuf.
1261 * @param filename
1262 * @return a GdkPixbuf containing the preview or NULL
1263 */
get_thumbnail_from_ser(char * filename,gchar ** descr)1264 GdkPixbuf* get_thumbnail_from_ser(char *filename, gchar **descr) {
1265 GdkPixbuf *pixbuf = NULL;
1266 int MAX_SIZE = com.pref.thumbnail_size;
1267 gchar *description = NULL;
1268 int i, j, k, l, N, M;
1269 int w, h, pixScale, Ws, Hs, n_channels, n_frames, bit;
1270 int sz;
1271 struct ser_struct ser;
1272 fits fit = { 0 };
1273
1274 ser_init_struct(&ser);
1275 if (ser_open_file(filename, &ser)) {
1276 return NULL;
1277 }
1278 float *pix = malloc(MAX_SIZE * sizeof(float));
1279 float *ima_data = NULL, *ptr, byte, n, m, max, min, wd, avr;
1280 guchar *pixbuf_data = NULL, *pptr;
1281
1282 w = ser.image_width;
1283 h = ser.image_height;
1284 sz = w * h;
1285 ima_data = malloc(sz * sizeof(float));
1286 pixbuf_data = malloc(3 * MAX_SIZE * MAX_SIZE * sizeof(guchar));
1287
1288 /* here no need to debayer, for performance purposes
1289 * we just display monochrome display */
1290 ser_read_frame(&ser, 0, &fit, FALSE, FALSE);
1291
1292 for (i = 0; i < sz; i++) {
1293 ima_data[i + 0] = (float)fit.pdata[RLAYER][i];
1294 }
1295
1296 i = (int) ceil((float) w / MAX_SIZE);
1297 j = (int) ceil((float) h / MAX_SIZE);
1298 pixScale = (i > j) ? i : j; // picture scale factor
1299 if (pixScale == 0) return NULL;
1300 Ws = w / pixScale; // picture width in pixScale blocks
1301 Hs = h / pixScale; // -//- height pixScale
1302
1303 n_frames = ser.frame_count;
1304 bit = ser.bit_pixel_depth;
1305
1306 switch (ser.color_id) {
1307 case SER_MONO:
1308 n_channels = 1;
1309 break;
1310 default:
1311 n_channels = 3;
1312 }
1313
1314 description = g_strdup_printf("%d x %d %s\n%d %s (%d bits)\n%d %s\n%s", w,
1315 h, ngettext("pixel", "pixels", h), n_channels,
1316 ngettext("channel", "channels", n_channels), bit, n_frames,
1317 ngettext("frame", "frames", n_frames), _("(Monochrome Preview)"));
1318
1319 M = 0; // line number
1320 for (i = 0; i < Hs; i++) { // cycle through a blocks by lines
1321 //pptr = &pixbuf_data[i * Ws * 3];
1322 for (j = 0; j < MAX_SIZE; j++)
1323 pix[j] = 0;
1324 m = 0.f; // amount of strings read in block
1325 for (l = 0; l < pixScale; l++, m++) { // cycle through a block lines
1326 ptr = &ima_data[M * w];
1327 N = 0; // number of column
1328 for (j = 0; j < Ws; j++) { // cycle through a blocks by columns
1329 n = 0.; // amount of columns read in block
1330 byte = 0.; // average intensity in block
1331 for (k = 0; k < pixScale; k++, n++) { // cycle through block pixels
1332 if (N++ < w) // row didn't end
1333 byte += *ptr++; // sum[(pix-min)/wd]/n = [sum(pix)/n-min]/wd
1334 else
1335 break;
1336 }
1337 pix[j] += byte / n; //(byte / n - min)/wd;
1338 }
1339 if (++M >= h)
1340 break;
1341 }
1342 // fill unused picture pixels
1343 ptr = &ima_data[i * Ws];
1344 for (l = 0; l < Ws; l++)
1345 *ptr++ = pix[l] / m;
1346 }
1347 ptr = ima_data;
1348 sz = Ws * Hs;
1349 max = min = *ptr;
1350 avr = 0;
1351 for (i = 0; i < sz; i++, ptr++) {
1352 float tmp = *ptr;
1353 if (tmp > max)
1354 max = tmp;
1355 else if (tmp < min)
1356 min = tmp;
1357 avr += tmp;
1358 }
1359 avr /= (float) sz;
1360 wd = max - min;
1361 avr = (avr - min) / wd; // normal average by preview
1362 if (avr > 1.)
1363 wd /= avr;
1364 ptr = ima_data;
1365 for (i = Hs - 1; i > -1; i--) { // fill pixbuf mirroring image by vertical
1366 pptr = &pixbuf_data[Ws * i * 3];
1367 for (j = 0; j < Ws; j++) {
1368 *pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1369 *pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1370 *pptr++ = (guchar) round_to_BYTE(255.f * (*ptr - min) / wd);
1371 ptr++;
1372 }
1373 }
1374
1375 ser_close_file(&ser);
1376 pixbuf = gdk_pixbuf_new_from_data(pixbuf_data, // guchar* data
1377 GDK_COLORSPACE_RGB, // only this supported
1378 FALSE, // no alpha
1379 8, // number of bits
1380 Ws, Hs, // size
1381 Ws * 3, // line length in bytes
1382 (GdkPixbufDestroyNotify) free_preview_data, // function (*GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
1383 NULL
1384 );
1385 free(ima_data);
1386 free(pix);
1387 *descr = description;
1388 return pixbuf;
1389 }
1390
ser_read_frame_date(struct ser_struct * ser_file,int frame_no)1391 GDateTime *ser_read_frame_date(struct ser_struct *ser_file, int frame_no) {
1392 if (ser_file->ts)
1393 return ser_timestamp_to_date_time(ser_file->ts[frame_no]);
1394 return NULL;
1395 }
1396
1397