pyimcom 1.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. pyimcom/__init__.py +1 -0
  2. pyimcom/_version.py +24 -0
  3. pyimcom/analysis.py +1480 -0
  4. pyimcom/coadd.py +2331 -0
  5. pyimcom/compress/__init__.py +0 -0
  6. pyimcom/compress/compressutils.py +506 -0
  7. pyimcom/compress/compressutils_wrapper.py +116 -0
  8. pyimcom/compress/i24.py +514 -0
  9. pyimcom/config.py +1245 -0
  10. pyimcom/diagnostics/__init__.py +0 -0
  11. pyimcom/diagnostics/context_figure.py +58 -0
  12. pyimcom/diagnostics/dynrange.py +274 -0
  13. pyimcom/diagnostics/layer_diagnostics.py +208 -0
  14. pyimcom/diagnostics/mosaicimage.py +80 -0
  15. pyimcom/diagnostics/noise/stability.py +126 -0
  16. pyimcom/diagnostics/noise_diagnostics.py +709 -0
  17. pyimcom/diagnostics/outimage_utils/__init__.py +0 -0
  18. pyimcom/diagnostics/outimage_utils/helper.py +82 -0
  19. pyimcom/diagnostics/report.py +366 -0
  20. pyimcom/diagnostics/run.py +64 -0
  21. pyimcom/diagnostics/starcube_nonoise.py +264 -0
  22. pyimcom/diagnostics/starcube_nonoise_coldescr.txt +24 -0
  23. pyimcom/diagnostics/stars.py +469 -0
  24. pyimcom/imdestripe.py +2454 -0
  25. pyimcom/lakernel.py +805 -0
  26. pyimcom/layer.py +1439 -0
  27. pyimcom/layer_wrapper.py +96 -0
  28. pyimcom/meta/__init__.py +0 -0
  29. pyimcom/meta/distortimage.py +748 -0
  30. pyimcom/meta/ginterp.py +340 -0
  31. pyimcom/pictures/__init__.py +0 -0
  32. pyimcom/pictures/genpic.py +229 -0
  33. pyimcom/psfutil.py +2199 -0
  34. pyimcom/routine.py +588 -0
  35. pyimcom/splitpsf/__init__.py +0 -0
  36. pyimcom/splitpsf/imsubtract.py +793 -0
  37. pyimcom/splitpsf/imsubtract_wrapper.py +107 -0
  38. pyimcom/splitpsf/splitpsf.py +497 -0
  39. pyimcom/splitpsf/splitpsf_wrapper.py +161 -0
  40. pyimcom/splitpsf/update_cube.py +136 -0
  41. pyimcom/truthcats.py +396 -0
  42. pyimcom/utils/__init__.py +0 -0
  43. pyimcom/utils/compareutils.py +207 -0
  44. pyimcom/utils/piffutils.py +223 -0
  45. pyimcom/wcsutil.py +839 -0
  46. pyimcom-1.2.1.dist-info/METADATA +67 -0
  47. pyimcom-1.2.1.dist-info/RECORD +52 -0
  48. pyimcom-1.2.1.dist-info/WHEEL +5 -0
  49. pyimcom-1.2.1.dist-info/licenses/LICENSE +21 -0
  50. pyimcom-1.2.1.dist-info/scm_file_list.json +285 -0
  51. pyimcom-1.2.1.dist-info/scm_version.json +8 -0
  52. pyimcom-1.2.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,514 @@
