1 /* FPACK
2 * R. Seaman, NOAO, with a few enhancements by W. Pence, HEASARC
3 *
4 * Calls fits_img_compress in the CFITSIO library by W. Pence, HEASARC
5 */
6
7 #include <ctype.h>
8 /* #include <signal.h> */
9 #include "fitsio.h"
10 #include "fpack.h"
11
12 /* ================================================================== */
main(int argc,char * argv[])13 int main(int argc, char *argv[])
14 {
15 fpstate fpvar;
16
17 if (argc <= 1) { fp_usage (); fp_hint (); exit (-1); }
18
19 fp_init (&fpvar);
20 fp_get_param (argc, argv, &fpvar);
21
22 if (fpvar.listonly) {
23 fp_list (argc, argv, fpvar);
24
25 } else {
26 fp_preflight (argc, argv, FPACK, &fpvar);
27 fp_loop (argc, argv, FPACK, fpvar);
28 }
29
30 exit (0);
31 }
32 /* ================================================================== */
fp_get_param(int argc,char * argv[],fpstate * fpptr)33 int fp_get_param (int argc, char *argv[], fpstate *fpptr)
34 {
35 int gottype=0, gottile=0, wholetile=0, iarg, len, ndim, ii, doffset;
36 int gotR=0, gotO=0;
37 char tmp[SZ_STR], tile[SZ_STR];
38
39 if (fpptr->initialized != FP_INIT_MAGIC) {
40 fp_msg ("Error: internal initialization error\n"); exit (-1);
41 }
42
43 tile[0] = 0;
44
45 /* flags must come first and be separately specified
46 */
47 for (iarg = 1; iarg < argc; iarg++) {
48 if ((argv[iarg][0] == '-' && strlen (argv[iarg]) == 2) ||
49 !strncmp(argv[iarg], "-q", 2) || !strncmp(argv[iarg], "-qz", 3) ||
50 !strncmp(argv[iarg], "-g1", 3) || !strncmp(argv[iarg], "-g2", 3) ||
51 !strncmp(argv[iarg], "-i2f", 4) ||
52 !strncmp(argv[iarg], "-n3ratio", 8) || !strncmp(argv[iarg], "-n3min", 6) ||
53 !strncmp(argv[iarg], "-tableonly", 10) || !strncmp(argv[iarg], "-table", 6) )
54 {
55
56 /* Rice is the default, so -r is superfluous */
57 if ( argv[iarg][1] == 'r') {
58 fpptr->comptype = RICE_1;
59 if (gottype) {
60 fp_msg ("Error: multiple compression flags\n");
61 fp_usage (); exit (-1);
62 } else
63 gottype++;
64
65 } else if (argv[iarg][1] == 'p') {
66 fpptr->comptype = PLIO_1;
67 if (gottype) {
68 fp_msg ("Error: multiple compression flags\n");
69 fp_usage (); exit (-1);
70 } else
71 gottype++;
72
73 } else if (argv[iarg][1] == 'g') {
74 /* test for modifiers following the 'g' */
75 if (argv[iarg][2] == '2')
76 fpptr->comptype = GZIP_2;
77 else
78 fpptr->comptype = GZIP_1;
79
80 if (gottype) {
81 fp_msg ("Error: multiple compression flags\n");
82 fp_usage (); exit (-1);
83 } else
84 gottype++;
85 /*
86 } else if (argv[iarg][1] == 'b') {
87 fpptr->comptype = BZIP2_1;
88 if (gottype) {
89 fp_msg ("Error: multiple compression flags\n");
90 fp_usage (); exit (-1);
91 } else
92 gottype++;
93 */
94 } else if (argv[iarg][1] == 'h') {
95 fpptr->comptype = HCOMPRESS_1;
96 if (gottype) {
97 fp_msg ("Error: multiple compression flags\n");
98 fp_usage (); exit (-1);
99 } else
100 gottype++;
101
102 } else if (argv[iarg][1] == 'd') {
103 fpptr->comptype = NOCOMPRESS;
104 if (gottype) {
105 fp_msg ("Error: multiple compression flags\n");
106 fp_usage (); exit (-1);
107 } else
108 gottype++;
109
110 } else if (!strcmp(argv[iarg], "-i2f")) {
111 /* this means convert integer images to float, and then */
112 /* quantize and compress the float image. This lossy */
113 /* compression method may give higher compression than the */
114 /* lossless compression method that is usually applied to */
115 /* integer images. */
116
117 fpptr->int_to_float = 1;
118
119 } else if (!strcmp(argv[iarg], "-n3ratio")) {
120 /* this is the minimum ratio between the MAD noise sigma */
121 /* and the q parameter value in the case where the integer */
122 /* image is quantized and compressed like a float image. */
123 if (++iarg >= argc) {
124 fp_usage (); exit (-1);
125 } else {
126 fpptr->n3ratio = (float) atof (argv[iarg]);
127 }
128 } else if (!strcmp(argv[iarg], "-n3min")) {
129 /* this is the minimum MAD noise sigma in the case where the */
130 /* integer image is quantized and compressed like a float image. */
131 if (++iarg >= argc) {
132 fp_usage (); exit (-1);
133 } else {
134 fpptr->n3min = (float) atof (argv[iarg]);
135 }
136 } else if (argv[iarg][1] == 'q') {
137 /* test for modifiers following the 'q' */
138
139 if (argv[iarg][2] == 'z') {
140 fpptr->dither_method = 2; /* preserve zero pixels */
141
142 if (argv[iarg][3] == 't') {
143 fpptr->dither_offset = -1; /* dither based on tile checksum */
144
145 } else if (isdigit(argv[iarg][3])) { /* is a number appended to q? */
146 doffset = atoi(argv[iarg]+3);
147
148 if (doffset == 0) {
149 fpptr->no_dither = 1; /* don't dither the quantized values */
150 } else if (doffset > 0 && doffset <= 10000) {
151 fpptr->dither_offset = doffset;
152 } else {
153 fp_msg ("Error: invalid q suffix\n");
154 fp_usage (); exit (-1);
155 }
156 }
157 } else {
158 if (argv[iarg][2] == 't') {
159 fpptr->dither_offset = -1; /* dither based on tile checksum */
160
161 } else if (isdigit(argv[iarg][2])) { /* is a number appended to q? */
162 doffset = atoi(argv[iarg]+2);
163
164 if (doffset == 0) {
165 fpptr->no_dither = 1; /* don't dither the quantized values */
166 } else if (doffset > 0 && doffset <= 10000) {
167 fpptr->dither_offset = doffset;
168 } else {
169 fp_msg ("Error: invalid q suffix\n");
170 fp_usage (); exit (-1);
171 }
172 }
173 }
174
175 if (++iarg >= argc) {
176 fp_usage (); exit (-1);
177 } else {
178 fpptr->quantize_level = (float) atof (argv[iarg]);
179 }
180 } else if (argv[iarg][1] == 'n') {
181 if (++iarg >= argc) {
182 fp_usage (); exit (-1);
183 } else {
184 fpptr->rescale_noise = (float) atof (argv[iarg]);
185 }
186 } else if (argv[iarg][1] == 's') {
187 if (++iarg >= argc) {
188 fp_usage (); exit (-1);
189 } else {
190 fpptr->scale = (float) atof (argv[iarg]);
191 }
192 } else if (!strcmp(argv[iarg], "-tableonly")) {
193 fpptr->do_tables = 1;
194 fpptr->do_images = 0;
195 /* Do not write this to stdout via fp_msg. Otherwise it will be placed at start of piped FITS
196 file, which will then be corrupted. */
197 fprintf(stderr, "Note: The table compression method used by fpack has been\n");
198 fprintf(stderr, " officially approved as part of FITS format standard since 2016.\n");
199 fprintf(stderr, " However users should be aware that the compressed table files may\n");
200 fprintf(stderr, " only be readable by a limited number of applications (including fpack).\n");
201
202 } else if (!strcmp(argv[iarg], "-table")) {
203 fpptr->do_tables = 1;
204 fprintf(stderr, "Note: The table compression method used by fpack has been\n");
205 fprintf(stderr, " officially approved as part of FITS format standard since 2016.\n");
206 fprintf(stderr, " However users should be aware that the compressed table files may\n");
207 fprintf(stderr, " only be readable by a limited number of applications (including fpack).\n");
208
209 } else if (argv[iarg][1] == 't') {
210 if (gottile) {
211 fp_msg ("Error: multiple tile specifications\n");
212 fp_usage (); exit (-1);
213 } else
214 gottile++;
215
216 if (++iarg >= argc) {
217 fp_usage (); exit (-1);
218 } else {
219 strncpy (tile, argv[iarg], SZ_STR-1); /* checked below */
220 tile[SZ_STR-1]=0;
221 }
222
223 } else if (argv[iarg][1] == 'v') {
224 fpptr->verbose = 1;
225
226 } else if (argv[iarg][1] == 'w') {
227 wholetile++;
228 if (gottile) {
229 fp_msg ("Error: multiple tile specifications\n");
230 fp_usage (); exit (-1);
231 } else
232 gottile++;
233
234 } else if (argv[iarg][1] == 'F') {
235 fpptr->clobber++; /* overwrite existing file */
236
237 } else if (argv[iarg][1] == 'D') {
238 fpptr->delete_input++;
239
240 } else if (argv[iarg][1] == 'Y') {
241 fpptr->do_not_prompt++;
242
243 } else if (argv[iarg][1] == 'S') {
244 fpptr->to_stdout++;
245
246 } else if (argv[iarg][1] == 'L') {
247 fpptr->listonly++;
248
249 } else if (argv[iarg][1] == 'C') {
250 fpptr->do_checksums = 0;
251
252 } else if (argv[iarg][1] == 'T') {
253 fpptr->test_all = 1;
254
255 } else if (argv[iarg][1] == 'R') {
256 if (gotO) {
257 fp_msg("Error: -R option is not allowed with -O\n");
258 exit(-1);
259 } else if (++iarg >= argc) {
260 fp_usage (); fp_hint (); exit (-1);
261 } else {
262 strncpy (fpptr->outfile, argv[iarg], SZ_STR-1);
263 fpptr->outfile[SZ_STR-1]=0;
264 gotR=1;
265 }
266
267 } else if (argv[iarg][1] == 'H') {
268 fp_help (); exit (0);
269
270 } else if (argv[iarg][1] == 'V') {
271 fp_version (); exit (0);
272
273 } else if (argv[iarg][1] == 'O') {
274 if (gotR) {
275 fp_msg("Error: -O option is not allowed with -R\n");
276 exit(-1);
277 } else if (++iarg >= argc) {
278 fp_usage (); fp_hint (); exit (-1);
279 } else {
280 strncpy (fpptr->outfile, argv[iarg], SZ_STR-1);
281 fpptr->outfile[SZ_STR-1]=0;
282 gotO=1;
283 }
284
285 } else {
286 fp_msg ("Error: unknown command line flag `");
287 fp_msg (argv[iarg]); fp_msg ("'\n");
288 fp_usage (); fp_hint (); exit (-1);
289 }
290
291 } else
292 break;
293 }
294
295 /* In earlier loop, already made sure both -O and -R are not being used.
296 This is essential, as each must store info in the same 'outfile' array.
297 Now do additional tests of -O and -R with other flags. */
298
299 if (gotR && !fpptr->test_all) {
300 fp_msg("Error: -R option may only be used with -T\n"); exit(-1);
301 }
302
303 if (gotO && (fpptr->test_all || fpptr->to_stdout)) {
304 fp_msg("Error: -O option may not be used with -S or -T\n"); exit(-1);
305 }
306
307 if (fpptr->scale != 0. &&
308 fpptr->comptype != HCOMPRESS_1 && fpptr->test_all != 1) {
309
310 fp_msg ("Error: `-s' requires `-h or -T'\n"); exit (-1);
311 }
312
313 if (fpptr->quantize_level == 0) {
314
315 if ((fpptr->comptype != GZIP_1) && (fpptr->comptype != GZIP_2)) {
316 fp_msg ("Error: `-q 0' only allowed with GZIP\n"); exit (-1);
317 }
318
319 if (fpptr->int_to_float == 1) {
320 fp_msg ("Error: `-q 0' not allowed with -i2f\n"); exit (-1);
321 }
322 }
323
324 if (wholetile) {
325 for (ndim=0; ndim < MAX_COMPRESS_DIM; ndim++)
326 fpptr->ntile[ndim] = (long) -1;
327
328 } else if (gottile) {
329 len = strlen (tile);
330 for (ii=0, ndim=0; ii < len; ) {
331 if (! (isdigit (tile[ii]) || tile[ii] == ',')) {
332 fp_msg ("Error: `-t' requires comma separated tile dims, ");
333 fp_msg ("e.g., `-t 100,100'\n"); exit (-1);
334 }
335
336 if (tile[ii] == ',') { ii++; continue; }
337
338 fpptr->ntile[ndim] = atol (&tile[ii]);
339 for ( ; isdigit(tile[ii]); ii++);
340
341 if (++ndim > MAX_COMPRESS_DIM) {
342 fp_msg ("Error: too many dimensions for `-t', max=");
343 snprintf (tmp, SZ_STR,"%d\n", MAX_COMPRESS_DIM); fp_msg (tmp);
344 exit (-1);
345 }
346 }
347 }
348
349 if (iarg >= argc) {
350 fp_msg ("Error: no FITS files to compress\n");
351 fp_usage (); exit (-1);
352 } else
353 fpptr->firstfile = iarg;
354
355 return(0);
356 }
357
358 /* ================================================================== */
fp_usage(void)359 int fp_usage (void)
360 {
361 fp_msg ("usage: fpack ");
362 fp_msg (
363 "[-r|-h|-g|-p] [-w|-t <axes>] [-q <level>] [-s <scale>] [-n <noise>] -v <FITS>\n");
364 fp_msg ("more: [-T] [-R] [-F] [-D] [-Y] [-O <file>] [-S] [-L] [-C] [-H] [-V] [-i2f]\n");
365 return(0);
366 }
367
368 /* ================================================================== */
fp_hint(void)369 int fp_hint (void)
370 { fp_msg (" `fpack -H' for help\n");
371 return(0);
372 }
373
374 /* ================================================================== */
fp_help(void)375 int fp_help (void)
376 {
377 fp_msg ("fpack, a FITS image compression program. Version ");
378 fp_version ();
379 fp_usage ();
380 fp_msg ("\n");
381 fp_msg ("NOTE: the compression parameters specified on the fpack command line may\n");
382 fp_msg ("be over-ridden by compression directive keywords in the header of each HDU\n");
383 fp_msg ("of the input file(s). See the fpack User's Guide for more details\n");
384 fp_msg ("\n");
385
386 fp_msg ("Flags must be separate and appear before filenames:\n");
387 fp_msg (" -r Rice compression [default], or\n");
388 fp_msg (" -h Hcompress compression, or\n");
389 fp_msg (" -g or -g1 GZIP_1 (per-tile) compression, or\n");
390 fp_msg (" -g2 GZIP_2 (per-tile) compression (with byte shuffling), or\n");
391 /*
392 fp_msg (" -b BZIP2 (per-tile) compression, or\n");
393 */
394 fp_msg (" -p PLIO compression (only for positive 8 or 16-bit integer images).\n");
395 fp_msg (" -d Tile the image without compression (debugging mode).\n");
396
397 fp_msg (" -w Compress the whole image as a single large tile.\n");
398 fp_msg (" -t <axes> Comma separated list of tile dimensions [default is row by row].\n");
399
400 fp_msg (" -q <level> Quantized level spacing when converting floating point images to\n");
401 fp_msg (" scaled integers. (+value relative to sigma of background noise;\n");
402 fp_msg (" -value is absolute). Default q value of 4 gives a compression ratio\n");
403 fp_msg (" of about 6 with very high fidelity (only 0.26% increase in noise).\n");
404 fp_msg (" Using q values of 2, or 1 will give compression ratios of\n");
405 fp_msg (" about 8, or 10, respectively (with 1.0% or 4.1% noise increase).\n");
406 fp_msg (" The scaled quantized values are randomly dithered using a seed \n");
407 fp_msg (" value determined from the system clock at run time.\n");
408 fp_msg (" Use -q0 instead of -q to suppress random dithering.\n");
409 fp_msg (" Use -qz instead of -q to not dither zero-valued pixels.\n");
410 fp_msg (" Use -qt or -qzt to compute random dithering seed from first tile checksum.\n");
411 fp_msg (" Use -qN or -qzN, (N in range 1 to 10000) to use a specific dithering seed.\n");
412 fp_msg (" Floating-point images can be losslessly compressed by selecting\n");
413 fp_msg (" the GZIP algorithm and specifying -q 0, but this is slower and often\n");
414 fp_msg (" produces much less compression than the default quantization method.\n");
415 fp_msg (" -i2f Convert integer images to floating point, then quantize and compress\n");
416 fp_msg (" using the specified q level. When used appropriately, this lossy\n");
417 fp_msg (" compression method can give much better compression than the normal\n");
418 fp_msg (" lossless compression methods without significant loss of information.\n");
419 fp_msg (" The -n3ratio and -n3min flags control the minimum noise thresholds;\n");
420 fp_msg (" Images below these thresholds will be losslessly compressed.\n");
421 fp_msg (" -n3ratio Minimum ratio of background noise sigma divided by q. Default = 2.0.\n");
422 fp_msg (" -n3min Minimum background noise sigma. Default = 6. The -i2f flag will be ignored\n");
423 fp_msg (" if the noise level in the image does not exceed both thresholds.\n");
424 fp_msg (" -s <scale> Scale factor for lossy Hcompress [default = 0 = lossless]\n");
425 fp_msg (" (+values relative to RMS noise; -value is absolute)\n");
426 fp_msg (" -n <noise> Rescale scaled-integer images to reduce noise and improve compression.\n");
427 fp_msg (" -v Verbose mode; list each file as it is processed.\n");
428 fp_msg (" -T Show compression algorithm comparison test statistics; files unchanged.\n");
429 fp_msg (" -R <file> Write the comparison test report (above) to a text file.\n");
430 fp_msg (" -table Compress FITS binary tables as well as compress any image HDUs.\n");
431 fp_msg (" -tableonly Compress only FITS binary tables; do not compress any image HDUs.\n");
432 fp_msg (" \n");
433
434 fp_msg ("\nkeywords shared with funpack:\n");
435
436 fp_msg (" -F Overwrite input file by output file with same name.\n");
437 fp_msg (" -D Delete input file after writing output.\n");
438 fp_msg (" -Y Suppress prompts to confirm -F or -D options.\n");
439
440 fp_msg (" -O <file> Specify full output file name. This may be used only when fpack\n");
441 fp_msg (" is run on a single input file.\n");
442 fp_msg (" -S Output compressed FITS files to STDOUT.\n");
443 fp_msg (" -L List contents; files unchanged.\n");
444
445 fp_msg (" -C Don't update FITS checksum keywords.\n");
446
447 fp_msg (" -H Show this message.\n");
448 fp_msg (" -V Show version number.\n");
449
450 fp_msg ("\n <FITS> FITS files to pack; enter '-' (a hyphen) to read input from stdin stream.\n");
451 fp_msg (" Refer to the fpack User's Guide for more extensive help.\n");
452 return(0);
453 }
454