def reg_to_roi(reg): """ Function to convert a region to an ROI This might be implementable as a dictionary/registry, but hard-coding it isn't more difficult... """ if isinstance(reg, regions.CirclePixelRegion): return CircularROI(xc=reg.center.x, yc=reg.center.y, radius=reg.radius) elif isinstance(reg, regions.PointPixelRegion): return PointROI(x=reg.center.x, y=reg.center.y) elif isinstance(reg, regions.RectanglePixelRegion): if reg.angle == 0: xmin, xmax = reg.center.x - reg.width / 2, reg.center.x + reg.width / 2 ymin, ymax = reg.center.y - reg.height / 2, reg.center.y + reg.height / 2 return RectangularROI(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax) else: if hasattr(reg, 'corners'): xverts = [c[0] for c in reg.corners] yverts = [c[1] for c in reg.corners] return PolygonalROI(vx=xverts, vy=yverts) else: raise NotImplementedError("Rectangles need to convert to polygons.") elif isinstance(reg, regions.PolygonPixelRegion): return PolygonalROI(vx=reg.vertices.x, vy=reg.vertices.y) else: raise NotImplementedError("Region {0} not recognized".format(reg))
def test_roi3d(dataxyz): roi_2d = PolygonalROI(vx=[0.5, 2.5, 2.5, 0.5], vy=[1, 1, 3.5, 3.5]) roi = Projected3dROI(roi_2d, projection_matrix=np.eye(4)) assert roi.contains(dataxyz['x'], dataxyz['y']).tolist() == [True, True, False] roi_2d = PolygonalROI(vx=[1.5, 3.5, 3.5, 1.5], vy=[4, 4, 6.5, 6.5]) roi = Projected3dROI(roi_2d, projection_matrix=xyzw2yxzw) assert roi.contains3d(dataxyz['x'], dataxyz['y'], dataxyz['z']).tolist() == [True, True, False]
def on_selection(self, data, other=None): with self.output_widget: W = np.matrix(self.figure.matrix_world).reshape((4, 4)).T P = np.matrix(self.figure.matrix_projection).reshape((4, 4)).T M = np.dot(P, W) if data['device']: if data['type'] == 'lasso': region = data['device'] vx, vy = zip(*region) roi_2d = PolygonalROI(vx=vx, vy=vy) elif data['type'] == 'circle': x1, y1 = data['device']['begin'] x2, y2 = data['device']['end'] dx = x2 - x1 dy = y2 - y1 r = (dx**2 + dy**2)**0.5 roi_2d = CircularROI(xc=x1, yc=y1, radius=r) elif data['type'] == 'rectangle': x1, y1 = data['device']['begin'] x2, y2 = data['device']['end'] x = [x1, x2] y = [y1, y2] roi_2d = RectangularROI(xmin=min(x), xmax=max(x), ymin=min(y), ymax=max(y)) roi = Projected3dROI(roi_2d, M) self.apply_roi(roi)
def apply_roi(self, roi): subset_state = RoiSubsetState() xroi, yroi = roi.to_polygon() x, y = self._get_plot_attributes() subset_state.xatt = x subset_state.yatt = y subset_state.roi = PolygonalROI(xroi, yroi) mode = EditSubsetMode() mode.update(self.data, subset_state, focus_data=self.display_data)
def setup(self): # Set up main 3-d dataset self.data = Data(values=np.random.random((512, 512, 512))) # Set up subset state which is a polygon defined in pixel space and # only covering a small fraction of the whole cube. self.polygonal_roi = PolygonalROI([440, 460, 450], [400, 455, 500]) self.polygonal_subset_state = RoiSubsetState(xatt=self.data.pixel_component_ids[0], yatt=self.data.pixel_component_ids[2], roi=self.polygonal_roi)
def test_apply_roi_xlog(self): self.client.add_layer(self.data) self.client.set_component(self.data.id['x']) self.data.edit_subset = [self.data.subsets[0]] self.client.xlog = True roi = PolygonalROI(vx=[1, 2, 3], vy=[2, 3, 4]) self.client.apply_roi(roi) state = self.data.subsets[0].subset_state assert isinstance(state, RangeSubsetState) np.testing.assert_allclose(state.lo, 7.3680629972807736) np.testing.assert_allclose(state.hi, 1000)
def on_selection(self, data, other=None): if data['type'] != self.selector: return if data['device']: with self.viewer.output_widget: region = data['device'] vx, vy = zip(*region) roi_2d = PolygonalROI(vx=vx, vy=vy) roi = Projected3dROI(roi_2d, self.projection_matrix) self.viewer.apply_roi(roi)
def release(self, event): if event.button == 1: if len(self.line_pos) > 0: vx, vy = np.array(self.line_pos).transpose() roi = Projected3dROI(roi_2d=PolygonalROI(vx, vy), projection_matrix=self.projection_matrix) self.apply_roi(roi) self.reset() self.viewer.toolbar.active_tool = None
def test_apply_roi(self): self.client.add_layer(self.data) self.client.set_component(self.data.id['y']) # bins are -7...-1 self.data.edit_subset = [self.data.subsets[0]] roi = PolygonalROI(vx=[-5.1, -4.5, -3.2], vy=[2, 3, 4]) self.client.apply_roi(roi) state = self.data.subsets[0].subset_state assert isinstance(state, RangeSubsetState) # range should expand to nearest bin edge assert state.lo == -6 assert state.hi == -3
def subset_lasso2d(self, x_att, y_att, lasso_x, lasso_y): """ Create a subset from a programmatic 2d lasso selection. Parameters ---------- x_att : `~glue.core.component_id.ComponentID` The attribute corresponding to the x values being selected. y_att : `~glue.core.component_id.ComponentID` The attribute corresponding to the x values being selected. lasso_x : iterable The x values of the lasso. lasso_y : iterable The y values of the lasso. """ roi = PolygonalROI(lasso_x, lasso_y) self.subset_roi([x_att, y_att], roi)
def test_polygonal_roi(self): xv = [1.3, 2, 3, 1.5, 0.5] yv = [10, 20.20, 30, 25, 17.17] subset_state = RoiSubsetState(self.data.pixel_component_ids[1], self.data.pixel_component_ids[0], PolygonalROI(xv, yv)) self.dc.new_subset_group(subset_state=subset_state, label='polygon') reg = self.data.get_selection_definition(format='astropy-regions') assert isinstance(reg, PolygonPixelRegion) assert_array_equal(reg.vertices.x, xv) assert_array_equal(reg.vertices.y, yv)
def subset_from_roi(self, att, roi, other_comp=None, other_att=None, coord='x'): """ Create a SubsetState object from an ROI. This encapsulates the logic for creating subset states with Components. See the documentation for CategoricalComponents for caveats involved with mixed-type plots. :param att: attribute name of this Component :param roi: an ROI object :param other_comp: The other Component for 2D ROIs :param other_att: The attribute name of the other Component :param coord: The orientation of this Component :param is_nested: True if this was passed from another Component. :return: A SubsetState (or subclass) object """ if coord not in ('x', 'y'): raise ValueError('coord should be one of x/y') other_coord = 'y' if coord == 'x' else 'x' if isinstance(roi, RangeROI): # The selection is either an x range or a y range if roi.ori == coord: # The selection applies to the current component lo, hi = roi.range() subset_state = RangeSubsetState(lo, hi, att) else: # The selection applies to the other component, so we delegate return other_comp.subset_from_roi(other_att, roi, other_comp=self, other_att=att, coord=other_coord) else: # The selection is polygon-like. Categorical components require # special care, so if the other component is categorical, we need to # delegate to CategoricalComponent.subset_from_roi. if isinstance(other_comp, CategoricalComponent): return other_comp.subset_from_roi(other_att, roi, other_comp=self, other_att=att, is_nested=True, coord=other_coord) else: subset_state = RoiSubsetState() subset_state.xatt = att subset_state.yatt = other_att x, y = roi.to_polygon() subset_state.roi = PolygonalROI(x, y) return subset_state
def subset_lasso2d(self, x, y, xvalues, yvalues): roi = PolygonalROI(xvalues, yvalues) self.subset_roi([x, y], roi)
def roi_to_subset_state(roi, x_att=None, y_att=None, x_comp=None, y_comp=None): """ Given a 2D ROI and attributes on the x and y axis, determine the corresponding subset state. """ if isinstance(roi, RangeROI): if roi.ori == 'x': att = x_att comp = x_comp else: att = y_att comp = y_comp if comp.categorical: return CategoricalROISubsetState.from_range(comp, att, roi.min, roi.max) else: return RangeSubsetState(roi.min, roi.max, att) elif x_comp.categorical or y_comp.categorical: if isinstance(roi, RectangularROI): # In this specific case, we can decompose the rectangular ROI into # two RangeROIs that are combined with an 'and' logical operation. range1 = XRangeROI(roi.xmin, roi.xmax) range2 = YRangeROI(roi.ymin, roi.ymax) subset1 = roi_to_subset_state(range1, x_att=x_att, x_comp=x_comp) subset2 = roi_to_subset_state(range2, y_att=y_att, y_comp=y_comp) return AndState(subset1, subset2) elif isinstance(roi, CategoricalROI): # The selection is categorical itself. We assume this is along the x axis return CategoricalROISubsetState(roi=roi, att=x_att) else: # The selection is polygon-like, which requires special care. if x_comp.categorical and y_comp.categorical: # For each category, we check which categories along the other # axis fall inside the polygon: selection = {} for code, label in enumerate(x_comp.categories): # Determine the coordinates of the points to check n_other = len(y_comp.categories) y = np.arange(n_other) x = np.repeat(code, n_other) # Determine which points are in the polygon, and which # categories these correspond to in_poly = roi.contains(x, y) categories = y_comp.categories[in_poly] if len(categories) > 0: selection[label] = set(categories) return CategoricalROISubsetState2D(selection, x_att, y_att) else: # If one of the components is not categorical, we treat this as # if each categorical component was mapped to a numerical value, # and at each value, we keep track of the polygon intersection # with the component. This will result in zero, one, or multiple # separate numerical ranges for each categorical value. # TODO: if we ever allow the category order to be changed, we # need to figure out how to update this! # We loop over each category and for each one we find the # numerical ranges selection = {} if x_comp.categorical: cat_comp = x_comp cat_att = x_att num_att = y_att x, y = roi.to_polygon() else: cat_comp = y_comp cat_att = y_att num_att = x_att y, x = roi.to_polygon() for code, label in enumerate(cat_comp.categories): # We determine all the numerical segments that represent the # ensemble of points in y that fall in the polygon # TODO: profile the following function segments = polygon_line_intersections(x, y, xval=code) if len(segments) > 0: selection[label] = segments return CategoricalMultiRangeSubsetState(selection, cat_att=cat_att, num_att=num_att) else: # The selection is polygon-like and components are numerical subset_state = RoiSubsetState() subset_state.xatt = x_att subset_state.yatt = y_att subset_state.roi = PolygonalROI(*roi.to_polygon()) return subset_state