def clip_tiling_area_to_range(w, h, pos, tiling_rng): """ Finds the intersection between the requested tiling area and the tiling range. w (float): width of the tiling area h (float): height of the tiling area pos (dict -> float): current position of the stage tiling_rng (dict -> list): the tiling range along x and y axes as (xmin, ymin, xmax, ymax), or (xmin, ymax, xmax, ymin) return (None or tuple of 4 floats): None if there is no intersection, or the rectangle representing the intersection as (xmin, ymin, xmax, ymax). """ area_req = (pos["x"] - w / 2, pos["y"] - h / 2, pos["x"] + w / 2, pos["y"] + h / 2) # clip the tiling area, if needed (or find the intersection between the active range and the requested area) return rect_intersect(area_req, (tiling_rng["x"][0], tiling_rng["y"][1], tiling_rng["x"][1], tiling_rng["y"][0]))
def convert_roi_phys_to_ccd(ccd, roi): """ Convert the ROI in physical coordinates into a optical detector (ccd) ROI (in pixels). :param roi: (4 floats) The roi (ltrb) position in m. :returns: (4 ints or None) The roi (ltrb) position in pixels, or None if no intersection. """ ccd_rect = get_ccd_fov(ccd) logging.info("CCD FoV = %s", ccd_rect) phys_width = (ccd_rect[2] - ccd_rect[0], ccd_rect[3] - ccd_rect[1]) logging.info("phys width: " + str(phys_width)) logging.info("roi: " + str(roi)) logging.info("ccd rect: " + str(ccd_rect)) # convert to a proportional ROI proi = ( (roi[0] - ccd_rect[0]) / phys_width[0], (roi[1] - ccd_rect[1]) / phys_width[1], (roi[2] - ccd_rect[0]) / phys_width[0], (roi[3] - ccd_rect[1]) / phys_width[1], ) # inverse Y (because physical Y goes down, while pixel Y goes up) proi = (proi[0], 1 - proi[3], proi[2], 1 - proi[1]) logging.info("proi: " + str(proi)) # convert to pixel values, rounding to slightly bigger area res = ccd.resolution.value pxroi = ( int(proi[0] * res[0]), int(proi[1] * res[1]), int(math.ceil(proi[2] * res[0])), int(math.ceil(proi[3] * res[1])), ) logging.info("pxroi: " + str(pxroi)) # Limit the ROI to the one visible in the FoV trunc_roi = util.rect_intersect(pxroi, (0, 0) + res) if trunc_roi is None: return None if trunc_roi != pxroi: logging.warning( "CCD FoV doesn't cover the whole ROI, it would need " "a ROI of %s in CCD referential.", pxroi) return trunc_roi
def get_ROA_rect(w, h, pos, tiling_rng): """ Finds the intersection between the requested tiling area and the tiling range. w (float): width of the tiling area h (float): height of the tiling area pos (dict -> float): current position of the stage tiling_rng (dict -> list): the tiling range along x and y axes """ tl = pos["x"] - w / 2, pos["y"] + h / 2 br = pos["x"] + w / 2, pos["y"] - h / 2 # clip the tiling area, if needed (or find the intersection between the active range and the requested area) # Note: the input is LTRB. The output is LBRT assuming y axis upwards, or LTRB assuming y axis downwards. rect_pts = rect_intersect((tl[0], tl[1], br[0], br[1]), (tiling_rng["x"][0], tiling_rng["y"][1], tiling_rng["x"][1], tiling_rng["y"][0])) return rect_pts
def convert_roi_phys_to_ccd(ccd, roi): """ Convert the ROI in physical coordinates into a CCD ROI (in pixels) roi (4 floats): ltrb positions in m return (4 ints or None): ltrb positions in pixels, or None if no intersection """ ccd_rect = get_ccd_fov(ccd) logging.info("CCD FoV = %s", ccd_rect) phys_width = (ccd_rect[2] - ccd_rect[0], ccd_rect[3] - ccd_rect[1]) logging.info("phys width: " + str(phys_width)) logging.info("roi: " + str(roi)) logging.info("ccd rect: " + str(ccd_rect)) # convert to a proportional ROI proi = ((roi[0] - ccd_rect[0]) / phys_width[0], (roi[1] - ccd_rect[1]) / phys_width[1], (roi[2] - ccd_rect[0]) / phys_width[0], (roi[3] - ccd_rect[1]) / phys_width[1], ) # inverse Y (because physical Y goes down, while pixel Y goes up) proi = (proi[0], 1 - proi[3], proi[2], 1 - proi[1]) logging.info("proi: " + str(proi)) # convert to pixel values, rounding to slightly bigger area shape = ccd.shape[0:2] pxroi = (int(proi[0] * shape[0]), int(proi[1] * shape[1]), int(math.ceil(proi[2] * shape[0])), int(math.ceil(proi[3] * shape[1])), ) logging.info("pxroi: " + str(pxroi)) # Limit the ROI to the one visible in the FoV trunc_roi = util.rect_intersect(pxroi, (0, 0) + shape) if trunc_roi is None: return None if trunc_roi != pxroi: logging.warning("CCD FoV doesn't cover the whole ROI, it would need " "a ROI of %s in CCD referential.", pxroi) return trunc_roi
def convert_roi_phys_to_ccd(self, roi): """ Convert the ROI in physical coordinates into a CCD ROI (in pixels) roi (4 floats): ltrb positions in m return (4 ints or None): ltrb positions in pixels, or None if no intersection """ ccd_rect = self.get_ccd_fov() logging.debug("CCD FoV = %s", ccd_rect) phys_width = (ccd_rect[2] - ccd_rect[0], ccd_rect[3] - ccd_rect[1]) # convert to a proportional ROI proi = ( (roi[0] - ccd_rect[0]) / phys_width[0], (roi[1] - ccd_rect[1]) / phys_width[1], (roi[2] - ccd_rect[0]) / phys_width[0], (roi[3] - ccd_rect[1]) / phys_width[1], ) # inverse Y (because physical Y goes down, while pixel Y goes up) proi = (proi[0], 1 - proi[3], proi[2], 1 - proi[1]) # convert to pixel values, rounding to slightly bigger area shape = self.ccd.shape[0:2] pxroi = ( int(proi[0] * shape[0]), int(proi[1] * shape[1]), int(math.ceil(proi[2] * shape[0])), int(math.ceil(proi[3] * shape[1])), ) # Limit the ROI to the one visible in the FoV trunc_roi = util.rect_intersect(pxroi, (0, 0) + shape) if trunc_roi is None: return None if trunc_roi != pxroi: logging.warning( "CCD FoV doesn't cover the whole ROI, it would need " "a ROI of %s in CCD referential.", pxroi) return trunc_roi
def test_clipping(self): tmp = "{}: {} - {} -> {}" for bb in bounding_boxes: outside, touching, overlap = relative_boxes(bb) for b in outside: r = util.rect_intersect(b, bb) msg = tmp.format("outside", b, bb, r) self.assertIsNone(r, msg) for b in touching: r = util.rect_intersect(b, bb) msg = tmp.format("touching", b, bb, r) self.assertIsNone(r, msg) for b in overlap: r = util.rect_intersect(b, bb) msg = tmp.format("overlap", b, bb, r) self.assertIsNotNone(r, msg) # 'Manual' checks if bb == (-1, -1, 1, 1): if b[:2] == (-2, -2): self.assertEqual(r, (-1, -1, 0, 0), msg) elif b[:2] == (0, -1): self.assertEqual(r, (0, -1, 1, 1), msg) elif b[:2] == (0, 0): self.assertEqual(r, (0, 0, 1, 1), msg) # full and exact overlap b = bb r = util.rect_intersect(b, bb) self.assertEqual(r, bb) # inner overlap b = (bb[0] + 1, bb[1] + 1, bb[2], bb[3]) r = util.rect_intersect(b, bb) self.assertEqual(r, b) # overflowing overlap b = (bb[0] - 1, bb[1] - 1, bb[2] + 1, bb[2] + 1) r = util.rect_intersect(b, bb) self.assertEqual(r, bb)
def convert_roi_phys_to_ccd(self, roi): """ Convert the ROI in physical coordinates into a CCD ROI (in pixels) roi (4 floats): ltrb positions in m return (4 ints or None): ltrb positions in pixels, or """ ccd_rect = self.get_ccd_fov() logging.debug("CCD FoV = %s", ccd_rect) phys_width = (ccd_rect[2] - ccd_rect[0], ccd_rect[3] - ccd_rect[1]) # convert to a proportional ROI proi = ((roi[0] - ccd_rect[0]) / phys_width[0], (roi[1] - ccd_rect[1]) / phys_width[1], (roi[2] - ccd_rect[0]) / phys_width[0], (roi[3] - ccd_rect[1]) / phys_width[1], ) # ensure it's in ltbr order proi = util.normalize_rect(proi) # convert to pixel values, rounding to slightly bigger area shape = self.ccd.shape[0:2] pxroi = (int(proi[0] * shape[0]), int(proi[1] * shape[1]), int(math.ceil(proi[2] * shape[0])), int(math.ceil(proi[3] * shape[1])), ) # Limit the ROI to the one visible in the FoV trunc_roi = util.rect_intersect(pxroi, (0, 0) + shape) if trunc_roi is None: return None if trunc_roi != pxroi: logging.warning("CCD FoV doesn't cover the whole ROI, it would need " "a ROI of %s in CCD referential.", pxroi) return trunc_roi