ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/JSOC/proj/util/apps/render_image.c
Revision: 1.25
Committed: Wed Mar 14 01:06:25 2018 UTC (5 years, 6 months ago) by phil
Content type: text/plain
Branch: MAIN
CVS Tags: Ver_LATEST, Ver_9-3, Ver_9-41, Ver_9-2, Ver_9-5, Ver_9-4
Changes since 1.24: +89 -31 lines
Log Message:
Add call to image_cutout, the new version of image_magrotate, to center and roll the
image using interpolation.  This is not the default and will produce images with
solar north up unless the -u flag is set.  The old upNcenter is called if r=0.

File Contents

# Content
1 /*
2 * render_image - make .png or .ppm image from a segment image file in a series.
3 */
4
5 /**
6 @defgroup render_image render_image Make .png or .ppm image from a segment image file in a series.
7 @ingroup su_util
8
9 @brief Make .png or .ppm image from a segment image file in a series.
10
11 @par Synopsis:
12 @code
13 render_image --help
14 render_iamge in=<RecordSet> out=<target> | {outname=<ident_for_filename>} {scaling=<scaletype>}
15 {palette=<color_table>} {type=<image_protocol>} {outid=<filename_code>}
16 {tkey=<time_keyword>} {min=<scale_min>} {max=<scale_max>} {prescale=<multby>}
17 {scale=<image_size_ratio>} {-c} {-u} {-w} {-x}
18 @endcode
19
20 @code
21 in=Input data series.
22 n=limit limit number of records to n from beginning or -n from end of 'in'
23 out=Output data series or full path of directory or pipe via ppm program.
24 outname=Output quantity name for filenames.
25 scaling=Color table type, minmax, minmax99, minmaxgiven, histeq, ...,, default=MINMAX
26 palette=Color table, GREY or filename, default=GREY (bad spelling as pallette still allowed)
27 type=Image protocol, png, ppm, ..., default=png
28 tkey=keyword name for time, default T_REC
29 outid=output identifier, NONE=none, #=record counter, time=time as yyyymmdd_hhmmss, default=#
30 min=Min value for scaling, default nan
31 max=Max value for scaling, default nan
32 prescale= value to multiply by prior to min/max scaling, default 1.0.
33 scale"Reduction factor list, default 1.
34 -c Crop flag, causes crop to RSUN_OBS
35 -r Rotate and reposition to center and solar north
36 -u use unchanged for rotation and centering
37 -w use white for missing pixels, instead of black
38 -x High-quality flag, sets bytespercolor to 2 instead of 1,
39 @endcode
40
41 @par
42 Render_image can make one or many images per record in the input recordset. It will make one image for each value
43 of scale where the values are given as a comma delimited list of divisors for the dimensions of the input image array.
44 The image file will be written to a filename created from outid_outname_type where outid is either "NONE" or "time" or "#",
45 outname is any literal string, and type is "png" or "ppm" or if out specifies a pipe, then type may be any file type
46 that is supported as the output of a pipeline which accepts a PPM file.
47 @par
48 The file location is spcified with the out parameter. The target may be a seriesname in which case the image will
49 be written into a record directory of that series. If the target seriesname is the same as the series specified in "in", then
50 each input record will be cloned. The image will be written into a segment named "image".
51 @par
52 If the target location begins with a "/" or "./" then the $target will be used as the path for a directory into which
53 the image file will be written. If the target location begins with a "|" character, then the target string will be used
54 as a pipe into which a .ppm file will be written. The pipe string will have " > <filename>" appended to it.
55 In the case of a pipe, the string given in the 'out' parameter will be scanned
56 for replacement tokens delimited by '{...}'. Inside the '{}' pair the following
57 replacement instructions are available:
58 @code
59 The word 'ID' is replaced by the filename ID as built from outid.
60 The word '%' is replaced by that percent of the height of the image rounded
61 to the nearest pixel. If the floating point number after the '%' is itself
62 followed by a ':' and an integer, then that integer will be used as the
63 smallest number for the replacement token. So, for instance '{%1.2:5}'
64 with the image height of 256 pixels will be processed as: 1.2% of 256 is 3.072
65 which rounds to 3 which is smaller than 5 so the result will be 5.
66 @endcode
67 @par
68 The image filename is generated from the outid, outname, and type parameters. If outid is the string "time" then
69 the time of the image as specified by the tkey (defaults to T_REC) will be used with the "." and ":" and "_<zone>" componenets deleted. I.e.
70 the time will be in the form "yyyymmdd_hhmmss". The outid = time can have a suffix specifying the number of time characters to
71 include. the format of the suffix is ":nn" where nn is from 1 to 15. If outid is a literal "#" then the "%04d" layout will be used to generate
72 the image sequence number within the run of the program (i.e. the record number within the current recordset). If outid = NONE then the filename will be built from outname alone. The
73 type parameter must be "png" or "ppm" unless the out parameter specifies a ppm pipeline in which case type should
74 be the suffix for the file type generated by the pipe.
75 @par
76 The scaling of image values to color table indices is controlled by the scaling parameter. scaling must be one of
77 MINMAX, MAXMIN, MINMAXGIVEN, MINMAX99, MAXMIN99, HISTEQ, MAG at present. The default is MINMAX. If the scaling is
78 MINMAX and both min and max parameters are given, then the MINMAXGIVEN scaling will be used. MINMAX99 and
79 MAXMIN99 mean to eliminate the top and bottom 0.5% of the histogram of the values before computing a linear scaling.
80 MAXMIN and MAXMIN99 result in a reversed scaling with the max value mapped to the first color in the table and the
81 min value mapped to the max color in the table. String case is ignored for the scaling parameter.
82 If prescale is set then the data will be multiplied by prescale prior to the above actions, i.e. prior
83 to any minmax limits applied and prior to any log, sqrt, histeq, etc.
84 @par
85 The MAG scaling uses a scaling of 400*M/pow((abs(M)+bias),0.75) bias=30, from user provided min and max (defaulting to +-1500).
86 This scaling emphasizes low absolute field strength but still shows detail up to a max of 3000 gauss. The default
87 of 1500 clips umbral fields with the tradeoff of more contrast for plage and network fields.
88 @par
89 The color table is specified by the palette parameter. The table may be a built in table, at persent the only
90 build-in is "grey". ("grey", "gray", "GREY", "GRAY" are all allowed). If not a build-in table name the palette
91 parameter is expected to be a file path to a table of .sao or .lut types. These table formats are described in the
92 ds9(1) documentation.
93 @par
94 @par Flags:
95 @c -u: Leave orientation unchanged. Else flip-flip if CROTA2 is near 180.
96
97 @c -r: rotate and reposition image to frame center with solar north at the top.
98 If the -u flag is not set the image will be centered and rotated to make solar north at the top. If
99 CROTA2 is near 180 degrees (within 0.1 degrees) the roll will be done as a flip about X and a flip about Y
100 and the centering will be to the nearest pixel. If -u is not set OR if -r is set the image will be remapped
101 with full centering and rotation. -u set takes precendence over -r.
102 if both u=0 and r=0 then the image will be flip-flipped if CROTA2 is within 1 degree of 180. The default for
103 'r' is set, so if you want to get the normal HMI image with solar north up without interpolation or sub-pixel
104 centering, call without -u but with r=0 (to override default r=1).
105
106 @c -c: Crop image. This flag will casue the image to be cropped at pixel radius RSUN_OBS/CDELT1 about CRPIX1,CRPIX2 center.
107
108 @c -w: White background, the -w flag will cause missing values to be white. Else black.
109
110 @c -x: the -x flag is used to indicate extra resolution in the color table, i.e. 16-bits per color.
111
112 @par Example to generate a directory full of simple grey scaled png images, perhaps for a movie
113 @code
114 render_image in='hmi.M_45s_nrt[2010.06.01_TAI/5d@30m]' out=./movie scaling=minmax99 outid=# outname=M
115 @endcode
116
117 @par Example to generate a set of 4096x4096 and 1024x1024, and 256x256 pixel images of magnetograms in the current directory
118 using a color table the adds dynamic range to small absolute values. The times will omit the seconds field in this example.
119 @code
120 render_image in='hmi.M_45s_nrt'$QRY \
121 outname=M \
122 palette=/home/phil/apps/mag.lut \
123 outid=time:13 \
124 -c \
125 min=-500 \
126 max=500 \
127 type=jpg \
128 -w \
129 scale=1,4,16 \
130 out='| ppmlabel -color black -size {%0.75:5} -x 15 -y {%98} -text "SDO/HMI Quick-Look Magnetogram: {ID}" | pnmtojpeg -quality=95'
131 @endcode
132
133 @bug
134 Using the MAG scaling with a list of scale sizes, any instance of scale=1 must appear last in the list. This is because the values of the
135 image data are replaced in the original array in the scale=1 case. This will affect any scaling type that modifies the values of the data.
136
137 @bug
138 Does not make 16-bit colors correctly, or I can not get ppm tools with correct flags...
139
140 */
141
142 #include "mypng.h"
143 #include "jsoc_main.h"
144
145 #define PNGDIE(msg) {fprintf(stderr,"%s\n",msg); \
146 if (png_ptr) png_destroy_write_struct(&png_ptr, &info_ptr); \
147 if (row_pointers) free(row_pointers); \
148 if (fp) fclose(fp); \
149 return(1);}
150
151 #define DIE(msg) {fflush(stdout);fprintf(stderr,"%s, status=%d\n",msg,status); return(status);}
152
153 #define MINMAX 1
154 #define MAXMIN 2
155 #define HISTEQ 3
156 #define MINMAX99 4
157 #define MAXMIN99 5
158 #define MINMAXGIVEN 6
159 #define MAG 7
160 #define MAGMINMAXGIVEN 8
161 #define LOG 9
162 #define SQRT 10
163
164 #define NEAR_VERTICAL (0.1) // threshold to use image_cutout instead of upNcenter
165
166 char *module_name = "render_image";
167
168 ModuleArgs_t module_args[] =
169 {
170 {ARG_STRING, "in", "NOT SPECIFIED", "Input data series."},
171 {ARG_INT, "n", "0", "Limit of number of records, 0 for no limin; >0 count from start; <0 count from end"},
172 {ARG_STRING, "out", "NOT SPECIFIED", "Output data series or full path of directory or pipe via ppm program."},
173 {ARG_STRING, "outname", "NOT SPECIFIED", "Output quantity name for filenames"},
174 {ARG_STRING, "scaling", "MINMAX", "Color table type, minmax, minmax99, minmaxgiven, histeq, log, sqrt, ..."},
175 {ARG_STRING, "pallette", "none", "Color table, GREY or filename"},
176 {ARG_STRING, "palette", "GREY", "Color table, GREY or filename"},
177 {ARG_STRING, "type", "png", "Image protocol, png, ppm, ..."},
178 {ARG_STRING, "outid", "#", "output identifier, #=record counter, time=time as yyyymmdd_hhmmss"},
179 {ARG_STRING, "tkey", "T_REC", "Time keyword name used if outid==time, defaults to T_REC"},
180 {ARG_FLOAT, "min", "DRMS_MISSING_FLOAT", "Min value for scaling"},
181 {ARG_FLOAT, "max", "DRMS_MISSING_FLOAT", "Max value for scaling"},
182 {ARG_FLOAT, "bias", "30.0", "Max value for mag scaling"},
183 {ARG_FLOAT, "prescale", "1.0", "Multiplication scaling prior to color scaling"},
184 {ARG_INTS, "scale", "1", "Reduction factors"},
185 {ARG_FLAG, "c", "0", "Crop flag, causes crop to RSUN_OBS"},
186 {ARG_FLAG, "r", "1", "Rotate to solar north up and reposition to image center"},
187 {ARG_FLAG, "u", "0", "use unchanged, is-is for rotation and centering"},
188 {ARG_FLAG, "w", "0", "use white for missing pixels, instead of black"},
189 {ARG_FLAG, "x", "0", "High-quality flag, sets bytespercolor to 2 instead of 1"},
190 {ARG_END}
191 };
192
193 #define Deg2Rad (M_PI/180.0)
194 #define Rad2arcsec (3600.0/Deg2Rad)
195 #define arcsec2Rad (Deg2Rad/3600.0)
196 #define Rad2Deg (180.0/M_PI)
197
198 struct ObsInfo_struct
199 {
200 // from observation info
201 TIME t_obs;
202 double rsun_obs, obs_vr, obs_vw, obs_vn;
203 double crpix1, crpix2, cdelt1, cdelt2, crota2;
204 double crval1, crval2;
205 double cosa, sina;
206 double obs_b0;
207 // observed point
208 int i,j;
209 // parameters for observed point
210 double x,y,r;
211 double rho;
212 double lon;
213 double lat;
214 double sinlat, coslat;
215 double sig;
216 double mu;
217 double chi;
218 double obs_v;
219 };
220
221 typedef struct ObsInfo_struct ObsInfo_t;
222
223 void rebinArraySF(DRMS_Array_t *out, DRMS_Array_t *in);
224
225 int add_png(char *imgPath, DRMS_Array_t *imgArray, int scaletype, int usewhite, char *palette, int colors,
226 int bytespercolor, double *minp, double *maxp, double prescale);
227
228 int add_ppm(char *imgPath, DRMS_Array_t *imgArray, int scaletype, int usewhite, char *palette, int colors,
229 int bytespercolor, double *minp, double *maxp, double prescale);
230
231 int make_png(char *imgPath, unsigned char *data, int height, int width, char *palette, int bytepercolor, int colors);
232
233 char *set_scaling(DRMS_Array_t *in, double *minp, double *maxp, int *nmissp, char *palette, int colors,
234 int bytepercolor, int scaling, int usewhite, double prescale);
235
236 int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc);
237 int reposition(DRMS_Array_t *arr, ObsInfo_t *ObsLoc);
238 int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc);
239
240 ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus);
241
242 int image_cutout(void *inarr, int nx, int ny, int in_type, float crota2, float mag, float dx, float dy,
243 void **outarr, int wide, int high, float xc, float yc, int regridtype, int do_stretch);
244
245 #define PNG 1
246 #define PPM 2
247 #define PPMPIPE 3
248
249 int DoIt(void)
250 {
251 int status = DRMS_SUCCESS;
252 DRMS_RecordSet_t *inRS, *outRS = NULL;
253 DRMS_Record_t *inRec, *outRec;
254 int irec, nrecs;
255 int fileonly=0;
256 int imgtype;
257 int quality;
258
259 char *inQuery = (char *)params_get_str(&cmdparams, "in");
260 char *outQuery = (char *)params_get_str(&cmdparams, "out");
261 char *outName = (char *)params_get_str(&cmdparams, "outname");
262 char *outid = (char *)params_get_str(&cmdparams, "outid");
263 char *tkey = (char *)params_get_str(&cmdparams, "tkey");
264 char *palette = (char *)params_get_str(&cmdparams, "palette");
265 if (strcmp((char *)params_get_str(&cmdparams, "pallette"), "none"))
266 palette = (char *)params_get_str(&cmdparams, "pallette");
267 int limit = params_get_int(&cmdparams, "n");
268 int bytespercolor = (params_isflagset(&cmdparams, "x") ? 2 : 1);
269 int missingwhite = params_isflagset(&cmdparams, "w");
270 int crop = params_isflagset(&cmdparams, "c");
271 int rotate = params_isflagset(&cmdparams, "r");
272 int asis = params_isflagset(&cmdparams, "u");
273 char *type = (char *)params_get_str(&cmdparams, "type");
274 char *scaling = (char *)params_get_str(&cmdparams, "scaling");
275 double called_min = params_get_double(&cmdparams, "min");
276 double called_max = params_get_double(&cmdparams, "max");
277 double prescale = params_get_double(&cmdparams, "prescale");
278 double min, max;
279 int *scales = NULL;
280 int iscale, nscales;
281 int colors;
282 int scaletype;
283 int srcNx = 0, srcNy = 0;
284 char *inSegName = NULL, *segnamep;
285
286 if (strcasecmp(palette, "grey") == 0 || strcasecmp(palette, "gray") == 0)
287 {
288 palette = "GREY";
289 colors = 1;
290 }
291 else
292 colors = 3;
293
294 if (strcasecmp(type, "png")==0) imgtype = PNG;
295 else if (strcasecmp(type, "ppm")==0) imgtype = PPM;
296 else imgtype = PPMPIPE;
297
298 nscales = cmdparams_get_intarr(&cmdparams, "scale", &scales, &status);
299 if (status)
300 DIE("scale parameter not found");
301
302 // for (iscale=0; iscale<nscales; iscale++)
303 // fprintf(stderr,"Scale #%d=%d ", iscale, scales[iscale]);
304 // fprintf(stderr," using min=%f, max=%f\n", min, max);
305
306 if (strcasecmp(scaling, "minmax") == 0) scaletype = MINMAX;
307 else if (strcasecmp(scaling, "maxmin") == 0) scaletype = MAXMIN;
308 else if (strcasecmp(scaling, "minmax") == 0) scaletype = MINMAX99;
309 else if (strcasecmp(scaling, "maxmin99") == 0) scaletype = MAXMIN99;
310 else if (strcasecmp(scaling, "histeq") == 0) scaletype = HISTEQ;
311 else if (strcasecmp(scaling, "minmaxgiven") == 0) scaletype = MINMAXGIVEN;
312 else if (strncasecmp(scaling, "mag", 3) == 0) scaletype = MAG;
313 else if (strncasecmp(scaling, "log", 3) == 0) scaletype = LOG;
314 else if (strncasecmp(scaling, "sqrt", 4) == 0) scaletype = SQRT;
315
316 else scaletype = 0;
317 fprintf(stdout, " using scaling %d\n", scaletype);
318 if (isnan(called_min) || isnan(called_max))
319 {
320 if (scaletype == MINMAXGIVEN)
321 DIE("min and max must be specified for type MINMAXGIVEN\n");
322 }
323 else
324 {
325 if (scaletype == MINMAX)
326 scaletype = MINMAXGIVEN;
327 else if (scaletype == MAG)
328 scaletype = MAGMINMAXGIVEN;
329 }
330
331 if (limit == 0)
332 inRS = drms_open_records(drms_env, inQuery, &status);
333 else
334 inRS = drms_open_nrecords(drms_env, inQuery, limit, &status);
335 if (status || inRS->n == 0)
336 DIE("No input data found");
337 drms_stage_records(inRS, 1, 1);
338 nrecs = inRS->n;
339 fprintf(stderr,"nrecs=%d\n",nrecs);
340 segnamep = index(inQuery, '{');
341 if (segnamep) // found a segment list in the input query, use that segment.
342 {
343 segnamep++;
344 inSegName = strdup(strtok(segnamep, ",}"));
345 }
346 else // use segment number 0
347 {
348 DRMS_Segment_t *seg0 = drms_segment_lookupindex(inRS->records[0], 0, 0);
349
350 if (seg0)
351 {
352 inSegName = strdup(seg0->info->name);
353 }
354 else
355 {
356 DIE("Series has no segments.");
357 }
358 }
359
360 if (!outQuery[0])
361 DIE("No valid output place specified");
362 if (outQuery[0] == '/' || (outQuery[0] == '.' && outQuery[1] && outQuery[1] == '/') || outQuery[0] == '|')
363 {
364 // Output to a directory
365 fileonly = 1;
366 }
367 else
368 {
369 // output to a record segment
370 fileonly = 0;
371 if (strcmp(inRS->records[0]->seriesinfo->seriesname, outQuery) == 0)
372 outRS = drms_clone_records(inRS, DRMS_PERMANENT, DRMS_SHARE_SEGMENTS, &status);
373 else
374 outRS = drms_create_records(drms_env, nrecs, outQuery, DRMS_PERMANENT, &status);
375 if (status)
376 DIE("Output recordset not created");
377 }
378
379 for (irec=0; irec<nrecs; irec++)
380 {
381 int status;
382 DRMS_Array_t *srcArray;
383 DRMS_Segment_t *srcSeg;
384 ObsInfo_t *ObsLoc;
385
386 char imageID[100];
387 inRec = inRS->records[irec];
388 quality = drms_getkey_int(inRec, "QUALITY", &status);
389 if (!status && quality < 0)
390 {
391 fprintf(stderr,"Quality bad for rec %d\n", irec);
392 continue;
393 }
394 else
395 {
396 if (inSegName)
397 {
398 srcSeg = drms_segment_lookup(inRec, inSegName);
399 if (srcSeg)
400 {
401 srcArray = drms_segment_read(srcSeg, DRMS_TYPE_FLOAT, &status);
402 }
403 else
404 {
405 DIE("Failure looking up segment by name.")
406 }
407 }
408 else
409 {
410 DIE("Could not obtain segment name.");
411 }
412
413 if (!srcArray || status)
414 {
415 fprintf(stderr," No data file found rec %d, status=%d\n", irec,status);
416 if (srcArray)
417 {
418 drms_free_array(srcArray);
419 srcArray = NULL;
420 }
421 continue;
422 }
423 ObsLoc = GetObsInfo(srcSeg, NULL, &status);
424 if (ObsLoc)
425 {
426 if (!asis)
427 {
428 if (rotate)
429 reposition(srcArray, ObsLoc);
430 else
431 upNcenter(srcArray, ObsLoc);
432 }
433 if (crop)
434 crop_image(srcArray, ObsLoc);
435 }
436 srcNx = srcArray->axis[0];
437 srcNy = srcArray->axis[1];
438 }
439 if (strcmp(outid,"#") == 0)
440 sprintf(imageID, "%04d", irec);
441 else if (strncasecmp(outid,"none",4) == 0)
442 imageID[0] = '\0';
443 else if (strncasecmp(outid,"time",4) == 0)
444 {
445 int timestatus;
446 char timebuf[100];
447 static int t[] = {0,1,2,3,5,6,8,9,10,11,12,14,15,17,18};
448 int i;
449 int maxtplace = 15;
450 char *tplace = index(outid,':');
451 TIME now = drms_getkey_time(inRec, tkey, &timestatus);
452 if (timestatus)
453 {
454 fprintf(stderr,"Time key given in 'tkey' param, %s, is not found in the input series.\n",tkey);
455 return(timestatus);
456 }
457 sprint_time(timebuf, now, "TAI", 0);
458 if (tplace)
459 sscanf(tplace+1,"%d",&maxtplace);
460 for (i=0; i<maxtplace; i++)
461 imageID[i] = timebuf[t[i]];
462 imageID[i] = '\0';
463 }
464 else
465 {
466 fprintf(stderr, "Invalid outid param: |%s|\n", outid);
467 return(1);
468 }
469 for (iscale=0; iscale<nscales; iscale++)
470 {
471 int dimxy;
472 char dimxytxt[50];
473 DRMS_Array_t *imageArray = NULL;
474 char imgPath[DRMS_MAXPATHLEN];
475 int scale = scales[iscale];
476 int imageDims[2];
477 int tmparray = 0;
478 char imageFileName[DRMS_MAXSEGFILENAME];
479 min = called_min;
480 max = called_max;
481 imageDims[0] = srcNx/scale;
482 imageDims[1] = srcNy/scale;
483 dimxy = imageDims[0];
484 sprintf(dimxytxt,"%d",dimxy);
485 // fprintf(stderr,"scale=%d, imageNy=%d, imageNx=%d\n", scale, imageDims[1], imageDims[0]);
486
487 if (scale != 1)
488 {
489 imageArray = drms_array_create(DRMS_TYPE_FLOAT, 2, imageDims, NULL, &status);
490 rebinArraySF(imageArray, srcArray);
491 tmparray = 1;
492 }
493 else
494 {
495 imageArray = srcArray;
496 tmparray = 0;
497 }
498
499 if (imageID[0] == '\0')
500 sprintf(imageFileName, "%s", outName);
501 else
502 sprintf(imageFileName, "%s%s%s_%s.%s", imageID, (strcmp(outid,"#")==0 ? "." : "_"), outName,
503 (dimxy == 4096 ? "4k" :
504 (dimxy == 2048 ? "2k" :
505 (dimxy == 1024 ? "1k" :
506 (dimxy == 512 ? "512" :
507 (dimxy == 256 ? "256" :
508 (dimxy == 128 ? "128" : dimxytxt)))))), type);
509
510 if (fileonly)
511 {
512 if (outQuery[0] == '|')
513 { // process outQuery for '{...}' replacement tokens
514 char buf[4096];
515 char *pbuf = buf, *c = outQuery;
516 while (*c && (pbuf-buf) < sizeof(buf))
517 {
518 if (*c == '{')
519 {
520 c++;
521 if (*c == '%')
522 {
523 float x;
524 int ix, minx;
525 c++;
526 x = strtof(c, &c);
527 ix = round(0.01*x*imageDims[1]);
528 if (*c == ':')
529 {
530 c++;
531 minx = (int)strtol(c, &c, 10);
532 }
533 else minx = 0;
534 pbuf += sprintf(pbuf,"%d",(ix < minx ? minx : ix));
535 }
536 else if (strncmp(c, "ID", 2) == 0)
537 {
538 pbuf += sprintf(pbuf, "%s", imageID);
539 c += 2;
540 }
541 if (*c++ != '}')
542 DIE("Unrecognized token in pipe string");
543 }
544 else
545 *pbuf++ = *c++;
546 }
547 *pbuf = '\0';
548 sprintf(imgPath, "%s > %s", buf, imageFileName);
549 }
550 else
551 sprintf(imgPath, "%s/%s", outQuery, imageFileName);
552 }
553 else
554 {
555 DRMS_Segment_t *imageSeg;
556 char recdir[DRMS_MAXPATHLEN];
557 outRec = outRS->records[irec];
558 drms_copykeys(outRec, inRec, 0, kDRMS_KeyClass_Explicit);
559 drms_record_directory(outRec, recdir, 0);
560 imageSeg = drms_segment_lookup(outRec, "image");
561 strcpy(imageSeg->filename, imageFileName);
562 drms_keyword_setdate(outRec);
563 sprintf(imgPath, "%s/%s", recdir, imageFileName);
564 }
565
566 if (imgtype == PNG)
567 add_png(imgPath, imageArray, scaletype, missingwhite, palette, colors, bytespercolor, &min, &max, prescale);
568 else if (imgtype == PPM || imgtype == PPMPIPE)
569 add_ppm(imgPath, imageArray, scaletype, missingwhite, palette, colors, bytespercolor, &min, &max, prescale);
570
571 if (tmparray)
572 drms_free_array(imageArray);
573 }
574 drms_free_array(srcArray);
575 }
576 fprintf(stderr,"render_image done, irec=%d\n",irec);
577 drms_close_records(inRS, DRMS_FREE_RECORD);
578 fprintf(stderr,"render_image close in records done\n");
579 if (!fileonly && outRS)
580 drms_close_records(outRS, DRMS_INSERT_RECORD);
581 fprintf(stderr,"leaving module\n");
582 return (DRMS_SUCCESS);
583 } // end of DoIt
584
585 /* ----------------------------------------------------------------------
586 functions
587 ------------------------------------------------------------
588 */
589
590 int add_ppm(char *imgPath, DRMS_Array_t *imageArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp, double prescale)
591 {
592 int i, Nx, Ny;
593 unsigned char *imgData = NULL;
594 int nmissing;
595 int maxpixval = (bytespercolor == 1 ? 255 : 65535);
596 FILE *out;
597 char *ppmcode;
598
599 imgData = (unsigned char*)set_scaling(imageArray, minp, maxp, &nmissing, palette, colors, bytespercolor, scaletype, usewhite, prescale);
600 if (!imgData)
601 {
602 fprintf(stderr,"scaling failed\n");
603 return(1);
604 }
605
606 ppmcode = (colors == 1 ? "P5" : "P6");
607
608 Nx = imageArray->axis[0];
609 Ny = imageArray->axis[1];
610
611 if (imgPath[0] == '|')
612 out = popen(imgPath+1, "w");
613 else
614 out = fopen(imgPath, "w");
615
616 fprintf(out, "%s\n%d %d %d\n", ppmcode, Nx, Ny, maxpixval);
617 for (i=Ny-1; i >=0; i--)
618 fwrite(imgData + i*Nx*bytespercolor*colors, bytespercolor, colors*Nx, out);
619 if (imgPath[0] == '|')
620 pclose(out);
621 else
622 fclose(out);
623
624 free(imgData);
625 return(0);
626 }
627
628 int add_png(char *imgPath, DRMS_Array_t *imageArray, int scaletype, int usewhite, char *palette, int colors, int bytespercolor, double *minp, double *maxp, double prescale)
629 {
630 int srcNx, srcNy;
631 int status=0;
632 unsigned char *imgData = NULL;
633 int nmissing;
634
635 imgData = (unsigned char*)set_scaling(imageArray, minp, maxp, &nmissing, palette, colors, bytespercolor, scaletype, usewhite, prescale);
636
637 if (!imgData)
638 {
639 fprintf(stderr,"scaling failed, status=%d\n",status);
640 return(status);
641 }
642
643 // should add text info to .png file with min, max, nmissing, etc.
644
645 if (make_png(imgPath, imgData, imageArray->axis[0], imageArray->axis[1], palette, bytespercolor, colors) != 0)
646 {
647 fprintf(stderr,"png failed, status=%d\n",status);
648 free(imgData);
649 return(status);
650 }
651 free(imgData);
652 return(0);
653 }
654 // ----------------------------------------------------------------------
655
656 void rebinArraySF(DRMS_Array_t *out, DRMS_Array_t *in)
657 {
658 float *inData = in->data;
659 float *outData = out->data;
660 float *inp, *outp;
661 int inNx=in->axis[0], inNy=in->axis[1];
662 int outNx=out->axis[0], outNy=out->axis[1];
663 int inI, inJ, outI, outJ, i;
664 int binsize = inNx/outNx;
665 double inRow[inNx], outRow[outNx];
666 int inRowN[inNx], outRowN[outNx];
667
668 for (outJ=0; outJ < outNy; outJ++)
669 {
670 int inRow0 = outJ * binsize;
671 for (outI=0; outI<outNx; outI++)
672 {
673 outRowN[outI] = 0;
674 outRow[outI] = 0.0;
675 }
676 for (inJ = inRow0; inJ < inRow0+binsize; inJ++)
677 {
678 inp = inData + inJ*inNx;
679 for (outI=0; outI<outNx; outI++)
680 for (i=0; i<binsize; i++, inp++)
681 if (*inp != DRMS_MISSING_FLOAT)
682 {
683 outRow[outI] += *inp;
684 outRowN[outI]++;
685 }
686 }
687 for (outI=0; outI < outNx; outI++)
688 outData[outJ*outNx + outI] = (outRowN[outI] ?
689 (outRow[outI]/outRowN[outI]) : DRMS_MISSING_FLOAT);
690 } // end outJ
691 }
692
693 // ---------------------------------------------------------------------
694
695 unsigned short *init_color_table(char * palette, int colors, int bytepercolor)
696 {
697 int c, i;
698 int tablesize = (bytepercolor == 1 ? 256 : 65536);
699 int maxcolor = tablesize - 2; // i.e. for 8-bit, max=254, leave top for missing
700 int lastcolor = tablesize - 1;
701 int mincolor = 1;
702 unsigned short *table = (unsigned short *)malloc(colors*tablesize*sizeof(unsigned short));
703
704 if (!table) return NULL;
705 if (strcmp(palette,"GREY") == 0)
706 for (i=0; i<tablesize; i++)
707 for (c = 0; c<colors; c++)
708 table[i*colors + c] = i;
709
710 else if (colors == 3 && access(palette, R_OK) == 0)
711 {
712 FILE *ct = fopen(palette,"r");
713 void proc_color(FILE *ct, unsigned short *t, int max);
714 char buf[256];
715 char *dot = rindex(palette, '.');
716 if (dot && strcmp(dot, ".lut")==0)
717 {
718 float fr, fg, fb;
719 table[0] = table[1] = table[2] = 0;
720 table[colors*lastcolor] = table[colors*lastcolor+1] = table[colors*lastcolor+2] = lastcolor;
721 for (i=1; i<lastcolor; i++)
722 {
723 fscanf(ct, "%f%f%f", &fr, &fg, &fb);
724 c = 0;
725 table[i*colors + c++] = maxcolor*fr;
726 table[i*colors + c++] = maxcolor*fg;
727 table[i*colors + c++] = maxcolor*fb;
728 }
729 }
730 else
731 {
732 // assume .sao type color table
733 unsigned short r[tablesize], g[tablesize], b[tablesize];
734 for (i=0; i<=tablesize; i++)
735 r[i] = g[i] = b[i] = 0;
736 while (fgets(buf,256,ct))
737 {
738 if (strncmp(buf,"RED:",4)==0)
739 proc_color(ct,r,maxcolor);
740 if (strncmp(buf,"GREEN:",6)==0)
741 proc_color(ct,g,maxcolor);
742 if (strncmp(buf,"BLUE:",5)==0)
743 proc_color(ct,b,maxcolor);
744 }
745 table[0] = table[1] = table[2] = 0;
746 table[colors*lastcolor] = table[colors*lastcolor+1] = table[colors*lastcolor+2] = lastcolor;
747 for (i=1; i<lastcolor; i++)
748 {
749 c = 0;
750 table[i*colors + c++] = r[i];
751 table[i*colors + c++] = g[i];
752 table[i*colors + c++] = b[i];
753 }
754 }
755 fclose(ct);
756 }
757 return(table);
758 }
759
760 void proc_color(FILE *ct, unsigned short *t, int max)
761 {
762 float cut, val;
763 int pos = -1;
764 double prev = 0.0;
765 int prevloc = 0;
766 double slope;
767 pos = -1;
768 prev = 0.0;
769 prevloc = 0;
770 while (fscanf(ct,"(%f,%f)", &cut, &val) == 2)
771 {
772 int loc = max*cut + 0.5;
773 val *= max;
774 // fprintf(stderr," got loc=%d, cut=%f %f\n",loc,cut,val);
775 if (pos < 0)
776 {
777 pos = 0;
778 prev = val;
779 prevloc = 0;
780 }
781 if (loc == prevloc)
782 slope = 0.0;
783 else
784 slope = (val - prev)/(loc - prevloc);
785 while (pos <= loc)
786 {
787 t[pos] = slope * (pos-prevloc) + prev;
788 pos++;
789 }
790 prev = t[loc];
791 prevloc = loc;
792 }
793 while (pos <= max)
794 t[pos++] = prev;
795 }
796
797
798 char *set_scaling(DRMS_Array_t *in, double *minp, double *maxp,
799 int *nmissp, char *palette, int colors, int bytepercolor, int scaling, int usewhite, double prescale)
800 {
801 int idata, ndata, nmiss = 0;
802 float *inData = in->data;
803 ndata = in->axis[0] * in->axis[1];
804 int tablesize = (bytepercolor == 1 ? 256 : 65536);
805 int color;
806 int grey = strcmp(palette, "GREY") == 0;
807 static unsigned short *table = NULL;
808 static unsigned short *greytable = NULL;
809 static unsigned short *colortable = NULL;
810 char *out;
811 out = (char *)malloc(ndata * colors * bytepercolor * sizeof(char));
812 int maxcolor = (bytepercolor == 1 ? 254 : 65534);
813 int missingcolor = (usewhite ? maxcolor + 1 : 0);
814
815 fprintf(stderr,"set_scaling called, scaling=%d, palette=%s, min=%f, max=%f, prescale=%f",scaling,palette,*minp,*maxp,prescale);
816 fprintf(stderr,"\n missingcolor %d maxcolor %d \n", missingcolor, maxcolor);
817 if (!out)
818 return(NULL);
819
820 // get color table unless palette is GREY and scaling is MINMAX, do grey inline for that case only
821 // since that combination is used to call set_scaling recursively for histogram generation
822 if (grey && scaling == MINMAX && colors == 1)
823 {
824 if (!greytable)
825 {
826 int i;
827 greytable = (unsigned short *)malloc(65536 * sizeof(unsigned short));
828 for (i=0; i<65536; i++)
829 greytable[i] = i;
830 colortable = greytable;
831 }
832 }
833 else
834 {
835 if (!table)
836 table = init_color_table(palette, colors, bytepercolor);
837 colortable = table;
838 }
839
840 switch(scaling)
841 {
842 case MAG:
843 case MAGMINMAXGIVEN:
844 {
845 float bias = params_get_double(&cmdparams, "bias");
846 for (idata=0; idata<ndata; idata++)
847 {
848 float val = inData[idata];
849 if (!isnan(val))
850 val *= prescale;
851 // inData[idata] = val/sqrt(fabs(val)+bias);
852 inData[idata] = 400.0*val/pow((fabs(val)+bias), 0.75);
853 }
854 if (isnan(*minp)) *minp = -1500.0;
855 if (isnan(*maxp)) *maxp = -*minp;
856 // no break;
857 }
858 case MINMAX:
859 case MAXMIN:
860 case MINMAXGIVEN:
861 case LOG:
862 case SQRT:
863 {
864 double min;
865 double max;
866 if (bytepercolor != 1 && bytepercolor != 2)
867 {fprintf(stderr,"wrong bytes per color\n"); return(NULL);}
868 if (scaling == MINMAXGIVEN || scaling == MAG || scaling == MAGMINMAXGIVEN)
869 {
870 min = *minp;
871 max = *maxp;
872 /* fprintf(stderr,"debug pt 1: min=%f max=%f\n scaling=%d",min,max,scaling ); */
873 }
874 else
875 {
876 if (isnan(*minp) || isnan(*maxp))
877 {
878 int n=0;
879 min = 1.0e20;
880 max = -min;
881 /* fprintf(stderr,"debug pt 2: min=%f max=%f\n",min,max); */
882 for (idata=0; idata<ndata; idata++)
883 {
884 float val = inData[idata];
885 if (!isnan(val))
886 {
887 val *= prescale;
888 n++;
889 if (val > max) max = val;
890 if (val < min) min = val;
891 }
892 }
893 if (n==0)
894 {
895 fprintf(stderr,"set_scaling, min or max is missing and no data values found.\n");
896 return(NULL);
897 }
898 }
899 if (isnan(*minp)) *minp = min;
900 if (isnan(*maxp)) *maxp = max;
901 min = *minp;
902 max = *maxp;
903 }
904 double use_min, use_max;
905 if (scaling == LOG)
906 {
907 if (min <= 0)
908 min = 1.0e-10;
909 if (max <= 0)
910 max = 1.0e-10;
911 use_min = log10(min);
912 use_max = log10(max);
913 }
914 else if (scaling == SQRT)
915 {
916 if (min < 0)
917 min = 0;
918 if (max < 0)
919 max = 0;
920 use_min = sqrt(min);
921 use_max = sqrt(max);
922 }
923 for (idata=0; idata<ndata; idata++)
924 {
925 /* fprintf(stderr,"debug pt 4: min=%f max=%f\n",min,max); */
926 float val = inData[idata];
927 int newval;
928 if (!isnan(val))
929 {
930 val *= prescale;
931 if (val <= min) val = min;
932 if (val >= max) val = max;
933 if (scaling == LOG)
934 newval = (maxcolor-1)*((log10(val)-use_min)/(use_max-use_min)) + 0.5;
935 else if (scaling == SQRT )
936 newval = (maxcolor-1)*((sqrt(val)-use_min)/(use_max-use_min)) + 0.5;
937 else
938 {
939 newval = (maxcolor-1)*(val-min)/(max-min) + 0.5;
940 if (scaling == MAXMIN)
941 newval = maxcolor - newval;
942 }
943 if (newval >= maxcolor) newval = maxcolor;
944 if (newval < 1) newval = 1;
945 }
946 else
947 {
948 newval = missingcolor;
949 nmiss += 1;
950 }
951 for (color=0; color<colors; color++)
952 {
953 if (bytepercolor == 1)
954 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
955 else
956 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
957 }
958 }
959 *maxp = max;
960 *minp = min;
961 *nmissp = nmiss;
962 fprintf(stderr," min %f max %f nmissp %d ndata %d\n", *minp, *maxp, nmiss, ndata);
963 break;
964 }
965
966 case HISTEQ:
967 {
968 int ihist, nhist=65536;
969 int *hist = (int *)malloc(nhist * sizeof(int));
970 int icolor, ncolor = (bytepercolor == 1 ? 255 : 65535);
971 int total, want;
972 unsigned int newval;
973 double valspercolor;
974 char *outa;
975 outa = set_scaling(in, minp, maxp, nmissp, "GREY", 1, 2, MINMAX, 0, prescale);
976 valspercolor = (ndata - *nmissp)/(double)ncolor;
977 for (ihist = 0; ihist < nhist; ihist++)
978 hist[ihist] = 0;
979 for (idata=0; idata<ndata; idata++)
980 hist[*((unsigned short *)outa+idata)] += 1;
981 for (icolor=0, total=0, ihist=0; icolor<=ncolor; icolor++)
982 {
983 want = icolor * valspercolor;
984 for ( ; ihist<nhist; ihist++)
985 {
986 if (total >= want)
987 break;
988 total += hist[ihist];
989 hist[ihist] = icolor;
990 }
991 }
992 for ( ; ihist<nhist; ihist++)
993 hist[ihist] = ncolor;
994 for (idata=0; idata<ndata; idata++)
995 {
996 if (!isnan(inData[idata]))
997 {
998 newval = hist[*((unsigned short *)outa+idata)];
999 if (newval > maxcolor) newval = maxcolor;
1000 if (newval < 1) newval = 1;
1001 }
1002 else
1003 newval = missingcolor;
1004 for (color=0; color<colors; color++)
1005 {
1006 if (bytepercolor == 1)
1007 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
1008 else
1009 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
1010 }
1011 }
1012 // fprintf(stderr," HISTEQ scaling %d \n", scaling);
1013 free(outa);
1014 free(hist);
1015 break;
1016 }
1017
1018 case MINMAX99:
1019 case MAXMIN99:
1020 {
1021 fprintf(stderr,"DOING set_scaling case MAXMIN99 \n");
1022 int ihist, nhist=65536;
1023 int *hist = (int *)malloc(nhist * sizeof(int));
1024 int icolor, ncolor = (bytepercolor == 1 ? 255 : 65535);
1025 int total, want;
1026 unsigned int newval;
1027 double newmin, newmax;
1028 char *outa;
1029 fprintf(stderr," CALLING set_scaling case MINMAX %d \n", MINMAX);
1030 *minp = 0;
1031 *maxp = 0;
1032 outa = set_scaling(in, minp, maxp, nmissp, "GREY", 1, 2, MINMAX, 0, prescale);
1033
1034 fprintf(stderr,"CONTINUING: set_scaling, case MAXMIN99 %f %f \n", *minp, *maxp);
1035
1036 for (ihist=0; ihist<nhist; ihist++)
1037 hist[ihist] = 0;
1038 for (idata=0; idata<ndata; idata++)
1039 {
1040 hist[*((unsigned short *)outa+idata)] += 1;
1041 }
1042
1043 want = ndata/600;
1044 fprintf(stderr,"ndata %d want %d nhist %d \n", ndata, want, nhist);
1045 total = 0;
1046 for (ihist=0; ihist<nhist; total += hist[ihist++])
1047 if (total >= want)
1048 break;
1049 newmin = *minp + ((double)ihist/(double)nhist) * (*maxp - *minp);
1050
1051 total = 0;
1052 for (ihist=nhist - 1; ihist>=0; total += hist[ihist--])
1053 if (total >= want)
1054 break;
1055 newmax = *minp + ((double)ihist/(double)nhist) * (*maxp - *minp);
1056 fprintf(stderr,"ihist %d newmax=%f newmin=%f \n", ihist, newmax, newmin);
1057 if (newmax <= newmin)
1058 {
1059 newmax = *maxp;
1060 newmin = *minp;
1061 }
1062
1063 fprintf(stderr,"newmax %f minp %f \n", newmax, *minp);
1064 for (idata=0; idata<ndata; idata++)
1065 {
1066 float val = inData[idata];
1067 unsigned short newval;
1068 if (!isnan(val)){
1069 if (val <= newmin) val=newmin;
1070 if (val >= newmax) val=newmax;
1071 newval = maxcolor*(val-newmin)/(newmax-newmin);
1072 if (scaling == MAXMIN99)
1073 newval = maxcolor - newval;
1074 if (newval > maxcolor) newval = maxcolor;
1075 if (newval < 1) newval = 1;
1076 }
1077
1078 else
1079 newval = missingcolor;
1080 for (color=0; color<colors; color++)
1081 {
1082 if (bytepercolor == 1)
1083 *((unsigned char *)out + colors*idata + color) = colortable[newval*colors + color];
1084 else
1085 *((unsigned short *)out + colors*idata + color) = colortable[newval*colors + color];
1086 }
1087 }
1088 free(outa);
1089 free(hist);
1090 break;
1091 }
1092 }
1093 return(out);
1094 }
1095
1096 // ----------------------------------------------------------------------
1097 int make_png(char *filename, unsigned char *data,
1098 int height, int width, char *palette, int bytepercolor, int colors)
1099 {
1100 int row;
1101 FILE *fp; // = NULL;
1102 png_byte **row_pointers = NULL;
1103 png_structp png_ptr = NULL;
1104 png_infop info_ptr = NULL;
1105
1106 fp = fopen(filename, "w");
1107 if (!fp)
1108 PNGDIE("cant open output file");
1109
1110 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
1111 NULL, NULL);
1112 if (!png_ptr)
1113 PNGDIE("cant create png_struct");
1114
1115 info_ptr = png_create_info_struct(png_ptr);
1116 if (!info_ptr)
1117 PNGDIE("cant create png_infop");
1118
1119 if (setjmp(png_jmpbuf(png_ptr)))
1120 PNGDIE("png fatal error");
1121
1122 png_init_io(png_ptr, fp);
1123 // fprintf(stderr, "ready png, bitdepth=8*bytepercolor=%d, colors=%d \n",8*bytepercolor,colors);
1124 png_set_IHDR(png_ptr, info_ptr, width, height, 8*bytepercolor,
1125 (colors == 1 ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_ADAM7,
1126 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
1127 png_write_info(png_ptr, info_ptr);
1128 if (bytepercolor > 1)
1129 png_set_swap(png_ptr);
1130 png_set_gAMA(png_ptr, info_ptr, 2.2);
1131 png_set_sRGB(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
1132 row_pointers = (png_byte **)malloc(height * sizeof(png_byte *));
1133
1134 for (row=0; row<height ; row++)
1135 {
1136 if (bytepercolor == 1)
1137 row_pointers[height - row - 1] = (png_byte *)((char *)data + colors*row*width);
1138 else
1139 row_pointers[height - row - 1] = (png_byte *)((short *)data + colors*row*width);
1140 }
1141 png_set_rows(png_ptr, info_ptr, row_pointers);
1142 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_SWAP_ENDIAN, NULL);
1143 fclose(fp);
1144
1145 png_destroy_write_struct(&png_ptr, &info_ptr);
1146 free(row_pointers);
1147 return(0);
1148 }
1149
1150 // ----------------------------------------------------------------------
1151
1152 /* reposition and rotate if necessary.
1153 * If the rotate flag is set or if CROTA2 more than 0.1 degree from 0 or 180
1154 * then use image_cutout to center and put solar north
1155 * at the top. Else use upNcenter to get older near-south and near-north
1156 * orientations set to north nearly up and centered to nearest pixel.
1157 */
1158 int reposition(DRMS_Array_t *arr, ObsInfo_t *ObsLoc)
1159 {
1160 int status;
1161 int regrid = 1; // use bicubic interpolation
1162 int stretch = 0; // leave off-image pixels missing
1163 int type_float = 3;
1164 int nx = arr->axis[0];
1165 int ny = arr->axis[1];
1166 float xc = 0.0; // shift to this X wrt Sun center for final image center
1167 float yc = 0.0; // shift to this y wrt Sun center for final image center
1168 float mag = 1.0;
1169 float dx = (nx + 1.0)/2.0 - ObsLoc->crpix1;
1170 float dy = (ny + 1.0)/2.0 - ObsLoc->crpix2;
1171 float *outarray;
1172 status = image_cutout(arr->data, nx, ny, type_float, ObsLoc->crota2,
1173 mag, dx, dy, &(void *)outarray, nx, ny, xc, yc, regrid, stretch);
1174 if (status) {fprintf(stderr,"image_cutout failed, staus=%d\n", status);}
1175 free(arr->data);
1176 arr->data = outarray;
1177 ObsLoc->crota2 = 0.0;
1178 ObsLoc->crpix1 += dx;
1179 ObsLoc->crpix2 += dy;
1180 return(status);
1181 }
1182
1183 /* center whith whole pixel shifts and rotate by 180 if needed */
1184 /* Only apply center if it will not result in an image crop. I.e. not ever
1185 for AIA, and not for HMI or MDI or other if a shift of more than 20 arcsec
1186 is implied */
1187 int upNcenter(DRMS_Array_t *arr, ObsInfo_t *ObsLoc)
1188 {
1189 int nx, ny, ix, iy, i, j, xoff, yoff, max_off;
1190 double rot, x0, y0, midx, midy;
1191 float *data;
1192 float *data2;
1193 if (!arr || !ObsLoc)
1194 return(1);
1195 data = arr->data;
1196 nx = arr->axis[0];
1197 ny = arr->axis[1];
1198 x0 = ObsLoc->crpix1 - 1;
1199 y0 = ObsLoc->crpix2 - 1;
1200 midx = (nx-1.0)/2.0;
1201 midy = (ny-1.0)/2.0;
1202 if ((rot = fabs(ObsLoc->crota2)) > 179 && rot < 181)
1203 {
1204 // rotate image by 180 degrees by a flip flip
1205 float val;
1206 int half = ny / 2;
1207 int odd = ny & 1;
1208 for (iy=0; iy<half; iy++)
1209 {
1210 for (ix=0; ix<nx; ix++)
1211 {
1212 i = iy*nx + ix;
1213 j = (ny - 1 - iy)*nx + (nx - 1 - ix);
1214 val = data[i];
1215 data[i] = data[j];
1216 data[j] = val;
1217 }
1218 }
1219 // reverse middle row if ny is odd
1220 if (odd) {
1221 for (ix=0; ix<nx/2; ++ix) {
1222 i = nx*half + ix;
1223 j = nx*half + nx - ix - 1;
1224 val = data[i];
1225 data[i] = data[j];
1226 data[j] = val;
1227 }
1228 }
1229 x0 = nx - 1 - x0;
1230 y0 = ny - 1 - y0;
1231 rot = ObsLoc->crota2 - 180.0;
1232 if (rot < -90.0) rot += 360.0;
1233 ObsLoc->crota2 = rot;
1234 }
1235 // Center to nearest pixel - if OK to do so
1236 xoff = round(x0 - midx);
1237 yoff = round(y0 - midy);
1238 max_off = 20.0 / ObsLoc->cdelt1;
1239 if (arr->parent_segment &&
1240 arr->parent_segment->record &&
1241 arr->parent_segment->record->seriesinfo &&
1242 arr->parent_segment->record->seriesinfo->seriesname &&
1243 strncasecmp(arr->parent_segment->record->seriesinfo->seriesname, "aia", 3) &&
1244 abs(xoff) < max_off && abs(yoff) < max_off)
1245 {
1246 if (abs(xoff) >= 1 || abs(yoff) >= 1)
1247 {
1248 data2 = malloc(4*nx*ny);
1249 for (iy=0; iy<ny; iy++)
1250 {
1251 int jy = iy + yoff;
1252 for (ix=0; ix<nx; ix++)
1253 {
1254 int jx = ix + xoff;
1255 int idx = jy*nx + jx;
1256 int idx2 = iy*nx + ix;
1257 if (jx<0 || jx>=nx || jy<0 || jy>=ny)
1258 data2[idx2] = DRMS_MISSING_FLOAT;
1259 else
1260 data2[idx2] = data[idx];
1261 }
1262 }
1263 x0 -= xoff;
1264 y0 -= yoff;
1265 free(data);
1266 arr->data = data2;
1267 }
1268 }
1269 // update center location
1270 ObsLoc->crpix1 = x0 + 1;
1271 ObsLoc->crpix2 = y0 + 1;
1272 return(0);
1273 }
1274
1275 // ----------------------------------------------------------------------
1276
1277 int crop_image(DRMS_Array_t *arr, ObsInfo_t *ObsLoc)
1278 {
1279 int nx, ny, ix, iy, i, j, xoff, yoff;
1280 double x0, y0;
1281 double rsun = ObsLoc->rsun_obs/ObsLoc->cdelt1;
1282 double scale, crop_limit;
1283 float *data;
1284 if (!arr || !ObsLoc)
1285 return(1);
1286 data = arr->data;
1287 nx = arr->axis[0];
1288 ny = arr->axis[1];
1289 x0 = ObsLoc->crpix1 - 1;
1290 y0 = ObsLoc->crpix2 - 1;
1291 scale = 1.0/rsun;
1292 crop_limit = 0.99975; // 1 - 1/4000, 1/2 HMI pixel.
1293 for (iy=0; iy<ny; iy++)
1294 for (ix=0; ix<nx; ix++)
1295 {
1296 double x, y, R2;
1297 float *Ip = data + iy*nx + ix;
1298 if (drms_ismissing_float(*Ip))
1299 continue;
1300 x = ((double)ix - x0) * scale; /* x,y in pixel coords */
1301 y = ((double)iy - y0) * scale;
1302 R2 = x*x + y*y;
1303 if (R2 > crop_limit)
1304 *Ip = DRMS_MISSING_FLOAT;
1305 }
1306 return(0);
1307 }
1308
1309 #define CHECK(keyname) {if (status) {fprintf(stderr,"Keyword failure to find: %s, status=%d\n",keyname,status); *rstatus=status; return(NULL);}}
1310
1311 ObsInfo_t *GetObsInfo(DRMS_Segment_t *seg, ObsInfo_t *pObsLoc, int *rstatus)
1312 {
1313 TIME t_prev;
1314 DRMS_Record_t *rec;
1315 TIME t_obs;
1316 double dv;
1317 ObsInfo_t *ObsLoc;
1318 int status;
1319 char t_key[100];
1320
1321 if (!seg || !(rec = seg->record))
1322 { *rstatus = 1; return(NULL); }
1323
1324 ObsLoc = (pObsLoc ? pObsLoc : (ObsInfo_t *)malloc(sizeof(ObsInfo_t)));
1325 if (!pObsLoc)
1326 memset(ObsLoc, 0, sizeof(ObsInfo_t));
1327
1328 t_prev = ObsLoc->t_obs;
1329 if (drms_keyword_lookup(rec, "T_OBS", 0))
1330 strcpy(t_key, "T_OBS");
1331 else
1332 strcpy(t_key, "T_REC");
1333 t_obs = drms_getkey_time(rec, t_key, &status); CHECK(t_key);
1334
1335 if (t_obs <= 0.0)
1336 { *rstatus = 2; return(NULL); }
1337
1338 if (t_obs != t_prev)
1339 {
1340 ObsLoc->crpix1 = drms_getkey_double(rec, "CRPIX1", &status); CHECK("CRPIX1");
1341 ObsLoc->crpix2 = drms_getkey_double(rec, "CRPIX2", &status); CHECK("CRPIX2");
1342 ObsLoc->crval1 = drms_getkey_double(rec, "CRVAL1", &status); CHECK("CRVAL1");
1343 ObsLoc->crval2 = drms_getkey_double(rec, "CRVAL2", &status); CHECK("CRVAL2");
1344 ObsLoc->cdelt1 = drms_getkey_double(rec, "CDELT1", &status); CHECK("CDELT1");
1345 ObsLoc->cdelt2 = drms_getkey_double(rec, "CDELT2", &status); CHECK("CDELT2");
1346 ObsLoc->crota2 = drms_getkey_double(rec, "CROTA2", &status); if (status) ObsLoc->crota2 = 0.0; // WCS default
1347 ObsLoc->sina = sin(ObsLoc->crota2*Deg2Rad);
1348 ObsLoc->cosa = sqrt (1.0 - ObsLoc->sina*ObsLoc->sina);
1349 ObsLoc->rsun_obs = drms_getkey_double(rec, "RSUN_OBS", &status);
1350 if (status)
1351 {
1352 double dsun_obs = drms_getkey_double(rec, "DSUN_OBS", &status); CHECK("DSUN_OBS");
1353 ObsLoc->rsun_obs = asin(696000000.0/dsun_obs)/arcsec2Rad;
1354 }
1355 ObsLoc->obs_vr = drms_getkey_double(rec, "OBS_VR", &status); CHECK("OBS_VR");
1356 ObsLoc->obs_vw = drms_getkey_double(rec, "OBS_VW", &status); CHECK("OBS_VW");
1357 ObsLoc->obs_vn = drms_getkey_double(rec, "OBS_VN", &status); CHECK("OBS_VN");
1358 ObsLoc->obs_b0 = drms_getkey_double(rec, "CRLT_OBS", &status); CHECK("CRLT_OBS");
1359 ObsLoc->t_obs = t_obs;
1360 }
1361 *rstatus = 0;
1362 return(ObsLoc);
1363 }
1364
1365
1366 // ----------------------------------------------------------------------