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 plans
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
84  *---------------------------------------------------------------------------*/
86 #include "fits2jpeg.h"
88 char *optarg;
89 int optindex;
91 /*--------------------------- Begin Main Progam -----------------------------*/
92 int main(int argc, char *argv[], char *envp[])
93 {
94     int scale = 0, jpgqual = 100, status = 0;
95     unsigned int i = 0, j = 0;
96     unsigned int customdir = 0, usrclip = 0, usrnegt = 0, usrzoom = 0;
97     long xdim = 0, ydim = 0, row_stride;
98     unsigned long npixels = 0;
99     float datamin = 0.0, datamax = 0.0, zoomfact = 1.0;
100     float *data;
101     char jpeg_file_name[MAX_TEXT] = "", fits_file_name[MAX_TEXT] = "";
102     char output_dir[MAX_TEXT] = "";
103     char *tmpstr, *sptr;
105     int opt;                                                   /* For getopt */
106     extern char *optarg;
107     extern int optindex;
108     mode_t mode = 0755;
109     FILE *jpeg_file;
110     struct jpeg_error_mgr jerr;
111     struct jpeg_compress_struct cinfo;
112     JSAMPROW row_pointer[1];
113     JSAMPLE *image_buffer;
114     /*---------------- End of variable initialization -----------------------*/
116     banner();              /* Header: Talks about program, version & purpose */
117     set_signals();                                  /* Trap un-natural exits */
119     /*-------------------- Parse command line inputs ------------------------*/
121     if (argc < 2) usage();  /* People who don't give any arguments need help */
122     while ( ( opt = my_getopt ( argc, argv, "d:e:hnq:r:s:z:" ) ) != EOF )
123         switch ( opt )
124         {
125         case 's':
126             if      (strcmp(optarg, "linear") == 0) scale = 0;
127             else if (strcmp(optarg, "sqroot") == 0)
128             {
129                 printinfo("Using square-root scale");
130                 scale = 1;
131             }
132             else if (strcmp(optarg, "square") == 0)
133             {
134                 printinfo("Using quadratic scale");
135                 scale = 2;
136             }
137             else if (strcmp(optarg, "cubic") == 0)
138             {
139                 printinfo("Using cubic scale");
140                 scale = 3;
141             }
142             else if (strcmp(optarg, "log") == 0)
143             {
144                 printinfo("Using log scale");
145                 scale = 4;
146             }
147             else if (strcmp(optarg, "normalize") == 0)
148             {
149                 printinfo("Performing histogram stretch (normalization)");
150                 scale = 5;
151             }
152             else if (strcmp(optarg, "equalize") == 0)
153             {
154                 printinfo("Performing Histogram Equalization");
155                 scale = 6;
156             }
157             else
158             {
159                 printwarn(strcat(optarg, " -- Unrecognized option"));
160                 scale = 0;
161             }
162             break;
164         case 'e':
165             /* Need to preserve it for backward compatibility */
166             if      (strcmp(optarg, "normalize") == 0) scale = 5;
167             else if (strcmp(optarg, "equalize") == 0) scale = 6;
168             else
169                 printerro(strcat(optarg, " -- Unrecognized option"));
170             break;
172         case 'r':
173             tmpstr = optarg;         /* make a copy.. and leave optarg alone */
174             sptr = strpbrk(tmpstr, ":");               /* Find the delimiter */
175             if (sptr == NULL)
176                 printerro("Expected a ':' as separator for min/max");
178             if (sptr+1 == tmpstr + strlen(tmpstr))         /* -c <datamin>: */
179                 datamax = FLT_MAX;
180             else
181                 datamax = atof(sptr+1);
183             /* There should be an easier way to find datamin alongside datamax,
184              * but I am too sleepy to try anything now                       */
185             sptr = strchr(tmpstr, ':');
186             if (sptr == tmpstr)                             /* -c :<datamax> */
187                 datamin = -1.0*FLT_MAX;
188             else
189             {
190                 sptr = strtok(tmpstr, ":");
191                 datamin = atof(sptr);
192             }
194             /* Remember.. now we have user specified range */
195             usrclip = 1;
196             break;
198         case 'n' :
199             usrnegt = 1;
200             break;
202         case 'q' :
203             jpgqual = atoi(optarg);
204             if (jpgqual < 0) jpgqual = 0;
205             if (jpgqual > 100) jpgqual = 100;
206             printf("INFO   : jpeg quality factor changed to %d\n", jpgqual);
207             break;
209         case 'z' :
210             zoomfact = atof(optarg);
211             zoomfact = fabs(zoomfact);
212             if (zoomfact < 0.01) zoomfact = 0.01;
213             if (zoomfact > 4.0) zoomfact = 4.0;
214             usrzoom = 1;
215             break;
217         case 'd':
218             strcpy(output_dir, optarg);
219             printf("INFO   : Output image directory is %s\n", output_dir);
220             customdir = 1;
221             status = make_tree(output_dir, mode);
222             if (status != 0) printerro("Unable to create output directory");
223             break;
225         case 'h':
226             usage();
227             break;
229         default :
230             printerro("You can try 'fits2jpeg -h' for valid options");
231             break;
232         }
233     if (scale == 0) printinfo("Using linear scale");
234     if ((argc - optindex) == 0) usage();        /* Someone gave only options */
236     /*---------------------- Begin processing files -------------------------*/
238     /* Getopt would have processed all argument with a "-" in front. optindex
239        is the counter, which keeps the number of options supplied. Whatever
240        remains in the list of command line arguments are the fits files.
241        NOTE: POSIX systems will do the regex matching and expand wild card
242              entries for us. Even windoze XP Pro is known to do that...      */
243     for (j = optindex; j < argc; j++)
244     {
246         strcpy(fits_file_name, argv[j]);
248         strcpy(jpeg_file_name, fits_file_name);
249         strtok(jpeg_file_name, ".");
250         strcat(jpeg_file_name, ".jpg");
252         if (customdir == 1)
253         {
254             if (output_dir[strlen(output_dir) - 1] != '/')
255                 strcat(output_dir, "/");
257             strcat(output_dir, basename(jpeg_file_name));
258             strcpy(jpeg_file_name, output_dir);
259         }
261         read_fits(fits_file_name, &xdim, &ydim, &data);
262         npixels = xdim * ydim;
264         printf("INFO   : data input from %s\n", fits_file_name);
267         /* Now we need to look at user options..                             */
268         /*-------------------------------------------------------------------*/
270         /* IF user has provided a range, fix them as min & max in image */
271         if (usrclip == 1)
272         {
273             printinfo("user defined pixel range");
274             for (i = 0; i < npixels; ++i)
275             {
276                 if (data[i] > datamax) data[i] = datamax;
277                 if (data[i] < datamin) data[i] = datamin;
278             } /*endfor*/
279         }
281         scale_pixels(scale, npixels, data, &image_buffer);
283         /* Before we write jpeg, check if there is any requirement to negate
284          * the image (usrnegt = 1)                                           */
285         if (usrnegt == 1)
286         {
287             printinfo("Negating Image");
288             for (i = 0; i <  npixels; ++i) image_buffer[i] ^= 0xff;
289         }
291         if (usrzoom == 1)
292         {
293             printf("INFO   : Image zoom factor = %3.2f\n", zoomfact);
294             resize_image(&xdim, &ydim, zoomfact, &image_buffer);
295         }
297         /*Write out data into JPEG file*/
298         jpeg_file = fopen(jpeg_file_name, "wb");/* Open JPEG file for writing*/
299         if (!jpeg_file) printerro("Unable to create output file");
300         cinfo.err = jpeg_std_error(&jerr);             /* JPEG error handler */
301         jpeg_create_compress(&cinfo);                 /* JPEG initialization */
302         jpeg_stdio_dest(&cinfo, jpeg_file); /* Send compressed data to stdio */
303         cinfo.image_width = xdim;                             /* Image width */
304         cinfo.image_height = ydim;                           /* Image height */
305         cinfo.input_components = 1;            /* Number of colors per pixel */
306         cinfo.in_color_space = JCS_GRAYSCALE;   /* colorspace of input image */
307         jpeg_set_defaults(&cinfo);                      /* Set JPEG defaults */
308         jpeg_set_quality(&cinfo, jpgqual, TRUE);  /* Set jpeg quality factor */
309         jpeg_start_compress(&cinfo, TRUE);       /* default data compression */
310         row_stride = xdim;                /* JSAMPLEs per row inimage buffer */
312         /* Now we have to turn the data upside down                          */
313         while (cinfo.next_scanline < cinfo.image_height)
314         {
315             row_pointer[0] = &image_buffer[(cinfo.image_height -
316                                             cinfo.next_scanline) *
317                                             row_stride];
319             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
320         }/*Loop through file writing one line at a time*/
322         jpeg_finish_compress(&cinfo);                  /* Finish compression */
323         fclose(jpeg_file);                                     /* Close file */
324         jpeg_destroy_compress(&cinfo);                     /* Release memory */
326         free(data);
327         free(image_buffer);
328         printf("INFO   : wrote output to %s\n", jpeg_file_name);
329     }
330     exit(EXIT_SUCCESS);
331 } /*End of program*/