1 /***************************************************************************
2  *   Copyright (C) 2012 by CADS/UV Software Team,                          *
3  *                         Indian Institute of Astrophysics                *
4  *                         Bangalore 560034                                *
5  *                         cads_AT_iiap.res.in                             *
6  *                                                                         *
7  *   This program 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 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  *   This program 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 this program; if not, write to the                         *
19  *   Free Software Foundation, Inc.,                                       *
20  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
21  ***************************************************************************/
22 /*---------------------------------------------------------------------------
23   This program will write out FITS data into a jpeg file.
24   Assume that data are in first extension and are two dimensional
26   Libraries needed are CFITSIO, JPEG
27   Original author: Jayant Murthy
28   Last Modification: 11/05/02
30   Reconstruction and additional features: Reks
31  *---------------------------------------------------------------------------*/
33 /*---------------------------------------------------------------------------
34   Modification History
36   Reks, 5th October 2007: v0.01
37   1. configure checks for jpeglib.
38      optional extra arguments --with-jpeglib=<location>
39                               --with-cfitsio=<location>
40   2. Makes blank images! datamin and max are not set..
42   Reks, 6th October 2007: v0.10
43   1. Added 2 functions to fetch max and min from image
44   2. user can choose various scales like log/square/square-root
45   3. Increased verbosity, added banner, help message, etc
46   4. Inputs through getopt. will Ask if mandatory input (fits file) is not
47      provided.
48   5. fitsio2
50   Reks, 10th October 2007: v0.9.0 (close to 1.0)
51   1. Found our niche :-) Can handle wild cards at command line,
52      batch processing.
53   2. Now users cannot specify the output file name though.. Its fixed to
54      <input_file_root>.jpg.
55   3. No getopt for input file.. getopt is limited to various image scaling
56      options.
58  Reks, 17th October 2007, v0.9.5
59   1. Merged all image scaling options into one input param: -s
60      optarg (from getopt) will decide scaling function. Now there is
61      enough room for additional flags (in future).
63  Reks, 22 June 2010 v1.0.0
64   1. Removed datamax() and datamin(). That was required only once!
65   2. option histoeq shifted to image enhancement option
66   3. Added "normalize" option
67      Now users can do scaling + enhance (if at all someone feels like it)
68   4. All options tested. First stable release :)
70   Reks 28 June 2012
71   1. rebranded to CADS/UVS
73   Reks 05-10 July 2012 v2.0 plans
74   1. Merged scale & operations to single flag
75   2. added fits2jpeg.h, help/banner functions moved to messages.c
76   3. Options to clip image min/max
77   4. negate image option
78   5. jpeg quality factor
79   6. pixel scaling operations moved to image.c
80   7. Image resizing works for reducing size. segfaults for zoom > 1 :(
81   TODO
82       1. move fits read to image.c
83       2. specify output directory
84       3. Image resizing options
85  *---------------------------------------------------------------------------*/
87 #include "fits2jpeg.h"
89 char *optarg;
90 int optindex;
92 /*--------------------------- Begin Main Progam -----------------------------*/
93 int main(int argc, char *argv[], char *envp[])
94 {
95     fitsfile *fptr;
97     int scale = 0, process = 0, status = 0, jpgqual = 100, nfound, anynull;
98     unsigned int i = 0, j = 0, usrclip = 0, usrnegt = 0, usrzoom = 0;
99     long xdim = 0, ydim = 0, naxes[2], row_stride;
100     unsigned long npixels = 0;
101     float datamin = 0.0, datamax = 0.0, nullval = 0.0, zoomfact = 1.0;
102     float *data;
103     char jpeg_file_name[MAX_TEXT] = "", fits_file_name[MAX_TEXT] = "";
104     char *tmpstr, *sptr;
106     int opt;                                                   /* For getopt */
107     extern char *optarg;
108     extern int optindex;
110     FILE *jpeg_file;
112     struct jpeg_error_mgr jerr;
113     struct jpeg_compress_struct cinfo;
114     JSAMPROW row_pointer[1];
115     JSAMPLE *image_buffer;
116     /*---------------- End of variable initialization -----------------------*/
118     PRINT_BANNER();        /* Header: Talks about program, version & purpose */
119     set_signals();                                  /* Trap un-natural exits */
121     /*-------------------- Parse command line inputs ------------------------*/
123     if (argc < 2) usage();  /* People who don't give any arguments need help */
124     while ( ( opt = my_getopt ( argc, argv, "e:hnq:r:s:z:" ) ) != EOF )
125         switch ( opt )
126         {
127         case 's':
128             if      (strcmp(optarg, "linear")   ==0) scale = 0;
129             else if (strcmp(optarg, "sqroot")   ==0) scale = 1;
130             else if (strcmp(optarg, "square")   ==0) scale = 2;
131             else if (strcmp(optarg, "cubic")    ==0) scale = 3;
132             else if (strcmp(optarg, "log")      ==0) scale = 4;
133             else if (strcmp(optarg, "normalize")==0) scale = 5;
134             else if (strcmp(optarg, "equalize") ==0) scale = 6;
135             else
136                 scale = 0;
137             break;
139         case 'e':
140             /* Need to preserve it for backward compatibility */
141             if      (strcmp(optarg, "normalize")==0) scale = 5;
142             else if (strcmp(optarg, "equalize") ==0) scale = 6;
143             else
144                 printerro(strcat(optarg, " -- Unrecognized option"));
145             break;
147         case 'r':
148             tmpstr = optarg;         /* make a copy.. and leave optarg alone */
149             sptr = strpbrk(tmpstr, ":");               /* Find the delimiter */
150             if (sptr == NULL)
151                 printerro("Expected a ':' as separator for min/max");
153             if (sptr+1 == tmpstr + strlen(tmpstr))         /* -c <datamin>: */
154                 datamax = FLT_MAX;
155             else
156                 datamax = atof(sptr+1);
158             /* There should be an easier way to find datamin alongside datamax,
159              * but I am too sleepy to try anything now                       */
160             sptr = strchr(tmpstr, ':');
161             if (sptr == tmpstr)                             /* -c :<datamax> */
162                 datamin = -1.0*FLT_MAX;
163             else
164             {
165                 sptr = strtok(tmpstr, ":");
166                 datamin = atof(sptr);
167             }
168             printinfo("User defined pixel range");
170             /* Remember.. now we have user specified range */
171             usrclip = 1;
172             break;
174         case 'n' :
175             usrnegt = 1;
176             break;
178         case 'q' :
179             jpgqual = atoi(optarg);
180             if (jpgqual < 0) jpgqual = 0;
181             if (jpgqual > 100) jpgqual = 100;
182             printf("INFO   : jpeg quality factor changed to %d\n", jpgqual);
183             break;
185         case 'z' :
187             /* Do something to scale image
188              */
189             zoomfact = atof(optarg);
190             if (zoomfact < 0.0) zoomfact = 1.0;
191             usrzoom = 1;
192             break;
194         case 'h':
195             usage();
196             break;
198         default :
199             printerro("You can try 'fits2jpeg -h' for valid options");
200             break;
201         }
203     if ((argc - optindex) == 0) usage();        /* Someone gave only options */
205     /*---------------------- Begin processing files -------------------------*/
207     /* Getopt would have processed all argument with a "-" in front. optindex
208        is the counter, which keeps the number of options supplied. Whatever
209        remains in the list of command line arguments are the fits files.
210        NOTE: POSIX systems will do the regex matching and expand wild card
211              entries for us. Even windoze XP Pro is known to do that...      */
212     for (j = optindex; j < argc; j++)
213     {
215         strcpy(fits_file_name, argv[j]);
217         strcpy(jpeg_file_name, fits_file_name);
218         strtok(jpeg_file_name, ".");
219         strcat(jpeg_file_name, ".jpg");
221         fits_open_file(&fptr, fits_file_name, READONLY, &status);
222         fits_read_keys_lng(fptr, "NAXIS", 1, 2, naxes, &nfound, &status);
223         if (status)
224             printerro(strcat(fits_file_name, " <-- Failed to open the file"));
226         /* Read in data */
227         npixels = naxes[0] * naxes[1];
228         data = (float *) malloc(sizeof(float) * npixels);
230         nullval = 0;
231         if (fits_read_img(fptr, TFLOAT, 1, npixels, &nullval, data, &anynull,
232                           &status))
233             printerro(strcat(fits_file_name, " has no valid fits image data"));
235         fits_close_file(fptr, &status);
236         /*Close data file*/
237         printf("INFO   : data input from %s\n", fits_file_name);
240         /* Now we need to look at user options..                             */
241         /*-------------------------------------------------------------------*/
243         /* IF user has provided a range, fix them as min & max in image */
244         if (usrclip == 1)
245         {
246             for (i = 0; i < npixels; ++i)
247             {
248                 if (data[i] > datamax) data[i] = datamax;
249                 if (data[i] < datamin) data[i] = datamin;
250             } /*endfor*/
251         }
253         /* Allocate image buffer */
254         image_buffer = (unsigned char *) malloc(sizeof(char) * npixels);
255         scale_pixels(scale, npixels, data, image_buffer);
257         /* Before we write jpeg, check if there is any requirement to negate
258          * the image (usrnegt = 1)                                           */
259         if (usrnegt == 1)
260         {
261             printinfo("Negating Image");
262             for (i = 0; i <  npixels; ++i) image_buffer[i] ^= 0xff;
263         }
265         xdim = naxes[0];
266         ydim = naxes[1];
267         if (usrzoom == 1) resize_image(&xdim, &ydim, zoomfact, image_buffer);
269         /*Write out data into JPEG file*/
270         jpeg_file = fopen(jpeg_file_name, "wb");/* Open JPEG file for writing*/
271         cinfo.err = jpeg_std_error(&jerr);             /* JPEG error handler */
272         jpeg_create_compress(&cinfo);                 /* JPEG initialization */
273         jpeg_stdio_dest(&cinfo, jpeg_file); /* Send compressed data to stdio */
274         cinfo.image_width = xdim;                             /* Image width */
275         cinfo.image_height = ydim;                           /* Image height */
276         cinfo.input_components = 1;            /* Number of colors per pixel */
277         cinfo.in_color_space = JCS_GRAYSCALE;   /* colorspace of input image */
278         jpeg_set_defaults(&cinfo);                      /* Set JPEG defaults */
279         jpeg_set_quality(&cinfo, jpgqual, TRUE);  /* Set jpeg quality factor */
280         jpeg_start_compress(&cinfo, TRUE);       /* default data compression */
281         row_stride = xdim;                /* JSAMPLEs per row inimage buffer */
283         /* Now we have to turn the data upside down                          */
284         while (cinfo.next_scanline < cinfo.image_height)
285         {
286             row_pointer[0] = &image_buffer[(cinfo.image_height -
287                                             cinfo.next_scanline) *
288                                             row_stride];
290             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
291         }/*Loop through file writing one line at a time*/
293         jpeg_finish_compress(&cinfo);                  /* Finish compression */
294         fclose(jpeg_file);                                     /* Close file */
295         jpeg_destroy_compress(&cinfo);                     /* Release memory */
297         free(data);
298         free(image_buffer);
299         printf("INFO   : wrote output to %s\n", jpeg_file_name);
300     }
301     exit(EXIT_SUCCESS);
302 } /*End of program*/