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, ×tatus); |
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 |
// ---------------------------------------------------------------------- |