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
81   TODO
82       1. move fits read to image.c
83       2. 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     fitsfile *fptr;
96     int scale = 0, status = 0, jpgqual = 100, nfound, anynull;
97     unsigned int i = 0, j = 0, usrclip = 0, usrnegt = 0, usrzoom = 0;
98     long xdim = 0, ydim = 0, naxes[2], row_stride;
99     unsigned long npixels = 0;
100     float datamin = 0.0, datamax = 0.0, nullval = 0.0, zoomfact = 1.0;
101     float *data;
102     char jpeg_file_name[MAX_TEXT] = "", fits_file_name[MAX_TEXT] = "";
103     char *tmpstr, *sptr;
105     int opt;                                                   /* For getopt */
106     extern char *optarg;
107     extern int optindex;
109     FILE *jpeg_file;
111     struct jpeg_error_mgr jerr;
112     struct jpeg_compress_struct cinfo;
113     JSAMPROW row_pointer[1];
114     JSAMPLE *image_buffer;
115     /*---------------- End of variable initialization -----------------------*/
117     PRINT_BANNER();        /* Header: Talks about program, version & purpose */
118     set_signals();                                  /* Trap un-natural exits */
120     /*-------------------- Parse command line inputs ------------------------*/
122     if (argc < 2) usage();  /* People who don't give any arguments need help */
123     while ( ( opt = my_getopt ( argc, argv, "e:hnq:r:s:z:" ) ) != EOF )
124         switch ( opt )
125         {
126         case 's':
127             if      (strcmp(optarg, "linear")   ==0) scale = 0;
128             else if (strcmp(optarg, "sqroot")   ==0) scale = 1;
129             else if (strcmp(optarg, "square")   ==0) scale = 2;
130             else if (strcmp(optarg, "cubic")    ==0) scale = 3;
131             else if (strcmp(optarg, "log")      ==0) scale = 4;
132             else if (strcmp(optarg, "normalize")==0) scale = 5;
133             else if (strcmp(optarg, "equalize") ==0) scale = 6;
134             else
135                 printwarn(strcat(optarg, " -- Unrecognized option"));
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             }
169             /* Remember.. now we have user specified range */
170             usrclip = 1;
171             break;
173         case 'n' :
174             usrnegt = 1;
175             break;
177         case 'q' :
178             jpgqual = atoi(optarg);
179             if (jpgqual < 0) jpgqual = 0;
180             if (jpgqual > 100) jpgqual = 100;
181             printf("INFO   : jpeg quality factor changed to %d\n", jpgqual);
182             break;
184         case 'z' :
186             /* Do something to scale image
187              */
188             zoomfact = atof(optarg);
189             zoomfact = abs(zoomfact);
190             if (zoomfact < 0.01) zoomfact = 0.01;
191             if (zoomfact > 4.0) zoomfact = 4.0;
192             usrzoom = 1;
193             break;
195         case 'h':
196             usage();
197             break;
199         default :
200             printerro("You can try 'fits2jpeg -h' for valid options");
201             break;
202         }
204     if ((argc - optindex) == 0) usage();        /* Someone gave only options */
206     /*---------------------- Begin processing files -------------------------*/
208     /* Getopt would have processed all argument with a "-" in front. optindex
209        is the counter, which keeps the number of options supplied. Whatever
210        remains in the list of command line arguments are the fits files.
211        NOTE: POSIX systems will do the regex matching and expand wild card
212              entries for us. Even windoze XP Pro is known to do that...      */
213     for (j = optindex; j < argc; j++)
214     {
216         strcpy(fits_file_name, argv[j]);
218         strcpy(jpeg_file_name, fits_file_name);
219         strtok(jpeg_file_name, ".");
220         strcat(jpeg_file_name, ".jpg");
222         fits_open_file(&fptr, fits_file_name, READONLY, &status);
223         fits_read_keys_lng(fptr, "NAXIS", 1, 2, naxes, &nfound, &status);
224         if (status)
225             printerro(strcat(fits_file_name, " <-- Failed to open the file"));
227         /* Read in data */
228         npixels = naxes[0] * naxes[1];
229         data = malloc(sizeof(float) * npixels);
231         nullval = 0;
232         if (fits_read_img(fptr, TFLOAT, 1, npixels, &nullval, data, &anynull,
233                           &status))
234             printerro(strcat(fits_file_name, " has no valid fits image data"));
236         fits_close_file(fptr, &status);
237         /*Close data file*/
238         printf("INFO   : data input from %s\n", fits_file_name);
241         /* Now we need to look at user options..                             */
242         /*-------------------------------------------------------------------*/
244         /* IF user has provided a range, fix them as min & max in image */
245         if (usrclip == 1)
246         {
247             printinfo("user defined pixel range");
248             for (i = 0; i < npixels; ++i)
249             {
250                 if (data[i] > datamax) data[i] = datamax;
251                 if (data[i] < datamin) data[i] = datamin;
252             } /*endfor*/
253         }
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)
268         {
269             printf("INFO   : Image zoom factor = %3.2f\n", zoomfact);
270             resize_image(&xdim, &ydim, zoomfact, &image_buffer);
271         }
273         /*Write out data into JPEG file*/
274         jpeg_file = fopen(jpeg_file_name, "wb");/* Open JPEG file for writing*/
275         cinfo.err = jpeg_std_error(&jerr);             /* JPEG error handler */
276         jpeg_create_compress(&cinfo);                 /* JPEG initialization */
277         jpeg_stdio_dest(&cinfo, jpeg_file); /* Send compressed data to stdio */
278         cinfo.image_width = xdim;                             /* Image width */
279         cinfo.image_height = ydim;                           /* Image height */
280         cinfo.input_components = 1;            /* Number of colors per pixel */
281         cinfo.in_color_space = JCS_GRAYSCALE;   /* colorspace of input image */
282         jpeg_set_defaults(&cinfo);                      /* Set JPEG defaults */
283         jpeg_set_quality(&cinfo, jpgqual, TRUE);  /* Set jpeg quality factor */
284         jpeg_start_compress(&cinfo, TRUE);       /* default data compression */
285         row_stride = xdim;                /* JSAMPLEs per row inimage buffer */
287         /* Now we have to turn the data upside down                          */
288         while (cinfo.next_scanline < cinfo.image_height)
289         {
290             row_pointer[0] = &image_buffer[(cinfo.image_height -
291                                             cinfo.next_scanline) *
292                                             row_stride];
294             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
295         }/*Loop through file writing one line at a time*/
297         jpeg_finish_compress(&cinfo);                  /* Finish compression */
298         fclose(jpeg_file);                                     /* Close file */
299         jpeg_destroy_compress(&cinfo);                     /* Release memory */
301         free(data);
302         free(image_buffer);
303         printf("INFO   : wrote output to %s\n", jpeg_file_name);
304     }
305     exit(EXIT_SUCCESS);
306 } /*End of program*/