class CoordinatesWidgetUI(QWidget): sig_cast_mask_coordinates_to_dmd = pyqtSignal(dict) sig_cast_mask_coordinates_to_galvo = pyqtSignal(list) sig_start_registration = pyqtSignal() sig_finished_registration = pyqtSignal() sig_cast_camera_image = pyqtSignal(np.ndarray) MessageBack = pyqtSignal(str) def __init__(self, parent=None, *args, **kwargs): super().__init__(*args, **kwargs) self.main_application = parent self.init_gui() self.sig_to_calling_widget = {} self.untransformed_mask_dict = {} # HamamatsuUI.CameraUI.signal_SnapImg.connect(self.receive_image_from_camera) def closeEvent(self, event): try: self.DMD except: pass else: self.DMD.disconnect_DMD() QtWidgets.QApplication.quit() event.accept() #%% def init_gui(self): self.setWindowTitle("Coordinate control") self.layout = QGridLayout() self.setMinimumSize(1250, 1000) self.setLayout(self.layout) self.image_mask_stack = QTabWidget() #---------------------------ROIs win---------------------------------- self.selection_view = DrawingWidget(self) self.selection_view.setMinimumWidth(900) self.selection_view.enable_drawing(True) # self.selection_view.getView().setLimits(xMin = 0, xMax = 2048, yMin = 0, yMax = 2048, \ # minXRange = 2048, minYRange = 2048, maxXRange = 2048, maxYRange = 2048) self.selection_view.ui.roiBtn.hide() self.selection_view.ui.menuBtn.hide() self.selection_view.ui.normGroup.hide() self.selection_view.ui.roiPlot.hide() # self.selection_view.setImage(plt.imread('CoordinatesManager/Registration_Images/StageRegistration/Distance200_Offset0/A1.png')) #---------------------------Mask win---------------------------------- self.mask_view = SquareImageView() self.mask_view.getView().setLimits(xMin = 0, xMax = 2048, yMin = 0, yMax = 2048, \ minXRange = 2048, minYRange = 2048, maxXRange = 2048, maxYRange = 2048) self.mask_view.ui.roiBtn.hide() self.mask_view.ui.menuBtn.hide() self.mask_view.ui.normGroup.hide() self.mask_view.ui.roiPlot.hide() self.mask_view.ui.histogram.hide() #-------------------------MAsk RCNN----------------------------------- MLmaskviewBox = QWidget() MLmaskviewBoxLayout = QGridLayout() self.Matdisplay_Figure = Figure() self.Matdisplay_Canvas = FigureCanvas(self.Matdisplay_Figure) # self.Matdisplay_Canvas.setFixedWidth(900) # self.Matdisplay_Canvas.setFixedHeight(900) self.Matdisplay_Canvas.mpl_connect('button_press_event', self._onclick) self.Matdisplay_toolbar = NavigationToolbar(self.Matdisplay_Canvas, self) MLmaskviewBoxLayout.addWidget(self.Matdisplay_toolbar, 0, 0) MLmaskviewBoxLayout.addWidget(self.Matdisplay_Canvas, 1, 0) MLmaskviewBox.setLayout(MLmaskviewBoxLayout) self.image_mask_stack.addTab(self.selection_view, 'Select') self.image_mask_stack.addTab(self.mask_view, 'Mask') self.image_mask_stack.addTab(MLmaskviewBox, 'Mask-RCNN') self.layout.addWidget(self.image_mask_stack, 0, 0, 4, 7) # ---------------------- Mask generation Container -------------- self.maskGeneratorContainer = roundQGroupBox() self.maskGeneratorContainer.setFixedSize(320, 220) self.maskGeneratorContainer.setTitle("Mask generator") self.maskGeneratorContainerLayout = QGridLayout() self.maskGeneratorLayout = QGridLayout() self.maskGeneratorContainer.setLayout( self.maskGeneratorContainerLayout) # self.loadMaskFromFileButton = QPushButton('Load mask') # self.loadMaskFromFileButton.clicked.connect(self.load_mask_from_file) self.addRoiButton = QPushButton("Add ROI") self.createMaskButton = QPushButton("Add mask") self.deleteMaskButton = QPushButton("Delete mask") self.removeSelectionButton = cleanButton() self.removeSelectionButton.setFixedHeight(25) self.removeSelectionButton.setToolTip("Remove rois and output signals") self.addRoiButton.clicked.connect(self.add_polygon_roi) self.createMaskButton.clicked.connect(self.create_mask) self.deleteMaskButton.clicked.connect(self.delete_mask) self.removeSelectionButton.clicked.connect(self.remove_selection) self.maskGeneratorContainerLayout.addWidget(self.addRoiButton, 0, 0) self.maskGeneratorContainerLayout.addWidget(self.createMaskButton, 2, 0) self.maskGeneratorContainerLayout.addWidget(self.deleteMaskButton, 2, 2) self.maskGeneratorContainerLayout.addWidget(self.removeSelectionButton, 2, 3) # self.maskGeneratorContainerLayout.addWidget(self.loadMaskFromFileButton, 2, 1) self.clearRoiButton = QPushButton("Clear ROI") self.clearRoiButton.clicked.connect( lambda: self.selection_view.clear_rois()) self.maskGeneratorContainerLayout.addWidget(self.clearRoiButton, 0, 1) self.maskGeneratorContainerLayout.addWidget(QLabel("Mask index:"), 1, 0) self.mask_index_spinbox = QSpinBox() self.mask_index_spinbox.setMinimum(1) self.mask_index_spinbox.setValue(1) self.maskGeneratorContainerLayout.addWidget(self.mask_index_spinbox, 1, 1) self.previous_mask_button = QPushButton() self.previous_mask_button.setStyleSheet( "QPushButton {color:white;background-color: #FFCCE5;}" "QPushButton:hover:!pressed {color:white;background-color: #CCFFFF;}" ) self.previous_mask_button.setToolTip( "Click arrow to enable WASD keyboard control") self.previous_mask_button.setFixedWidth(60) self.previous_mask_button.setIcon(QIcon('./Icons/LeftArrow.png')) self.previous_mask_button.clicked.connect( lambda: self.show_mask_with_index(-1)) self.maskGeneratorContainerLayout.addWidget(self.previous_mask_button, 1, 2) self.next_mask_button = QPushButton() self.next_mask_button.setStyleSheet( "QPushButton {color:white;background-color: #FFCCE5;}" "QPushButton:hover:!pressed {color:white;background-color: #CCFFFF;}" ) self.next_mask_button.setToolTip( "Click arrow to enable WASD keyboard control") self.next_mask_button.setFixedWidth(60) self.next_mask_button.setIcon(QIcon('./Icons/RightArrow.png')) self.next_mask_button.clicked.connect( lambda: self.show_mask_with_index(1)) self.maskGeneratorContainerLayout.addWidget(self.next_mask_button, 1, 3) self.selectionOptionsContainer = roundQGroupBox() self.selectionOptionsContainer.setTitle('Options') self.selectionOptionsLayout = QGridLayout() self.fillContourButton = QCheckBox() self.invertMaskButton = QCheckBox() self.thicknessSpinBox = QSpinBox() self.thicknessSpinBox.setRange(1, 25) self.selectionOptionsLayout.addWidget(QLabel('Fill contour:'), 0, 0) self.selectionOptionsLayout.addWidget(self.fillContourButton, 0, 1) self.selectionOptionsLayout.addWidget(QLabel('Invert mask:'), 1, 0) self.selectionOptionsLayout.addWidget(self.invertMaskButton, 1, 1) self.selectionOptionsLayout.addWidget(QLabel('Thickness:'), 2, 0) self.selectionOptionsLayout.addWidget(self.thicknessSpinBox, 2, 1) lasers = ['640', '532', '488'] self.transform_for_laser_menu = QListWidget() self.transform_for_laser_menu.addItems(lasers) self.transform_for_laser_menu.setFixedHeight(48) self.transform_for_laser_menu.setFixedWidth(65) self.transform_for_laser_menu.setCurrentRow(0) self.selectionOptionsLayout.addWidget(QLabel('To be used with laser:'), 0, 2) self.selectionOptionsLayout.addWidget(self.transform_for_laser_menu, 1, 2) self.selectionOptionsContainer.setLayout(self.selectionOptionsLayout) self.maskGeneratorContainerLayout.addWidget( self.selectionOptionsContainer, 3, 0, 2, 3) #----------------------------Mask-RCNN-------------------------------- self.MLOptionsContainer = roundQGroupBox() self.MLOptionsContainer.setTitle('Mask-RCNN') self.MLOptionsContainerLayout = QGridLayout() self.init_ML_button = QPushButton('Init. ML', self) self.MLOptionsContainerLayout.addWidget(self.init_ML_button, 0, 0) self.init_ML_button.clicked.connect( lambda: self.run_in_thread(self.init_ML)) self.run_ML_button = QPushButton('Analysis', self) self.MLOptionsContainerLayout.addWidget(self.run_ML_button, 1, 0) self.run_ML_button.clicked.connect(self.run_ML_onImg_and_display) self.generate_MLmask_button = QPushButton('To ROIs', self) self.MLOptionsContainerLayout.addWidget(self.generate_MLmask_button, 2, 0) self.generate_MLmask_button.clicked.connect( lambda: self.run_in_thread(self.generate_MLmask)) self.MLOptionsContainer.setLayout(self.MLOptionsContainerLayout) self.maskGeneratorContainerLayout.addWidget(self.MLOptionsContainer, 3, 3, 2, 1) self.layout.addWidget(self.maskGeneratorContainer, 0, 8, 1, 3) self.DMDWidget = DMDWidget.DMDWidget() self.layout.addWidget(self.DMDWidget, 1, 8, 1, 3) """-------------------------------------------------------------------- # Singal sent out from DMDWidget to ask for mask generated here. # And then the generated roi list is sent back to function:receive_mask_coordinates in DMDWidget. # -------------------------------------------------------------------- """ self.DMDWidget.sig_request_mask_coordinates.connect( lambda: self.cast_mask_coordinates('dmd')) self.sig_cast_mask_coordinates_to_dmd.connect( self.DMDWidget.receive_mask_coordinates) self.DMDWidget.sig_start_registration.connect( lambda: self.sig_start_registration.emit()) self.DMDWidget.sig_finished_registration.connect( lambda: self.sig_finished_registration.emit()) #---------------------------Galvo control----------------------------- self.GalvoWidget = GalvoWidget.GalvoWidget() self.GalvoWidget.setFixedWidth(200) self.GalvoWidget.setFixedHeight(180) self.layout.addWidget(self.GalvoWidget, 2, 8, 2, 1) self.GalvoWidget.sig_request_mask_coordinates.connect( lambda: self.cast_mask_coordinates('galvo')) self.sig_cast_mask_coordinates_to_galvo.connect( self.GalvoWidget.receive_mask_coordinates) self.GalvoWidget.sig_start_registration.connect( lambda: self.sig_start_registration.emit()) self.GalvoWidget.sig_finished_registration.connect( lambda: self.sig_finished_registration.emit()) #-------------------------Manual registration------------------------- self.ManualRegistrationWidget = ManualRegistration.ManualRegistrationWidget( ) self.ManualRegistrationWidget.setFixedWidth(100) self.ManualRegistrationWidget.sig_request_camera_image.connect( self.cast_camera_image) self.sig_cast_camera_image.connect( self.ManualRegistrationWidget.receive_camera_image) self.layout.addWidget(self.ManualRegistrationWidget, 2, 9, 1, 1) #-------------------------Stage collect------------------------------- self.StageRegistrationWidget = StageRegistrationWidget.StageWidget() self.StageRegistrationWidget.setFixedWidth(100) self.layout.addWidget(self.StageRegistrationWidget, 3, 9, 1, 1) #===================================================================== #%% def cast_transformation_to_DMD(self, transformation, laser): self.DMDWidget.transform[laser] = transformation self.DMDWidget.save_transformation() def cast_transformation_to_galvos(self, transformation): self.GalvoWidget.transform = transformation self.GalvoWidget.save_transformation() def cast_camera_image(self): """ Send out the image in the image view to ManualRegistration """ image = self.selection_view.image if type(image) == np.ndarray: self.sig_cast_camera_image.emit(image) def receive_image_from_camera(self, snap_from_camera): """ Receive the emitted snap image singal from camera. """ self.selection_view.setImage(snap_from_camera) def cast_mask_coordinates(self, receiver): """ Upon receiving mask rois request signal from DMD or galvo widget, generate list signal including list of rois from the current ROIitems in "Select" Drawwidget and send it back to the calling widget. Parameters ---------- receiver : string. Specifies which widget is requesting. """ if receiver == 'dmd': self.sig_cast_mask_coordinates_to_dmd.emit( self.sig_to_calling_widget) else: self.sig_cast_mask_coordinates_to_galvo.emit( self.sig_to_calling_widget) def get_list_of_rois(self): """ Return the list of rois from the current ROIitems in "Select" Drawwidget. """ view = self.selection_view list_of_rois = [] for roi in view.roilist: roi_handle_positions = roi.getLocalHandlePositions() roi_origin = roi.pos() for idx, pos in enumerate(roi_handle_positions): roi_handle_positions[idx] = pos[1] num_vertices = len(roi_handle_positions) vertices = np.zeros([num_vertices, 2]) for idx, vertex in enumerate(roi_handle_positions): vertices[idx, :] = np.array( [vertex.x() + roi_origin.x(), vertex.y() + roi_origin.y()]) list_of_rois.append(vertices) return list_of_rois def create_mask(self): """ Create untransformed binary mask, sent out the signal to DMD widget for further transformation. Returns ------- None. """ flag_fill_contour = self.fillContourButton.isChecked() flag_invert_mode = self.invertMaskButton.isChecked() contour_thickness = self.thicknessSpinBox.value() target_laser = self.transform_for_laser_menu.selectedItems()[0].text() # Get the list of rois from the current ROIitems in "Select" Drawwidget. list_of_rois = self.get_list_of_rois() # Signal to mask requesting widget. current_mask_sig = [ list_of_rois, flag_fill_contour, contour_thickness, flag_invert_mode, target_laser ] #---- This is the roi list sent to DMD to generate final stack of masks.---- self.sig_to_calling_widget["mask_{}".format( self.mask_index_spinbox.value())] = current_mask_sig # Show the untransformed mask self.current_mask = ProcessImage.CreateBinaryMaskFromRoiCoordinates(list_of_rois, \ fill_contour = flag_fill_contour, \ contour_thickness = contour_thickness, \ invert_mask = flag_invert_mode) self.untransformed_mask_dict["mask_{}".format( self.mask_index_spinbox.value())] = self.current_mask self.mask_view.setImage(self.current_mask) def show_mask_with_index(self, direction): """ If direction == 1, then show next mask, elif direction == -1, show previous one. Parameters ---------- direction : TYPE -1: Previous; 1: Next. Returns ------- None. """ try: self.mask_index_spinbox.setValue(self.mask_index_spinbox.value() + direction) self.mask_view.setImage( self.untransformed_mask_dict["mask_{}".format( self.mask_index_spinbox.value())]) except: print("show_mask failed.") def delete_mask(self): """ Remove the last mask from the list. """ del self.sig_to_calling_widget["mask_{}".format( self.mask_index_spinbox.value())] del self.untransformed_mask_dict["mask_{}".format( self.mask_index_spinbox.value())] def remove_selection(self): self.sig_to_calling_widget = {} self.selection_view.clear_rois() def add_polygon_roi(self): view = self.selection_view x = (view.getView().viewRect().x()) * 0.3 y = (view.getView().viewRect().y()) * 0.3 a = (view.getView().viewRect().width() + x) * 0.3 b = (view.getView().viewRect().height() + y) * 0.3 c = (view.getView().viewRect().width() + x) * 0.7 d = (view.getView().viewRect().height() + y) * 0.7 polygon_roi = pg.PolyLineROI([[a, b], [c, b], [c, d], [a, d]], pen=view.pen, closed=True, movable=True, removable=True) view.getView().addItem(polygon_roi) view.append_to_roilist(polygon_roi) def load_mask_from_file(self): """ Open a file manager to browse through files, load image file """ self.loadFileName, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Select file', './CoordinateManager/Images/', "Image files (*.jpg *.tif *.png)") try: image = plt.imread(self.loadFileName) self.current_mask = image self.mask_view.setImage(self.current_mask) self.untransformed_mask_dict["mask_{}".format( self.mask_index_spinbox.value())] = self.current_mask except: print('fail to load file.') # ============================================================================= # MaskRCNN detection part # ============================================================================= def init_ML(self): # Initialize the detector instance and load the model. self.ProcessML = ProcessImageML() self.MessageBack.emit("Mask-RCNN environment configured.") def run_ML_onImg_and_display(self): # self.ResetLiveImgView() # For testing # snap_from_camera = plt.imread\ # (r"M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\Data\Vidya\Imaging\Octoscope\2020-10-08 Archon lib V7\V7_gfp_5v_telescope_2.TIF") # self.selection_view.setImage(snap_from_camera) """Run MaskRCNN on input image""" self.Matdisplay_Figure.clear() self.Matdisplay_Figure_axis = self.Matdisplay_Figure.add_subplot(111) self.MLtargetedImg = self.selection_view.image print(self.MLtargetedImg.shape) # Depends on show_mask or not, the returned figure will be input raw image with mask or not. self.MLresults = self.ProcessML.DetectionOnImage( self.MLtargetedImg, axis=self.Matdisplay_Figure_axis, show_mask=False, show_bbox=False) self.Mask = self.MLresults['masks'] self.Label = self.MLresults['class_ids'] self.Score = self.MLresults['scores'] self.Bbox = self.MLresults['rois'] self.SelectedCellIndex = 0 self.NumCells = int(len(self.Label)) self.selected_ML_Index = [] self.selected_cells_infor_dict = {} self.Matdisplay_Figure_axis.imshow(self.MLtargetedImg.astype(np.uint8)) self.Matdisplay_Figure.tight_layout() self.Matdisplay_Canvas.draw() #%% # ============================================================================= # Configure click event to add clicked cell mask # ============================================================================= def _onclick(self, event): """Highlights the cell selected in the figure by the user when clicked on""" if self.NumCells > 0: ShapeMask = np.shape(self.Mask) # get coorinates at selected location in image coordinates if event.xdata == None or event.ydata == None: return xcoor = min(max(int(event.xdata), 0), ShapeMask[1]) ycoor = min(max(int(event.ydata), 0), ShapeMask[0]) # search for the mask coresponding to the selected cell for EachCell in range(self.NumCells): if self.Mask[ycoor, xcoor, EachCell]: self.SelectedCellIndex = EachCell break # highlight selected cell if self.SelectedCellIndex not in self.selected_ML_Index: # Get the selected cell's contour coordinates and mask patch self.contour_verts, self.Cell_patch = self.get_cell_polygon( self.Mask[:, :, self.SelectedCellIndex]) self.Matdisplay_Figure_axis.add_patch(self.Cell_patch) self.Matdisplay_Canvas.draw() self.selected_ML_Index.append(self.SelectedCellIndex) self.selected_cells_infor_dict['cell{}_verts'.format( str(self.SelectedCellIndex))] = self.contour_verts else: # If click on the same cell self.Cell_patch.remove() self.Matdisplay_Canvas.draw() self.selected_ML_Index.remove(self.SelectedCellIndex) self.selected_cells_infor_dict.pop('cell{}_verts'.format( str(self.SelectedCellIndex))) def get_cell_polygon(self, mask): # Mask Polygon # Pad to ensure proper polygons for masks that touch image edges. padded_mask = np.zeros((mask.shape[0] + 2, mask.shape[1] + 2), dtype=np.uint8) padded_mask[1:-1, 1:-1] = mask contours = find_contours(padded_mask, 0.5) for verts in contours: # Subtract the padding and flip (y, x) to (x, y) verts = np.fliplr(verts) - 1 contour_polygon = mpatches.Polygon( verts, facecolor=self.random_colors(1)[0]) return contours, contour_polygon def random_colors(self, N, bright=True): """ Generate random colors. To get visually distinct colors, generate them in HSV space then convert to RGB. """ brightness = 1.0 if bright else 0.7 hsv = [(i / N, 1, brightness) for i in range(N)] colors = list(map(lambda c: colorsys.hsv_to_rgb(*c), hsv)) random.shuffle(colors) return colors #%% # ============================================================================= # For mask generation # ============================================================================= def generate_MLmask(self): """ Generate binary mask with all selected cells""" self.MLmask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) if len(self.selected_ML_Index) > 0: for selected_index in self.selected_ML_Index: self.MLmask = np.add(self.MLmask, self.Mask[:, :, selected_index]) self.add_rois_of_selected() def add_rois_of_selected(self): """ Generate ROI items from ML selected mask. Using find_contours to get list of contour coordinates in the binary mask, and then generate polygon rois based on these coordinates. """ handle_downsample_step = 2 for selected_index in self.selected_ML_Index: contours = self.selected_cells_infor_dict['cell{}_verts'.format( str(selected_index))] # contours = find_contours(self.Mask[:,:,selected_index], 0.5) # Find iso-valued contours in a 2D array for a given level value. for n, contour in enumerate(contours): contour_coord_array = contours[n] #Swap columns contour_coord_array[:, 0], contour_coord_array[:, 1] = contour_coord_array[:, 1], contour_coord_array[:, 0].copy( ) #Down sample the coordinates otherwise it will be too dense. contour_coord_array_del = np.delete( contour_coord_array, np.arange(2, contour_coord_array.shape[0] - 3, handle_downsample_step), 0) for _ in range(3): contour_coord_array_del = np.delete( contour_coord_array_del, np.arange(2, contour_coord_array_del.shape[0] - 3, handle_downsample_step), 0) self.selected_cells_infor_dict['cell{}_ROIitem'.format(str(selected_index))] = \ pg.PolyLineROI(positions=contour_coord_array_del, closed=True) # Add ROI item, and append_to_roilist self.selection_view.getView().addItem( self.selected_cells_infor_dict['cell{}_ROIitem'.format( str(selected_index))]) self.selection_view.append_to_roilist( self.selected_cells_infor_dict['cell{}_ROIitem'.format( str(selected_index))]) def run_in_thread(self, fn, *args, **kwargs): """ Send target function to thread. Usage: lambda: self.run_in_thread(self.fn) Parameters ---------- fn : function Target function to put in thread. Returns ------- thread : TYPE Threading handle. """ thread = threading.Thread(target=fn, args=args, kwargs=kwargs) thread.start() return thread def ResetLiveImgView(self): """Closes the widget nicely, making sure to clear the graphics scene and release memory.""" self.selection_view.close() # Replot the imageview self.selection_view = DrawingWidget(self) self.selection_view.enable_drawing(True) self.selection_view.getView().setLimits(xMin = 0, xMax = 2048, yMin = 0, yMax = 2048, minXRange = 2048, \ minYRange = 2048, maxXRange = 2048, maxYRange = 2048) self.selection_view.ui.roiBtn.hide() self.selection_view.ui.menuBtn.hide() self.selection_view.ui.normGroup.hide() self.selection_view.ui.roiPlot.hide() self.image_mask_stack.addTab(self.selection_view, 'Select')
class CoordinatesWidgetUI(QWidget): sig_cast_mask_coordinates_to_dmd = pyqtSignal(list) sig_cast_mask_coordinates_to_galvo = pyqtSignal(list) sig_start_registration = pyqtSignal() sig_finished_registration = pyqtSignal() sig_cast_camera_image = pyqtSignal(np.ndarray) def __init__(self, parent=None, *args, **kwargs): super().__init__(*args, **kwargs) self.main_application = parent self.init_gui() def closeEvent(self, event): try: self.DMD except: pass else: self.DMD.disconnect_DMD() QtWidgets.QApplication.quit() event.accept() def init_gui(self): self.setWindowTitle("Coordinate control") self.layout = QGridLayout() self.setMinimumSize(1250, 1000) self.setLayout(self.layout) self.image_mask_stack = QTabWidget() self.selection_view = DrawingWidget(self) self.selection_view.enable_drawing(True) self.selection_view.getView().setLimits(xMin=0, xMax=2048, yMin=0, yMax=2048, minXRange=2048, minYRange=2048, maxXRange=2048, maxYRange=2048) self.selection_view.ui.roiBtn.hide() self.selection_view.ui.menuBtn.hide() self.selection_view.ui.normGroup.hide() self.selection_view.ui.roiPlot.hide() # self.selection_view.setImage(plt.imread('CoordinatesManager/Registration_Images/StageRegistration/Distance200_Offset0/A1.png')) self.mask_view = SquareImageView() self.mask_view.getView().setLimits(xMin=0, xMax=2048, yMin=0, yMax=2048, minXRange=2048, minYRange=2048, maxXRange=2048, maxYRange=2048) self.mask_view.ui.roiBtn.hide() self.mask_view.ui.menuBtn.hide() self.mask_view.ui.normGroup.hide() self.mask_view.ui.roiPlot.hide() self.mask_view.ui.histogram.hide() self.image_mask_stack.addTab(self.selection_view, 'Select') self.image_mask_stack.addTab(self.mask_view, 'Mask') self.layout.addWidget(self.image_mask_stack, 0, 0, 5, 1) # ---------------------- Mask generation Container -------------- self.maskGeneratorContainer = roundQGroupBox() self.maskGeneratorContainer.setFixedSize(320, 220) self.maskGeneratorContainer.setTitle("Mask generator") self.maskGeneratorContainerLayout = QGridLayout() self.maskGeneratorLayout = QGridLayout() self.maskGeneratorContainer.setLayout( self.maskGeneratorContainerLayout) self.loadMaskFromFileButton = QPushButton('Open mask') self.loadMaskFromFileButton.clicked.connect(self.load_mask_from_file) self.addRoiButton = QPushButton("Add ROI") self.createMaskButton = QPushButton("Create mask") self.removeSelectionButton = QPushButton("Remove ROIs") self.addRoiButton.clicked.connect(self.add_polygon_roi) self.createMaskButton.clicked.connect(self.create_mask) self.removeSelectionButton.clicked.connect(self.remove_selection) self.maskGeneratorContainerLayout.addWidget(self.addRoiButton, 1, 0) self.maskGeneratorContainerLayout.addWidget(self.createMaskButton, 2, 0) self.maskGeneratorContainerLayout.addWidget(self.removeSelectionButton, 1, 1) self.selectionOptionsContainer = roundQGroupBox() self.selectionOptionsContainer.setTitle('Options') self.selectionOptionsLayout = QGridLayout() self.fillContourButton = QCheckBox() self.invertMaskButton = QCheckBox() self.thicknessSpinBox = QSpinBox() self.thicknessSpinBox.setRange(1, 25) self.selectionOptionsLayout.addWidget(QLabel('Fill contour:'), 0, 0) self.selectionOptionsLayout.addWidget(self.fillContourButton, 0, 1) self.selectionOptionsLayout.addWidget(QLabel('Invert mask:'), 1, 0) self.selectionOptionsLayout.addWidget(self.invertMaskButton, 1, 1) self.selectionOptionsLayout.addWidget(QLabel('Thickness:'), 2, 0) self.selectionOptionsLayout.addWidget(self.thicknessSpinBox, 2, 1) self.selectionOptionsContainer.setLayout(self.selectionOptionsLayout) self.snapFovButton = QPushButton('Image FOV') self.snapFovButton.clicked.connect(self.snap_fov) self.maskGeneratorContainerLayout.addWidget(self.snapFovButton, 0, 0, 1, 1) self.maskGeneratorContainerLayout.addWidget( self.loadMaskFromFileButton, 0, 1, 1, 1) self.maskGeneratorContainerLayout.addWidget( self.selectionOptionsContainer, 2, 1, 2, 1) self.layout.addWidget(self.maskGeneratorContainer, 0, 1) self.DMDWidget = DMDWidget.DMDWidget() self.layout.addWidget(self.DMDWidget, 1, 1) self.DMDWidget.sig_request_mask_coordinates.connect( lambda: self.cast_mask_coordinates('dmd')) self.sig_cast_mask_coordinates_to_dmd.connect( self.DMDWidget.receive_mask_coordinates) self.DMDWidget.sig_start_registration.connect( lambda: self.sig_start_registration.emit()) self.DMDWidget.sig_finished_registration.connect( lambda: self.sig_finished_registration.emit()) self.GalvoWidget = GalvoWidget.GalvoWidget() self.layout.addWidget(self.GalvoWidget, 2, 1) self.GalvoWidget.sig_request_mask_coordinates.connect( lambda: self.cast_mask_coordinates('galvo')) self.sig_cast_mask_coordinates_to_galvo.connect( self.GalvoWidget.receive_mask_coordinates) self.GalvoWidget.sig_start_registration.connect( lambda: self.sig_start_registration.emit()) self.GalvoWidget.sig_finished_registration.connect( lambda: self.sig_finished_registration.emit()) self.ManualRegistrationWidget = ManualRegistration.ManualRegistrationWidget( self) self.ManualRegistrationWidget.sig_request_camera_image.connect( self.cast_camera_image) self.sig_cast_camera_image.connect( self.ManualRegistrationWidget.receive_camera_image) self.layout.addWidget(self.ManualRegistrationWidget, 3, 1) self.StageRegistrationWidget = StageRegistrationWidget.StageWidget( self) self.layout.addWidget(self.StageRegistrationWidget, 4, 1) def cast_transformation_to_DMD(self, transformation, laser): self.DMDWidget.transform[laser] = transformation self.DMDWidget.save_transformation() def cast_transformation_to_galvos(self, sig): transformation = sig self.GalvoWidget.transform = transformation self.GalvoWidget.save_transformation() def cast_camera_image(self): image = self.selection_view.image if type(image) == np.ndarray: self.sig_cast_camera_image.emit(image) def snap_fov(self): self.DMDWidget.interupt_projection() self.DMDWidget.project_full_white() self.cam = CamActuator() self.cam.initializeCamera() image = self.cam.SnapImage(0.04) self.cam.Exit() self.selection_view.setImage(image) def cast_mask_coordinates(self, receiver): list_of_rois = self.get_list_of_rois() sig = [ list_of_rois, self.fillContourButton.isChecked(), self.thicknessSpinBox.value(), self.invertMaskButton.isChecked() ] if receiver == 'dmd': self.sig_cast_mask_coordinates_to_dmd.emit(sig) else: self.sig_cast_mask_coordinates_to_galvo.emit(sig) def get_list_of_rois(self): view = self.selection_view list_of_rois = [] for roi in view.roilist: roi_handle_positions = roi.getLocalHandlePositions() roi_origin = roi.pos() for idx, pos in enumerate(roi_handle_positions): roi_handle_positions[idx] = pos[1] num_vertices = len(roi_handle_positions) vertices = np.zeros([num_vertices, 2]) for idx, vertex in enumerate(roi_handle_positions): vertices[idx, :] = np.array( [vertex.x() + roi_origin.x(), vertex.y() + roi_origin.y()]) list_of_rois.append(vertices) return list_of_rois def create_mask(self): flag_fill_contour = self.fillContourButton.isChecked() flag_invert_mode = self.invertMaskButton.isChecked() contour_thickness = self.thicknessSpinBox.value() list_of_rois = self.get_list_of_rois() self.mask = ProcessImage.CreateBinaryMaskFromRoiCoordinates(list_of_rois, \ fill_contour = flag_fill_contour, \ contour_thickness = contour_thickness, \ invert_mask = flag_invert_mode) self.mask_view.setImage(self.mask) def remove_selection(self): self.selection_view.clear_rois() def set_camera_image(self, sig): self.selection_view.setImage(sig) def add_polygon_roi(self): view = self.selection_view x = (view.getView().viewRect().x()) * 0.3 y = (view.getView().viewRect().y()) * 0.3 a = (view.getView().viewRect().width() + x) * 0.3 b = (view.getView().viewRect().height() + y) * 0.3 c = (view.getView().viewRect().width() + x) * 0.7 d = (view.getView().viewRect().height() + y) * 0.7 polygon_roi = pg.PolyLineROI([[a, b], [c, b], [c, d], [a, d]], pen=view.pen, closed=True, movable=True, removable=True) view.getView().addItem(polygon_roi) view.append_to_roilist(polygon_roi) def load_mask_from_file(self): """ Open a file manager to browse through files, load image file """ self.loadFileName, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Select file', './CoordinateManager/Images/', "(*.png, *.tiff, *.jpg)") try: image = plt.imread(self.loadFileName) self.mask = image self.mask_view.setImage(self.mask) except: print('fail to load file.')
class MainGUI(QWidget): # signal_DMDmask is cictionary with laser specification as key and binary mask as content. signal_DMDmask = pyqtSignal(dict) signal_DMDcontour = pyqtSignal(list) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) os.chdir('./') # Set directory to current folder. self.setFont(QFont("Arial")) # self.setMinimumSize(900, 1020) self.setWindowTitle("Cell Selection") self.layout = QGridLayout(self) self.roi_list_freehandl_added = [] self.selected_ML_Index = [] self.selected_cells_infor_dict = {} self.mask_color_multiplier = [1, 1, 0] # ============================================================================= # Container for image display # ============================================================================= graphContainer = StylishQT.roundQGroupBox() graphContainerLayout = QGridLayout() self.Imgviewtabs = QTabWidget() MLmaskviewBox = QWidget() MLmaskviewBoxLayout = QGridLayout() self.Matdisplay_Figure = Figure() self.Matdisplay_Canvas = FigureCanvas(self.Matdisplay_Figure) self.Matdisplay_Canvas.setFixedWidth(500) self.Matdisplay_Canvas.setFixedHeight(500) self.Matdisplay_Canvas.mpl_connect('button_press_event', self._onclick) self.Matdisplay_toolbar = NavigationToolbar(self.Matdisplay_Canvas, self) MLmaskviewBoxLayout.addWidget(self.Matdisplay_toolbar, 0, 0) MLmaskviewBoxLayout.addWidget(self.Matdisplay_Canvas, 1, 0) MLmaskviewBox.setLayout(MLmaskviewBoxLayout) self.Imgviewtabs.addTab(MLmaskviewBox, "MaskRCNN") # ============================================================================= # Mask editing tab # ============================================================================= MLmaskEditBox = QWidget() MLmaskEditBoxLayout = QGridLayout() self.Mask_edit_view = DrawingWidget(self) self.Mask_edit_view.enable_drawing(False) # Disable drawing first # self.Mask_edit_view = pg.ImageView() # self.Mask_edit_view.getView().setLimits(xMin = 0, xMax = 2048, yMin = 0, yMax = 2048, minXRange = 2048, minYRange = 2048, maxXRange = 2048, maxYRange = 2048) self.Mask_edit_viewItem = self.Mask_edit_view.getImageItem() # self.ROIitem = pg.PolyLineROI([[0,0], [80,0], [80,80], [0,80]], closed=True) self.Mask_edit_view_getView = self.Mask_edit_view.getView() # self.Mask_edit_view_getView.addItem(self.ROIitem) self.Mask_edit_view.ui.roiBtn.hide() self.Mask_edit_view.ui.menuBtn.hide() self.Mask_edit_view.ui.normGroup.hide() self.Mask_edit_view.ui.roiPlot.hide() MLmaskEditBoxLayout.addWidget(self.Mask_edit_view, 0, 0) MLmaskEditBox.setLayout(MLmaskEditBoxLayout) self.Imgviewtabs.addTab(MLmaskEditBox, "Mask edit") graphContainerLayout.addWidget(self.Imgviewtabs, 0, 0) graphContainer.setLayout(graphContainerLayout) # ============================================================================= # Operation container # ============================================================================= operationContainer = StylishQT.roundQGroupBox() operationContainerLayout = QGridLayout() self.init_ML_button = QPushButton('Initialize ML', self) operationContainerLayout.addWidget(self.init_ML_button, 0, 0) self.init_ML_button.clicked.connect(self.init_ML) #---------------------Load image from file----------------------------- self.textbox_loadimg = QLineEdit(self) operationContainerLayout.addWidget(self.textbox_loadimg, 1, 0) self.button_import_img_browse = QPushButton('Browse', self) operationContainerLayout.addWidget(self.button_import_img_browse, 1, 1) self.button_import_img_browse.clicked.connect(self.get_img_file_tif) self.run_ML_button = QPushButton('Analysis', self) operationContainerLayout.addWidget(self.run_ML_button, 2, 0) self.run_ML_button.clicked.connect(self.run_ML_onImg_and_display) self.generate_MLmask_button = QPushButton('Mask', self) operationContainerLayout.addWidget(self.generate_MLmask_button, 2, 1) self.generate_MLmask_button.clicked.connect(self.generate_MLmask) self.update_MLmask_button = QPushButton('Update mask', self) operationContainerLayout.addWidget(self.update_MLmask_button, 3, 0) self.update_MLmask_button.clicked.connect(self.update_mask) self.enable_modify_MLmask_button = QPushButton('Enable free-hand', self) self.enable_modify_MLmask_button.setCheckable(True) operationContainerLayout.addWidget(self.enable_modify_MLmask_button, 4, 0) self.enable_modify_MLmask_button.clicked.connect(self.enable_free_hand) # self.modify_MLmask_button = QPushButton('Add patch', self) # operationContainerLayout.addWidget(self.modify_MLmask_button, 4, 1) # self.modify_MLmask_button.clicked.connect(self.addedROIitem_to_Mask) self.clear_roi_button = QPushButton('Clear ROIs', self) operationContainerLayout.addWidget(self.clear_roi_button, 5, 0) self.clear_roi_button.clicked.connect(self.clear_edit_roi) # self.maskLaserComboBox = QComboBox() # self.maskLaserComboBox.addItems(['640', '532', '488']) # operationContainerLayout.addWidget(self.maskLaserComboBox, 6, 0) # # self.generate_transformed_mask_button = QPushButton('Transform mask', self) # operationContainerLayout.addWidget(self.generate_transformed_mask_button, 6, 1) # self.generate_transformed_mask_button.clicked.connect(self.generate_transformed_mask) self.emit_transformed_mask_button = QPushButton('Emit mask', self) operationContainerLayout.addWidget(self.emit_transformed_mask_button, 7, 1) self.emit_transformed_mask_button.clicked.connect( self.emit_mask_contour) operationContainer.setLayout(operationContainerLayout) # ============================================================================= # Mask para container # ============================================================================= MaskparaContainer = StylishQT.roundQGroupBox() MaskparaContainerContainerLayout = QGridLayout() #---------------------------------------------------------------------- self.fillContourButton = QCheckBox() self.invertMaskButton = QCheckBox() self.thicknessSpinBox = QSpinBox() self.thicknessSpinBox.setRange(1, 25) MaskparaContainerContainerLayout.addWidget(QLabel('Fill contour:'), 0, 0) MaskparaContainerContainerLayout.addWidget(self.fillContourButton, 0, 1) MaskparaContainerContainerLayout.addWidget(QLabel('Invert mask:'), 1, 0) MaskparaContainerContainerLayout.addWidget(self.invertMaskButton, 1, 1) MaskparaContainerContainerLayout.addWidget(QLabel('Thickness:'), 2, 0) MaskparaContainerContainerLayout.addWidget(self.thicknessSpinBox, 2, 1) MaskparaContainer.setLayout(MaskparaContainerContainerLayout) # ============================================================================= # Device operation container # ============================================================================= deviceOperationContainer = StylishQT.roundQGroupBox() deviceOperationContainerLayout = QGridLayout() #---------------------------------------------------------------------- self.CamExposureBox = QDoubleSpinBox(self) self.CamExposureBox.setDecimals(6) self.CamExposureBox.setMinimum(0) self.CamExposureBox.setMaximum(100) self.CamExposureBox.setValue(0.001501) self.CamExposureBox.setSingleStep(0.001) deviceOperationContainerLayout.addWidget(self.CamExposureBox, 0, 1) deviceOperationContainerLayout.addWidget(QLabel("Exposure time:"), 0, 0) cam_snap_button = QPushButton('Cam snap', self) deviceOperationContainerLayout.addWidget(cam_snap_button, 0, 2) cam_snap_button.clicked.connect(self.cam_snap) cam_snap_button = QPushButton('Cam snap', self) deviceOperationContainerLayout.addWidget(cam_snap_button, 0, 2) cam_snap_button.clicked.connect(self.cam_snap) deviceOperationContainer.setLayout(deviceOperationContainerLayout) self.layout.addWidget(graphContainer, 0, 0, 3, 1) self.layout.addWidget(operationContainer, 0, 1) self.layout.addWidget(MaskparaContainer, 1, 1) self.layout.addWidget(deviceOperationContainer, 2, 1) self.setLayout(self.layout) #%% # ============================================================================= # MaskRCNN detection part # ============================================================================= # @run_in_thread def init_ML(self): # Initialize the detector instance and load the model. self.ProcessML = ProcessImageML() def get_img_file_tif(self): self.img_tif_filePath, _ = QtWidgets.QFileDialog.getOpenFileName( self, 'Single File', 'M:/tnw/ist/do/projects/Neurophotonics/Brinkslab/Data', "(*.tif)") self.textbox_loadimg.setText(self.img_tif_filePath) if self.img_tif_filePath != None: self.Rawimage = imread(self.img_tif_filePath) self.MLtargetedImg_raw = self.Rawimage.copy() self.MLtargetedImg = self.convert_for_MaskRCNN( self.MLtargetedImg_raw) self.show_raw_image(self.MLtargetedImg) self.addedROIitemMask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) self.MLmask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) def show_raw_image(self, image): # display a single image try: self.Matdisplay_Figure.clear() except: pass ax1 = self.Matdisplay_Figure.add_subplot(111) ax1.set_xticks([]) ax1.set_yticks([]) ax1.imshow(image) self.Matdisplay_Figure.tight_layout() self.Matdisplay_Canvas.draw() RGB_image = gray2rgb(image) self.Mask_edit_viewItem.setImage(RGB_image) def convert_for_MaskRCNN(self, input_img): """Convert the image size and bit-depth to make it suitable for MaskRCNN detection.""" if input_img.shape[0] > 1024 or input_img.shape[1] > 1024: resized_img = resize(input_img, [1024, 1024], preserve_range=True).astype(input_img.dtype) minval = np.min(resized_img) maxval = np.max(resized_img) return ((resized_img - minval) / (maxval - minval) * 255).astype( np.uint8) def run_ML_onImg_and_display(self): """Run MaskRCNN on input image""" self.Matdisplay_Figure.clear() ax1 = self.Matdisplay_Figure.add_subplot(111) # Depends on show_mask or not, the returned figure will be input raw image with mask or not. self.MLresults, self.Matdisplay_Figure_axis, self.unmasked_fig = self.ProcessML.DetectionOnImage( self.MLtargetedImg, axis=ax1, show_mask=False, show_bbox=False) self.Mask = self.MLresults['masks'] self.Label = self.MLresults['class_ids'] self.Score = self.MLresults['scores'] self.Bbox = self.MLresults['rois'] self.SelectedCellIndex = 0 self.NumCells = int(len(self.Label)) self.selected_ML_Index = [] self.selected_cells_infor_dict = {} self.Matdisplay_Figure_axis.imshow(self.unmasked_fig.astype(np.uint8)) self.Matdisplay_Figure.tight_layout() self.Matdisplay_Canvas.draw() #%% # ============================================================================= # Configure click event to add clicked cell mask # ============================================================================= def _onclick(self, event): """Highlights the cell selected in the figure by the user when clicked on""" if self.NumCells > 0: ShapeMask = np.shape(self.Mask) # get coorinates at selected location in image coordinates if event.xdata == None or event.ydata == None: return xcoor = min(max(int(event.xdata), 0), ShapeMask[1]) ycoor = min(max(int(event.ydata), 0), ShapeMask[0]) # search for the mask coresponding to the selected cell for EachCell in range(self.NumCells): if self.Mask[ycoor, xcoor, EachCell]: self.SelectedCellIndex = EachCell break # highlight selected cell if self.SelectedCellIndex not in self.selected_ML_Index: # Get the selected cell's contour coordinates and mask patch self.contour_verts, self.Cell_patch = self.get_cell_polygon( self.Mask[:, :, self.SelectedCellIndex]) self.Matdisplay_Figure_axis.add_patch(self.Cell_patch) self.Matdisplay_Canvas.draw() self.selected_ML_Index.append(self.SelectedCellIndex) self.selected_cells_infor_dict['cell{}_verts'.format( str(self.SelectedCellIndex))] = self.contour_verts else: # If click on the same cell self.Cell_patch.remove() self.Matdisplay_Canvas.draw() self.selected_ML_Index.remove(self.SelectedCellIndex) self.selected_cells_infor_dict.pop('cell{}_verts'.format( str(self.SelectedCellIndex))) def get_cell_polygon(self, mask): # Mask Polygon # Pad to ensure proper polygons for masks that touch image edges. padded_mask = np.zeros((mask.shape[0] + 2, mask.shape[1] + 2), dtype=np.uint8) padded_mask[1:-1, 1:-1] = mask contours = find_contours(padded_mask, 0.5) for verts in contours: # Subtract the padding and flip (y, x) to (x, y) verts = np.fliplr(verts) - 1 contour_polygon = mpatches.Polygon( verts, facecolor=self.random_colors(1)[0]) return contours, contour_polygon def random_colors(self, N, bright=True): """ Generate random colors. To get visually distinct colors, generate them in HSV space then convert to RGB. """ brightness = 1.0 if bright else 0.7 hsv = [(i / N, 1, brightness) for i in range(N)] colors = list(map(lambda c: colorsys.hsv_to_rgb(*c), hsv)) random.shuffle(colors) return colors #%% # ============================================================================= # For mask generation # ============================================================================= def generate_MLmask(self): """ Generate binary mask with all selected cells""" self.MLmask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) if len(self.selected_ML_Index) > 0: for selected_index in self.selected_ML_Index: self.MLmask = np.add(self.MLmask, self.Mask[:, :, selected_index]) self.intergrate_into_final_mask() self.add_rois_of_selected() else: self.intergrate_into_final_mask() # self.Mask_edit_viewItem.setImage(gray2rgb(self.MLtargetedImg)) def add_rois_of_selected(self): """ Using find_contours to get list of contour coordinates in the binary mask, and then generate polygon rois based on these coordinates. """ for selected_index in self.selected_ML_Index: contours = self.selected_cells_infor_dict['cell{}_verts'.format( str(selected_index))] # contours = find_contours(self.Mask[:,:,selected_index], 0.5) # Find iso-valued contours in a 2D array for a given level value. for n, contour in enumerate(contours): contour_coord_array = contours[n] #Swap columns contour_coord_array[:, 0], contour_coord_array[:, 1] = contour_coord_array[:, 1], contour_coord_array[:, 0].copy( ) #Down sample the coordinates otherwise it will be too dense. contour_coord_array_del = np.delete( contour_coord_array, np.arange(2, contour_coord_array.shape[0] - 3, 2), 0) self.selected_cells_infor_dict['cell{}_ROIitem'.format(str(selected_index))] = \ pg.PolyLineROI(positions=contour_coord_array_del, closed=True) self.Mask_edit_view.getView().addItem( self.selected_cells_infor_dict['cell{}_ROIitem'.format( str(selected_index))]) def update_mask(self): """ Regenerate the masks for MaskRCNN and free-hand added (in case they are changed), and show in imageview. !!!ISSUE: getLocalHandlePositions: moving handles changes the position read out, dragging roi as a whole doesn't. """ # Binary mask from ML detection if len(self.selected_ML_Index) > 0: # Delete items in dictionary that are not roi items roi_dict = self.selected_cells_infor_dict.copy() del_key_list = [] for key in roi_dict: print(key) if 'ROIitem' not in key: del_key_list.append(key) for key in del_key_list: del roi_dict[key] self.MLmask = ProcessImage.ROIitem2Mask( roi_dict, mask_resolution=(self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) # Binary mask of added rois self.addedROIitemMask = ProcessImage.ROIitem2Mask( self.roi_list_freehandl_added, mask_resolution=(self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) self.intergrate_into_final_mask() # if type(self.roi_list_freehandl_added) is list: # for ROIitem in self.roi_list_freehandl_added: # # ROIitem.sigHoverEvent.connect(lambda: self.show_roi_detail(ROIitem)) # # plt.figure() # plt.imshow(self.addedROIitemMask) # plt.show() # ============================================================================= # For free-hand rois # ============================================================================= def enable_free_hand(self): if self.enable_modify_MLmask_button.isChecked(): self.Mask_edit_view.enable_drawing(True) else: self.Mask_edit_view.enable_drawing(False) def add_freehand_roi(self, roi): # For drawwidget self.roi_list_freehandl_added.append(roi) def clear_edit_roi(self): """ Clean up all the free-hand rois. """ for roi in self.roi_list_freehandl_added: self.Mask_edit_view.getView().removeItem(roi) self.roi_list_freehandl_added = [] if len(self.selected_cells_infor_dict) > 0: # Remove all selected masks for roiItemkey in self.selected_cells_infor_dict: if 'ROIitem' in roiItemkey: self.Mask_edit_view.getView().removeItem( self.selected_cells_infor_dict[roiItemkey]) self.selected_cells_infor_dict = {} self.MLmask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) self.intergrate_into_final_mask() def intergrate_into_final_mask(self): # Binary mask of added rois self.addedROIitemMask = ProcessImage.ROIitem2Mask( self.roi_list_freehandl_added, mask_resolution=(self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) #Display the RGB mask, ML mask plus free-hand added. self.Mask_edit_viewItem.setImage(gray2rgb(self.addedROIitemMask) * self.mask_color_multiplier + \ gray2rgb(self.MLmask) * self.mask_color_multiplier + gray2rgb(self.MLtargetedImg)) self.final_mask = self.MLmask + self.addedROIitemMask # In case the input image is 2048*2048, and it is resized to fit in MaskRCNN, need to convert back to original size for DMD tranformation. if self.final_mask.shape[0] != self.Rawimage.shape[ 0] or self.final_mask.shape[1] != self.Rawimage.shape[1]: self.final_mask = resize( self.final_mask, [self.Rawimage.shape[0], self.Rawimage.shape[1]], preserve_range=True).astype(self.final_mask.dtype) # self.final_mask = np.where(self.final_mask <= 1, self.final_mask, int(1)) plt.figure() plt.imshow(self.final_mask) plt.show() # ============================================================================= # For DMD transformation and mask generation # ============================================================================= def generate_transformed_mask(self): self.read_transformations_from_file() # self.transform_to_DMD_mask(laser = self.maskLaserComboBox.currentText(), dict_transformations = self.dict_transformations) target_laser = self.maskLaserComboBox.currentText() self.final_DMD_mask = self.finalmask_to_DMD_mask( laser=target_laser, dict_transformations=self.dict_transformations) plt.figure() plt.imshow(self.final_DMD_mask) plt.show() def emit_mask_contour(self): """Use find_contours to get a list of (n,2)-ndarrays consisting of n (row, column) coordinates along the contour, and then feed the list of signal:[list_of_rois, flag_fill_contour, contour_thickness, flag_invert_mode] to the receive_mask_coordinates function in DMDWidget. """ contours = find_contours(self.final_mask, 0.5) sig = [ contours, self.fillContourButton.isChecked(), self.thicknessSpinBox.value(), self.invertMaskButton.isChecked() ] self.signal_DMDcontour.emit(sig) def emit_mask(self): target_laser = self.maskLaserComboBox.currentText() final_DMD_mask_dict = {} final_DMD_mask_dict['camera-dmd-' + target_laser] = self.final_DMD_mask self.signal_DMDmask.emit(final_DMD_mask_dict) def read_transformations_from_file(self): try: with open( r'M:\tnw\ist\do\projects\Neurophotonics\Brinkslab\People\Xin Meng\Code\Python_test\DMDManager\Registration\transformation.txt', 'r') as json_file: self.dict_transformations = json.load(json_file) except: print( 'No transformation could be loaded from previous registration run.' ) return # def transform_to_DMD_mask(self, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1, flag_invert_mode = False, mask_resolution = (1024, 768)): # """ # Get roi vertices from all roi items and perform the transformation, and then create the mask for DMD. # """ # # #list of roi vertices each being (n,2) numpy array for added rois # if len(self.roi_list_freehandl_added) > 0: # self.addedROIitem_vertices = ProcessImage.ROIitem2Vertices(self.roi_list_freehandl_added) # #addedROIitem_vertices needs to be seperated to be inidividual (n,2) np.array # self.ROIitems_mask_transformed = ProcessImage.vertices_to_DMD_mask(self.addedROIitem_vertices, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1,\ # flag_invert_mode = False, mask_resolution = (1024, 768)) # # #Dictionary with (n,2) numpy array for clicked cells # if len(self.selected_cells_infor_dict) > 0: # #Convert dictionary to np.array # for roiItemkey in self.selected_cells_infor_dict: # #Each one is 'contours' from find_contour # if '_verts' in roiItemkey: # self.selected_cells_infor_dict[roiItemkey] # # self.MLitems_mask_transformed = ProcessImage.vertices_to_DMD_mask(self.selected_cells_infor_dict, laser, dict_transformations, flag_fill_contour = True, contour_thickness = 1,\ # flag_invert_mode = False, mask_resolution = (1024, 768)) # # if len(self.roi_list_freehandl_added) > 0: # self.final_DMD_mask = self.ROIitems_mask_transformed + self.MLitems_mask_transformed # self.final_DMD_mask[self.final_DMD_mask>1] = 1 # else: # self.final_DMD_mask = self.MLitems_mask_transformed # # return self.final_DMD_mask def finalmask_to_DMD_mask(self, laser, dict_transformations, flag_fill_contour=True, contour_thickness=1, flag_invert_mode=False, mask_resolution=(1024, 768)): """ Same goal as transform_to_DMD_mask, with input being the final binary mask and using find_contour to get all vertices and perform transformation, and then coordinates to mask. """ self.final_DMD_mask = ProcessImage.binarymask_to_DMD_mask(self.final_mask, laser, dict_transformations, flag_fill_contour = True, \ contour_thickness = 1, flag_invert_mode = False, mask_resolution = (1024, 768)) return self.final_DMD_mask def closeEvent(self, event): QtWidgets.QApplication.quit() event.accept() # def apply_mask(self, image, mask, color, alpha=0.5): # """Apply the given mask to the image. # """ # for c in range(3): # image[:, :, c] = np.where(mask == 1, # image[:, :, c] * # (1 - alpha) + alpha * color[c] * 255, # image[:, :, c]) # return image #%% # @run_in_thread def cam_snap(self): """Get a image from camera""" self.cam = CamActuator() self.cam.initializeCamera() exposure_time = self.CamExposureBox.value() self.Rawimage = self.cam.SnapImage(exposure_time) self.cam.Exit() print('Snap finished') self.MLtargetedImg_raw = self.Rawimage.copy() self.MLtargetedImg = self.convert_for_MaskRCNN(self.MLtargetedImg_raw) self.show_raw_image(self.MLtargetedImg) self.addedROIitemMask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1])) self.MLmask = np.zeros( (self.MLtargetedImg.shape[0], self.MLtargetedImg.shape[1]))