1
+ """Functions to compress float arrays to 24 bit integers.
2
+
3
+ Functions
4
+ ---------
5
+ lsbf_fwd
6
+ Least significant bits moved first.
7
+ lsbf_rev
8
+ Inverse of `lsbf_fwd`.
9
+ diff_fwd
10
+ Replaces an image with an array of differences.
11
+ diff_rev
12
+ Inverse of `diff_fwd`.
13
+ smallnum_fwd
14
+ Re-maps numbers of small absolute value to be near 0 when unsigned.
15
+ smallnum_rev
16
+ Inverse of `smallnum_fwd`.
17
+ i24compress
18
+ Compresses an array using the 'I24' scheme.
19
+ i24decompress
20
+ Decompresses an array using the 'I24' scheme.
21
+
22
+ Classes
23
+ -------
24
+ I24Cube
25
+ Object that can be reformatted as floating point, integer, or compressed integer
26
+ in the 'I24' scheme.
27
+
28
+ """
29
+
30
+ import copy
31
+
32
+ import numpy as np
33
+ from astropy.io import fits
34
+
35
+ # Compression scheme table
36
+ i24_recognized_schemes = ["I24A", "I24B"]
37
+
38
+ ### Utilities for reorganizing uint8 cubes ###
39
+
40
+
41
+ def lsbf_fwd(im):
42
+ """
43
+ Takes a 2D uint8 image and swaps the bits so that the least significant
44
+ bit goes first, then the next, etc. and the most significant bit goes last.
45
+
46
+ If given a 3D image, then does each 2D slice. The intent would be to use
47
+ this on objects with a shape of (3,ny,nx); it will work for any number of slices,
48
+ but it will be slow if the number of slices (3 in this case) is large.
49
+
50
+ Parameters
51
+ ----------
52
+ im : np.array of uint8
53
+ 2D or 3D image.
54
+
55
+ Returns
56
+ -------
57
+ out_im : np.array of uint8
58
+ 2D or 3D bit-swapped image. Same shape as `im`.
59
+
60
+ See Also
61
+ --------
62
+ lsbf_rev : Inverse function.
63
+
64
+ """
65
+
66
+ # 3D images are done one slice at a time
67
+ if len(np.shape(im)) == 3:
68
+ out_im = np.zeros_like(im)
69
+ for j in range(np.shape(im)[0]):
70
+ out_im[j, :, :] = lsbf_fwd(im[j, :, :])
71
+ return out_im
72
+
73
+ # now we have a 2D image
74
+ ny, nx = np.shape(im)
75
+ return np.packbits(
76
+ np.transpose(np.unpackbits(im, bitorder="little").reshape((ny, nx, 8)), axes=(2, 0, 1)).reshape(
77
+ (ny, nx, 8)
78
+ ),
79
+ bitorder="little",
80
+ ).reshape((ny, nx))
81
+ bits = np.zeros((8, ny, nx), dtype=bool)
82
+ for j in range(8):
83
+ bits[j, :, :] = (im >> j) % 2 == 1
84
+ bits = bits.reshape((ny, nx, 8))
85
+ out_im = np.zeros_like(im)
86
+ for j in range(8):
87
+ out_im += (2**j * bits[:, :, j]).astype(np.uint8)
88
+ return out_im
89
+
90
+
91
+ def lsbf_rev(im):
92
+ """Inverse of the bit-swapping function lsbf_fwd.
93
+
94
+ Parameters
95
+ ----------
96
+ im : np.array of uint8
97
+ 2D or 3D image.
98
+
99
+ Returns
100
+ -------
101
+ out_im : np.array of uint8
102
+ 2D or 3D bit-swapped image. Same shape as `im`.
103
+
104
+ See Also
105
+ --------
106
+ lsbf_fwd : Forward function.
107
+
108
+ """
109
+
110
+ # 3D images are done one slice at a time
111
+ if len(np.shape(im)) == 3:
112
+ out_im = np.zeros_like(im)
113
+ for j in range(np.shape(im)[0]):
114
+ out_im[j, :, :] = lsbf_rev(im[j, :, :])
115
+ return out_im
116
+
117
+ # now we have a 2D image
118
+ ny, nx = np.shape(im)
119
+ return np.packbits(
120
+ np.transpose(np.unpackbits(im, bitorder="little").reshape((8, ny, nx)), axes=(1, 2, 0)),
121
+ bitorder="little",
122
+ ).reshape((ny, nx))
123
+
124
+
125
+ ### Utilities for differencing integer cubes ###
126
+
127
+
128
+ def diff_fwd(im, bitkeep):
129
+ """
130
+ Replace an int32 image with differences of the same shape.
131
+
132
+ Parameters
133
+ ----------
134
+ im : np.array of int32
135
+ A 2D input image.
136
+ bitkeep : int
137
+ Number of bits to keep. The maximum is 31.
138
+
139
+ Returns
140
+ -------
141
+ np.array of int32
142
+ The image of differences, same shape as `im`.
143
+
144
+ See Also
145
+ --------
146
+ diff_rev : Inverse function.
147
+
148
+ """
149
+
150
+ ny, nx = np.shape(im)
151
+ c = im.flatten()
152
+ c[1:] = c[1:] - c[:-1]
153
+ c = (2**bitkeep + c) % 2**bitkeep
154
+ return c.reshape((ny, nx)).astype(np.int32)
155
+
156
+
157
+ def diff_rev(im, bitkeep):
158
+ """Reconstruct an image from differences. Inverse function of diff_fwd.
159
+
160
+ Parameters
161
+ ----------
162
+ im : np.array of int32
163
+ A 2D input image of differences.
164
+ bitkeep : int
165
+ Number of bits to keep. The maximum is 31.
166
+
167
+ Returns
168
+ -------
169
+ np.array of int32
170
+ The original image, same shape as `im`.
171
+
172
+ See Also
173
+ --------
174
+ diff_fwd : Forward function.
175
+
176
+ """
177
+
178
+ ny, nx = np.shape(im)
179
+ c = im.astype(np.uint32).flatten()
180
+ c = np.cumsum(c) & np.uint32(2**bitkeep - 1)
181
+ return c.reshape((ny, nx)).astype(np.int32)
182
+
183
+
184
+ ### Utilities for packaging small numbers together ###
185
+
186
+
187
+ def smallnum_fwd(im, bitkeep):
188
+ """
189
+ Remapping to package small differences of either sign near 0.
190
+
191
+ Works on an int32 image with bitkeep bits used
192
+ and negative numbers rolling over as ``-j --> 2**bitkeep-j``.
193
+
194
+ Parameters
195
+ ----------
196
+ im : np.array of int32
197
+ A 2D input image.
198
+ bitkeep : int
199
+ Number of bits to keep. The maximum is 31.
200
+
201
+ Returns
202
+ -------
203
+ np.array of int32
204
+ Re-mapped image, same shape as `im`.
205
+
206
+ See Also
207
+ --------
208
+ smallnum_rev : Inverse function.
209
+
210
+ """
211
+
212
+ return np.where(im >= 2 ** (bitkeep - 1), 2 * (2**bitkeep - im) - 1, 2 * im)
213
+
214
+
215
+ def smallnum_rev(im, bitkeep):
216
+ """
217
+ Reconstructs images that have been re-mapped. Inverse of smallnum_fwd.
218
+
219
+ Parameters
220
+ ----------
221
+ im : np.array of int32
222
+ A 2D input image that has been re-mapped.
223
+ bitkeep : int
224
+ Number of bits to keep. The maximum is 31.
225
+
226
+ Returns
227
+ -------
228
+ np.array of int32
229
+ Original image, same shape as `im`.
230
+
231
+ See Also
232
+ --------
233
+ smallnum_fwd : Forward function.
234
+
235
+ """
236
+
237
+ return np.where(im % 2, 2**bitkeep - 1 - im // 2, im // 2)
238
+
239
+
240
+ class I24Cube:
241
+ """
242
+ Class to compress a float cube to int24 with scaling, and reconstruct the original image.
243
+
244
+ Parameters
245
+ ----------
246
+ inarray : np.array
247
+ The input array. Options are
248
+ * 2D float32 (original image)
249
+ * 2D int32 (intermediate step: leading byte not used)
250
+ * 3D uint8 (compressed form)
251
+ pars : dict
252
+ Parameters for the compression scheme. The possible keys are
253
+ VMIN, VMAX, SOFTBIAS, DIFF, ALPHA, BITKEEP, and REORDER.
254
+ overflow : astropy.io.fits.BinTableHDU or None, optional
255
+ Overflow table (y,x,value). Needed if a compressed image is given as input.
256
+
257
+ Attributes
258
+ ----------
259
+ ny : int
260
+ Height of image
261
+ nx : int
262
+ Width of image
263
+ pars : dict
264
+ Compression parameters specified when constructed.
265
+ data : np.array
266
+ Current image data.
267
+ mode : str
268
+ Current image type. One of 'float32', 'int32', or 'uint8'.
269
+ vmin : float
270
+ Minimum of compression range.
271
+ vmax : float
272
+ Maximum of compression range.
273
+ alpha : float
274
+ Power law index of compression (1=linear).
275
+ bitkeep : int
276
+ Number of bits to keep (maximum=24).
277
+ reorder : bool
278
+ Implement bit reordering?
279
+ softbias : int
280
+ Integer in [0,2**24) to add (to avoid slight negative fluctuations being 111111).
281
+ If -1, uses smallnum compression instead.
282
+ diff : bool
283
+ Use difference of successive pixels?
284
+ overflow : astropy.io.fits.BinTableHDU or None
285
+ Overflow table (y,x,value).
286
+ reorder = use bit reordering? (boolean)
287
+
288
+ Methods
289
+ --------
290
+ __init__
291
+ Constructor.
292
+ to_mode
293
+ Change image format to a different (often compressed) storage mode.
294
+
295
+ """
296
+
297
+ def __init__(self, inarray, pars, overflow=None):
298
+ # figure out what we have
299
+ self.pars = copy.copy(pars)
300
+ s = np.shape(inarray)
301
+ self.ny, self.nx = s[-2:]
302
+ self.data = copy.copy(inarray)
303
+ d = len(s)
304
+
305
+ # get the mode
306
+ if d == 2 and inarray.dtype.name == "float32":
307
+ self.mode = "float32"
308
+ elif d == 2 and inarray.dtype.name == "int32":
309
+ self.mode = "int32"
310
+ elif d == 3 and inarray.dtype.name == "uint8":
311
+ self.mode = "uint8"
312
+ else:
313
+ raise TypeError("Can't initialize I24Cube: unrecognized data type or dimension.")
314
+
315
+ # extract minimum and maximum (these need to be provided)
316
+ self.vmin = float(pars["VMIN"])
317
+ self.vmax = float(pars["VMAX"])
318
+ if "SOFTBIAS" in pars:
319
+ self.softbias = int(pars["SOFTBIAS"])
320
+ else:
321
+ self.softbias = 0
322
+ if "DIFF" in pars:
323
+ self.diff = bool(pars["DIFF"])
324
+ else:
325
+ self.diff = False
326
+ if "ALPHA" in pars:
327
+ self.alpha = float(pars["ALPHA"])
328
+ else:
329
+ self.alpha = 1.0
330
+ if "BITKEEP" in pars:
331
+ self.bitkeep = int(pars["BITKEEP"])
332
+ if self.bitkeep >= 24 or self.bitkeep <= 0:
333
+ raise ValueError(f"Can't keep {self.bitkeep:d} bits")
334
+ else:
335
+ self.bitkeep = 24
336
+ if "REORDER" in pars:
337
+ self.reorder = bool(pars["REORDER"])
338
+ else:
339
+ self.reorder = True
340
+
341
+ # overflow
342
+ self.overflow = overflow
343
+
344
+ def to_mode(self, mode):
345
+ """
346
+ Converts to the given mode.
347
+
348
+ Parameters
349
+ ----------
350
+ mode : str
351
+ Options are 'float32', 'int32', and 'uint8'.
352
+
353
+ Returns
354
+ -------
355
+ None
356
+
357
+ """
358
+
359
+ if mode not in ["float32", "int32", "uint8"]:
360
+ raise Exception(f"Unrecognized mode: {mode:s}")
361
+
362
+ # nothing to do if there's no conversion
363
+ if self.mode == mode:
364
+ return
365
+
366
+ # need to convert float32->int32
367
+ if self.mode == "float32":
368
+ posy, posx = np.where(np.logical_or(self.data < self.vmin, self.data > self.vmax))
369
+ self.overflow = fits.BinTableHDU.from_columns(
370
+ [
371
+ fits.Column(name="y", format="J", array=posy),
372
+ fits.Column(name="x", format="J", array=posx),
373
+ fits.Column(name="value", format="E", array=np.copy(self.data[posy, posx])),
374
+ ]
375
+ )
376
+ y = (np.clip(self.data, self.vmin, self.vmax) - self.vmin) / (self.vmax - self.vmin)
377
+ y = 2**self.bitkeep * y**self.alpha
378
+ self.data = np.clip(np.floor(y).astype(np.int32), 0, 2**self.bitkeep - 1)
379
+ del y
380
+ if self.diff:
381
+ self.data = diff_fwd(self.data, self.bitkeep)
382
+ if self.softbias > 0:
383
+ self.data = ((self.softbias + self.data) % 2**self.bitkeep).astype(np.int32)
384
+ elif self.softbias == -1:
385
+ self.data = smallnum_fwd(self.data, self.bitkeep)
386
+ self.mode = "int32"
387
+
388
+ # need to convert uint8->int32
389
+ if self.mode == "uint8":
390
+ if self.reorder:
391
+ x = lsbf_rev(self.data).astype(np.int32)
392
+ else:
393
+ x = copy.copy(self.data.astype(np.int32))
394
+ self.data = np.zeros((self.ny, self.nx), dtype=np.int32)
395
+ for j in range(np.shape(x)[0]):
396
+ self.data += x[j, :, :] << (8 * j)
397
+ self.mode = "int32"
398
+
399
+ # if we're done
400
+ if self.mode == mode:
401
+ return
402
+
403
+ # if we still need to convert, we are starting from int32
404
+
405
+ # need to convert int32->float32
406
+ if mode == "float32":
407
+ # note transformations are in the opposite order to float32->int32
408
+ if self.softbias > 0:
409
+ self.data = (2**self.bitkeep - self.softbias + self.data) % 2**self.bitkeep
410
+ elif self.softbias == -1:
411
+ self.data = smallnum_rev(self.data, self.bitkeep)
412
+ if self.diff:
413
+ self.data = diff_rev(self.data, self.bitkeep)
414
+ y = (0.5 + self.data) / 2**self.bitkeep
415
+ self.data = (self.vmin + (self.vmax - self.vmin) * y ** (1 / self.alpha)).astype(np.float32)
416
+ if self.overflow is not None:
417
+ posy = np.array(self.overflow.data["y"])
418
+ posx = np.array(self.overflow.data["x"])
419
+ self.data[posy, posx] = self.overflow.data["value"]
420
+ self.mode = "float32"
421
+
422
+ # need to convert int32->uint8
423
+ if mode == "uint8":
424
+ # make the first axis however big it needs to be
425
+ newarray = np.zeros(((self.bitkeep + 7) // 8, self.ny, self.nx), dtype=np.uint8)
426
+ newarray[0, :, :] = self.data % 256
427
+ if self.bitkeep > 8:
428
+ self.data >>= 8
429
+ newarray[1, :, :] = self.data % 256
430
+ if self.bitkeep > 16:
431
+ self.data >>= 8
432
+ newarray[2, :, :] = self.data % 256
433
+ if self.reorder:
434
+ self.data = lsbf_fwd(newarray)
435
+ else:
436
+ self.data = newarray
437
+ self.mode = "uint8"
438
+
439
+
440
+ # stand-alone functions
441
+
442
+
443
+ def i24compress(im, scheme, pars):
444
+ """
445
+ Compresses an image.
446
+
447
+ Parameters
448
+ ----------
449
+ im : np.array of uint8
450
+ 2D or 3D image.
451
+ scheme : str
452
+ Compression scheme (right now supports 'I24A', 'I24B').
453
+ pars : dict
454
+ Parameters to pass to compression algorithm.
455
+
456
+ Returns
457
+ -------
458
+ data : np.array
459
+ The compressed data cube.
460
+ overflow : astropy.io.fits.BinTableHDU
461
+ The table of values that overflowed the quantization range.
462
+
463
+ See Also
464
+ --------
465
+ pyimcom.compress.i24.I24Cube : Compression class; see for possible keys in `pars`.
466
+
467
+ """
468
+
469
+ if scheme not in i24_recognized_schemes:
470
+ return im, None # unrecognized scheme
471
+
472
+ cube = I24Cube(im, pars)
473
+ if scheme == "I24A":
474
+ cube.to_mode("int32")
475
+ elif scheme == "I24B":
476
+ cube.to_mode("uint8")
477
+
478
+ return cube.data, cube.overflow
479
+
480
+
481
+ def i24decompress(im, scheme, pars, overflow=None):
482
+ """
483
+ Decompresses an image.
484
+
485
+ Parameters
486
+ ----------
487
+ im : np.array of uint8
488
+ 2D or 3D image.
489
+ scheme : str
490
+ Compression scheme (right now supports 'I24A', 'I24B').
491
+ pars : dict
492
+ Parameters to pass to compression algorithm.
493
+ overflow: astropy.io.fits.BinTableHDU or None, optional
494
+ Overflow table (y,x,value). Needed if a compressed image is given as input.
495
+
496
+ Returns
497
+ -------
498
+ data : np.array
499
+ The de-compressed data cube.
500
+
501
+ See Also
502
+ --------
503
+ pyimcom.compress.i24.I24Cube : Compression class; see for possible keys in `pars`.
504
+
505
+ """
506
+
507
+ if scheme not in i24_recognized_schemes:
508
+ return im # unrecognized scheme
509
+
510
+ cube = I24Cube(im, pars, overflow=overflow)
511
+ if scheme == "I24A" or scheme == "I24B":
512
+ cube.to_mode("float32")
513
+
514
+ return cube.data