1 /***************************************************************************
2  * This file is a part of CADS/UVS fits2jpeg conversion software           *
3  *   Copyright (C) 2012 by CADS/UV Software Team,                          *
4  *                         Indian Institute of Astrophysics                *
5  *                         Bangalore 560034                                *
6  *                         cads_AT_iiap.res.in                             *
7  *                                                                         *
8  *   This program is free software; you can redistribute it and/or modify  *
9  *   it under the terms of the GNU General Public License as published by  *
10  *   the Free Software Foundation; either version 2 of the License, or     *
11  *   (at your option) any later version.                                   *
12  *                                                                         *
13  *   This program is distributed in the hope that it will be useful,       *
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
16  *   GNU General Public License for more details.                          *
17  *                                                                         *
18  *   You should have received a copy of the GNU General Public License     *
19  *   along with this program; if not, write to the                         *
20  *   Free Software Foundation, Inc.,                                       *
21  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
22  ***************************************************************************/
23 /*---------------------------------------------------------------------------
24   This program will write out FITS data into a jpeg file.
25   Assume that data are in first extension and are two dimensional
27   Libraries needed are CFITSIO, JPEG
28   Original author: Jayant Murthy
29   Last Modification: 11/05/02
31   Reconstruction and additional features: Reks
32  *---------------------------------------------------------------------------*/
34 /*---------------------------------------------------------------------------
35   Modification History
37   Reks, 5th October 2007: v0.01
38   1. configure checks for jpeglib.
39      optional extra arguments --with-jpeglib=<location>
40                               --with-cfitsio=<location>
41   2. Makes blank images! datamin and max are not set..
43   Reks, 6th October 2007: v0.10
44   1. Added 2 functions to fetch max and min from image
45   2. user can choose various scales like log/square/square-root
46   3. Increased verbosity, added banner, help message, etc
47   4. Inputs through getopt. will Ask if mandatory input (fits file) is not
48      provided.
49   5. fitsio2
51   Reks, 10th October 2007: v0.9.0 (close to 1.0)
52   1. Found our niche :-) Can handle wild cards at command line,
53      batch processing.
54   2. Now users cannot specify the output file name though.. Its fixed to
55      <input_file_root>.jpg.
56   3. No getopt for input file.. getopt is limited to various image scaling
57      options.
59  Reks, 17th October 2007, v0.9.5
60   1. Merged all image scaling options into one input param: -s
61      optarg (from getopt) will decide scaling function. Now there is
62      enough room for additional flags (in future).
64  Reks, 22 June 2010 v1.0.0
65   1. Removed datamax() and datamin(). That was required only once!
66   2. option histoeq shifted to image enhancement option
67   3. Added "normalize" option
68      Now users can do scaling + enhance (if at all someone feels like it)
69   4. All options tested. First stable release :)
71   Reks 28 June 2012
72   1. rebranded to CADS/UVS
74   Reks 05-10 July 2012 v2.0 
75   1. Merged scale & operations to single flag
76   2. added fits2jpeg.h, help/banner functions moved to messages.c
77   3. Options to clip image min/max
78   4. negate image option
79   5. jpeg quality factor
80   6. pixel scaling operations moved to image.c
81   7. Image resizing
82   8. moved fits read to image.c
83   9. Option to specify output directory
85   Reks Oct/Nov 2012 v2.1
86   1. Fixed a segfault on zero/blank fits images
87  *---------------------------------------------------------------------------*/
89 #include "fits2jpeg.h"
91 char *optarg;
92 int optindex;
94 /*--------------------------- Begin Main Progam -----------------------------*/
95 int main(int argc, char *argv[], char *envp[])
96 {
97     int scale = 0, jpgqual = 100, status = 0;
98     unsigned int i = 0, j = 0;
99     unsigned int customdir = 0, usrclip = 0, usrnegt = 0, usrzoom = 0;
100     long xdim = 0, ydim = 0, row_stride;
101     unsigned long npixels = 0;
102     float datamin = 0.0, datamax = 0.0, zoomfact = 1.0;
103     float *data;
104     char jpeg_file_name[MAX_TEXT] = "", fits_file_name[MAX_TEXT] = "";
105     char output_dir[MAX_TEXT] = "";
106     char *tmpstr, *sptr;
108     int opt;                                                   /* For getopt */
109     extern char *optarg;
110     extern int optindex;
111     mode_t mode = 0755;
112     FILE *jpeg_file;
113     struct jpeg_error_mgr jerr;
114     struct jpeg_compress_struct cinfo;
115     JSAMPROW row_pointer[1];
116     JSAMPLE *image_buffer;
117     /*---------------- End of variable initialization -----------------------*/
119     banner();              /* Header: Talks about program, version & purpose */
120     set_signals();                                  /* Trap un-natural exits */
122     /*-------------------- Parse command line inputs ------------------------*/
124     if (argc < 2) usage();  /* People who don't give any arguments need help */
125     while ( ( opt = my_getopt ( argc, argv, "d:e:hnq:r:s:z:" ) ) != EOF )
126         switch ( opt )
127         {
128         case 's':
129             if      (strcmp(optarg, "linear") == 0) scale = 0;
130             else if (strcmp(optarg, "sqroot") == 0)
131             {
132                 printinfo("Using square-root scale");
133                 scale = 1;
134             }
135             else if (strcmp(optarg, "square") == 0)
136             {
137                 printinfo("Using quadratic scale");
138                 scale = 2;
139             }
140             else if (strcmp(optarg, "cubic") == 0)
141             {
142                 printinfo("Using cubic scale");
143                 scale = 3;
144             }
145             else if (strcmp(optarg, "log") == 0)
146             {
147                 printinfo("Using log scale");
148                 scale = 4;
149             }
150             else if (strcmp(optarg, "normalize") == 0)
151             {
152                 printinfo("Performing histogram stretch (normalization)");
153                 scale = 5;
154             }
155             else if (strcmp(optarg, "equalize") == 0)
156             {
157                 printinfo("Performing Histogram Equalization");
158                 scale = 6;
159             }
160             else
161             {
162                 printwarn(strcat(optarg, " -- Unrecognized option"));
163                 scale = 0;
164             }
165             break;
167         case 'e':
168             /* Need to preserve it for backward compatibility */
169             if      (strcmp(optarg, "normalize") == 0) scale = 5;
170             else if (strcmp(optarg, "equalize") == 0) scale = 6;
171             else
172                 printerro(strcat(optarg, " -- Unrecognized option"));
173             break;
175         case 'r':
176             tmpstr = optarg;         /* make a copy.. and leave optarg alone */
177             sptr = strpbrk(tmpstr, ":");               /* Find the delimiter */
178             if (sptr == NULL)
179                 printerro("Expected a ':' as separator for min/max");
181             if (sptr+1 == tmpstr + strlen(tmpstr))         /* -c <datamin>: */
182                 datamax = FLT_MAX;
183             else
184                 datamax = atof(sptr+1);
186             /* There should be an easier way to find datamin alongside datamax,
187              * but I am too sleepy to try anything now                       */
188             sptr = strchr(tmpstr, ':');
189             if (sptr == tmpstr)                             /* -c :<datamax> */
190                 datamin = -1.0*FLT_MAX;
191             else
192             {
193                 sptr = strtok(tmpstr, ":");
194                 datamin = atof(sptr);
195             }
197             /* Remember.. now we have user specified range */
198             usrclip = 1;
199             break;
201         case 'n' :
202             usrnegt = 1;
203             break;
205         case 'q' :
206             jpgqual = atoi(optarg);
207             if (jpgqual < 0) jpgqual = 0;
208             if (jpgqual > 100) jpgqual = 100;
209             printf("INFO   : jpeg quality factor changed to %d\n", jpgqual);
210             break;
212         case 'z' :
213             zoomfact = atof(optarg);
214             zoomfact = fabs(zoomfact);
215             if (zoomfact < 0.01) zoomfact = 0.01;
216             if (zoomfact > 4.0) zoomfact = 4.0;
217             usrzoom = 1;
218             break;
220         case 'd':
221             strcpy(output_dir, optarg);
222             printf("INFO   : Output image directory is %s\n", output_dir);
223             customdir = 1;
224             status = make_tree(output_dir, mode);
225             if (status != 0) printerro("Unable to create output directory");
226             break;
228         case 'h':
229             usage();
230             break;
232         default :
233             printerro("You can try 'fits2jpeg -h' for valid options");
234             break;
235         }
236     if (scale == 0) printinfo("Using linear scale");
237     if ((argc - optindex) == 0) usage();        /* Someone gave only options */
239     /*---------------------- Begin processing files -------------------------*/
241     /* Getopt would have processed all argument with a "-" in front. optindex
242        is the counter, which keeps the number of options supplied. Whatever
243        remains in the list of command line arguments are the fits files.
244        NOTE: POSIX systems will do the regex matching and expand wild card
245              entries for us. Even windoze XP Pro is known to do that...      */
246     for (j = optindex; j < argc; j++)
247     {
249         strcpy(fits_file_name, argv[j]);
251         strcpy(jpeg_file_name, fits_file_name);
252         strtok(jpeg_file_name, ".");
253         strcat(jpeg_file_name, ".jpg");
255         if (customdir == 1)
256         {
257             if (output_dir[strlen(output_dir) - 1] != '/')
258                 strcat(output_dir, "/");
260             strcat(output_dir, basename(jpeg_file_name));
261             strcpy(jpeg_file_name, output_dir);
262         }
264         read_fits(fits_file_name, &xdim, &ydim, &data);
265         npixels = xdim * ydim;
267         printf("INFO   : data input from %s\n", fits_file_name);
270         /* Now we need to look at user options..                             */
271         /*-------------------------------------------------------------------*/
273         /* IF user has provided a range, fix them as min & max in image */
274         if (usrclip == 1)
275         {
276             printinfo("user defined pixel range");
277             for (i = 0; i < npixels; ++i)
278             {
279                 if (data[i] > datamax) data[i] = datamax;
280                 if (data[i] < datamin) data[i] = datamin;
281             } /*endfor*/
282         }
284         scale_pixels(scale, npixels, data, &image_buffer);
286         /* Before we write jpeg, check if there is any requirement to negate
287          * the image (usrnegt = 1)                                           */
288         if (usrnegt == 1)
289         {
290             printinfo("Negating Image");
291             for (i = 0; i <  npixels; ++i) image_buffer[i] ^= 0xff;
292         }
294         if (usrzoom == 1)
295         {
296             printf("INFO   : Image zoom factor = %3.2f\n", zoomfact);
297             resize_image(&xdim, &ydim, zoomfact, &image_buffer);
298         }
300         /*Write out data into JPEG file*/
301         jpeg_file = fopen(jpeg_file_name, "wb");/* Open JPEG file for writing*/
302         if (!jpeg_file) printerro("Unable to create output file");
303         cinfo.err = jpeg_std_error(&jerr);             /* JPEG error handler */
304         jpeg_create_compress(&cinfo);                 /* JPEG initialization */
305         jpeg_stdio_dest(&cinfo, jpeg_file); /* Send compressed data to stdio */
306         cinfo.image_width = xdim;                             /* Image width */
307         cinfo.image_height = ydim;                           /* Image height */
308         cinfo.input_components = 1;            /* Number of colors per pixel */
309         cinfo.in_color_space = JCS_GRAYSCALE;   /* colorspace of input image */
310         jpeg_set_defaults(&cinfo);                      /* Set JPEG defaults */
311         jpeg_set_quality(&cinfo, jpgqual, TRUE);  /* Set jpeg quality factor */
312         jpeg_start_compress(&cinfo, TRUE);       /* default data compression */
313         row_stride = xdim;                /* JSAMPLEs per row inimage buffer */
315         /* Now we have to turn the data upside down                          */
316         while (cinfo.next_scanline < cinfo.image_height)
317         {
318             row_pointer[0] = &image_buffer[(cinfo.image_height -
319                                             cinfo.next_scanline) *
320                                             row_stride];
322             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
323         }/*Loop through file writing one line at a time*/
325         jpeg_finish_compress(&cinfo);                  /* Finish compression */
326         fclose(jpeg_file);                                     /* Close file */
327         jpeg_destroy_compress(&cinfo);                     /* Release memory */
329         free(data);
330         free(image_buffer);
331         printf("INFO   : wrote output to %s\n", jpeg_file_name);
332     }
333     exit(EXIT_SUCCESS);
334 } /*End of program*/