diff --git a/src/decolace/acquisition/acquisition_area.py b/src/decolace/acquisition/acquisition_area.py index ccfcee6..fc83b5d 100644 --- a/src/decolace/acquisition/acquisition_area.py +++ b/src/decolace/acquisition/acquisition_area.py @@ -70,10 +70,10 @@ def _hexagonal_cover(polygon, radius): def rectangular_cover(polygon, radius): # Define a regular hexagon with side length equal to the sphere radius - sqaure = Polygon( + square = Polygon( [ (-radius,radius), - {-radius,-radius}, + (-radius,-radius), (radius,-radius), (radius,radius), (-radius,radius) @@ -84,8 +84,8 @@ def rectangular_cover(polygon, radius): minx, miny, maxx, maxy = polygon.bounds # Compute the offset required to center the hexagonal grid within the bounding box - dx = sqaure.bounds[2] - square.bounds[0] - dy = sqaure.bounds[3] - sqaure.bounds[1] + dx = square.bounds[2] - square.bounds[0] + dy = square.bounds[3] - square.bounds[1] offsetx = (maxx - minx - dx) / 2 offsety = (maxy - miny - dy) / 2 @@ -105,9 +105,9 @@ def rectangular_cover(polygon, radius): minx + offsetx + (i * 2 * radius) - + (j % 2) * radius + ) - if polygon.intersects(affinity.translate(sqaure, xoff=x, yoff=y)): + if polygon.intersects(affinity.translate(square, xoff=x, yoff=y)): centers.append((x, y)) return np.array(centers) @@ -177,7 +177,7 @@ def load_from_disk(self): def initialize_from_napari( - self, map_navigator_id: int, center_coordinate, corner_coordinates + self, map_navigator_id: int, center_coordinate, corner_coordinates, affine= None ): serialem = connect_sem() serialem.LoadOtherMap(map_navigator_id, "A") @@ -220,13 +220,19 @@ def initialize_from_napari( ) self.state.corner_positions_image = np.array(corner_coordinates) - def calculate_acquisition_positions_from_napari(self, beam_radius, add_overlap=0.05): + def calculate_acquisition_positions_from_napari(self, beam_radius, add_overlap=0.05, use_square_beam=False): polygon = Polygon(self.state.corner_positions_specimen) - center = _hexagonal_cover( - polygon, beam_radius * (1 - add_overlap) - ) + + if use_square_beam: + center = rectangular_cover( + polygon, beam_radius * (1 - add_overlap) + ) + else: + center = _hexagonal_cover( + polygon, beam_radius * (1 - add_overlap) + ) self.state.acquisition_positions = center self.state.positions_acquired = np.zeros( diff --git a/src/decolace/acquisition/cli_acquisition.py b/src/decolace/acquisition/cli_acquisition.py index feb1d6a..01661ce 100644 --- a/src/decolace/acquisition/cli_acquisition.py +++ b/src/decolace/acquisition/cli_acquisition.py @@ -369,10 +369,20 @@ def setup_areas( import napari from napari.layers import Shapes from magicgui import magicgui + viewer = napari.view_image(np.array(maps)) + + # Now test if there are already areas set up + existing_acquisiton_positions = False + if len(session_o.active_grid.state.acquisition_areas) > 0: + if sum([len(aa.state.acquisition_positions) for aa in session_o.active_grid.acquisition_areas]) > 0: + existing_acquisiton_positions = True + + if existing_acquisiton_positions: + affine = session_o.active_grid.draw_acquisition_positions_into_napari(viewer, map_navids, session_o.state.beam_radius) @magicgui(shapes={'label': 'Setup areas'}) - def my_widget(shapes: Shapes, use_rectangular_grid: bool = False): + def my_widget(shapes: Shapes, use_square_beam: bool = False): points = [] areas = shapes.data for area in areas: @@ -382,14 +392,14 @@ def my_widget(shapes: Shapes, use_rectangular_grid: bool = False): name = f"area{len(session_o.active_grid.state.acquisition_areas)+2}" polygon = shapely.geometry.Polygon(area[:,1:3]) aa = AcquisitionAreaSingle(name,Path(session_o.active_grid.directory,name).as_posix(),tilt=session_o.active_grid.state.tilt) - aa.initialize_from_napari(map_navids[int(map_id)], [polygon.centroid.y, polygon.centroid.x], area[:,1:3]) - aa.calculate_acquisition_positions_from_napari(beam_radius=session_o.state.beam_radius) + aa.initialize_from_napari(map_navids[int(map_id)], [polygon.centroid.y, polygon.centroid.x], area[:,1:3],affine=affine) + aa.calculate_acquisition_positions_from_napari(beam_radius=session_o.state.beam_radius, use_square_beam=use_square_beam) aa.write_to_disk() session_o.active_grid.state.acquisition_areas.append([aa.name,aa.directory]) + session_o.active_grid.acquisition_areas.append(aa) session_o.active_grid.write_to_disk() session_o.active_grid.save_navigator() - - + session_o.active_grid.draw_acquisition_positions_into_napari(viewer, map_navids, session_o.state.beam_radius,use_square_beam=use_square_beam) viewer.window.add_dock_widget(my_widget) napari.run() diff --git a/src/decolace/acquisition/grid.py b/src/decolace/acquisition/grid.py index 3833ae0..b076eb2 100644 --- a/src/decolace/acquisition/grid.py +++ b/src/decolace/acquisition/grid.py @@ -13,7 +13,7 @@ from .acquisition_area import AcquisitionAreaSingle class GridState(BaseModel): - acquisition_areas: List[str] = [] + acquisition_areas: List[List] = [] desired_defocus: float = -1.0 tilt: float = 0.0 view_file: Optional[str] = None @@ -58,6 +58,7 @@ def load_from_disk(self): raise (FileNotFoundError("Couldn't find saved files")) most_recent = sorted(potential_files)[-1] self.state = np.load(most_recent, allow_pickle=True).item() + #self.state = GridState(**self.state) self.acquisition_areas = [] for area in self.state.acquisition_areas: self.acquisition_areas.append(AcquisitionAreaSingle(area[0], Path(self.directory) / area[0])) @@ -172,3 +173,48 @@ def start_acquisition(self, initial_defocus, progress_callback=None): aa.acquire(initial_defocus=initial_defocus, progress_callback=callback,initial_beamshift=initial_beamshift) aa.write_to_disk() serialem.SetColumnOrGunValve(0) + + def draw_acquisition_positions_into_napari(self, viewer, map_navids, beam_radius, use_square_beam=False): + from skimage.transform import AffineTransform + write_to_disktext = { + 'string': '{order}', + 'size': 10, + 'color': 'white', + 'translation': np.array([0, 0]), + } + order = [[] for a in map_navids] + positions = [[] for a in map_navids] + corner_positions = [] + for i, aa in enumerate(self.acquisition_areas): + + map_index = map_navids.index(aa.state.navigator_map_index) + + # Get the affine matrix converting aa.state.corner_positions_specimen into aa.state.corner_positions_image + affine = AffineTransform() + affine.estimate(aa.state.corner_positions_specimen, aa.state.corner_positions_image) + + + pos = affine(aa.state.acquisition_positions[:, ::1]) + # Concatenat i to pos along axis 1 + pos = np.concatenate((np.ones((len(pos), 1)) * map_index, pos), axis=1) + # Find position of aa.navigator_map_index in nav_ids + + positions[map_index].extend(pos) + order[map_index].extend(np.array(range(len(aa.state.acquisition_positions)))) + pos = np.concatenate(positions, axis = 0) + order = np.concatenate(order, axis = 0) + + if use_square_beam: + symbol='square' + else: + symbol='o' + viewer.add_points( + pos, + name="exposures", + symbol=symbol, + size=beam_radius * 2 * affine.scale[0], + face_color="#00000000", + features={"order":np.array(order)}, + text=write_to_disktext + ) + return affine diff --git a/src/decolace/acquisition/session.py b/src/decolace/acquisition/session.py index 593a096..681056c 100644 --- a/src/decolace/acquisition/session.py +++ b/src/decolace/acquisition/session.py @@ -12,7 +12,7 @@ from .grid import grid class SessionState(BaseModel): - grids: List[grid] = [] + grids: List[List] = [] microscope_settings: dict = {} active_grid: Optional[int] = None beam_radius: Optional[float] = None @@ -50,6 +50,7 @@ def load_from_disk(self): most_recent = sorted(potential_files)[-1] #print(f"Loading file {most_recent}") self.state = np.load(most_recent, allow_pickle=True).item() + #self.state = SessionState(**self.state) for grid_info in self.state.grids: if not Path(grid_info[1]).exists(): grid_info[1] = Path(self.directory) / Path(grid_info[1]).parts[-1]