1#!/usr/bin/env python 2from __future__ import absolute_import, division, print_function 3"""Code to handle imager windowing (subimage) and binning. 4 5Note: uses the expansionist or inclusive philosophy. 6The new window is grown to the maximum possible extent 7whenever binning or unbinning. For example, assuming 1-based and inclusive: 8- An unbinned window of [1/2/3, 1/2/3, 4/5/6, 4/5/6] will bin 3x3 to [1, 1, 2, 2] 9- A 3x3 binned window of [1, 1, 2, 2] will unbin to [1, 1, 6, 6] 10 112008-11-06 ROwen 122008-12-15 ROwen Bug fix: binWindow was mis-computing limits. 132009-02-25 ROwen Added getFullBinWindow method. 14""" 15import math 16 17__all__ = ["ImageWindow"] 18 19class ImageWindow(object): 20 """Class to handle imager windowing and binning. 21 22 Users typically prefer to specify windows (subregions) in binned coordinates, 23 but then what happens if the user changes the bin factor? This class offers 24 functions that help handle such changes. 25 26 Inputs: 27 - imSize image size (x, y unbinned pixels) 28 - isOneBased if True, the address of the lower left pixel is (1,1), else it is (0, 0) 29 - isInclusive if True, the upper right portion of the image window is included in the data, 30 else it is omitted. 31 """ 32 def __init__(self, 33 imSize, 34 isOneBased = True, 35 isInclusive = True, 36 ): 37 if len(imSize) != 2: 38 raise ValueError("imSize must be two integers; imSize = %r" % (imSize,)) 39 self.imSize = tuple([int(mc) for mc in imSize]) 40 self.isInclusive = bool(isInclusive) 41 42 if self.isInclusive: 43 urPosAdj = 0 44 ubUROff = 0 45 binUROff = -1 46 else: 47 urPosAdj = 1 48 ubUROff = 1 49 binUROff = 0 50 51 def posToWin(xyPos, urOff): 52 return tuple(xyPos) + tuple([val + urOff for val in xyPos]) 53 54 if isOneBased: 55 imLL = (1, 1) 56 else: 57 imLL = (0, 0) 58 imUR = [(imLL[ind] + imSize[ind] - 1) for ind in range(2)] 59 self.minWin = posToWin(imLL, urPosAdj) 60 self.maxUBWin = posToWin(imUR, urPosAdj) 61 62 self.binWinOffset = posToWin(imLL, ubUROff) 63 self.ubWinOffset = posToWin(imLL, binUROff) 64 65 def binWindow(self, ubWin, binFac): 66 """Converts unbinned window to binned. 67 68 The output is constrained to be in range for the given bin factor, 69 though this is only an issue if ubWin is out of range. 70 71 Inputs: 72 - ubWin: unbinned window coords (LL x, LL y, UR x, UR y) 73 74 Returns binned window coords (LL x, LL y, UR x, UR y) 75 76 If any element of ubWin or binFac is None, all returned elements are None. 77 """ 78 if None in ubWin or None in binFac: 79 return (None,)*4 80 ubWin = self._getWin(ubWin, "ubWin") 81 binXYXY = self._getBinXYXY(binFac) 82 83 # bin window, ignoring limits 84 binWin = [int(math.floor(self.binWinOffset[ind] + ((ubWin[ind] - self.binWinOffset[ind]) / float(binXYXY[ind])))) 85 for ind in range(4)] 86 87 # apply limits 88 binWin = [min(max(binWin[ind], self.minWin[ind]), self.maxUBWin[ind] // binXYXY[ind]) for ind in range(4)] 89 90# print "binWindow(ubWin=%r, binFac=%r) = %r" % (ubWin, binFac, binWin) 91 return binWin 92 93 def unbinWindow(self, binWin, binFac): 94 """Converts binned window to unbinned. 95 96 The output is constrained to be in range for the given bin factor. 97 98 Inputs: 99 - binWin: binned window coords (LL x, LL y, UR x, UR y) 100 101 Returns unbinned window coords: (LL x, LL y, UR x, UR y) 102 103 If any element of ubWin or binFac is None, all returned elements are None. 104 """ 105 if None in binWin or None in binFac: 106 return (None,)*4 107 binWin = self._getWin(binWin, "binWin") 108 binXYXY = self._getBinXYXY(binFac) 109 110 binWin = [int(val) for val in binWin] 111 112 # unbin window, ignoring limits 113 ubWin = [((binWin[ind] - self.ubWinOffset[ind]) * binXYXY[ind]) + self.ubWinOffset[ind] 114 for ind in range(len(binWin))] 115 116 # apply limits 117 ubWin = [min(max(ubWin[ind], self.minWin[ind]), self.maxUBWin[ind]) 118 for ind in range(4)] 119# print "unbinWindow(binWin=%r, binFac=%r) = %r" % (binWin, binFac, ubWin) 120 return ubWin 121 122 def getMinWindow(self): 123 """Return the minimum window coords (which is independent of bin factor). 124 125 Returns [LL x, LL y, UR x, UR y] 126 """ 127 return list(self.minWin) 128 129 def getMaxBinWindow(self, binFac=(1,1)): 130 """Return the maximum binned window coords, given a bin factor. 131 Note: the minimum window coords are the same binned or unbinned: self.minWin 132 133 Returns [LL x, LL y, UR x, UR y] 134 """ 135 binXYXY = self._getBinXYXY(binFac) 136 return [int(math.floor(self.binWinOffset[ind] + ((self.maxUBWin[ind] - self.binWinOffset[ind]) / float(binXYXY[ind])))) 137 for ind in range(4)] 138 139 def getFullBinWindow(self, binFac=(1,1)): 140 """Return the full binned window coords 141 142 Returns [LL x, LL y, UR x, UR y] 143 """ 144 return self.getMinWindow()[0:2] + self.getMaxBinWindow(binFac)[2:] 145 146 def _getBinXYXY(self, binFac): 147 """Check bin factor and return as 4 ints: x, y, x, y""" 148 if len(binFac) != 2: 149 raise ValueError("binFac=%r; must have 2 elements" % (binFac,)) 150 try: 151 binXY = [int(bf) for bf in binFac] 152 except Exception: 153 raise ValueError("binFac=%r; must have 2 integers" % (binFac,)) 154 if min(binXY) < 1: 155 raise ValueError("binFac=%r; must be >= 1" % (binFac,)) 156 return binXY * 2 157 158 def _getWin(self, win, winName="window"): 159 """Check window argument and return as 4 ints""" 160 if len(win) != 4: 161 raise ValueError("%s=%r; must have 4 elements" % (winName, win)) 162 try: 163 return [int(w) for w in win] 164 except Exception: 165 raise ValueError("%s=%r; must have 4 integers" % (winName, win)) 166 167 168if __name__ == "__main__": 169 ci = ImageWindow(imSize=(2045, 1024)) 170 for bfx in range(1, 4): 171 binFac = (bfx, bfx) 172 for begInd in range(1, bfx + 1): 173 for endInd in range(begInd, bfx + 1): 174 unbWin = [begInd, begInd, endInd, endInd] 175 binWin = tuple(ci.binWindow(unbWin, binFac)) 176 if binWin != (1, 1, 1, 1): 177 raise RuntimeError("Test failed; unbWin=%s != (1, 1, 1, 1), binWin=%s; binFac=%s" % \ 178 (unbWin, binWin, binFac)) 179 newUnbWin = tuple(ci.unbinWindow(binWin, binFac)) 180 if newUnbWin != (1, 1, bfx, bfx): 181 raise RuntimeError("Test failed; newUnbWin=%s != (1, 1, %s, %s), binWin=%s; binFac=%s" % \ 182 (newUnbWin, bfx, bfx, binWin, binFac)) 183 print ("OK") 184