diff --git a/src/odemis/acq/feature.py b/src/odemis/acq/feature.py index 893fd915aa..62f27a4e8a 100644 --- a/src/odemis/acq/feature.py +++ b/src/odemis/acq/feature.py @@ -77,7 +77,7 @@ def __init__(self): self.fib_fiducials : List[Target] = [] self.fib_surface_fiducial : Target = None - self.correlation_result: float = None + self.correlation_result = {} self.refractive_index_correction: bool = True self.fib_projected_pois: List[Target] = [] self.fib_projected_fiducials: List[Target] = [] @@ -92,9 +92,9 @@ def __init__(self): def reset_attributes(self): # rest of the attributes is set to none except the streams - self.correlation_result = None - self.fib_projected_pois = None - self.fib_projected_fiducials = None + self.correlation_result = {} + self.fib_projected_pois = [] + self.fib_projected_fiducials = [] class CryoFeature(object): @@ -130,19 +130,20 @@ def get_features_dict(features: List[CryoFeature]) -> Dict[str, str]: :return: list of JSON serializable features """ flist = [] - correlation_targets = {} for feature in features: # todo make a new function # TODO add stream names and other values + correlation_targets = {} if feature.correlation_targets: for key, ct_class in feature.correlation_targets.items(): - correlation_targets[key] = {} all_targets = [] + correlation_targets[key] = {} correlation_targets[key]['coordinates'] = [] correlation_targets[key]['index'] = [] correlation_targets[key]['type'] = [] correlation_targets[key]['name'] = [] correlation_targets[key]['fm_focus_position'] = [] + correlation_targets[key]['correlation_result'] = ct_class.correlation_result if ct_class.fm_fiducials: all_targets.append(ct_class.fm_fiducials) if ct_class.fm_pois: @@ -182,6 +183,8 @@ def __init__(self, *args, **kwargs): json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs) def object_hook(self, obj): + # if 'output' in obj: + # return obj if 'coordinates' in obj and not 'pos' in obj: # correlation_targets = obj['correlation_targets'] # correlation_targets_rest = self.decode_correlation_targets(obj) @@ -232,6 +235,7 @@ def decode_correlation_targets(self, correlation_targets_obj): types = ct_obj.get('type', []) names = ct_obj.get('name', []) fm_focus_positions = ct_obj.get('fm_focus_position', []) + # correlation_result = ct_obj.get('correlation_result', []) for i in range(len(coordinates)): @@ -255,6 +259,7 @@ def decode_correlation_targets(self, correlation_targets_obj): correlation_target.fm_fiducials.append(target) elif "POI" in names[i] and types[i] == "RegionOfInterest": correlation_target.fm_pois.append(target) + # correlation_target.correlation_result = correlation_result decoded_correlation_targets[key] = correlation_target diff --git a/src/odemis/acq/target.py b/src/odemis/acq/target.py index 5af23d948f..1af55847f5 100644 --- a/src/odemis/acq/target.py +++ b/src/odemis/acq/target.py @@ -26,7 +26,7 @@ class Target: def __init__(self, x,y,z, name:str, type:str, index: int, fm_focus_position: float, size: float = None ): - self.coordinates = model.ListVA((x, y, z), unit="px") + self.coordinates = model.ListVA((x, y, z), unit="m") self.type = model.StringVA(type) self.name = model.StringVA(name) # The index and target name are in sync. diff --git a/src/odemis/gui/cont/multi_point_correlation.py b/src/odemis/gui/cont/multi_point_correlation.py index e9d588a2ba..ede8df3d80 100644 --- a/src/odemis/gui/cont/multi_point_correlation.py +++ b/src/odemis/gui/cont/multi_point_correlation.py @@ -24,10 +24,12 @@ import copy import itertools import logging +import os import threading import time from enum import Enum +import numpy import wx # IMPORTANT: wx.html needs to be imported for the HTMLWindow defined in the XRC @@ -35,8 +37,10 @@ # This is not related to any particular wxPython version and is most likely permanent. import wx.html -from odemis.acq.align.tdct import get_optimized_z_gauss +from odemis.acq.align.tdct import get_optimized_z_gauss, _convert_das_to_numpy_stack, run_tdct_correlation, \ + get_reprojected_poi_coordinate from odemis.acq.feature import save_features, CorrelationTarget +from odemis.acq.target import Target from odemis.gui import conf from odemis.gui.model import TOOL_REGION_OF_INTEREST, TOOL_FIDUCIAL, TOOL_SURFACE_FIDUCIAL @@ -108,9 +112,6 @@ def __init__(self, tab_data, panel, tab, viewports): self.grid.EnableEditing(True) # TODO make sure before initializing this class, feature ana feature status is fixed ? (Controller) - # if DEBUG: - # self.correlation_target = CorrelationTarget() - # else: self.correlation_target = None # self._tab_data_model.main.currentFeature.correlation_targets[self._tab_data_model.main.currentFeature.status.value] = CorrelationTarget() @@ -125,16 +126,15 @@ def __init__(self, tab_data, panel, tab, viewports): self.previous_group = None # reset targets and current target and populate it based on the current feature + #TODO unsubscription when exiting the dialog box self._tab_data_model.main.targets = model.ListVA() self._tab_data_model.main.currentTarget = model.VigilantAttribute(None) self._tab_data_model.fib_surface_point = model.VigilantAttribute(None) - self._tab_data_model.main.targets.subscribe(self._on_target_changes, init=True) + self._tab_data_model.main.targets.subscribe(self._on_target_changes)#, init=True) self.current_target_coordinate_subscription = False - self._tab_data_model.main.currentTarget.subscribe(self._on_current_target_changes, init=True) - # self._tab_data_model.fib_surface_point.value.coordinates.subscribe(self._on_current_coordinates_fib_surface, - # init=True) - self._tab_data_model.fib_surface_point.subscribe(self._on_current_fib_surface, init=True) - # self.add_streams() + self._tab_data_model.main.currentTarget.subscribe(self._on_current_target_changes)#, init=True) + self._tab_data_model.fib_surface_point.subscribe(self._on_current_fib_surface)#, init=True) + if self._tab_data_model.main.currentFeature.value.correlation_targets: # load the values # to be outside correlation button maybe? @@ -142,7 +142,7 @@ def __init__(self, tab_data, panel, tab, viewports): self._tab_data_model.main.currentFeature.value.status.value] # load FM and FIB stream in the panel # TODO save the FM and FIB streams in the json file because fiducials are dependent on the dataarray - # self.correlation_target = correlation_target + if self.correlation_target.fm_streams: # Will load the streams from the given streams self.add_streams(self.correlation_target.fm_streams) @@ -164,32 +164,17 @@ def __init__(self, tab_data, panel, tab, viewports): targets.append(self.correlation_target.fm_pois) if self.correlation_target.fib_fiducials and self.correlation_target.fib_stream: targets.append(self.correlation_target.fib_fiducials) - if self.correlation_target.fib_projected_fiducials: - projected_points.append(self.correlation_target.fib_projected_fiducials) - if self.correlation_target.fib_projected_pois: - projected_points.append(self.correlation_target.fib_projected_pois) - - - - # TOdo not used as output gets reset + # TODO Temporarily commented, the correlation result is calculated quickly (no need to load it) + # if self.correlation_target.fib_projected_fiducials: + # projected_points.append(self.correlation_target.fib_projected_fiducials) + # if self.correlation_target.fib_projected_pois: + # projected_points.append(self.correlation_target.fib_projected_pois) # projected_points = list( # itertools.chain.from_iterable([x] if not isinstance(x, list) else x for x in projected_points)) # self._tab_data_model.projected_points = projected_points # flatten the list of lists targets = list( itertools.chain.from_iterable([x] if not isinstance(x, list) else x for x in targets)) - - # check the update TODO - # TODO same stream (?) limit to 1, is there a stream - # for target in targets: - # if "FIB" in target.name.value: - # p_pos = self.correlation_target.fib_stream.getPhysicalCoordinates( - # (target.coordinates.value[0], target.coordinates.value[1])) - # else: - # p_pos = self.correlation_target.fm_streams[0].getPhysicalCoordinates( - # (target.coordinates.value[0], target.coordinates.value[1])) - # target.coordinates.value[0:2] = p_pos - self._tab_data_model.main.targets.value = targets if self.correlation_target.fib_surface_fiducial: self._tab_data_model.fib_surface_point.value = self.correlation_target.fib_surface_fiducial @@ -203,7 +188,6 @@ def __init__(self, tab_data, panel, tab, viewports): self._tab_data_model.main.currentFeature.value.status.value] self.add_streams() - # self.add_streams() panel.fp_correlation_panel.Show(True) for vp in self._viewports: vp.canvas.Bind(wx.EVT_CHAR, self.on_char) @@ -241,7 +225,6 @@ def _on_fm_streams_change(self, stream_projections: ListVA) -> None: # self._update_stream_group_visibility(group_key) streams_list = self._tab_data_model.main.currentFeature.value.streams.value for key, indices in self.stream_groups.items(): - # self.correlation_target.fm_streams = [] for index in indices: stream = streams_list[index] # find stream controller for the stream @@ -253,8 +236,6 @@ def _on_fm_streams_change(self, stream_projections: ListVA) -> None: group_key = key if key == group_key: - # # TODO it should not be here, - # self.correlation_target.fm_streams.append(stream) ssc.stream_panel.set_visible(True) ssc.stream_panel.collapse(False) elif isinstance(stream, StaticFluoStream): @@ -313,56 +294,43 @@ def add_streams(self, streams: list = None) -> None: # target.coordinates.value[0:2] = p_pos self._tab_data_model.main.targets.value.extend(self.correlation_target.fib_fiducials) - # Get the data array - # stream.raw[0] - elif stream != self.correlation_target.fib_stream: self._tab.streambar_controller.removeStreamPanel(self.correlation_target.fib_stream) self.correlation_target.fib_stream = stream - return - else: streams_list = self._tab_data_model.main.currentFeature.value.streams.value stream_groups = {} for stream_index, stream in enumerate(streams_list): - current_shape = stream.raw[0].shape centre_pos = stream.raw[0].metadata[model.MD_POS] if isinstance(stream, StaticFluoStream): check_zindex = getattr(stream, "zIndex", None) # Use getattr to handle if zIndex is not present stream_name = stream.name.value - if check_zindex: # Create the key based on current shape, centre position, and zIndex key = (current_shape, centre_pos) - # Check if a set for this key already exists if key not in stream_groups: # Create a new set if the combination of shape, centre position, and zIndex is different stream_groups[key] = set() - # Check if there's already a stream with the same name in the set # If a stream with the same name exists, replace the previous index to keep the latest stream index indices_to_remove = set() for idx in stream_groups[key]: if streams_list[idx].name.value == stream_name: indices_to_remove.add(idx) - # Remove old indices with the same name stream_groups[key] -= indices_to_remove - # Add the new stream index to the set stream_groups[key].add(stream_index) - elif isinstance(stream, StaticFIBStream): # TODO check fib stream index key = (current_shape, centre_pos) if key not in stream_groups: stream_groups[key] = set() stream_groups[key].add(stream_index) - # add streams in stream controller # TODO add FIB stream logic first_valid_key = None @@ -387,18 +355,12 @@ def update_feature_correlation_target(self, surface_fiducial=False): # , fm_poi return if surface_fiducial: - # todo check if it has a value fib_surface_fiducial = self._tab_data_model.fib_surface_point.value self.correlation_target.fib_surface_fiducial = fib_surface_fiducial - # elif fm_poi: - # self.correlation_target.fm_pois = self._tab_data_model.fm_poi.value else: - # if True: fib_fiducials = [] fm_fiducials = [] self.correlation_target.fm_pois = [] - # fm_pois = [] - # fib_surface_fiducial = None for target in self._tab_data_model.main.targets.value: if "FIB" in target.name.value: fib_fiducials.append(target) @@ -417,26 +379,11 @@ def update_feature_correlation_target(self, surface_fiducial=False): # , fm_poi save_features(acq_conf.pj_last_path, self._tab_data_model.main.features.value) def check_correlation_conditions(self): - # Todo conditions change when initializing the correlation target - # should npt be here, duplicate - if not DEBUG: - if not self._tab_data_model.main.currentFeature.value: - return False - elif not self._tab_data_model.main.currentFeature.value.correlation_targets: - self._tab_data_model.main.currentFeature.value.correlation_targets[ - self._tab_data_model.main.currentFeature.value.status.value] = CorrelationTarget() - self.correlation_target = self._tab_data_model.main.currentFeature.value.correlation_targets[ - self._tab_data_model.main.currentFeature.value.status.value] - # draw - # for vp in self._viewports: - # # if vp.view.name.value == "SEM Overview": - # vp.canvas.update_drawing() - if self.correlation_target: if not DEBUG: if (len(self.correlation_target.fib_fiducials) >= 4 and len( self.correlation_target.fm_fiducials) >= 4 and self.correlation_target.fm_pois and len( - self._tab_data_model.views.value[0].stream_tree) > 0): + self._tab_data_model.views.value[0].stream_tree) > 0) and self.correlation_target.fib_stream: return True else: self.correlation_target.reset_attributes() @@ -494,35 +441,60 @@ def process_latest_change(self): self.do_correlation() with self.lock: - self.correlation_txt.SetLabel("Correlation RMS Deviation : Result") + rms = self.correlation_target.correlation_result["output"]["error"]["rms_error"] + self.correlation_txt.SetLabel(f"Correlation RMS Deviation : " + f"{rms}") self.is_processing = False # Mark that processing is complete # @call_in_wx_main def do_correlation(self): - # type the text - time.sleep(5) # Will slow down + fm_das = [stream.raw[0] for stream in self.correlation_target.fm_streams] + fm_image = _convert_das_to_numpy_stack(fm_das) + fib_da = self.correlation_target.fib_stream.raw[0] + fib_coords = [] + fm_coords = [] + poi_coords = [] + path = os.getcwd() + for fib_coord in self.correlation_target.fib_fiducials: + fib_coord = self.correlation_target.fm_streams[0].getPixelCoordinates_alt(fib_coord.coordinates.value[0:2]) + fib_coords.append(fib_coord) + fib_coords = numpy.array(fib_coords, dtype=numpy.float32) + for fm_coord in self.correlation_target.fm_fiducials: + fm_coord_2d = self.correlation_target.fm_streams[0].getPixelCoordinates_alt(fm_coord.coordinates.value[0:2]) + fm_coords.append([fm_coord_2d[0], fm_coord_2d[1], fm_coord.coordinates.value[2]]) + fm_coords = numpy.array(fm_coords, dtype=numpy.float32) + # Make fm coords and fib coords the same length by limiting the other + min_n = min(len(fib_coords), len(fm_coords)) + fib_coords = fib_coords[:min_n] + fm_coords = fm_coords[:min_n] + poi_coord = self.correlation_target.fm_pois + poi_coord_2d = self.correlation_target.fm_streams[0].getPixelCoordinates_alt(poi_coord.coordinates.value[0:2]) + poi_coords.append([poi_coord_2d[0], poi_coord_2d[1], poi_coord.coordinates.value[2]]) + poi_coords = numpy.array(poi_coords, dtype=numpy.float32) + # run the correlation + self.correlation_target.correlation_result = run_tdct_correlation(fib_coords=fib_coords,fm_coords=fm_coords,poi_coords=poi_coords, + fib_image=fib_da,fm_image=fm_image,path=path) + self._tab_data_model.projected_points = [] - for target in self._tab_data_model.main.targets.value: - if "FIB" in target.name.value: - # deep copy of target such that type can be changed without changing the target - target_copy = copy.deepcopy(target) - target_copy.type.value = "ProjectedPoints" - self._tab_data_model.projected_points.append(target_copy) - - if self._tab_data_model.projected_points: - self.correlation_target.fib_projected_fiducials = self._tab_data_model.projected_points - target_copy = copy.deepcopy(target_copy) - target_copy.type.value = "ProjectedPOI" - self._tab_data_model.projected_points.append(target_copy) - self.correlation_target.fib_projected_pois = [self._tab_data_model.projected_points[-1]] + self.correlation_target.fib_projected_fiducials = [] + points = self.correlation_target.correlation_result['output']['error']['reprojected_3d'] + for n, i in enumerate(points[0]): + p_pos = self.correlation_target.fib_stream.getPhysicalCoordinates((points[0][n], points[1][n])) + target = Target(x=p_pos[0], y=p_pos[1], z=0, name="PP" +str(n+1), type="ProjectedPoints", index=n+1, + fm_focus_position=0) + self._tab_data_model.projected_points.append(target) + self.correlation_target.fib_projected_fiducials.append(target) + + projected_poi = get_reprojected_poi_coordinate(self.correlation_target.correlation_result) + projected_poi_target = Target(x=projected_poi[0], y=projected_poi[1], z=0, name= "PPOI", type= "ProjectedPOI", index=1, + fm_focus_position=0) + self._tab_data_model.projected_points.append(projected_poi_target) + self.correlation_target.fib_projected_pois = [projected_poi_target] for vp in self._viewports: if vp.view.name.value == "SEM Overview": vp.canvas.update_drawing() - # self._tab_data_model.main.selected_target_type.value = "ProjectedPoints" - # self._tab_data_model.tool.value = TOOL_FIDUCIAL - def on_char(self, evt: wx.Event) -> None: """handle key presses :param evt: (wx.Event) the event""" @@ -555,7 +527,6 @@ def on_delete_row(self, event): return selected_rows = self.grid.GetSelectedRows() - # row_evt = event.GetRow() if selected_rows: for row in selected_rows: self.grid.DeleteRows(pos=row, numRows=1, updateLabels=True) @@ -647,6 +618,7 @@ def on_cell_changing(self, event): wx.OK | wx.ICON_ERROR) event.Veto() # Prevent the change return + elif col_name in [GridColumns.X.name, GridColumns.Y.name, GridColumns.Z.name]: try: if col_name == GridColumns.X.name: @@ -709,13 +681,12 @@ def _on_current_coordinates_changes(self, coordinates): self.current_target_coordinate_subscription = False temp_check = False for row in range(self.grid.GetNumberRows()): - # TODO Check grid cells are changing - # TODO add changes due to Z + # TODO Connect the z with MIP and Refine Z targetting if self.selected_target_in_grid(target, row): if "FM" or "POI" in target.name.value: pixel_coords = self.correlation_target.fm_streams[0].getPixelCoordinates_alt( (target.coordinates.value[0], target.coordinates.value[1])) - if int(self.grid.GetCellValue(row, GridColumns.Z.value)) != int(target.coordinates.value[2]): + if float(self.grid.GetCellValue(row, GridColumns.Z.value)) != float(target.coordinates.value[2]): temp_check = True self.grid.SetCellValue(row, GridColumns.Z.value, f"{target.coordinates.value[2]:.{GRID_PRECISION}f}") else: @@ -747,10 +718,6 @@ def _on_target_changes(self, targets): for index in indices: stream = streams_list[index] self.correlation_target.fm_streams.append(stream) - # find stream controller for the stream - # ssc = next( - # (sc for sc in self._tab.streambar_controller.stream_controllers - # if sc.stream == stream), None) else: for index in indices: stream = streams_list[index] diff --git a/src/odemis/gui/main_xrc.py b/src/odemis/gui/main_xrc.py index ab08429064..9e913e834f 100644 --- a/src/odemis/gui/main_xrc.py +++ b/src/odemis/gui/main_xrc.py @@ -114,10 +114,9 @@ def __init__(self, parent): self.scr_win_right = xrc.XRCCTRL(self, "scr_win_right") self.fp_correlation_panel = xrc.XRCCTRL(self, "fp_correlation_panel") self.pnl_correlation = xrc.XRCCTRL(self, "pnl_correlation") + self.btn_delete_row = xrc.XRCCTRL(self, "btn_delete_row") self.btn_z_targeting = xrc.XRCCTRL(self, "btn_z_targeting") self.table_grid = xrc.XRCCTRL(self, "table_grid") - self.btn_delete_row = xrc.XRCCTRL(self, "btn_delete_row") - self.btn_refractive_index = xrc.XRCCTRL(self, "btn_refractive_index") self.txt_correlation_rms = xrc.XRCCTRL(self, "txt_correlation_rms") self.fp_correlation_streams = xrc.XRCCTRL(self, "fp_correlation_streams") self.pnl_correlation_streams = xrc.XRCCTRL(self, "pnl_correlation_streams") @@ -1803,19 +1802,6 @@ def __init_resources(): wxVERTICAL - - - - - - - - - - - - - @@ -1825,17 +1811,29 @@ def __init_resources(): 16 + wxALL|wxEXPAND + 10 - - - 1 + + wxHORIZONTAL + wxALL|wxEXPAND + 10 + + + + + + + + + @@ -1843,6 +1841,7 @@ def __init_resources(): 1 wxLEFT + 10 diff --git a/src/odemis/gui/xmlh/resources/dialog_correlation_tdct.xrc b/src/odemis/gui/xmlh/resources/dialog_correlation_tdct.xrc index 08674d03f8..f347159540 100644 --- a/src/odemis/gui/xmlh/resources/dialog_correlation_tdct.xrc +++ b/src/odemis/gui/xmlh/resources/dialog_correlation_tdct.xrc @@ -82,19 +82,6 @@ wxVERTICAL - - - - - - - - - - - - - @@ -104,17 +91,29 @@ 16 + wxALL|wxEXPAND + 10 - - - 1 + + wxHORIZONTAL + wxALL|wxEXPAND + 10 + + + + + + + + + @@ -122,6 +121,7 @@ 1 wxLEFT + 10