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  *---------------------------------------------------------------------------*/
32 /*---------------------------------------------------------------------------
33   Modification History
35   Reks, 5th October 2007: v0.01
36   1. configure checks for jpeglib.
37      optional extra arguments --with-jpeglib=<location>
38                               --with-cfitsio=<location>
39   2. Makes blank images! datamin and max are not set..
41   Reks, 6th October 2007: v0.10
42   1. Added 2 functions to fetch max and min from image
43   2. user can choose various scales like log/square/square-root
44   3. Increased verbosity, added banner, help message, etc
45   4. Inputs through getopt. will Ask if mandatory input (fits file) is not
46      provided.
47   5. fitsio2
49   Reks, 10th October 2007: v0.9.0 (close to 1.0)
50   1. Found our niche :-) Can handle wild cards at command line,
51      batch processing.
52   2. Now users cannot specify the output file name though.. Its fixed to
53      <input_file_root>.jpg.
54   3. No getopt for input file.. getopt is limited to various image scaling
55      options.
57  Reks, 17th October 2007, v0.9.5
58   1. Merged all image scaling options into one input param: -s
59      optarg (from getopt) will decide scaling function. Now there is
60      enough room for additional flags (in future).
62  Reks, 22 June 2010 v1.0.0
63   1. Removed datamax() and datamin(). That was required only once!
64   2. option histoeq shifted to image enhancement option
65   3. Added "normalize" option
66      Now users can do scaling + enhance (if at all someone feels like it)
67   4. All options tested. First stable release :)
69   Reks 28 June 2012
70   1. rebranded to CADS/UVS
71  *-------------------------------------------------------------------------*/
73 #ifdef HAVE_CONFIG_H
74 #include <config.h>
75 #endif
77 /*Header Definitions*/
78 #include <stdio.h>
79 #include <stdlib.h>
80 #include <string.h>
81 #include <math.h>
82 #include <libgen.h>
83 #include <dirent.h>
84 #include <fitsio2.h>
85 #include <jpeglib.h>
87 #define PROGRAM "fits2jpeg"
88 #define r2d (90./atan2(1,0))
89 #define MAX_TEXT 150
90 int my_getopt(int, char * const *, const char *);
91 void signals_handler(int);
92 void set_signals(void);
94 char *optarg;
95 int optindex;
96 /*---------------------------------------------------------------------------*/
97 void PRINT_BANNER()
98 {
99     printf("\n");
100     printf(" %s %s\n", PROGRAM, VERSION);
101     printf(" Converts fits image files to jpeg\n");
102     printf("-----------------------------------------------------------\n");
104 /*---------------------------------------------------------------------------*/
105 void usage()
107     printf ("\n Usage: fits2jpeg [-s <options>] <fits_file>             \n");
108     printf ("  Options are:                                             \n");
109     printf ("    -h help                                                \n");
110     printf ("    -s <scale_type>                                        \n");
111     printf ("       scale for output image, where <scale_type> can be:  \n");
112     printf ("         linear         Linear scale, default            \n\n");
113     printf ("         sqroot         for square root scale              \n");
114     printf ("         square         for quadratic scale                \n");
115     printf ("         cubic          for cubic scale                    \n");
116     printf ("         log            for log scale                      \n");
117     printf ("    -e <operation>                                         \n");
118     printf ("       Image enhancements, where <operation> can be:       \n");
119     printf ("         normalize      for linear histogram stretch       \n");
120     printf ("         equalize       for histogram equalization         \n");
121     printf ("  Output will be written to <fits_file_root>.jpg. Wild card\n");
122     printf ("  entries allowed in <fits_file>; For eg: *.fits, m31*.fits\n");
123     printf ("  ngc???.fits etc.                                         \n");
124     printf ("-----------------------------------------------------------\n");
125     printf ("Copyright (c) 2012 The CADS Team, IIAp. [GPL v3.0 or later]\n");
126     printf ("Report Bugs to: http://cads.iiap.res.in/bugzilla           \n");
127     printf ("Documentation : http://cads.iiap.res.in/software         \n\n");
128     exit(1);
130 /*---------------------------------------------------------------------------*/
132 /*--------------------------- Begin Main Progam -----------------------------*/
133 int main(int argc, char *argv[], char *envp[])
135     fitsfile *fptr;
137     int scale = 0, process = 0, status = 0, nfound, anynull;
138     unsigned int i = 0, j = 0, npixels = 0;
139     long naxes[2], row_stride;
140     float datamin = 0.0, datamax = 0.0;
141     float nullval = 0.0, scl_data;
142     float *data;
143     float tmp = 0.0;
144     float hist[256] = {0.0}, cumhist[256] = {0.0};
145     char jpeg_file_name[MAX_TEXT] = "", fits_file_name[MAX_TEXT] = "";
147     int opt;                                                   /* For getopt */
148     extern char *optarg;
149     extern int optindex;
151     FILE *jpeg_file;
153     struct jpeg_error_mgr jerr;
154     struct jpeg_compress_struct cinfo;
155     int JMAXVAL = 255;                    /* Max value for greyscale in jpeg */
156     JSAMPROW row_pointer[1];
157     JSAMPLE *image_buffer;
158     /*---------------- End of variable initialization -----------------------*/
160     PRINT_BANNER();        /* Header: Talks about program, version & purpose */
161     set_signals();                                  /* Trap un-natural exits */
163     /*-------------------- Parse command line inputs ------------------------*/
165     if (argc < 2) usage();  /* People who don't give any arguments need help */
166     while ( ( opt = my_getopt ( argc, argv, "s:e:h" ) ) != EOF )
167         switch ( opt )
168         {
169         case 's':
170             if (strcmp(optarg, "sqroot")   ==0) scale = 1;
171             if (strcmp(optarg, "square")   ==0) scale = 2;
172             if (strcmp(optarg, "cube")     ==0) scale = 3;
173             if (strcmp(optarg, "log")      ==0) scale = 4;
174             break;
176         case 'e':
177             if (strcmp(optarg, "normalize")==0) process = 1;
178             if (strcmp(optarg, "equalize") ==0) process = 2;
179             break;
181         case 'h':
182             usage();
183             break;
185         case '*':
186             usage();
187             break;
188         }
190     if ((argc - optindex) == 0) usage();       /* Somebody gave only options */
192     /*---------------------- Begin processing files -------------------------*/
194     /* Getopt would have processed all argument with a "-" in front. optindex
195        is the counter, which keeps the number of options supplied. Whatever
196        remains in the list of command line arguments are the fits files.
197        NOTE: POSIX systems will do the regex matching and expand wild card
198              entries for us. Even windoze XP Pro is known to do that...      */
199     for (j = optindex; j < argc; j++)
200     {
202         strcpy(fits_file_name, argv[j]);
204         strcpy(jpeg_file_name, fits_file_name);
205         strtok(jpeg_file_name, ".");
206         strcat(jpeg_file_name, ".jpg");
208         fits_open_file(&fptr, fits_file_name, READONLY, &status);
209         fits_read_keys_lng(fptr, "NAXIS", 1, 2, naxes, &nfound, &status);
210         if (status)
211         {
212             fprintf(stderr, "ERROR  : Could not open file: %s\n",
213                     fits_file_name);
214             exit(EXIT_FAILURE);
215         }/*Endif*/
217         /* Read in data */
218         npixels = naxes[0] * naxes[1];
219         data = (float *) malloc(sizeof(float) * npixels);
221         nullval = 0;
222         if (fits_read_img(fptr, TFLOAT, 1, npixels, &nullval, data, &anynull,
223                           &status))
224         {
225             fprintf(stderr, "ERROR  : No valid fits image data in %s\n",
226                     fits_file_name);
227             exit(EXIT_FAILURE);
228         }/*Endif*/
230         fits_close_file(fptr, &status);
231         /*Close data file*/
232         printf("INFO   : data input from %s\n", fits_file_name);
234         /* find min & max in image */
235         datamax = -1.0e9;
236         datamin = +1.0e9;
237         for (i = 0; i < npixels; ++i)
238         {
239             if (data[i] > datamax) datamax = data[i];
240             if (data[i] < datamin) datamin = data[i];
241         } /*endfor*/
243         /* Convert data into bytscaled values for jpeg file                 */
244         /* the dynamic range is reduced to 255 for jpeg                     */
245         scl_data = (datamax - datamin)/(float)JMAXVAL;
246         for (i = 0; i < npixels; ++i)
247             data[i] = (data[i] - datamin)/scl_data;
249         /* All data is now squeezed into the range 0 - 255                   */
250         /* NOTE: At this point onwards min & max is 0 and 255 respectively   */
253         /* Now we need to look at user options..                             */
254         /* 1. scale it as per user's requirement.                            */
255         /* 2. image enhancing operations                                     */
256                                                     /* Allocate image buffer */
257         image_buffer = (unsigned char *) malloc(sizeof(char) * npixels);
258         scl_data = 1.0;
259         switch (scale)
260         {
261         case 1 :                                              /* Square root */
262             printf("INFO   : Using square-root scale\n");
263             scl_data = sqrt((float)JMAXVAL)/(float)JMAXVAL;
264             for (i = 0; i < npixels; ++i)
265                 image_buffer[i] = (int)(sqrt(data[i])/scl_data);
266             break;
268         case 2 :                                                   /* Square */
269             printf("INFO   : Using quadratic scale\n");
270             scl_data = pow((float)JMAXVAL,2)/(float)JMAXVAL;
271             for (i = 0; i < npixels; ++i)
272                 image_buffer[i] = (int)abs((pow(data[i],2) - 1.0)/scl_data);
273             break;
275         case 3 :                                                    /* Cubic */
276             printf("INFO   : Using cubic scale\n");
277             scl_data = pow((float)JMAXVAL,3)/(float)JMAXVAL;
278             for (i = 0; i < npixels; ++i)
279                 image_buffer[i] = (int)abs((pow(data[i],3) - 1.0)/scl_data);
280             break;
282         case 4 :                                                      /* log */
283             printf("INFO   : Using log scale\n");
284             scl_data = log(1.0 + (float)JMAXVAL)/(float)JMAXVAL;
285             for (i = 0; i < npixels; ++i)
286                 image_buffer[i] = (int)((log(abs(data[i]) + 1.0))/scl_data);
287             break;
289         default:                                   /* Linear scale (min-max) */
290             for (i = 0; i < npixels; ++i)
291                 image_buffer[i] = (int)(data[i]);
292             break;
293         }
295         /* initialize image histogram. ensure all are zeroes in hist[]       */
296         for (i = 0; i <= JMAXVAL; ++i) hist[i] = 0;
298         /* construct the image histogram */
299         tmp = 1.0/(float)npixels;
300         for (i = 0; i <= npixels; ++i)
301             hist[(int)floor(data[i])] += tmp;
303         /* And the cumulative histogram */
304         cumhist[0] = hist[0];
305         for (i = 1; i <= JMAXVAL; ++i)
306             cumhist[i] += cumhist[i - 1] + hist[i];
308         switch (process)
309         {
310         case 1 :                                         /* contrast stretch */
311             printf("INFO   : Performing contrast stretch (normalization) \n");
313             datamax = (float)JMAXVAL;
314             datamin = 0.0;
316             /* We need to go through the cumulative histogram to pick the
317                appropriate values for datamin and datamax                    */
318             i = 0;
319             while (i < JMAXVAL)
320             {
321                 if (cumhist[i] >= 0.01)
322                 {
323                     datamin = (float) i;
324                     break;
325                 }
326                 i++;
327             }
328             i = JMAXVAL;
329             while (i > 0)
330             {
331                 if (cumhist[i] <= 0.99)
332                 {
333                     datamax = (float) i;
334                     break;
335                 }
336                 i--;
337             }
338             scl_data = (datamax - datamin)/(float)JMAXVAL;
339             for (i = 0; i < npixels; ++i)
340             {
341                 if (image_buffer[i] >= datamax)
342                     image_buffer[i] = JMAXVAL;
343                 else if (image_buffer[i] <= datamin)
344                     image_buffer[i] = 0;
345                 else
346                     image_buffer[i]=(int)abs((image_buffer[i]-datamin)/scl_data);
347             }
348             break;
351         case 2 :                                   /* histogram equalization */
352             printf("INFO   : Performing Histogram Equalization\n");
353             for (i = 0; i <  npixels; ++i)
354                 image_buffer[i] = cumhist[image_buffer[i]]*255;
355             break;
356         }
358         /*Write out data into JPEG file*/
359         cinfo.err = jpeg_std_error(&jerr);             /* JPEG error handler */
360         jpeg_create_compress(&cinfo);                 /* JPEG initialization */
361         jpeg_file = fopen(jpeg_file_name, "wb");/* Open JPEG file for writing*/
362         jpeg_stdio_dest(&cinfo, jpeg_file); /* Send compressed data to stdio */
363         cinfo.image_width = naxes[0];                         /* Image width */
364         cinfo.image_height = naxes[1];                       /* Image height */
365         cinfo.input_components = 1;            /* Number of colors per pixel */
366         cinfo.in_color_space = JCS_GRAYSCALE;   /* colorspace of input image */
367         jpeg_set_defaults(&cinfo);                      /* Set JPEG defaults */
368         jpeg_start_compress(&cinfo, TRUE);       /* default data compression */
369         row_stride = naxes[0];            /* JSAMPLEs per row inimage buffer */
371         /* Now we have to turn the data upside down                          */
372         while (cinfo.next_scanline < cinfo.image_height)
373         {
374             row_pointer[0] = &image_buffer[(cinfo.image_height -
375                                             cinfo.next_scanline) *
376                                             row_stride];
377             (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
378         }/*Loop through file writing one line at a time*/
380         jpeg_finish_compress(&cinfo);                  /* Finish compression */
381         fclose(jpeg_file);                                     /* Close file */
382         jpeg_destroy_compress(&cinfo);                     /* Release memory */
384         free(data);
385         free(image_buffer);
386         printf("INFO   : wrote output to %s\n", jpeg_file_name);
387     }
388     exit(EXIT_SUCCESS);
389 } /*End of program*/