class Example(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setGeometry(300, 300, 300, 200) self.setWindowTitle('Camera') self.show() self.camera = Camera(1) self.camera.initialize() self.central_widget = QWidget() self.layout = QVBoxLayout(self.central_widget) #define data capture button self.button_frame = QPushButton('Capture', self.central_widget) self.button_frame.clicked.connect(self.update_image) self.layout.addWidget(self.button_frame) #define image view widget self.image_view = ImageView() self.layout.addWidget(self.image_view) #add central widget self.setCentralWidget(self.central_widget) def update_image(self): frame = self.camera.get_frame() self.image_view.setImage(frame.T) print('Maximum in frame: {}, Minimum in frame: {}'.format( np.max(frame), np.min(frame)))
class VideoWidget(QPyDesignerCustomWidgetPlugin): def __init__(self, parent=None): super().__init__(parent=parent) self.initialized = False self.view = ImageView() def initialize(self, formEditor): if self.initialized: return manager = formEditor.extensionManager() if manager: # self.factory = VideoWidgetTaskMenuFactory(manager) # manager.registerExtensions(self.factory, 'com.trolltech.Qt.Designer.TaskMenu') pass def createWidget(self, widget): return VideoWidget(widget) def name(self): return "VideoWidget" def includeFile(self): return "QQ_Widgets.videowidget" def update_data(self, data): self.view.setImage(data)
def setImage(self, *args, **kwargs): if args[0].ndim == 3: # For a 3 dimensional image, we need to specify which axes we are providing and their indices in the # array's shape attribute # If we don't do this then it is interpreted incorrectly for very small images by ImageView.setImage # Note that, for our purposes, the t axis corresponds to angle data kwargs['axes'] = kwargs.get('axes', { 't': 0, 'x': 2, 'y': 1, 'c': None }) ImageView.setImage(self, *args, **kwargs) self.check_for_bad_data()
class StartWindow(QMainWindow): def __init__(self): super().__init__() self.central_widget = QWidget() self.button_min = QPushButton('Get Minimum', self.central_widget) self.button_max = QPushButton('Get Maximum', self.central_widget) self.layout = QVBoxLayout(self.central_widget) self.layout.addWidget(self.button_min) self.layout.addWidget(self.button_max) self.setCentralWidget(self.central_widget) self.image_view = ImageView() self.layout.addWidget(self.image_view) self.button_max.clicked.connect(self.update_image) def camera_init(self): # Configure depth and color streams pipeline = rs.pipeline() config = rs.config() # config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30) config.enable_stream(rs.stream.color, 640, 480, rs.format.bgr8, 60) # # Start streaming pipeline.start(config) frames = pipeline.wait_for_frames() # depth_frame = frames.get_depth_frame() color_frame = frames.get_color_frame() self.color_image = np.asanyarray(color_frame.get_data()) # images = np.hstack((color_image, color_image)) # print(color_image) # print(np.min(color_image)) # print(np.max(color_image)) # # gray = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY) def update_image(self): self.camera_init() self.image_view.setImage(self.color_image.T) def button_clicked(self): print('Button Clicked')
class StartWindow(QMainWindow): def __init__(self, camera = None): super().__init__() self.camera = camera self.central_widget = QWidget() self.button_frame = QPushButton('Acquire Frame', self.central_widget) self.button_movie = QPushButton('Start Movie', self.central_widget) self.image_view = ImageView() self.image_view.ui.histogram.hide() self.image_view.ui.roiBtn.hide() self.image_view.ui.menuBtn.hide() self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0,10) self.layout = QVBoxLayout(self.central_widget) self.layout.addWidget(self.button_frame) self.layout.addWidget(self.button_movie) self.layout.addWidget(self.image_view) self.layout.addWidget(self.slider) self.setCentralWidget(self.central_widget) self.button_frame.clicked.connect(self.update_image) self.button_movie.clicked.connect(self.start_movie) self.slider.valueChanged.connect(self.update_brightness) self.update_timer = QTimer() self.update_timer.timeout.connect(self.update_movie) def update_image(self): frame = self.camera.get_frame() self.image_view.setImage(frame.T) def update_movie(self): self.image_view.setImage(self.camera.last_frame.T) def update_brightness(self, value): value /= 10 self.camera.set_brightness(value) def start_movie(self): self.movie_thread = MovieThread(self.camera) self.movie_thread.start() self.update_timer.start(30)
def __init__(self): QtWidgets.QMainWindow.__init__(self) path = QtWidgets.QFileDialog.getOpenFileName(self, 'Choose file', '/', "(*.tiff *.tif)") if not path[0]: return path = path[0] self.tif = TiffFile(path) self.widget = QtWidgets.QWidget(parent=self) self.vlayout = QtWidgets.QVBoxLayout(self.widget) iv = ImageView(parent=self) iv.setImage(self.tif.asarray(key=0)) self.vlayout.addWidget(iv) self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal, parent=self) self.slider.setMaximum(len(self.tif.series) - 1) self.slider.setMinimum(0) self.slider.setSingleStep(1) self.slider.setPageStep(10) self.spinbox = QtWidgets.QSpinBox(parent=self) self.spinbox.setMaximum(len(self.tif.series) - 1) self.spinbox.setMinimum(0) self.spinbox.valueChanged.connect(self.slider.setValue) self.vlayout.addWidget(self.spinbox) self.vlayout.addWidget(self.slider) self.slider.valueChanged.connect( lambda i: iv.setImage( self.tif.asarray(key=i), autoRange=False, autoLevels=False, autoHistogramRange=False ) ) self.slider.valueChanged.connect(self.spinbox.setValue) self.setCentralWidget(self.widget)
class ApplicationWindow(QtWidgets.QMainWindow): def __init__(self, camera = None): QtWidgets.QMainWindow.__init__(self) self.setAttribute(QtCore.Qt.WA_DeleteOnClose) self.setWindowTitle("application main window") self.image_view = ImageView() #self.layout.addWidget(self.image_view) ## Create window with ImageView widget self.setCentralWidget(self.image_view) while True: try: self.image_view.setImage(get_img()) except KeyboardInterrupt: break def update_image(self): pass
def makeWidget(self): self.subItem = QTreeWidgetItem() self.addChild(self.subItem) w = ImageView() w.value = lambda: w.image w.setValue = lambda image: w.setImage(np.squeeze(image)) w.sigChanged = None self.hideWidget = False return w
class StartWindow(QMainWindow): def __init__(self, camera=None): super().__init__() self.camera = camera self.central_widget = QWidget() self.button_frame = QPushButton('Draw', self.central_widget) self.image_view = ImageView() self.button_submit = QPushButton('Predict', self.central_widget) self.layout = QVBoxLayout(self.central_widget) self.layout.addWidget(self.button_frame) self.layout.addWidget(self.image_view) self.setCentralWidget(self.central_widget) self.button_frame.clicked.connect(self.update_image) self.button_submit.clicked.connect(self.submit) def update_image(self): #frame = self.camera.get_frame() self.frame = canvas() self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2GRAY) print(self.frame.shape) self.image_view.setImage(self.frame.T) def submit(self): # DEBUG #print(self.frame) images = torch.from_numpy(self.frame) images = images.unsqueeze(0) #images = images.unsqueeze(0) output = model( images.float() ) #.float() because i got error RuntimeError: Expected object of scalar type Float but got scalar type Double for argument #4 'mat1' _, preds = torch.max(output, dim=1) print("Prediction: ", preds.item())
class Masker(QDialog): """ An interface for the user to draw masks on an image or movie. This includes: - draw masks on the raw image, either by placing discrete vertices or freestyle drawing - modify masks by adding / deleting / moving vertices - change between frames - export masks - apply masks to image files init ---- image_path : str, path to an image file (e.g. ND2 or TIF) max_points_freestyle : int, the maximum number of points to allow for a freestyle mask. This limits memory usage. parent : root QWidget """ def __init__(self, image_path, max_points_freestyle=40, dialog_mode=False, parent=None): super(Masker, self).__init__(parent=parent) self.image_path = image_path self.max_points_freestyle = max_points_freestyle self.dialog_mode = dialog_mode self.initData() self.initUI() def initData(self): """ Load the images and related data. """ # Create the image reader self.imageReader = ImageReader(self.image_path) # Get the first image self.image = self.imageReader.get_frame(0) def initUI(self): """ Initialize the user interface. """ # Main layout L_master = QGridLayout(self) # An ImageView on the left to contain the subject of masking self.imageView = ImageView(parent=self) L_master.addWidget(self.imageView, 0, 0, 15, 2) self.imageView.setImage(self.image) # Override the default ImageView.imageItem.mouseClickEvent self.imageView.imageItem.mouseClickEvent = self._imageView_mouseClickEvent # Override the default ImageView.imageItem.mouseDoubleClickEvent self.imageView.imageItem.mouseDoubleClickEvent = \ self._imageView_mouseDoubleClickEvent # All currently clicked points self.points = [] # All current PolyLineROI objects self.polyLineROIs = [] # ScatterPlotItem, to show current accumulated clicks self.scatterPlotItem = ScatterPlotItem() self.scatterPlotItem.setParentItem(self.imageView.imageItem) ## WIDGETS widget_align = Qt.AlignTop # Frame slider self.frame_slider = IntSlider(minimum=0, maximum=self.imageReader.n_frames - 1, interval=1, init_value=0, name='Frame', parent=self) L_master.addWidget(self.frame_slider, 0, 2, 1, 1, alignment=widget_align) self.frame_slider.assign_callback(self.frame_slider_callback) # Button: create new ROI self.create_roi_mode = False self.B_create_ROI = QPushButton("Draw ROI", parent=self) self.B_create_ROI.clicked.connect(self.B_create_ROI_callback) L_master.addWidget(self.B_create_ROI, 10, 2, 1, 1, alignment=widget_align) # Button: freestyle drawing to make ROI self.freestyle_mode = False self.B_freestyle = QPushButton("Freestyle", parent=self) self.B_freestyle.clicked.connect(self.B_freestyle_callback) L_master.addWidget(self.B_freestyle, 11, 2, 1, 1, alignment=widget_align) # Pure dialog mode: no options to save or load masks, only # define and accept if self.dialog_mode: # Button: accept the current set of masks. This is only meaningful # when this class is being used as a dialog by other classes. self.B_accept = QPushButton("Accept masks", parent=self) self.B_accept.clicked.connect(self.B_accept_callback) L_master.addWidget(self.B_accept, 12, 2, 1, 1, alignment=widget_align) # Non-dialog mode: full range of options else: # Button: apply these masks to the localizations in a file self.B_apply = QPushButton("Apply masks", parent=self) self.B_apply.clicked.connect(self.B_apply_callback) L_master.addWidget(self.B_apply, 12, 2, 1, 1, alignment=widget_align) # Button: save masks to a file self.B_save = QPushButton("Save masks", parent=self) self.B_save.clicked.connect(self.B_save_callback) L_master.addWidget(self.B_save, 13, 2, 1, 1, alignment=widget_align) # Button: load preexisting masks from a file self.B_load = QPushButton("Load masks", parent=self) self.B_load.clicked.connect(self.B_load_callback) L_master.addWidget(self.B_load, 14, 2, 1, 1, alignment=widget_align) # Resize and launch self.resize(800, 600) self.show() ## CORE FUNCTIONS def update_image(self, frame_index=None, autoRange=False, autoLevels=False, autoHistogramRange=False): if frame_index is None: frame_index = self.frame_slider.value() self.image = self.imageReader.get_frame(frame_index) self.imageView.setImage(self.image, autoRange=autoRange, autoLevels=autoLevels, autoHistogramRange=autoHistogramRange) def update_scatter(self): """ Write the contents of self.points to self.scatterPlotItem. """ self.scatterPlotItem.setData( pos=np.asarray(self.points), pxMode=False, symbol='o', pen={ 'color': '#FFFFFF', 'width': 3.0 }, size=2.0, brush=None, ) def clear_scatter(self): self.points = [] self.scatterPlotItem.setData() def createPolyLineROI(self, points): p = PolyLineROI(self.points, closed=True, removable=True) self.polyLineROIs.append(p) self.imageView.view.addItem(self.polyLineROIs[-1]) # If the user requests to remove this ROI, remove it self.polyLineROIs[-1].sigRemoveRequested.connect(self._remove_ROI) def getPoints(self, polyLineROI): """ Return the set of points that make up a PolyLineROI as a 2D ndarray (shape (n_points, 2)). """ state = polyLineROI.getState() return np.asarray([[p.x(), p.y()] for p in state['points']]) def get_currdir(self): """ Get the last directory that was accessed by the user. If no directory was previously accessed, then this is just the same directory as the image file. """ if not hasattr(self, "_currdir"): self.set_currdir(self.image_path) return self._currdir def set_currdir(self, path): """ Set the directory returned by self.get_currdir(). args ---- path : str, a file path or directory path. If a file path, its parent directory is used. """ if os.path.isfile(path): self._currdir = os.path.split(os.path.abspath(path))[0] elif os.path.isdir(path): self._currdir = path def clear_polyLineROIs(self): """ Destroy all current PolyLineROI objects. """ for p in self.polyLineROIs[::-1]: self._remove_ROI(p) ## SIGNAL CALLBACKS def _imageView_mouseClickEvent(self, ev): if ev.button() == QtCore.Qt.RightButton: if self.imageView.imageItem.raiseContextMenu(ev): ev.accept() if ev.button() == QtCore.Qt.LeftButton and self.create_roi_mode: self.points.append(ev.pos()) self.update_scatter() def _imageView_mouseDoubleClickEvent(self, ev): if self.create_roi_mode: self.createPolyLineROI(self.points) self.clear_scatter() self.create_roi_mode = False self.imageView.view.state['mouseEnabled'] = np.array([True, True]) elif self.freestyle_mode: self.B_freestyle_callback() else: pass def _remove_ROI(self, polylineroi): polylineroi.sigRemoveRequested.disconnect(self._remove_ROI) self.polyLineROIs.remove(polylineroi) self.imageView.view.removeItem(polylineroi) del polylineroi ## WIDGET CALLBACKS def frame_slider_callback(self): """ Callback for user changes to the frame slider, which changes the current frame shown beneath the masks. """ frame_index = self.frame_slider.value() self.update_image(frame_index=frame_index) def B_create_ROI_callback(self): """ Callback for user selection of the "Draw ROI" button. Enter create ROI mode, which allows the user to place discrete vertices sequentially to build a mask. """ self.create_roi_mode = True self.imageView.view.state['mouseEnabled'] = np.array([False, False]) def B_freestyle_callback(self): """ Callback for user selection of the "freestyle" button. Enter freestyle mode, which allows the user to draw a mask on the image by dragging the mouse. """ # End freestyle mode by creating a mask from the drawn points if self.freestyle_mode: self.freestyle_mode = False self.imageView.imageItem.setDrawKernel(kernel=None, mask=None, center=None) mask = (self.image == self.draw_val) self.points = get_ordered_mask_points( mask, max_points=self.max_points_freestyle) self.createPolyLineROI(self.points) self.frame_slider_callback() self.points = [] self.B_freestyle.setText("Freestyle") # Start freestyle mode by enabling drawing on self.imageView.imageItem else: self.freestyle_mode = True self.draw_val = int(self.image.max() + 2) kernel = np.array([[self.draw_val]]) self.imageView.imageItem.setDrawKernel(kernel=kernel, mask=None, center=(0, 0)) self.B_freestyle.setText("Finish freestyle") def B_save_callback(self): """ Save the current set of masks to a file. Every currently defined mask is saved in a CSV-like format indexed by mask vertex. This CSV has the following columns: filename : str, the source filename mask_index : int, the index of this mask y : float, the y-coordinate of the vertex x : float, the x-coordinate of the vertex vertex : int, the index of this vertex in the context of its mask The "vertex" always ascends from 0 to n-1, where n is the number of vertices in that mask. """ # If no masks are defined, do nothing if len(self.polyLineROIs) == 0: return # Prompt the user to select an output filename out_path = getSaveFilePath(self, "Select output CSV", "{}_masks.csv".format( os.path.splitext(self.image_path)[0]), "CSV files (*.csv)", initialdir=self.get_currdir()) # For each PolyLineROI mask, get the corresponding set of vertices point_arrays = [self.getPoints(p) for p in self.polyLineROIs] n_masks = len(point_arrays) # Get the total size of the output dataframe m = sum([arr.shape[0] for arr in point_arrays]) # Format as a pandas.DataFrame df = pd.DataFrame( index=np.arange(m), columns=["filename", "mask_index", "y", "x", "vertex"]) df["filename"] = self.image_path c = 0 for mask_index, arr in enumerate(point_arrays): l = arr.shape[0] df.loc[c:c + l - 1, "mask_index"] = mask_index df.loc[c:c + l - 1, "y"] = arr[:, 0] df.loc[c:c + l - 1, "x"] = arr[:, 1] df.loc[c:c + l - 1, "vertex"] = np.arange(l) c += l # Save df.to_csv(out_path, index=False) # Save this directory as the last accessed self.set_currdir(out_path) def B_load_callback(self): """ Load a set of masks from a file previously saved with this GUI. This erases any currently defined masks. """ # Prompt the user to select a file path = getOpenFilePath(self, "Select mask CSV", "CSV and TIF files (*.csv *.tif *.tiff)", initialdir=self.get_currdir()) self.set_currdir(path) ext = os.path.splitext(path)[-1] # Open this file and check that it contains the necessary info if ext == ".csv": try: df = pd.read_csv(path) for c in ["filename", "mask_index", "y", "x", "vertex"]: assert c in df.columns except: print("File {} not in the correct format; must be a CSV with " \ "the 'filename', 'mask_index', 'y', 'x', and 'vertex' " \ "columns".format(path)) return # Warn the user if the image file path in this file doesn't # match the current image file path if df.loc[0, "filename"] != self.image_path: print("WARNING: original file path for this mask file is different " \ "than the current image file.\nOriginal: {}\nCurrent: {}".format( df.loc[0, "filename"], self.image_path)) elif ext in [".tif", ".tiff"]: # Load the mask image masks_im = tifffile.imread(path) assert len(masks_im.shape) == 2, "WARNING: RGB TIFs not supported" # Get the set of unique masks defined in this image n_points = 100 mask_indices = [j for j in list(np.unique(masks_im)) if j != 0] dfs = [] for mi in mask_indices: mask_im = masks_im == mi edges = get_edges(mask_im) points = get_ordered_mask_points(edges, max_points=n_points) df = pd.DataFrame(index=list(range(points.shape[0])), columns=["filename", "mask_index", "y", "x"]) df["filename"] = path df["mask_index"] = mi df["y"] = points[:, 0] df["x"] = points[:, 1] df["vertex"] = list(range(points.shape[0])) dfs.append(df) df = pd.concat(dfs, axis=0, ignore_index=True) # Erase the current set of masks, if any self.clear_polyLineROIs() # Generate a PolyLineROI object for each of the loaded masks for mask_index, mask_frame in df.groupby("mask_index"): self.points = np.asarray(mask_frame[["y", "x"]]) self.createPolyLineROI(self.points) self.points = [] def B_apply_callback(self): """ Callback for user selection of the "Apply masks" button. The user is prompted to select a file containing localizations, and the localizations in that file are classified according to which mask they belong to. """ # Prompt the user to select a file containing localizations path = getOpenFilePath( self, "Select localization file", "CSV files and *Tracked.mat files (*.csv *Tracked.mat)", initialdir=self.get_currdir()) self.set_currdir(path) # Open this file and make sure it actually contains localizations ext = os.path.splitext(path)[-1] try: if ext == ".csv": locs = pd.read_csv(path) assert all([c in locs.columns for c in ['frame', 'y', 'x']]) elif ext == ".mat": locs = tracked_mat_to_csv(path) except: print("Could not load file {}; must either be *.csv or " \ "*Tracked.mat format".format(path)) return # Prompt the user to input the mask column. This can be something # as simple as "mask_index", but the user may want something more # descriptive - like "nuclear_mask" or something. This becomes useful # when the user applies masks multiple times to the same file - for # instance, to label primary and secondary features (e.g. nuclei and # nucleoli) in an image. col = getTextInputs(["Output mask column name"], ["mask_index"], title="Select output column name")[0] # Prompt the user to select a mode for the assignment of trajectories # to masks. This can either be "by_localization" (independent of the # trajectory to which each localization is assigned), "single_point" # (assign all localizations to a mask if a single localization is # inside the mask), or "all_points" (only assign all localizations to a # mask if all localizations are inside the mask) options = ["by_localization", "single_point", "all_points"] box = SingleComboBoxDialog( "Method by which trajectories are assigned to masks", options, init_value="single_point", title="Assignment mode", parent=self) box.exec_() if box.Accepted: mode = box.return_val else: print("Dialog not accepted") return # Assign each localization to one of the current masks point_sets = [self.getPoints(p) for p in self.polyLineROIs] locs[col] = apply_masks(point_sets, locs, mode=mode) # Save to the same file if ext == ".csv": out_path = path elif ext == ".mat": if "_Tracked.mat" in path: out_path = path.replace("_Tracked.mat", ".csv") elif "Tracked.mat" in path: out_path = path.replace("Tracked.mat", ".csv") else: out_path = "{}.csv".format(os.path.splitext(path)[0]) locs.to_csv(out_path, index=False) # Save only the masked trajectories to a different file out_file_inside = "{}_in_mask.csv".format( os.path.splitext(out_path)[0]) out_file_outside = "{}_outside_mask.csv".format( os.path.splitext(out_path)[0]) locs[locs["mask_index"] > 0].to_csv(out_file_inside, index=False) locs[locs["mask_index"] == 0].to_csv(out_file_outside, index=False) # Show the result show_mask_assignments(point_sets, locs, mask_col=col, max_points_scatter=5000) def B_accept_callback(self): """ If this GUI is being used as a dialog, accept and return the currently defined set of masks. """ self.return_val = [self.getPoints(p) for p in self.polyLineROIs] print(self.return_val) self.accept()
class Ui_MainWindow(object): def __init__(self): self._timer = QtCore.QTimer() self._number_of_clicked = 0 self.worker = Pool(1) self.result_space = None def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1101, 700) pg.setConfigOption('background', 'w') self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout_3.setObjectName("verticalLayout_3") self.formLayout = QtWidgets.QFormLayout() self.formLayout.setObjectName("formLayout") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setObjectName("label") self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) self.comboBox_neighbourRule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_neighbourRule.setObjectName("comboBox_2") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_neighbourRule) self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setObjectName("label_3") self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3) self.comboBox_borderRule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_borderRule.setObjectName("comboBox") self.comboBox_borderRule.addItem("") self.comboBox_borderRule.addItem("") self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.comboBox_borderRule) self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setObjectName("label_2") self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_2) self.lineEdit_spaceSize = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_spaceSize.setObjectName("lineEdit") self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.lineEdit_spaceSize) self.label_4 = QtWidgets.QLabel(self.centralwidget) self.label_4.setObjectName("label_4") self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_4) self.lineEdit_randomGrain = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_randomGrain.setObjectName("lineEdit_2") self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.lineEdit_randomGrain) self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_4.setObjectName("pushButton_4") self.pushButton_4.clicked.connect(self.openFileNameDialog) self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.pushButton_4) self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_3.setObjectName("lineEdit_3") self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.lineEdit_3) self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_5.clicked.connect(self.openSaveDialog) self.pushButton_5.setObjectName("pushButton_5") self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.pushButton_5) self.radioButton = QtWidgets.QRadioButton(self.centralwidget) self.radioButton.setObjectName("radioButton") self.formLayout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.radioButton) self.verticalLayout_3.addLayout(self.formLayout) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.verticalLayout.addItem(spacerItem) # Animation and displaying widget self.graphicsView = ImageView(self.centralwidget) self.graphicsView.setMinimumSize(QtCore.QSize(0, 200)) self.graphicsView.setObjectName("graphicsView") self.graphicsView.ui.histogram.hide() self.graphicsView.ui.roiBtn.hide() self.graphicsView.ui.menuBtn.hide() self.graphicsView.show() self.verticalLayout.addWidget(self.graphicsView) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.verticalLayout.addItem(spacerItem1) self.verticalLayout_3.addLayout(self.verticalLayout) self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.pushButton_init = QtWidgets.QPushButton(self.centralwidget) self.pushButton_init.setObjectName("pushButton_init") self.pushButton_init.clicked.connect(self.init_ca_algo) self.verticalLayout_2.addWidget(self.pushButton_init) # Push button START/STOP self.pushButton_startStop = QtWidgets.QPushButton(self.centralwidget) self.pushButton_startStop.setObjectName("pushButton_3") self.pushButton_startStop.clicked.connect(self._init_image_timer) self.verticalLayout_2.addWidget(self.pushButton_startStop) self.pushButton_oneStep = QtWidgets.QPushButton(self.centralwidget) self.pushButton_oneStep.setObjectName("pushButton") self.pushButton_oneStep.clicked.connect(self._one_step) self.verticalLayout_2.addWidget(self.pushButton_oneStep) self.pushButton_clearSpace = QtWidgets.QPushButton(self.centralwidget) self.pushButton_clearSpace.setObjectName("pushButton_2") self.pushButton_clearSpace.clicked.connect(self._clear_space) self.verticalLayout_2.addWidget(self.pushButton_clearSpace) self.verticalLayout_3.addLayout(self.verticalLayout_2) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1101, 26)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) # fix self.init_ca_algo() self._clear_space() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", "Neighbours rule")) self.comboBox_neighbourRule.setItemText( 0, _translate("MainWindow", "VONNEUMANN")) self.comboBox_neighbourRule.setItemText( 1, _translate("MainWindow", "MOORE")) self.comboBox_neighbourRule.setItemText( 2, _translate("MainWindow", "HEXAGONAL_LEFT")) self.comboBox_neighbourRule.setItemText( 3, _translate("MainWindow", "HEXAGONAL_RIGHT")) self.comboBox_neighbourRule.setItemText( 4, _translate("MainWindow", "PENTAGONAL_LEFT")) self.comboBox_neighbourRule.setItemText( 5, _translate("MainWindow", "PENTAGONAL_RIGHT")) self.label_3.setText(_translate("MainWindow", "Border rule")) self.comboBox_borderRule.setItemText( 0, _translate("MainWindow", "ABSORBING")) self.comboBox_borderRule.setItemText( 1, _translate("MainWindow", "SNAKELIKE")) self.label_2.setText(_translate("MainWindow", "Space size")) self.lineEdit_spaceSize.setText(_translate("MainWindow", "50")) self.label_4.setText(_translate("MainWindow", "Number of grains")) self.lineEdit_randomGrain.setText(_translate("MainWindow", "10")) self.pushButton_4.setText(_translate("MainWindow", "Import csv")) self.pushButton_5.setText(_translate("MainWindow", "Export csv")) self.radioButton.setText(_translate("MainWindow", "Extended mode")) self.pushButton_startStop.setText( _translate("MainWindow", "Start/Stop")) self.pushButton_oneStep.setText(_translate("MainWindow", "Step")) self.pushButton_clearSpace.setText( _translate("MainWindow", "Clear space")) self.pushButton_init.setText(_translate("MainWindow", "Init space")) def init_ca_algo(self): self.result_space = None self._ca_algo = CellularAutomata( int(self.lineEdit_randomGrain.text()), int(self.lineEdit_spaceSize.text()), int(self.lineEdit_spaceSize.text()), str(self.comboBox_borderRule.currentText()), str(self.comboBox_neighbourRule.currentText())) self._ca_algo.add_random() self.generatePgColormap() self.display_image() def _init_image_timer(self): self._number_of_clicked += 1 if self._number_of_clicked % 2: self._timer.timeout.connect(self._update_func) self._timer.start(50) else: self._timer.stop() def _update_func(self): if self.result_space is None: self.result_space = self.worker.apply_async(self._ca_algo.one_step) return if self.result_space.ready(): self._ca_algo.space = self.result_space.get() self.display_image() self.result_space = self.worker.apply_async(self._ca_algo.one_step) def _clear_space(self): self.result_space = None self._timer.stop() self._ca_algo.space = self._ca_algo.space_clear self.graphicsView.clear() def _one_step(self): self._timer.stop() self._ca_algo.one_step() self.display_image() def display_image(self): self.graphicsView.setImage( self._ca_algo.space.T, levels=(0.0, self._ca_algo.number_of_reserved_ids + float(self.lineEdit_randomGrain.text()))) def generatePgColormap(self): self.pos = np.linspace( 0.0, 1.0, self._ca_algo.number_of_reserved_ids + int(self.lineEdit_randomGrain.text())) self.cmap = pg.ColorMap(pos=self.pos, color=self._ca_algo.color_id) self.graphicsView.setColorMap(self.cmap) def openSaveDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self.centralwidget, "Save to CSV file", "", "CSV Files (*.csv)", options=options) if fileName: pd.DataFrame(self._ca_algo.space).to_csv(fileName) def openFileNameDialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self.centralwidget, "Open CSV file", "", "CSV Files (*.csv)", options=options) if fileName: self._ca_algo.space = pd.read_csv(fileName, index_col=0).astype(int).values self.graphicsView.setImage(self._ca_algo.space.T)
class StartWindow(QMainWindow): def __init__(self, camera=None, spectrometer=None): super().__init__() self.camera = camera self.spectrometer = spectrometer self.title = 'LECacqGUI' self.left = 1000 self.top = 1000 self.width = 1000 self.height = 1000 self.icon = "logo_LEC.ico" self.setWindowTitle(self.title) self.setWindowIcon(QtGui.QIcon(self.icon)) self.central_widget = QWidget() self.button_frame = QPushButton('Acquire Frame', self.central_widget) self.button_frame.resize(5000, 5000) self.button_movie = QPushButton('Start Live View', self.central_widget) # self.button_stop_movie = QPushButton('Stop Live View', self.central_widget) self.button_single_spectra = QPushButton('Acquire Single Spectra', self.central_widget) self.button_live_spectra = QPushButton('Start Live AcqSpectra', self.central_widget) self.image_view = ImageView() self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 1000) self.spectra_view = pg.GraphicsLayoutWidget() self.layout = QGridLayout(self.central_widget) self.showFullScreen() self.layout.addWidget(self.image_view, 0, 0) self.layout.addWidget(self.slider, 1, 0) self.layout.addWidget(self.button_frame, 2, 0) self.layout.addWidget(self.button_movie, 3, 0) # self.layout.addWidget(self.button_stop_movie, 4, 0) self.setCentralWidget(self.central_widget) self.layout.addWidget(self.spectra_view, 0, 1) self.layout.addWidget(self.button_single_spectra, 1, 1) self.layout.addWidget(self.button_live_spectra, 2, 1) self.spectra_plot = self.spectra_view.addPlot( title='Live Spectra Acquisition') self.spectra_plot.showGrid(x=True, y=True, alpha=1.0) x_axis = self.spectra_plot.getAxis('bottom') y_axis = self.spectra_plot.getAxis('left') x_axis.setLabel(text='Wavelength [nm]') # set axis labels y_axis.setLabel(text='Intensity [u.a.]') self.drawplot = self.spectra_plot.plot(pen='y') self.x = wavel_array self.y = wavel_array self.drawplot.setData(self.x, self.y) QtGui.QApplication.processEvents() self.exporter = pg.exporters.ImageExporter(self.spectra_view.scene()) self.image_counter = 1 self.button_frame.clicked.connect(self.update_image) self.button_movie.clicked.connect(self.start_movie) # self.button_stop_movie.clicked.connect(self.camera.stop) self.button_single_spectra.clicked.connect(self.update_single_spectra) self.button_live_spectra.clicked.connect(self.start_live_spectra) self.slider.valueChanged.connect(self.update_brightness) self.update_timer = QTimer() self.update_timer.timeout.connect(self.update_movie) self.spectra_update_timer = QTimer() self.spectra_update_timer.timeout.connect(self.update_live_spectra) def update_image(self): self.camera.stop() frame = self.camera.get_frame() self.image_view.setImage(frame, autoHistogramRange=True) def update_movie(self): self.image_view.setImage(self.camera.last_frame) def update_single_spectra(self): self.spectrometer.stop() single_spectra = self.spectrometer.measure_spectra(2, '50 ms') self.drawplot.setData(single_spectra) def update_live_spectra(self): # intensity = self.spectrometer.measure_spectra(2,'50 ms') self.drawplot.setData(self.spectrometer.last_intensity) # self.update_framerate() def update_brightness(self, value): value /= 10 self.camera.set_brightness(value) def start_movie(self): self.camera.stopped = False self.movie_thread = MovieThread(self.camera) self.movie_thread.start() self.update_timer.start(100) def start_live_spectra(self): self.spectrometer.stopped = False self.spectra_thread = SpectraThread(self.spectrometer) self.spectra_thread.start() self.spectra_update_timer.start(10)
class Ui_MainWindow(object): def __init__(self): self._timer = QtCore.QTimer() self._number_of_clicked = 0 def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1101, 876) pg.setConfigOption('background', 'w') self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout_3.setObjectName("verticalLayout_3") self.formLayout = QtWidgets.QFormLayout() self.formLayout.setObjectName("formLayout") self.label = QtWidgets.QLabel(self.centralwidget) self.label.setObjectName("label") self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label) self.comboBox_neighbourRule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_neighbourRule.setObjectName("comboBox_2") self.comboBox_neighbourRule.addItem("") self.comboBox_neighbourRule.addItem("") self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_neighbourRule) self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setObjectName("label_3") self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_3) self.comboBox_borderRule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_borderRule.setObjectName("comboBox") self.comboBox_borderRule.addItem("") self.comboBox_borderRule.addItem("") self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.comboBox_borderRule) self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setObjectName("label_2") self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_2) self.lineEdit_spaceSize = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_spaceSize.setObjectName("lineEdit") self.formLayout.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.lineEdit_spaceSize) self.label_4 = QtWidgets.QLabel(self.centralwidget) self.label_4.setObjectName("label_4") self.formLayout.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_4) self.lineEdit_randomGrain = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_randomGrain.setObjectName("lineEdit_2") self.formLayout.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.lineEdit_randomGrain) self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_4.setObjectName("pushButton_4") self.formLayout.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.pushButton_4) self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_3.setObjectName("lineEdit_3") self.formLayout.setWidget(7, QtWidgets.QFormLayout.FieldRole, self.lineEdit_3) self.pushButton_5 = QtWidgets.QPushButton(self.centralwidget) self.pushButton_5.setObjectName("pushButton_5") self.formLayout.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.pushButton_5) self.radioButton = QtWidgets.QRadioButton(self.centralwidget) self.radioButton.setObjectName("radioButton") self.formLayout.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.radioButton) self.verticalLayout_3.addLayout(self.formLayout) self.verticalLayout = QtWidgets.QVBoxLayout() self.verticalLayout.setObjectName("verticalLayout") spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.verticalLayout.addItem(spacerItem) # Animation and displaying widget self.graphicsView = ImageView(self.centralwidget) self.graphicsView.setMinimumSize(QtCore.QSize(0, 500)) self.graphicsView.setObjectName("graphicsView") self.graphicsView.ui.histogram.hide() self.graphicsView.ui.roiBtn.hide() self.graphicsView.ui.menuBtn.hide() self.graphicsView.show() self.verticalLayout.addWidget(self.graphicsView) spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) self.verticalLayout.addItem(spacerItem1) self.verticalLayout_3.addLayout(self.verticalLayout) self.verticalLayout_2 = QtWidgets.QVBoxLayout() self.verticalLayout_2.setObjectName("verticalLayout_2") self.pushButton_init = QtWidgets.QPushButton(self.centralwidget) self.pushButton_init.setObjectName("pushButton_init") self.pushButton_init.clicked.connect(self.init_ca_algo) self.verticalLayout_2.addWidget(self.pushButton_init) # Push button START/STOP self.pushButton_startStop = QtWidgets.QPushButton(self.centralwidget) self.pushButton_startStop.setObjectName("pushButton_3") self.pushButton_startStop.clicked.connect(self._init_image_timer) self.verticalLayout_2.addWidget(self.pushButton_startStop) self.pushButton_oneStep = QtWidgets.QPushButton(self.centralwidget) self.pushButton_oneStep.setObjectName("pushButton") self.pushButton_oneStep.clicked.connect(self._one_step) self.verticalLayout_2.addWidget(self.pushButton_oneStep) self.pushButton_clearSpace = QtWidgets.QPushButton(self.centralwidget) self.pushButton_clearSpace.setObjectName("pushButton_2") self.pushButton_clearSpace.clicked.connect(self._clear_space) self.verticalLayout_2.addWidget(self.pushButton_clearSpace) self.verticalLayout_3.addLayout(self.verticalLayout_2) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1101, 26)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label.setText(_translate("MainWindow", "Neighbours rule")) self.comboBox_neighbourRule.setItemText( 0, _translate("MainWindow", "VONNEUMANN")) self.comboBox_neighbourRule.setItemText( 1, _translate("MainWindow", "MOORE")) self.label_3.setText(_translate("MainWindow", "Border rule")) self.comboBox_borderRule.setItemText( 0, _translate("MainWindow", "ABSORBING")) self.comboBox_borderRule.setItemText( 1, _translate("MainWindow", "SNAKELIKE")) self.label_2.setText(_translate("MainWindow", "Space size")) self.lineEdit_spaceSize.setText(_translate("MainWindow", "50")) self.label_4.setText(_translate("MainWindow", "Number of grains")) self.lineEdit_randomGrain.setText(_translate("MainWindow", "10")) self.pushButton_4.setText(_translate("MainWindow", "Import csv")) self.pushButton_5.setText(_translate("MainWindow", "Export csv")) self.radioButton.setText(_translate("MainWindow", "Extended mode")) self.pushButton_startStop.setText( _translate("MainWindow", "Start/Stop")) self.pushButton_oneStep.setText(_translate("MainWindow", "Step")) self.pushButton_clearSpace.setText( _translate("MainWindow", "Clear space")) self.pushButton_init.setText(_translate("MainWindow", "Init space")) def init_ca_algo(self): self._ca_algo = CellularAutomata( int(self.lineEdit_randomGrain.text()), int(self.lineEdit_spaceSize.text()), int(self.lineEdit_spaceSize.text()), str(self.comboBox_borderRule.currentText()), str(self.comboBox_neighbourRule.currentText())) self._ca_algo.add_random() self.generatePgColormap() self.graphicsView.setImage(self._ca_algo.space) def _init_image_timer(self): self._number_of_clicked += 1 if self._number_of_clicked % 2: self._timer.timeout.connect(self._update_func) self._timer.start(50) else: self._timer.stop() def _update_func(self): if self._ca_algo.cell_empty in self._ca_algo.space: self._ca_algo.one_step() self.graphicsView.setImage(self._ca_algo.space) def _clear_space(self): self._timer.stop() self._ca_algo.space = self._ca_algo.space_clear self.graphicsView.clear() def _one_step(self): self._timer.stop() self._ca_algo.one_step() self.graphicsView.setImage(self._ca_algo.space) def generatePgColormap(self): self.pos = np.linspace(0.0, 1.0, 2 + int(self.lineEdit_randomGrain.text())) self.cmap = pg.ColorMap(pos=self.pos, color=self._ca_algo.color_id) self.graphicsView.setColorMap(self.cmap)
class StartWindow(QMainWindow): def __init__(self, camera=None): super().__init__() # Main Window Widget pg.setConfigOption('background', 'w') self.central_widget = QWidget() self.aimwig = QWidget() QApplication.setStyle(QStyleFactory.create('Fusion')) # self.changePalette() # parameters self.framerate = 50 self.roi = [195, 148, 224, 216] self.datalen = 150 self.movingpt = 50 self.exp = 50 self.gain = 50 self.level = None self.lock = True self.avgval = 44 self.roi_flag = False # Camera self.camera = camera # First Horizon row Widgets self.button_start = QPushButton("Start/Stop") self.button_start.setStyleSheet("background-color:rgb(252,42,71)") self.button_start.setCheckable(True) self.button_reset= QPushButton('Reset/Update') self.button_save= QPushButton('SaveData') # Second Horizontal row Widgets self.button_locklevel= QPushButton("LockLevel") self.button_locklevel.setCheckable(True) self.value_locklevel= QLineEdit(str(self.level)) self.value_locklevel.textChanged.connect(self.update_parameters) self.label_framerate = QLabel("FRate(millisec)") self.value_framerate = QLineEdit(str(self.framerate)) self.value_framerate.textChanged.connect(self.update_parameters) self.label_movingpt = QLabel("MovingPoints") self.value_movingpt = QLineEdit(str(self.movingpt)) self.value_movingpt.textChanged.connect(self.update_parameters) self.label_roi = QLabel("ROI") self.value_roi = QLineEdit(str(self.roi)[1:-1]) self.value_roi.setFixedWidth(200) self.value_roi.textChanged.connect(self.change_reset_col) self.label_datalen = QLabel("Length") self.value_datalen = QLineEdit(str(self.datalen)) self.value_datalen.textChanged.connect(self.update_parameters) self.cbox_raw= QCheckBox("RawCurve") self.cbox_raw.setChecked(True) self.label_avgval = QLabel("AvgVal: " + str(format(int(self.avgval),"010d"))) self.label_avgval.setStyleSheet("border: 1px solid black"); # Bottom slider Widgets self.label_eslider= QLabel("Exposure: " + str(self.exp)) self.slider_eslider = QSlider(Qt.Horizontal) self.slider_eslider.setRange(0, 100) self.camera.set_exposure(self.exp) self.slider_eslider.setValue(self.exp) self.label_gslider= QLabel("Gain" + str(self.gain) ) self.slider_gslider = QSlider(Qt.Horizontal) self.slider_gslider.setRange(0, 100) self.slider_gslider.setValue(self.gain) # Image View Widgets self.image_view = ImageView(self.aimwig) self.roi_view = ImageView() # self.roi = pg.CircleROI([80, 50], [20, 20], pen=(4,9)) # self.image_view.addItem(self.roi) # Intensity Graph Widget self.gwin = pg.GraphicsWindow() self.rplt = self.gwin.addPlot() self.pen1 = pg.mkPen('r', width=2) self.pen2 = pg.mkPen(color=(255, 15, 15),width=2) self.pen3 = pg.mkPen(color=(000, 155, 115), style=QtCore.Qt.DotLine) self.curve = self.rplt.plot(pen=self.pen3) self.curve2 = self.rplt.plot(pen=self.pen2) self.rplt.showGrid(x=True, y=True) self.data = [] self.avg_data = [] self.count = 0 # Layouts self.mainlayout = QVBoxLayout(self.central_widget) self.btn1layout = QHBoxLayout() self.btn2layout = QHBoxLayout() self.img1layout = QHBoxLayout() self.sld1layout = QHBoxLayout() self.btn1layout.addWidget(self.button_start) self.btn1layout.addWidget(self.button_reset) self.btn1layout.addWidget(self.button_save) self.btn2layout.addWidget(self.button_locklevel) self.btn2layout.addWidget(self.value_locklevel) self.btn2layout.addWidget(self.label_framerate) self.btn2layout.addWidget(self.value_framerate) self.btn2layout.addWidget(self.label_datalen) self.btn2layout.addWidget(self.value_datalen) self.btn2layout.addWidget(self.label_movingpt) self.btn2layout.addWidget(self.value_movingpt) self.btn2layout.addWidget(self.label_roi) self.btn2layout.addWidget(self.value_roi) self.btn2layout.addWidget(self.cbox_raw) self.btn2layout.addWidget(self.label_avgval) self.img1layout.addWidget(self.image_view) self.img1layout.addWidget(self.roi_view) self.sld1layout.addWidget(self.label_eslider) self.sld1layout.addWidget(self.slider_eslider) self.sld1layout.addWidget(self.label_gslider) self.sld1layout.addWidget(self.slider_gslider) self.mainlayout.addLayout(self.btn1layout) self.mainlayout.addLayout(self.btn2layout) self.mainlayout.addLayout(self.img1layout) self.mainlayout.addLayout(self.sld1layout) self.mainlayout.addWidget(self.gwin) self.setCentralWidget(self.central_widget) # Functionality self.button_start.clicked.connect(self.update_image) self.button_start.clicked.connect(self.change_start_col) self.button_reset.clicked.connect(self.reset_run) self.button_locklevel.clicked.connect(self.locklevel) self.button_save.clicked.connect(self.save_parameters) self.slider_eslider.valueChanged.connect(self.update_exposure) self.slider_gslider.valueChanged.connect(self.update_gain) self.update_timer = QTimer() self.update_timer.timeout.connect(self.update_image) self.update_timer.timeout.connect(self.update_plot) ## SETTING UP FIRST IMAGE self.first_frame = self.camera.get_frame() self.update_image() self.first_roi = self.getroiimage() def change_reset_col(self): self.button_reset.setStyleSheet("background-color:rgb(252,42,71)") self.roi_flag = True def change_start_col(self): if self.button_start.isChecked(): self.button_start.setStyleSheet("default") if self.button_start.isChecked() is False: self.button_start.setStyleSheet("background-color:rgb(252,42,71)") def update_image(self): self.frame = self.camera.get_frame() self.roi_img = self.getroiimage() if(np.sum(self.roi_img)>100): self.roi_view.setImage(self.roi_img.T, autoLevels=self.lock, levels=self.level) self.image_view.setImage(self.frame.T, autoLevels=self.lock, levels=self.level) if self.button_start.isChecked(): self.update_timer.start(self.framerate) if self.button_start.isChecked() is False: self.update_timer.stop() def locklevel(self): if self.button_locklevel.isChecked(): self.level = self.image_view.quickMinMax(self.frame) self.lock = False if self.button_locklevel.isChecked() is False: self.level = None self.lock = True def reset_run(self): if self.roi_flag == False: self.data=[] self.avg_data=[] self.curve.clear() self.curve2.clear() if self.roi_flag == True: self.update_parameters() self.roi_flag = False def getroiimage(self): # r = [195, 148, 224, 216] r = self.roi r = np.array(r) self.roi_img = self.frame[int(r[1]):int(r[1] + r[3]), int(r[0]):int(r[0] + r[2])] return self.roi_img def update_exposure(self, value): self.exp = value self.button_start.setChecked(False) self.camera.set_exposure(self.exp) self.label_eslider.setText("Exposure: "+str(self.exp)) self.button_start.setStyleSheet("background-color:rgb(252,42,71)") def update_gain(self, value): self.gain = value self.button_start.setChecked(False) self.camera.set_gain(self.gain) self.label_gslider.setText("Gain: "+str(self.gain)) self.button_start.setStyleSheet("background-color:rgb(252,42,71)") def moving_average(self): a = np.array(self.data) tsum = np.cumsum(a, dtype=float) tsum[self.movingpt:] = tsum[self.movingpt:] - tsum[:-self.movingpt] tsum = tsum[self.movingpt - 1:] / self.movingpt return tsum def update_plot(self): mlen = self.datalen self.data.append(np.sum(self.roi_img)) if len(self.data) > self.datalen: self.data.pop(0) if len(self.data) > self.movingpt + 5: mdata = self.moving_average() mlen = len(mdata) self.curve2.setData(mdata) if self.cbox_raw.isChecked(): self.curve.setData(np.hstack(self.data[-mlen:])) else: self.curve.clear() if len(self.data) > 21: self.avgval = np.average(self.data[-20:]) self.label_avgval.setText("AvgVal: " + str(format(int(self.avgval),"010d"))) def update_parameters(self): if self.value_framerate.text().isdigit(): self.framerate = int(self.value_framerate.text()) if self.value_datalen.text().isdigit(): self.datalen = int(self.value_datalen.text()) if(self.datalen < len(self.data)): self.reset_run() if self.value_movingpt.text().isdigit(): self.movingpt= int(self.value_movingpt.text()) templevel = self.value_locklevel.text().split(sep=",") if (len(templevel) == 2): self.level= tuple([int(float(i)) for i in templevel]) print(self.level) del templevel temproi = self.value_roi.text().split(sep=",") if (len(temproi) == 4): self.roi = [int(float(i)) for i in temproi] del temproi print("hitesh") self.button_reset.setStyleSheet("default") # def save_parameters(self): # tfile = open("./log.txt", "a+") # tfile.write("\nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") # tfile.write("\nDateTime:: ") # tfile.write(str(datetime.now())) # tfile.write("\nROI:: ") # tfile.write(str(self.roi)) # tfile.write("\nExposure:: ") # tfile.write(str(self.exp)) # tfile.write("\nGain:: ") # tfile.write(str(self.gain)) # tfile.write("\n") # tfile.write("Intensity:: ") # tfile.write(str(np.sum(self.roi_img))) # tfile.write("\n") # tfile.write("\nLevel:: ") # tfile.write(str(self.level)) # tfile.write("\n") # np.sum(self.roi_img) # timestamp = datetime.timestamp(datetime.now()) # filename = "camimg" + str(timestamp) + ".png" # tfile.write("ImageFile:: ") # tfile.write(str(filename)) # tfile.write("\n") # cv2.imwrite(filename,self.frame[:,:,0]) # tfile.close() def save_parameters(self): tfile = open("./log.txt", "a+") timestamp = datetime.timestamp(datetime.now()) filename = "camimg" + str(timestamp) + ".png" tfile.write("\n") cv2.imwrite(filename,self.frame[:,:,0]) tfile.write("DateTime,\t\t\t\t\tROI,\t\t\t\t\tExposure,\tGain,\tIntensity(Total),\tIntensity(ROI),\tLevel,\tImagefile,\n") tfile.write(str(datetime.now()) + ",\t" + str(self.roi) + ",\t" + str(self.exp) + ",\t\t\t" + str(self.gain) + ",\t\t" + str(np.sum(self.frame)) + ",\t\t\t" + str(np.sum(self.roi_img)) +",\t\t\t" + str(self.image_view.quickMinMax(self.frame)) + ",\t" + str(filename)) tfile.close()
class StartWindow(QMainWindow): ''' Main interface window class with the follow main elements: - Button to open data capture folder - Image frame widget to visualize images - Slider to transition images among the sequence of the capture ''' def __init__(self): ''' UI initialization with essential feature set for the media player ''' super(QMainWindow, self).__init__() self.setWindowTitle("Media player Vision system") self.central_widget = QWidget() # Central widget of the window self.button_loadfolder = QPushButton( 'Load folder' ) # Defines button and attaches it to the central widget self.button_next_capture = QPushButton('Next capture') self.button_previous_capture = QPushButton('Prev capture') self.button_next_camera = QPushButton( 'Next camera' ) # Access to next camera image sequence for a given capture self.button_previous_camera = QPushButton( 'Prev camera' ) # Access to previous camera image sequence for a given capture self.button_play = QPushButton( 'Play/pause' ) # Reproduce a sequence of images for a given capture and camera self.info_label = QLabel() self.camera_number_label = QLabel() self.camera_label = QLabel() self.capture_label = QLabel() self.image_view = ImageView() # Image frame definition self.image_view.ui.histogram.hide() # Hides histogram from image frame self.image_view.ui.roiBtn.hide() # Hides roi button from image frame self.image_view.ui.menuBtn.hide() # Hides menu button from image frame self.slider = QSlider(Qt.Horizontal) # Horizontal slider definition self.slider.setRange(0, 100) # Sets range of slider between 0 and 100 self.slider.setTickPosition( QSlider.TicksBelow) # Position ticks below slider self.slider.setTickInterval(10) # Tick interval set to 10 self.layout_left = QVBoxLayout() # Vertical layout definition self.layout_left.addWidget( self.info_label) # Information label with basic instructions self.layout_left.addWidget( self.button_loadfolder) # Add button load folder to layout self.layout_left.addWidget( self.button_next_capture) # Add button load folder to layout self.layout_left.addWidget( self.button_previous_capture) # Add button load folder to layout self.layout_left.addWidget( self.button_next_camera) # Add button load folder to layout self.layout_left.addWidget( self.button_previous_camera) # Add button load folder to layout self.layout_left.addWidget( self.button_play) # Add button play/pause to layout self.layout_left.addWidget(self.camera_number_label) self.layout_left.addWidget(self.camera_label) self.layout_left.addWidget(self.capture_label) self.layout_left.addWidget( self.image_view) # Add image frame to layout #self.layout_left.addWidget(self.slider) # Add horizontal slider to layout self.info_label.setWordWrap(True) self.info_label.setText( "1) Load a data capture folder opening the Load Folder dialog. \n\n2) Then interact with different captures, cameras and sequence player using the buttons." ) self.stitcher_label = QLabel() self.stitcher_view = ImageView() # Image frame definition self.checkbox_status_label = QLabel() self.stitcher_checkbox = QCheckBox() self.stitcher_view.ui.histogram.hide( ) # Hides histogram from image frame self.stitcher_view.ui.roiBtn.hide( ) # Hides roi button from image frame self.stitcher_view.ui.menuBtn.hide( ) # Hides menu button from image frame self.layout_right = QVBoxLayout() self.layout_right.addWidget(self.stitcher_label) self.layout_right.addWidget(self.checkbox_status_label) self.layout_right.addWidget(self.stitcher_checkbox) self.layout_right.addWidget(self.stitcher_view) self.stitcher_label.setWordWrap(True) self.stitcher_label.setText("Stitched image\n\n") self.checkbox_status_label.setWordWrap(True) self.checkbox_status_label.setText("Check to enable stitching") self.layout_h = QHBoxLayout() self.layout_h.addLayout(self.layout_left) self.layout_h.addLayout(self.layout_right) self.layout = QVBoxLayout(self.central_widget) self.layout.addLayout(self.layout_h) self.layout.addWidget(self.slider) self.setCentralWidget(self.central_widget) self.button_loadfolder.clicked.connect( self.load_files ) # Connects function self.load_files to the action clicked over button loadfolder self.button_next_capture.clicked.connect( self.next_capture ) # Connects function next_capture to action clicked over button self.button_previous_capture.clicked.connect( self.previous_capture ) # Connects function previous_capture to action clicked over button self.button_next_camera.clicked.connect(self.next_camera) self.button_previous_camera.clicked.connect(self.previous_camera) self.button_play.clicked.connect(self.play_pause) self.slider.valueChanged.connect( self.update_image ) # Connects function self.update_image to action change in slider position self.stitcher_checkbox.setChecked(False) self.stitcher_checkbox.stateChanged.connect(self.check_stitching) self.button_next_capture.setEnabled(False) self.button_previous_capture.setEnabled(False) self.button_next_camera.setEnabled(False) self.button_previous_camera.setEnabled(False) self.button_play.setEnabled(False) self.slider.setEnabled(False) self.data_reader = data_reader() # Instantiation of data reader class self.threadpool = QThreadPool() print("Multithreading with maximum %d threads" % self.threadpool.maxThreadCount()) self.inThread = False # True when thread of image sequence is running self.playing = False # True when play is active self.endThread = False # True when signal to end thread has been activated self.image_index = None # Used to define image index in current reproduction thread self.calibrator = calibration_utils() self.calibrator.load_calibration() self.local_intrinsic = True self.cam_labels = None self.stitcher_ready = False def load_files(self): ''' This function loads all the images from a data capture folder using a data_reader object and stores the data in a customized data structure (3-d list). The dimensionality of this array is used to scale the extend of the capture index, camera index and the slider (timestamp sequence). ''' # Options definition for QFileDialog options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog options |= QFileDialog.DontUseCustomDirectoryIcons dialog = QFileDialog() # File dialog object instantiation dialog.setOptions(options) # Assign options to QFileDialog dialog.setFileMode(QFileDialog.DirectoryOnly ) # Set file dialog to directory search only folder = dialog.getExistingDirectory( self, 'Select directory' ) # Assigns the variable folder when path has been defined in the dialog if folder: print(folder) try: if self.inThread: self.endThread = True self.data_reader.load_data( folder ) # load data from selected folder using the object data_reader self.cam_labels = dict([ (value, key) for key, value in self.data_reader.camera_labels.items() ]) self.slider.setRange( 0, len(self.data_reader.images[ self.data_reader.current_capture][ self.data_reader.current_camera]) - 1 ) # Sets slider range according to dimensions of self.data_reader.images list self.slider.setTickInterval( int( len(self.data_reader.images[ self.data_reader.current_capture][ self.data_reader.current_camera]) / 10)) # Positions ticks 1/10th of the total list length self.image_index = self.slider.value() self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.slider.value()]) self.camera_number_label.setText( "There are " + str(len(self.data_reader.camera_labels)) + " cameras in the current capture.") self.capture_label.setText("Capture " + str( (self.data_reader.current_capture) + 1)) self.camera_label.setText("Camera " + str( (2 - self.data_reader.current_camera) + 1)) self.button_next_capture.setEnabled(True) self.button_previous_capture.setEnabled(True) self.button_next_camera.setEnabled(True) self.button_previous_camera.setEnabled(True) self.button_play.setEnabled(True) self.slider.setEnabled(True) except: print( 'Error reading folder, verify that folder includes csv file and belongs to a datacapture type.' ) def update_image(self, value): ''' Updates image when moving the slider along the length of sequence of images for a given capture image set ''' # If value is in the range of self.data_reader.images if value < len( self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]): self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][value]) self.image_index = value else: print('Slider value is outside the range of images' ) # Value in slider is out of range of list indexes def next_capture(self): ''' Access to next capture data structure. Updates the image frame with the first image of the capture for the first camera index. ''' self.data_reader.current_capture += 1 if (self.data_reader.current_capture > (len(self.data_reader.images) - 1)): self.data_reader.current_capture = 0 self.data_reader.current_camera = len( self.data_reader.camera_labels) - 1 self.camera_number_label.setText( "There are " + str(len(self.data_reader.camera_labels)) + " in the current capture.") self.capture_label.setText("Capture " + str((self.data_reader.current_capture) + 1)) self.camera_label.setText("Camera " + str((2 - self.data_reader.current_camera) + 1)) if self.inThread: self.endThread = True self.slider.setRange( 0, len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) - 1) # Sets range of slider between 0 and 100 self.slider.setTickInterval( int( len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) / 10)) # Tick interval set to 10 self.slider.setValue(0) self.image_index = 0 self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.slider.value()]) def previous_capture(self): ''' Access to previous capture data structure. Updates the image frame with the first image of the capture for the first camera index. ''' self.data_reader.current_capture -= 1 if (self.data_reader.current_capture < 0): self.data_reader.current_capture = len(self.data_reader.images) - 1 self.data_reader.current_camera = len( self.data_reader.camera_labels) - 1 self.camera_number_label.setText( "There are " + str(len(self.data_reader.camera_labels)) + " in the current capture.") self.capture_label.setText("Capture " + str((self.data_reader.current_capture) + 1)) self.camera_label.setText("Camera " + str((2 - self.data_reader.current_camera) + 1)) if self.inThread: self.endThread = True self.slider.setRange( 0, len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) - 1) # Sets range of slider between 0 and 100 self.slider.setTickInterval( int( len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) / 10)) # Tick interval set to 10 self.slider.setValue(0) self.image_index = 0 self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.slider.value()]) def next_camera(self): ''' Access to next camera image set. Updates the image frame with the first image of the camera image set. ''' self.data_reader.current_camera -= 1 if (self.data_reader.current_camera < 0): self.data_reader.current_camera = len( self.data_reader.camera_labels) - 1 self.camera_label.setText("Camera " + str((2 - self.data_reader.current_camera) + 1)) if not (self.inThread): self.slider.setRange( 0, len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) - 1) # Sets range of slider between 0 and 100 self.slider.setTickInterval( int( len(self.data_reader.images[ self.data_reader.current_capture][ self.data_reader.current_camera]) / 10)) # Tick interval set to 10 self.slider.setValue(0) self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.slider.value()]) self.image_index = self.slider.value() # When a reproduction thread is running and pause is active, update image with new camera index if (self.inThread and not (self.playing)): self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.image_index]) def previous_camera(self): ''' Access to next camera image set. Updates the image frame with the first image of the camera image set. ''' self.data_reader.current_camera += 1 if (self.data_reader.current_camera > (len(self.data_reader.camera_labels) - 1)): self.data_reader.current_camera = 0 self.camera_label.setText("Camera " + str((2 - self.data_reader.current_camera) + 1)) if not (self.inThread): self.slider.setRange( 0, len(self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera]) - 1) # Sets range of slider between 0 and 100 self.slider.setTickInterval( int( len(self.data_reader.images[ self.data_reader.current_capture][ self.data_reader.current_camera]) / 10)) # Tick interval set to 10 self.slider.setValue(0) self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.slider.value()]) self.image_index = self.slider.value() # When a reproduction thread is running and pause is active, update image with new camera index if (self.inThread and not (self.playing)): self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.image_index]) def show_image(self, image_path): image = cv2.imread(image_path) # Loads image image = cv2.flip(image, 0) # If intrinsic calibration available if self.calibrator.intrinsic_calibration[ "mtx"] is not None and self.local_intrinsic: # Undistord the image image = cv2.undistort( src=image, cameraMatrix=self.calibrator.intrinsic_calibration["mtx"], distCoeffs=self.calibrator.intrinsic_calibration["dist"]) #print('Intrinsic has been applied!') # If extrinsic calibration available current_camera_label = self.cam_labels[ self.data_reader.current_camera] if self.calibrator.extrinsic_calibrations[current_camera_label][ "M"] is not None: image = cv2.warpPerspective( src=image, M=self.calibrator. extrinsic_calibrations[current_camera_label]["M"], dsize=self.calibrator. extrinsic_calibrations[current_camera_label]["dst_size"]) #draw_extrinsic(img_src=image, src_pts=self.calibrator.extrinsic_calibrations[current_camera_label]["src_pts"]) #print('Extrinsic has been applied!') self.image_view.setImage( image[:, :, 0].T, autoRange=True) # Displays image in image frame if self.stitcher_checkbox.isChecked() != 0: self.show_stitcher() def show_stitcher(self): images_dic = { self.cam_labels[i]: self.data_reader.images[ self.data_reader.current_capture][i][self.image_index] for i in self.cam_labels.keys() } if not (self.stitcher_ready): # Stitcher variables stitcher_conf_path = save_path = os.path.join( os.path.dirname(os.getenv(key="CAM_PORTS_PATH")), 'Stitcher_config.pkl') self.camera_stitcher = Stitcher(images_dic=images_dic, super_mode=False) self.camera_stitcher = self.camera_stitcher.load_stitcher( load_path=stitcher_conf_path) self.stitcher_ready = True print('Stitcher file has been loaded!') #print(images_dic) for key in images_dic.keys(): images_dic[key] = cv2.flip( cv2.imread(self.data_reader.path + '/data/' + images_dic[key]), 0)[:, :, 0].T stitcher_img = self.camera_stitcher.stitch(images_dic=images_dic) #print('Shape: ', stitcher_img.shape) self.stitcher_view.setImage(stitcher_img, autoRange=True) def progress_fn(self, n): ''' Information of progress of the thread ''' print("%d%%" % n) def print_output(self, s): ''' Print results of different operations in the thread ''' print(s) def thread_complete(self): ''' Method called when the thread has been completed ''' print("Sequence reproduction complete!") self.inThread = False self.playing = False self.endThread = False # Enables slider after video reproduction self.slider.setEnabled(True) def logic_play_pause(self, rate, progress_callback): ''' Main play and pause logic used to show a sequence of images at a give rate ''' self.image_index = 0 total_images = len(self.data_reader.images[ self.data_reader.current_capture][self.data_reader.current_camera]) while (self.image_index < total_images): # When play function is enabled, play the sequence, otherwise do nothing if (self.endThread): break else: if (self.playing): self.show_image( self.data_reader.path + '/data/' + self.data_reader.images[ self.data_reader.current_capture] [self.data_reader.current_camera][self.image_index]) self.slider.setValue(self.image) time.sleep(rate) # Rate of reproduction of image sequence progress_callback.emit( self.image_index * 100.0 / total_images ) # Emit value of sequence progress to callback function self.image_index += 1 # Increase image index to read next image in sequence else: time.sleep(0.2) def play_pause(self): ''' Play the sequence of images for a capture and camera at a given time rate This function creates another thread where the sequence of images will be reproduced ''' self.playing = not (self.playing) # Toggles self.playing flag if not (self.inThread): worker = Worker( self.logic_play_pause, 0.016) # Any other args, kwargs are passed to the run function worker.signals.result.connect(self.print_output) worker.signals.finished.connect(self.thread_complete) worker.signals.progress.connect(self.progress_fn) # Execute self.threadpool.start(worker) self.inThread = True # Set thread in active state # Disables slider when video is being reproduced #self.slider.setEnabled(False) def check_stitching(self, value): if value != 0: print('Checkbox has been checked!') print('These are camera labels: ', self.cam_labels.keys()) print('Camera index: ', self.data_reader.current_camera) print('Image index: ', self.image_index) self.show_stitcher() else: image = cv2.imread( self.data_reader.path + '/data/' + self.data_reader.images[self.data_reader.current_capture][ self.data_reader.current_camera][self.image_index] ) # Loads image image = image[:, :, 0].T shape = image.shape dummy = np.zeros(shape) self.stitcher_view.setImage(dummy) print('Check is unchecked!')
class Ui_MainWindow(object): def __init__(self): self.timer = QtCore.QTimer() self.gbc_is_on = False self.dph_is_on = False self.sbs_is_on = False self.number_of_clicked = 0 self.number_of_clicked_gbc = 0 self.number_of_clicked_dph = 0 self.number_of_clicked_sbs = 0 self.worker = Pool(1) self.result_space = None self.deleted_ids = list() def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1000, 700) MainWindow.setMaximumSize(QtCore.QSize(1000, 700)) setConfigOption('background', 'w') self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.formLayout_settings = QtWidgets.QFormLayout() self.formLayout_settings.setObjectName("formLayout_settings") self.label_neighbours_rule = QtWidgets.QLabel(self.centralwidget) self.label_neighbours_rule.setObjectName("label_neighbours_rule") self.formLayout_settings.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_neighbours_rule) self.comboBox_neighbours_rule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_neighbours_rule.setObjectName("comboBox_neighbours_rule") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.formLayout_settings.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_neighbours_rule) self.label_border_condition = QtWidgets.QLabel(self.centralwidget) self.label_border_condition.setObjectName("label_border_condition") self.formLayout_settings.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_border_condition) self.comboBox_border_condition = QtWidgets.QComboBox(self.centralwidget) self.comboBox_border_condition.setObjectName("comboBox_border_condition") self.comboBox_border_condition.addItem("") self.comboBox_border_condition.addItem("") self.formLayout_settings.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.comboBox_border_condition) self.label_size_of_space = QtWidgets.QLabel(self.centralwidget) self.label_size_of_space.setObjectName("label_size_of_space") self.formLayout_settings.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_size_of_space) self.lineEdit_size_of_space = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_size_of_space.setObjectName("lineEdit_size_of_space") self.formLayout_settings.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEdit_size_of_space) self.label_number_of_grains = QtWidgets.QLabel(self.centralwidget) self.label_number_of_grains.setObjectName("label_number_of_grains") self.formLayout_settings.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_number_of_grains) self.lineEdit_number_of_grains = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_number_of_grains.setObjectName("lineEdit_number_of_grains") self.formLayout_settings.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.lineEdit_number_of_grains) self.label_inclusions_number = QtWidgets.QLabel(self.centralwidget) self.label_inclusions_number.setObjectName("label_inclusions_number") self.formLayout_settings.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_inclusions_number) self.lineEdit_inclusions_number = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_inclusions_number.setObjectName("lineEdit_inclusions_number") self.formLayout_settings.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.lineEdit_inclusions_number) self.label_min_radius = QtWidgets.QLabel(self.centralwidget) self.label_min_radius.setObjectName("label_min_radius") self.formLayout_settings.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_min_radius) self.lineEdit_min_radius = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_min_radius.setObjectName("lineEdit_min_radius") self.formLayout_settings.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.lineEdit_min_radius) self.label_max_radius = QtWidgets.QLabel(self.centralwidget) self.label_max_radius.setObjectName("label_max_radius") self.formLayout_settings.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_max_radius) self.lineEdit_max_radius = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_max_radius.setObjectName("lineEdit_max_radius") self.formLayout_settings.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.lineEdit_max_radius) self.pushButton_gbc_feature = QtWidgets.QPushButton(self.centralwidget) self.pushButton_gbc_feature.setObjectName("radioButton_gbc_feature") self.pushButton_gbc_feature.setStyleSheet("background-color: red") self.pushButton_gbc_feature.clicked.connect(self.controller_gbc_init) self.formLayout_settings.setWidget(7, QtWidgets.QFormLayout.LabelRole, self.pushButton_gbc_feature) self.pushButton_delete_grains = QtWidgets.QPushButton(self.centralwidget) self.pushButton_delete_grains.setObjectName("pushButton_delete_grains") self.pushButton_delete_grains.clicked.connect(self.view_delete_grains) self.formLayout_settings.setWidget(10, QtWidgets.QFormLayout.LabelRole, self.pushButton_delete_grains) self.pushButton_keep_selected = QtWidgets.QPushButton(self.centralwidget) self.pushButton_keep_selected.setObjectName("pushButton_keep_selected") self.pushButton_keep_selected.clicked.connect(self.view_keep_selected) self.formLayout_settings.setWidget(11, QtWidgets.QFormLayout.LabelRole, self.pushButton_keep_selected) self.comboBox_list_of_grains = QtWidgets.QComboBox(self.centralwidget) self.comboBox_list_of_grains.setObjectName("comboBox_list_of_grains") self.formLayout_settings.setWidget(10, QtWidgets.QFormLayout.FieldRole, self.comboBox_list_of_grains) self.pushButton_import_from_csv = QtWidgets.QPushButton(self.centralwidget) self.pushButton_import_from_csv.setObjectName("pushButton_import_from_csv") self.pushButton_import_from_csv.clicked.connect(self.io_open_file_name_dialog) self.formLayout_settings.setWidget(12, QtWidgets.QFormLayout.LabelRole, self.pushButton_import_from_csv) self.pushButton_export_to_csv = QtWidgets.QPushButton(self.centralwidget) self.pushButton_export_to_csv.setObjectName("pushButton_export_to_csv") self.pushButton_export_to_csv.clicked.connect(self.io_open_save_dialog) self.formLayout_settings.setWidget(13, QtWidgets.QFormLayout.LabelRole, self.pushButton_export_to_csv) self.pushButton_export_to_png = QtWidgets.QPushButton(self.centralwidget) self.pushButton_export_to_png.setObjectName("pushButton_export_to_png") self.pushButton_export_to_png.clicked.connect(self.io_open_save_dialog_image) self.formLayout_settings.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.pushButton_export_to_png) self.line_horizontal = QtWidgets.QFrame(self.centralwidget) self.line_horizontal.setFrameShape(QtWidgets.QFrame.HLine) self.line_horizontal.setFrameShadow(QtWidgets.QFrame.Sunken) self.line_horizontal.setObjectName("line") self.formLayout_settings.setWidget(15, QtWidgets.QFormLayout.SpanningRole, self.line_horizontal) self.pushButton_init_space = QtWidgets.QPushButton(self.centralwidget) self.pushButton_init_space.setMaximumSize(QtCore.QSize(16777215, 40)) self.pushButton_init_space.setObjectName("pushButton_init_space") self.pushButton_init_space.clicked.connect(self.controller_init_ca_algo) self.formLayout_settings.setWidget(17, QtWidgets.QFormLayout.FieldRole, self.pushButton_init_space) self.pushButton_start_stop = QtWidgets.QPushButton(self.centralwidget) self.pushButton_start_stop.setObjectName("pushButton_start_stop") self.pushButton_start_stop.clicked.connect(self.controller_init_image_timer) self.formLayout_settings.setWidget(18, QtWidgets.QFormLayout.FieldRole, self.pushButton_start_stop) self.pushButton_step = QtWidgets.QPushButton(self.centralwidget) self.pushButton_step.setObjectName("pushButton_step") self.pushButton_step.clicked.connect(self.controller_one_step) self.formLayout_settings.setWidget(19, QtWidgets.QFormLayout.FieldRole, self.pushButton_step) self.pushButton_clear_space = QtWidgets.QPushButton(self.centralwidget) self.pushButton_clear_space.setObjectName("pushButton_clear_space") self.pushButton_clear_space.clicked.connect(self.view_clear_space) self.formLayout_settings.setWidget(20, QtWidgets.QFormLayout.FieldRole, self.pushButton_clear_space) self.pushButton_draw_boundaries = QtWidgets.QPushButton(self.centralwidget) self.pushButton_draw_boundaries.setObjectName("pushButton_draw_boundaries") self.pushButton_draw_boundaries.clicked.connect(self.view_draw_boundaries) self.formLayout_settings.setWidget(21, QtWidgets.QFormLayout.FieldRole, self.pushButton_draw_boundaries) self.pushButton_dual_phase = QtWidgets.QPushButton(self.centralwidget) self.pushButton_dual_phase.setObjectName("pushButton_dual_phase") self.pushButton_dual_phase.clicked.connect(self.controller_init_dual_phase) self.formLayout_settings.setWidget(17, QtWidgets.QFormLayout.LabelRole, self.pushButton_dual_phase) self.pushButton_dual_phase_init = QtWidgets.QPushButton(self.centralwidget) self.pushButton_dual_phase_init.setObjectName("pushButton_dual_phase") self.pushButton_dual_phase_init.clicked.connect(self.controller_dual_phase_add_random) self.formLayout_settings.setWidget(18, QtWidgets.QFormLayout.LabelRole, self.pushButton_dual_phase_init) self.pushButton_substructures = QtWidgets.QPushButton(self.centralwidget) self.pushButton_substructures.setObjectName("pushButton_dual_phase") self.pushButton_substructures.clicked.connect(self.controller_init_substructures) self.formLayout_settings.setWidget(19, QtWidgets.QFormLayout.LabelRole, self.pushButton_substructures) self.label = QtWidgets.QLabel(self.centralwidget) self.label.setText("") self.label.setObjectName("label") self.formLayout_settings.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label) self.label_probability = QtWidgets.QLabel(self.centralwidget) self.label_probability.setObjectName("label_probability") self.formLayout_settings.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_probability) self.lineEdit_prob_threshold = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_prob_threshold.setObjectName("lineEdit_prob_threshold") self.formLayout_settings.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.lineEdit_prob_threshold) self.horizontalLayout.addLayout(self.formLayout_settings) self.line_vertical = QtWidgets.QFrame(self.centralwidget) self.line_vertical.setFrameShape(QtWidgets.QFrame.VLine) self.line_vertical.setFrameShadow(QtWidgets.QFrame.Sunken) self.line_vertical.setObjectName("line_2") self.horizontalLayout.addWidget(self.line_vertical) self.graphicsView = ImageView(self.centralwidget) self.graphicsView.setMinimumSize(QtCore.QSize(501, 501)) self.graphicsView.setMaximumSize(QtCore.QSize(501, 501)) self.graphicsView.setObjectName("graphicsView") self.graphicsView.ui.histogram.hide() self.graphicsView.ui.roiBtn.hide() self.graphicsView.ui.menuBtn.hide() self.graphicsView.show() self.horizontalLayout.addWidget(self.graphicsView) # Other stuff MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1000, 26)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.controller_init_ca_algo() # self._clear_space() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label_neighbours_rule.setText(_translate("MainWindow", "Neighbours rule")) self.comboBox_neighbours_rule.setItemText(0, _translate("MainWindow", "VONNEUMANN")) self.comboBox_neighbours_rule.setItemText(1, _translate("MainWindow", "MOORE")) self.comboBox_neighbours_rule.setItemText(2, _translate("MainWindow", "HEXAGONAL_LEFT")) self.comboBox_neighbours_rule.setItemText(3, _translate("MainWindow", "HEXAGONAL_RIGHT")) self.comboBox_neighbours_rule.setItemText(4, _translate("MainWindow", "PENTAGONAL_LEFT")) self.comboBox_neighbours_rule.setItemText(5, _translate("MainWindow", "PENTAGONAL_RIGHT")) self.label_border_condition.setText(_translate("MainWindow", "Border rule")) self.comboBox_border_condition.setItemText(0, _translate("MainWindow", "ABSORBING")) self.comboBox_border_condition.setItemText(1, _translate("MainWindow", "SNAKELIKE")) self.label_size_of_space.setText(_translate("MainWindow", "Size of space")) self.lineEdit_size_of_space.setText(_translate("MainWindow", "100")) self.label_number_of_grains.setText(_translate("MainWindow", "Number of grains")) self.lineEdit_number_of_grains.setText(_translate("MainWindow", "120")) self.label_inclusions_number.setText(_translate("MainWindow", "Number of incl.")) self.lineEdit_inclusions_number.setText(_translate("MainWindow", "0")) self.label_min_radius.setText(_translate("MainWindow", "Min radius")) self.lineEdit_min_radius.setText(_translate("MainWindow", "1")) self.label_max_radius.setText(_translate("MainWindow", "Max radius")) self.lineEdit_max_radius.setText(_translate("MainWindow", "6")) self.pushButton_gbc_feature.setText(_translate("MainWindow", "GBC feature")) self.pushButton_delete_grains.setText(_translate("MainWindow", "Delete grains")) self.pushButton_keep_selected.setText(_translate("MainWindow", "Keep selected")) self.pushButton_import_from_csv.setText(_translate("MainWindow", "Import csv")) self.pushButton_export_to_csv.setText(_translate("MainWindow", "Export csv")) self.pushButton_export_to_png.setText(_translate("MainWindow", "Export png")) self.pushButton_init_space.setText(_translate("MainWindow", "Init space")) self.pushButton_start_stop.setText(_translate("MainWindow", "Start/Stop")) self.pushButton_step.setText(_translate("MainWindow", "Step")) self.pushButton_clear_space.setText(_translate("MainWindow", "Clear space")) self.pushButton_draw_boundaries.setText(_translate("MainWindow", "Draw bound.")) self.pushButton_dual_phase.setText(_translate("MainWindow", "Dual phase")) self.pushButton_dual_phase_init.setText(_translate("MainWindow", "Dual init")) self.pushButton_substructures.setText(_translate("MainWindow", "Substructures")) self.label_probability.setText(_translate("MainWindow", "Probability")) self.lineEdit_prob_threshold.setText(_translate("MainWindow", "60")) def view_generate_pg_colormap(self): self.pos = np.linspace(0.0, 1.0, self._ca_algo.number_of_reserved_ids+int(self.lineEdit_number_of_grains.text())) self.cmap = ColorMap(pos=self.pos, color=self._ca_algo.color_id) self.graphicsView.setColorMap(self.cmap) def view_display_image(self): self.graphicsView.setImage(self._ca_algo.space.T, levels=(0.0, self._ca_algo.number_of_reserved_ids+float(self.lineEdit_number_of_grains.text()))) def view_generate_set_of_ids(self): self.comboBox_list_of_grains.clear() for item in self._ca_algo.list_of_id: self.comboBox_list_of_grains.addItem(str(item)) self.comboBox_list_of_grains.setEnabled(False) self.pushButton_delete_grains.setEnabled(False) self.pushButton_keep_selected.setEnabled(False) def view_delete_grains(self): id_to_delete = int(self.comboBox_list_of_grains.currentText()) if id_to_delete in self._ca_algo.space: self._ca_algo.space[self._ca_algo.space == id_to_delete] = 0 self.result_space = None self.deleted_ids.append(id_to_delete) self.view_display_image() else: return def view_keep_selected(self): id_to_keep = int(self.comboBox_list_of_grains.currentText()) if id_to_keep in self._ca_algo.space: self._ca_algo.space[self._ca_algo.space != id_to_keep] = 0 self.result_space = None self.view_display_image() else: return def view_draw_boundaries(self): for x in range(self._ca_algo.space_width): for y in range(self._ca_algo.space_width): cell = self._ca_algo.space[x, y] if x < self._ca_algo.space_width-1: cell_neigh_1 = self._ca_algo.space[x+1, y] else: cell_neigh_1 = cell if y < self._ca_algo.space_width-1: cell_neigh_2 = self._ca_algo.space[x, y+1] else: cell_neigh_2 = cell if cell > self._ca_algo.cell_inclusion and (cell != cell_neigh_1 or cell != cell_neigh_2): self._ca_algo.space[x, y] = self._ca_algo.cell_inclusion self.result_space = self._ca_algo.space self.view_display_image() grain_boundaries = np.count_nonzero(self._ca_algo.space == 1) grain_size = len(self._ca_algo.list_of_id) grain_aver = (self._ca_algo.space_width * self._ca_algo.space_width) / grain_size msg = QMessageBox() msg.setWindowTitle('Result') msg.setText('Total length of the boundaries: ' + str(grain_boundaries) + '\n' + 'Average size of a grain: ' + str(int(grain_aver))) msg.exec_() def view_clear_space(self): self.result_space = None self.timer.stop() self._ca_algo.space = self._ca_algo.space_clear self.graphicsView.clear() def controller_init_ca_algo(self): self._ca_algo = None self.deleted_ids = list() if not self.gbc_is_on: self.result_space = None self._ca_algo = CellularAutomata(int(self.lineEdit_number_of_grains.text()), int(self.lineEdit_inclusions_number.text()), int(self.lineEdit_min_radius.text()), int(self.lineEdit_max_radius.text()), int(self.lineEdit_size_of_space.text()), int(self.lineEdit_size_of_space.text()), str(self.comboBox_border_condition.currentText()), str(self.comboBox_neighbours_rule.currentText())) self._ca_algo.add_random() self._ca_algo.add_inclusions() self.view_generate_pg_colormap() self.view_generate_set_of_ids() self.view_display_image() else: self.result_space = None self._ca_algo = CellularAutomataGBC(int(self.lineEdit_number_of_grains.text()), int(self.lineEdit_inclusions_number.text()), int(self.lineEdit_min_radius.text()), int(self.lineEdit_max_radius.text()), int(self.lineEdit_size_of_space.text()), int(self.lineEdit_size_of_space.text()), str(self.comboBox_border_condition.currentText()), int(self.lineEdit_prob_threshold.text())) self._ca_algo.add_random() self._ca_algo.add_inclusions() self.view_generate_pg_colormap() self.view_generate_set_of_ids() self.view_display_image() def controller_init_image_timer(self): self.number_of_clicked += 1 if self.number_of_clicked % 2: self.pushButton_start_stop.setStyleSheet("background-color: green") self.timer.timeout.connect(self.controller_update_func) self.timer.start(50) else: self.pushButton_start_stop.setStyleSheet("background-color: none") self.timer.stop() def controller_update_func(self): if self._ca_algo.cell_empty in self._ca_algo.space: if self.result_space is None: self.result_space = self.worker.apply_async(self._ca_algo.one_step) return if self.result_space.ready(): self._ca_algo.space = self.result_space.get() self.view_display_image() self.result_space = self.worker.apply_async(self._ca_algo.one_step) else: self.comboBox_list_of_grains.setEnabled(True) self.pushButton_delete_grains.setEnabled(True) self.pushButton_keep_selected.setEnabled(True) def controller_one_step(self): self.timer.stop() self.controller_update_func() def controller_gbc_init(self): self.number_of_clicked_gbc += 1 if self.number_of_clicked_gbc % 2: self.gbc_is_on = True self.pushButton_gbc_feature.setStyleSheet("background-color: green") self.comboBox_neighbours_rule.setCurrentText('MOORE') self.comboBox_neighbours_rule.setEnabled(False) else: self.gbc_is_on = False self.pushButton_gbc_feature.setStyleSheet("background-color: red") self.comboBox_neighbours_rule.setEnabled(True) def controller_init_substructures(self): self.number_of_clicked_sbs += 1 if self.number_of_clicked_sbs % 2: self.pushButton_substructures.setStyleSheet("background-color: green") self.sbs_is_on = True else: self.pushButton_substructures.setStyleSheet("background-color: none") self.sbs_is_on = False self.substr_kept_ids = self._ca_algo.list_of_id for one_id in self.deleted_ids: self.substr_kept_ids.remove(one_id) for one_id in self.substr_kept_ids: self._ca_algo.grain_model[one_id] = self._ca_algo.phase_nonzero self._ca_algo.color_id[one_id] = self._ca_algo.color_id[2] self.result_space = None self.view_display_image() def controller_init_dual_phase(self): self.number_of_clicked_dph += 1 if self.number_of_clicked_dph % 2: self.pushButton_dual_phase.setStyleSheet("background-color: green") self.dph_is_on = True else: self.pushButton_dual_phase.setStyleSheet("background-color: none") self.dph_is_on = False kept_ids = self._ca_algo.list_of_id for one_id in self.deleted_ids: kept_ids.remove(one_id) for one_id in kept_ids: self._ca_algo.space[self._ca_algo.space == one_id] = 2 self.result_space = None self.view_display_image() def controller_dual_phase_add_random(self): if self.sbs_is_on and not self.dph_is_on: self._ca_algo.add_random_dual_phase(self.substr_kept_ids) else: self._ca_algo.add_random_dual_phase() self.result_space = None self.view_display_image() def controller_substructures_add_random(self): self._ca_algo.add_random_dual_phase(self.substr_kept_ids) self.result_space = None self.view_display_image() def io_open_save_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self.centralwidget, "Save to CSV file", "", "CSV Files (*.csv)", options=options) if fileName: pd.DataFrame(self._ca_algo.space).to_csv(fileName) def io_open_file_name_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self.centralwidget, "Open CSV file", "", "CSV Files (*.csv)", options=options) if fileName: self._ca_algo.space = pd.read_csv(fileName, index_col=0).astype(int).values self.view_display_image() def io_open_save_dialog_image(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self.centralwidget, "Save to PNG file", "", "PNG Files (*.png)", options=options) if fileName: self.graphicsView.export(fileName)
from camera import FakeCamera from pyqtgraph import ImageView from PyQt5 import QtGui, QtCore c = FakeCamera() app = QtGui.QApplication([]) win = QtGui.QMainWindow() win.resize(800, 800) imv = ImageView() win.setCentralWidget(imv) win.show() win.setWindowTitle('pyqtgraph example: ImageView') c.set_resolution(1280, 1024) img = c.get_frame() print(img) print(img.min(), img.max()) imv.setImage(img) if __name__ == '__main__': import sys if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_()
class TimeSeriesReconstructionDialog(QtGui.QDialog): time_series_reconstructed = QtCore.pyqtSignal(str) time_series_loaded = QtCore.pyqtSignal(object) _reconstruction_update_signal = QtCore.pyqtSignal(int) def __init__(self, **kwargs): super(TimeSeriesReconstructionDialog, self).__init__(**kwargs) self.time_series = None self._propagation_distances = None self._fourier_mask = None self.setModal(True) self.setWindowTitle('Reconstruct time-series') self.holograms_viewer = ImageView(parent=self, name='Raw holograms') self.holograms_slider = QtGui.QSlider(QtCore.Qt.Horizontal, parent=self) self.holograms_slider.setDisabled(True) self.holograms_slider.valueChanged.connect( self.update_holograms_viewer) self.time_point_label = QtGui.QLabel('') self.time_point_label.setAlignment(QtCore.Qt.AlignCenter) labeled_slider = QtGui.QHBoxLayout() labeled_slider.addWidget(self.time_point_label) labeled_slider.addWidget(self.holograms_slider) progress_label = QtGui.QLabel('<h3>Reconstruction Progress</h3>') progress_label.setAlignment(QtCore.Qt.AlignCenter) self.reconstruction_progress = QtGui.QProgressBar(parent=self) self.reconstruction_progress.setRange(0, 100) self.reconstruction_progress.setValue(0) self.reconstruction_progress.setAlignment(QtCore.Qt.AlignCenter) self._reconstruction_update_signal.connect( self.reconstruction_progress.setValue) #self.reconstruction_progress.hide() self.recons_params_widget = ReconstructionParametersWidget(parent=self) self.recons_params_widget.propagation_distance_signal.connect( self.update_propagation_distance) self.recons_params_widget.fourier_mask_signal.connect( self.update_fourier_mask) load_time_series_btn = QtGui.QPushButton('Load time-series', self) load_time_series_btn.clicked.connect(self.load_time_series) accept_btn = QtGui.QPushButton('Reconstruct time-series', self) accept_btn.clicked.connect(self.accept) cancel_btn = QtGui.QPushButton('Cancel', self) cancel_btn.clicked.connect(self.reject) btns = QtGui.QHBoxLayout() btns.addWidget(accept_btn) btns.addWidget(cancel_btn) layout = QtGui.QVBoxLayout() layout.addWidget(load_time_series_btn) layout.addWidget(self.holograms_viewer) layout.addLayout(labeled_slider) layout.addWidget(self.recons_params_widget) layout.addWidget(progress_label) layout.addWidget(self.reconstruction_progress) layout.addLayout(btns) self.setLayout(layout) @QtCore.pyqtSlot() def load_time_series(self): path = QtGui.QFileDialog.getOpenFileName( self, caption='Select an HDF5 hologram time-series', filter='*.hdf5 *.h5')[0] if not path: return self.time_series = TimeSeries(name=path, mode='r+') self.holograms_slider.setRange(0, len(self.time_series.time_points) - 1) self.holograms_slider.setEnabled(True) self.update_holograms_viewer(index=0) @QtCore.pyqtSlot(int) def update_holograms_viewer(self, index): """ Update the holograms viewer by time-point index """ time_point = self.time_series.time_points[index] self.time_point_label.setNum(time_point) hologram = self.time_series.hologram(time_point) self.holograms_viewer.setImage(hologram.hologram) @QtCore.pyqtSlot(object) def update_propagation_distance(self, dist): self._propagation_distances = dist @QtCore.pyqtSlot(object) def update_fourier_mask(self, mask): self._fourier_mask = mask @QtCore.pyqtSlot() def accept(self): self.time_series.batch_reconstruct( propagation_distance=self._propagation_distances, fourier_mask=self._fourier_mask, callback=self._reconstruction_update_signal.emit) self.time_series.close() super().accept() @QtCore.pyqtSlot() def reject(self): if self.time_series: self.time_series.close() super().reject()
class ImageViewer(QWidget): """ Show a single frame from a movie with a slider to change the frame. This essentially harnesses pyqtgraph.ImageView for a simple image viewer that is occasionally easier than Fiji. init ---- """ def __init__(self, path, parent=None): super(ImageViewer, self).__init__(parent=parent) self.path = path self.initData() self.initUI() def initData(self): """ Try to read the image data at the target path. """ self.ImageReader = ImageReader(self.path) def initUI(self): """ Initialize the user interface. """ # Main window self.win = QWidget() self.win.setWindowTitle(self.path) layout = QGridLayout(self.win) # ImageView self.ImageView = ImageView(parent=self.win) layout.addWidget(self.ImageView, 0, 0, 2, 2) # Frame slider self.frame_slider = IntSlider(minimum=0, interval=1, maximum=self.ImageReader.n_frames - 1, init_value=1, name='Frame', parent=self.win) layout.addWidget(self.frame_slider, 2, 0, 1, 1, alignment=Qt.AlignTop) self.frame_slider.assign_callback(self.frame_slider_callback) # Buttons to make projections self.B_max_int = QPushButton("Make projection", self.win) layout.addWidget(self.B_max_int, 3, 0, 1, 1, alignment=Qt.AlignLeft) self.B_max_int.clicked.connect(self.B_max_int_callback) # Use the right/left keys to tab through frames self.left_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Left), self.win) self.right_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Right), self.win) self.left_shortcut.activated.connect(self.prev_frame) self.right_shortcut.activated.connect(self.next_frame) # Update the frame self.load_frame(0, reset=True) # Resize main window self.win.resize(600, 600) # Show the main window self.win.show() def load_frame(self, frame_index, reset=False): """ Change the current frame. args ---- frame_index : int reset : bool, reset the LUTs and ROI """ self.image = self.ImageReader.get_frame(frame_index) self.ImageView.setImage(self.image, autoRange=reset, autoLevels=reset, autoHistogramRange=reset) def next_frame(self): """ Go to the frame after the current one. """ next_idx = int(self.frame_slider.value()) if next_idx < self.frame_slider.maximum: next_idx += 1 self.frame_slider.setValue(next_idx) def prev_frame(self): """ Go the frame before the current one. """ prev_idx = int(self.frame_slider.value()) if prev_idx > self.frame_slider.minimum: prev_idx -= 1 self.frame_slider.setValue(prev_idx) def frame_slider_callback(self): """ Change the current frame. """ self.load_frame(self.frame_slider.value()) def B_max_int_callback(self): """ Make a maximum intensity projection. """ ex = ChooseProjectionDialog(self.ImageReader.n_frames, parent=self) if ex.exec_() == QDialog.Accepted: method, start_frame, stop_frame = ex.return_val # Perform the projection result = getattr(self.ImageReader, method)(start=int(start_frame), stop=int(stop_frame)) # Make a standalone window showing the projection ex = SingleImageWindow(result, title=method, parent=self) ex.show()
class ImageViewModule(QFrame): """ This class wraps the pyqt imageview model, takes care of configuring it and adds a set image method to it """ def __init__(self, main_widget, histogram=True, crop_selector=False): super().__init__() self.main_widget = main_widget # self.setMinimumWidth(600) # self.setMinimumHeight(300) # self.setStyleSheet("ImageViewModule {margin:5px; border:1px solid rgb(50, 65, " # "75);} ") self.setStyleSheet("ImageViewModule {margin:0px; border:0px solid rgb(50, 65, " "75); padding: 0px;} ") self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) # self.layout.setAlignment(Qt.AlignHCenter) self.image_label = QLabel() self.layout.addWidget(self.image_label) self.setLayout(self.layout) # self.already_loaded = True # self.no_image_message = QPushButton("Please open a dataset first") # self.no_image_message.clicked.connect(main_widget.open_file_dialog) # self.no_image_message.setStyleSheet("QPushButton {font-size:80;}") self.image_view = ImageView(view=PlotItem()) self.image_view.keyPressEvent = self.keyPressEvent self.image_view.ui.layoutWidget.setContentsMargins(0, 0, 0, 0) # self.image_view.ui.roiBtn.hide() self.image_view.ui.menuBtn.hide() if not histogram: self.image_view.ui.histogram.hide() if not crop_selector: self.image_view.ui.roiBtn.hide() # self.image_view.getRoiPlot().hide() self.image_item = self.image_view.getImageItem() self.layout.addWidget(self.image_view) @property def data_handler(self): return self.main_widget.data_handler def keyPressEvent(self, ev): if ev.key() == QtCore.Qt.Key_Space and False: if self.image_view.playRate == 0: fps = (self.image_view.getProcessedImage().shape[0] - 1) / ( self.image_view.tVals[-1] - self.image_view.tVals[0]) self.image_view.play(fps) # print fps else: self.image_view.play(0) ev.accept() elif ev.key() == QtCore.Qt.Key_Home: self.image_view.setCurrentIndex(0) self.image_view.play(0) ev.accept() elif ev.key() == QtCore.Qt.Key_End: self.image_view.setCurrentIndex( self.image_view.getProcessedImage().shape[0] - 1) self.image_view.play(0) ev.accept() elif ev.key() in self.image_view.noRepeatKeys: ev.accept() if ev.isAutoRepeat(): return self.image_view.keysPressed[ev.key()] = 1 self.image_view.evalKeyState() else: QWidget.keyPressEvent(self.image_view, ev) def setImage(self, data): # if self.already_loaded == False: # print("changed image") # self.already_loaded = True # self.layout.removeWidget(self.no_image_message) # self.no_image_message.deleteLater() # # self.layout.setAlignment(Qt.AlignLeft) # self.image_view = ImageView() # # self.layout.addWidget(self.image_view) self.image_view.setImage(data, levelMode='mono', autoRange=True, autoLevels=True, autoHistogramRange=True) bottom_5 = np.percentile(data, 5) top_5 = np.percentile(data, 95) bottom_2 = np.percentile(data, 2) top_2 = np.percentile(data, 98) self.image_view.setLevels(bottom_5, top_5) self.image_view.setHistogramRange(bottom_2, top_2)
class StartWindow(QMainWindow): def __init__(self, camera, logger=None): super().__init__() self.camera = camera self.logger = logger if self.logger is not None: self.logger.info("Created Camera") self.central_widget = QWidget() # Widgets: Buttons, Sliders, ... self.button_start = QPushButton("Start", self.central_widget) self.button_stop = QPushButton("Stop", self.central_widget) self.slider = QSlider(Qt.Horizontal) self.slider.setRange(0, 10) self.slider.setValue(self.camera.get_brightness()) self.image_view = ImageView() # Signals self.button_start.clicked.connect(self.start) self.button_stop.clicked.connect(self.stop) self.slider.valueChanged.connect(self.update_brightness) # Timer for acquiring images at regular intervals self.acquisition_timer = QTimer() self.acquisition_timer.timeout.connect(self.update_image) # Widgets layout self.layout = QGridLayout(self.central_widget) self.layout.setColumnStretch(0, 9) self.layout.setColumnStretch(1, 1) self.layout.addWidget(self.button_start, 0, 1) self.layout.addWidget(self.button_stop, 1, 1) self.layout.addWidget(self.slider, 2, 1) self.layout.addWidget(self.image_view, 0, 0, 10, 1) self.setCentralWidget(self.central_widget) def start(self, update_interval=30): if not self.camera.isInitialized(): self.camera.initialize() while self.camera.get_frame() is None: pass # self.movie_thread = MovieThread(self.camera) # self.movie_thread.start() self.acquisition_timer.start(update_interval) def stop(self): self.camera.close_camera() def update_image(self): frame = self.camera.get_frame() self.image_view.setImage(frame.T) if self.logger is not None: self.logger.info( "Updated frame with Maximum in frame: {}, Minimum in frame: {}" .format(np.max(frame), np.min(frame))) def update_brightness(self, value): value /= 10 self.camera.set_brightness(value) if self.logger is not None: self.logger.info("Brightness set to: {}".format(value))
class SpotViewer(QWidget): """ Overlay localizations or trajectories onto a raw movie. Useful for QC. The user sees a single image window with the current frame and some overlaid localizations. Each localization is either colored identically or according to a color scheme. Available color schemes are - color each localization by its trajectory index (same color for locs in the same trajectory) - color each localization by one of its numerical attributes (e.g. intensity, BG, H_det, etc.) - color each localization one of two colors according to its value for a boolean attribute. SpotViewer provides the user a way to create new boolean attributes by thresholding attributes, via the "Create Boolean attribute" button. args ---- image_path : str, path to image file (e.g. *.nd2, *.tif, or *.tiff) locs_path : str, path to CSV with localizations gui_size : int start_frame : int, the initial frame shown parent : root QWidget """ def __init__(self, image_path, locs_path, gui_size=800, start_frame=0, parent=None): super(SpotViewer, self).__init__(parent=parent) self.image_path = image_path self.locs_path = locs_path self.gui_size = gui_size self.start_frame = start_frame self.initData() self.initUI() def initData(self): """ Load the datasets for this SpotViewer instance. """ # Make sure the required paths exist assert os.path.isfile(self.image_path) assert os.path.isfile(self.locs_path) assert os.path.splitext(self.image_path)[1] in \ ['.nd2', '.tif', '.tiff'], \ "Image file must be one of (*.nd2 *.tif *.tiff)" # Make an image file reader self.imageReader = ImageReader(self.image_path) # Load the set of localizations self.locs = pd.read_csv(self.locs_path) # Some additional options if these localizations are # assigned a trajectory if 'trajectory' in self.locs.columns: # Assign the traj_len, if it doesn't already exist self.locs = traj_length(self.locs) # Keep an array of localizations as an ndarray, for # fast indexing when overlaying trajectory histories self.tracks = np.asarray( self.locs.loc[ self.locs['traj_len']>1, ['frame', 'trajectory', 'y', 'x'] ].sort_values(by=['trajectory', 'frame']) ) # Adjust for off-by-1/2 plot pixel indexing self.tracks[:,2:] = self.tracks[:,2:] + 0.5 # Generate the set of colors self.generate_color_schemes() # Assign condition colors (initially all negative) self.assign_condition_colors('y') # Load the first round of spots self.load_image(self.start_frame) # Assign each trajectory a color self.assign_traj_colors() # Assign self.locs_curr, the current set of spots self.load_spots(self.start_frame) def initUI(self): """ Initialize the user interface. """ # Main window self.win = QWidget() # Figure out the GUI size self.AR = float(self.imageReader.width) / self.imageReader.height self.win.resize(self.gui_size*1.5, self.gui_size) L_main = QGridLayout(self.win) # A subwindow on the left for widgets, and a subwindow # on the right for the image self.win_right = QWidget(self.win) self.win_left = QWidget(self.win) L_right = QGridLayout(self.win_right) L_left = QGridLayout(self.win_left) L_main.addWidget(self.win_right, 0, 1, 1, 3) L_main.addWidget(self.win_left, 0, 0, 1, 1) ## IMAGES / OVERLAYS # ImageView, for rendering image self.imageView = ImageView(parent=self.win_right) L_right.addWidget(self.imageView, 0, 0) # ScatterPlotItem, for overlaying localizations self.scatterPlotItem = ScatterPlotItem() self.scatterPlotItem.setParentItem(self.imageView.imageItem) # GraphItem, for overlaying trajectory histories when # desired self.graphItem = GraphItem() self.graphItem.setParentItem(self.imageView.imageItem) # # Make spots clickable self.lastClicked = [] self.scatterPlotItem.sigClicked.connect(self.spot_clicked) ## WIDGETS widget_align = Qt.AlignTop # Frame slider self.frame_slider = IntSlider(minimum=0, interval=1, maximum=self.imageReader.n_frames-1, init_value=self.start_frame, name='Frame', parent=self.win_left) L_left.addWidget(self.frame_slider, 0, 0, rowSpan=2, alignment=widget_align) self.frame_slider.assign_callback(self.frame_slider_callback) # Button to toggle spot overlays self.B_overlay_state = True self.B_overlay = QPushButton("Overlay spots", parent=self.win_left) self.B_overlay.clicked.connect(self.B_overlay_callback) L_left.addWidget(self.B_overlay, 1, 0, alignment=widget_align) # Button to toggle trajectory trails self.B_overlay_trails_state = False self.B_overlay_trails = QPushButton("Overlay histories", parent=self.win_left) self.B_overlay_trails.clicked.connect(self.B_overlay_trails_callback) L_left.addWidget(self.B_overlay_trails, 1, 1, alignment=widget_align) self.B_overlay_trails.stackUnder(self.B_overlay) # Menu to select current overlay symbol symbol_options = keys_to_str(symbol_sizes.keys()) self.M_symbol = LabeledQComboBox(symbol_options, "Overlay symbol", init_value="o", parent=self.win_left) self.M_symbol.assign_callback(self.M_symbol_callback) L_left.addWidget(self.M_symbol, 2, 0, alignment=widget_align) # Menu to select how the spots are colored color_by_options = ["None", "Trajectory", "Quantitative attribute", "Boolean attribute"] self.M_color_by = LabeledQComboBox(color_by_options, "Color spots by", init_value="None", parent=self.win_left) self.M_color_by.assign_callback(self.M_color_by_callback) L_left.addWidget(self.M_color_by, 3, 0, alignment=widget_align) # Create a new binary spot condition self.B_create_condition = QPushButton("Create Boolean attribute", parent=self.win_left) self.B_create_condition.clicked.connect(self.B_create_condition_callback) L_left.addWidget(self.B_create_condition, 4, 0, alignment=widget_align) # Select the binary column that determines color when "condition" # is selected as the color-by attribute condition_options = [c for c in self.locs.columns if self.locs[c].dtype == 'bool'] self.M_condition = LabeledQComboBox(condition_options, "Boolean attribute", init_value="None", parent=self.win_left) L_left.addWidget(self.M_condition, 3, 1, alignment=widget_align) self.M_condition.assign_callback(self.M_condition_callback) # Compare spots in a separate window that shows a grid of spots self.B_compare_spots = QPushButton("Compare spots", parent=self) L_left.addWidget(self.B_compare_spots, 2, 1, alignment=widget_align) self.B_compare_spots.clicked.connect(self.B_compare_spot_callback) # Some placeholder widgets, for better formatting for j in range(6, 18): q = QLabel(parent=self.win_left) L_left.addWidget(q, j, 0, alignment=widget_align) ## KEYBOARD SHORTCUTS - tab right/left through frames self.left_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Left), self.win) self.right_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Right), self.win) self.left_shortcut.activated.connect(self.tab_prev_frame) self.right_shortcut.activated.connect(self.tab_next_frame) ## DISPLAY self.update_image(autoRange=True, autoLevels=True, autoHistogramRange=True) self.overlay_spots() if "trajectory" in self.locs.columns: self.M_color_by.setCurrentText("Trajectory") self.M_color_by_callback() self.win.show() ## CORE FUNCTIONS def update_image(self, autoRange=False, autoLevels=False, autoHistogramRange=False): """ Update self.imageView with the current value of self.image and change the scatter plots if necessary. """ # Update the image view self.imageView.setImage(self.image, autoRange=autoRange, autoLevels=autoLevels, autoHistogramRange=autoHistogramRange) def load_spots(self, frame_index=None): """ Load a new set of localizations. """ if frame_index is None: frame_index = self.frame_slider.value() self.locs_curr = self.locs.loc[self.locs['frame']==frame_index, :] self.locs_pos = np.asarray(self.locs_curr[['y', 'x']]) self.locs_data = self.locs_curr.to_dict(orient='records') def load_image(self, frame_index): """ Load a new image. """ self.image = self.imageReader.get_frame(frame_index) def change_frame(self, frame_index): """ Load a new frame with a new set of spots. """ # Load the new image self.load_image(frame_index) # Load the new set of spots self.load_spots(frame_index) def tab_next_frame(self): """ Load the next frame. """ next_idx = int(self.frame_slider.value()) if next_idx < self.frame_slider.maximum: next_idx += 1 self.frame_slider.setValue(next_idx) def tab_prev_frame(self): """ Load the previous frame. """ prev_idx = int(self.frame_slider.value()) if prev_idx > self.frame_slider.minimum: prev_idx -= 1 self.frame_slider.setValue(prev_idx) def generate_color_spot_dict(self, color_col): """ From the current set of spots, generate an argument suitable to update the scatter plot while coloring each localization by its value of *color_col*. Usually *color_col* is either "traj_color" or "attrib_color". """ symbol = self.M_symbol.currentText() return [{'pos': tuple(self.locs_pos[i,:]+0.5), 'size': symbol_sizes[symbol], 'symbol': symbol, 'pen': {'color': self.locs_data[i][color_col], "width": pen_width}, 'brush': None} for i in range(self.locs_pos.shape[0])] def overlay_spots(self): """ Overlay the current set of spots onto the current image. """ if self.B_overlay_state: # Get the current overlay symbol symbol = self.M_symbol.currentText() # Get the current coloring scheme color_by = self.M_color_by.currentText() # Overlay the spots if color_by == "None": self.scatterPlotItem.setData(pos=self.locs_pos+0.5, data=self.locs_data, symbol=symbol, size=symbol_sizes[symbol], **overlay_params) elif color_by == "Trajectory": self.scatterPlotItem.setData( spots=self.generate_color_spot_dict("traj_color"), data=self.locs_data ) elif color_by == "Quantitative attribute": self.scatterPlotItem.setData( spots=self.generate_color_spot_dict("attrib_color"), data=self.locs_data, ) elif color_by == "Boolean attribute": self.scatterPlotItem.setData( spots=self.generate_color_spot_dict("condition_color"), data=self.locs_data, ) else: self.scatterPlotItem.setData([]) # Also overlay the trajectory histories, if desired if self.B_overlay_trails_state: self.overlay_trails() def generate_color_schemes(self): """ Generate two color schemes for this dataframe. """ self.n_colors = 133 # Color scheme 1: viridis cmap = cm.get_cmap("viridis", self.n_colors) self.colors_0 = np.array( [mpl_colors.rgb2hex(cmap(i)[:3]) for i in range(self.n_colors)] ) # Color scheme 2: inferno cmap = cm.get_cmap("cool", self.n_colors) self.colors_1 = np.array( [mpl_colors.rgb2hex(cmap(i)[:3]) for i in range(self.n_colors)] ) def assign_traj_colors(self): """ Assign the "traj_color" column of the dataframe, which assign each trajectory a different hex color. """ # If the dataframe does not have a "trajectory" column, then # all of the spots get the same color if not "trajectory" in self.locs.columns: print("assign_traj_colors: `trajectory` column not found in dataframe") self.locs["traj_color"] = MASTER_COLOR else: self.locs["traj_color"] = self.colors_0[ (self.locs["trajectory"]*9277) % self.n_colors] # If a spot is not assigned to a trajectory, its color is white unassigned = self.locs["trajectory"] == -1 self.locs.loc[unassigned, "traj_color"] = "#FFFFFF" def assign_attribute_colors(self, attrib): """ Assign the "color" column of the dataframe, which determines how individual spots are colored. """ # Get all of the numerical values for this attribute x = np.asarray(self.locs[attrib].astype("float64")) sanitary = (~np.isinf(x)) & (~np.isnan(x)) # Min and max values for the color map cmin = np.percentile(x, 1) cmax = np.percentile(x, 95) # Divide up the range into bins for each color bins = np.linspace(cmin, cmax, self.n_colors-1) # Assign each localization to one bin on the basis # of its attribute assignments = np.zeros(x.shape[0], dtype='int64') assignments[sanitary] = np.digitize(x[sanitary], bins=bins) # Generate the color indices self.locs["attrib_color"] = self.colors_1[assignments] # Unsanitary inputs are colored white self.locs.loc[~sanitary, "attrib_color"] = "#FFFFFF" def assign_condition_colors(self, condition_col): """ Assign the "condition_color" of the dataframe which is keyed to the truth value of *condition_col*. """ indices = self.locs[condition_col].astype('int64') self.locs["condition_color"] = condition_colors[0] self.locs.loc[self.locs[condition_col].astype('bool'), "condition_color"] = condition_colors[1] def overlay_trails(self): """ For each trajectory running in the current frame, overlay that trajectory on the current image. Relies on an updated self.locs_curr. """ if len(self.locs_curr) == 0: self.graphItem.setData() elif "trajectory" in self.locs.columns and \ self.B_overlay_trails_state: # Get the current frame index frame_index = self.frame_slider.value() # Get all spots before or at the current frame T = self.tracks[self.tracks[:,0]<=frame_index, :] # Find the minimum trajectory index present in this frame t = T[T[:,0]==frame_index, 1].min() # Get all trajectories that coincide with this frame T = T[np.where(T[:,1]==t)[0][0]:,:] # Figure out which points correspond to the same # trajectory indicator = (T[1:,1] - T[:-1,1]) == 0 connect_indices = np.where(indicator)[0] adj = np.asarray([connect_indices, connect_indices+1]).T # Overlay self.graphItem.setData(pos=T[:,2:4], adj=adj, pen={'color': MASTER_COLOR, 'width': pen_width}, symbol=None, brush=None, pxMode=False) else: self.graphItem.setData() ## WIDGET CALLBACKS def spot_clicked(self, plot, points): """ Respond to a spot being selected by the user: change its color and print all of its associated information to the terminal. """ for p in self.lastClicked: p.resetPen() for p in points: print("Spot:") print(format_dict(p.data())) p.setPen('w', width=3) self.lastClicked = points def frame_slider_callback(self): """ Respond to a user change in the frame slider. """ # Get the new frame frame_index = self.frame_slider.value() # Load a new image and set of localizations self.change_frame(frame_index) # Update the image self.update_image() # Update the scatter plot self.overlay_spots() def B_overlay_callback(self): """ Toggle the spot overlay. """ self.B_overlay_state = not self.B_overlay_state self.overlay_spots() def M_color_by_callback(self): """ Change the way that the spots are colored: either None (all the same color), "trajectory", or "attribute". """ color_by = self.M_color_by.currentText() if color_by == "Quantitative attribute": # Prompt the user to select an attribute to color by options = [c for c in self.locs.columns if \ self.locs[c].dtype in ["int64", "float64", "uint8", \ "uint16", "float32", "uint32", "int32"]] ex = SingleComboBoxDialog("Attribute", options, init_value="I0", title="Choose attribute color by", parent=self) ex.exec_() # Dialog accepted if ex.result() == 1: attrib = ex.return_val # Dialog rejected; default to I0 else: print("rejected; defaulting to I0") attrib = "I0" # Generate the color indices self.assign_attribute_colors(attrib) # Update the current set of localizations self.load_spots(self.frame_slider.value()) elif color_by == "Boolean attribute": self.assign_attribute_colors(self.M_condition.currentText()) self.overlay_spots() else: pass # Update the scatter plot self.overlay_spots() def M_symbol_callback(self): """ Change the spot overlay symbol. """ self.overlay_spots() def B_overlay_trails_callback(self): """ Show the history of each trajectory on the raw image. """ self.B_overlay_trails_state = not self.B_overlay_trails_state # Do nothing if these are not trajectories if not "trajectory" in self.locs.columns: return # Else overlay the trajectory histories self.overlay_trails() def B_create_condition_callback(self): """ Launch a sub-GUI to generate a Boolean attribute on from one of the spot columns. """ # Prompt the user to create a condition ex = ConditionDialog(self.locs, parent=self) ex.exec() # Accepted - unpack result if ex.result() == 1: bounds, col = ex.return_val l_bound, u_bound = bounds # Add this as an option condition_name = '%s_condition' % col if condition_name not in self.locs.columns: self.M_condition.QComboBox.addItems([condition_name]) # Create a new binary column on the dataframe self.locs[condition_name] = np.logical_and( self.locs[col]>=l_bound, self.locs[col]<=u_bound, ) print("%d/%d localizations in condition %s" % ( self.locs[condition_name].sum(), len(self.locs), condition_name )) # Update plots, assuming that the user wants to see the # result immediately self.M_condition.QComboBox.setCurrentText(condition_name) self.M_color_by.QComboBox.setCurrentText("Boolean attribute") self.assign_condition_colors(condition_name) self.load_spots() self.overlay_spots() def M_condition_callback(self): """ Select the current Boolean attribute to use for determining color when using the "Boolean attribute" option in M_color_by. """ # Get the new condition name c = self.M_condition.currentText() # Assign each localization a color depending on its value # with this value self.assign_condition_colors(c) # Update the plots self.load_spots() self.overlay_spots() def B_compare_spot_callback(self): """ Launch a subwindow that shows a grid of spots in the current frame and subsequent frames. This callback has two behaviors, depending on the current state of self.M_color_by. If we're color by a Boolean attribute, then launch a ImageSubpositionCompare window. Else launch a ImageSubpositionWindow. The former compares two sets of spots side-by-side, while the second is just a single grid of spots. """ if self.M_color_by.currentText() == "Boolean attribute": # Size of image grid N = 8 # Get the current Boolean attribute column col = self.M_condition.currentText() # Get as many spots as we can to fill this image grid frame_index = self.frame_slider.value() positions_false = [] positions_true = [] images = [] while frame_index<self.imageReader.n_frames and \ (sum([i.shape[0] for i in positions_false])<N**2 or (sum([i.shape[0] for i in positions_true])<N**2)): im = self.imageReader.get_frame(frame_index) frame_locs = self.locs.loc[ self.locs['frame']==frame_index, ['y', 'x', col], ] frame_locs_true = np.asarray(frame_locs.loc[ frame_locs[col], ['y', 'x']]).astype(np.int64) frame_locs_false = np.asarray(frame_locs.loc[ ~frame_locs[col], ['y', 'x']]).astype(np.int64) images.append(im) positions_true.append(frame_locs_true) positions_false.append(frame_locs_false) frame_index += 1 # Launch a ImageSubpositionCompare instance ex = ImageSubpositionCompare(images, positions_true, positions_false, w=15, N=N, colors=condition_colors, parent=self) else: # Size of image grid N = 10 # Get as many spots as we can to fill this image grid frame_index = self.frame_slider.value() positions = [] images = [] while sum([i.shape[0] for i in positions])<N**2 and \ frame_index<self.imageReader.n_frames: im = self.imageReader.get_frame(frame_index) frame_locs = np.asarray(self.locs.loc[ self.locs['frame']==frame_index, ['y', 'x']]).astype(np.int64) images.append(im) positions.append(frame_locs) frame_index += 1 # Launch an ImageSubpositionWindow ex = ImageSubpositionWindow(images, positions, w=15, N=N, parent=self)
class SymmetryPlugin(GUIPlugin): """Symmetry plugin""" # Define the name of the plugin (how it is displayed in Xi-CAM) name = "Symmetry Plugin" def __init__(self, *args, **kwargs): """Constructs the ExamplePlugin This will set up the widgets that we want the ExamplePlugin to have, the layout for the widgets (how the interface will look) in the ExamplePlugin, and an example workflow. """ self._catalog_viewer = CatalogView( ) # Create a widget to view the loaded catalog self._results_viewer = ImageView( ) #DynImageView() # Create a widget to view the result image self._workflow = SymmetryWorkflow() # Create a workflow # Create a widget for the workflow; this shows the operations and their paramters, # and we can run the workflow with this widget self._workflow_editor = WorkflowEditor(workflow=self._workflow) # The WorkflowEditor emits a "sigRunWorkflow" signal when its "Run Workflow" is clicked # This will call our run_workflow method whenever this signal is emitted (whenever the button is clicked). self._workflow_editor.sigRunWorkflow.connect(self.run_workflow) self._results_sliders = ImageCorrectionSliders() self._results_sliders.blur_slider.slider.valueChanged.connect( self.update_blur) self._results_sliders.color_slider.slider.valueChanged.connect( self.update_color) self.output_image = None # Create a layout to organize our widgets # The first argument (which corresponds to the center widget) is required. catalog_viewer_layout = GUILayout(self._catalog_viewer, right=self._workflow_editor, bottom=self._results_viewer, rightbottom=self._results_sliders) # Create a "View" stage that has the catalog viewer layout self.stages = {"View": catalog_viewer_layout} # For classes derived from GUIPlugin, this super __init__ must occur at end super(SymmetryPlugin, self).__init__(*args, **kwargs) def appendCatalog(self, catalog, **kwargs): """Re-implemented from GUIPlugin - gives us access to a catalog reference You MUST implement this method if you want to load catalog data into your GUIPlugin. """ # Set the catalog viewer's catalog, stream, and field (so it knows what to display) # This is a quick and simple demonstration; stream and field should NOT be hardcoded stream = "primary" field = "img" self._catalog_viewer.setCatalog(catalog, stream, field) def run_workflow(self): """Run the internal workflow. In this example, this will be called whenever the "Run Workflow" in the WorkflowEditor is clicked. """ if not self._catalog_viewer.catalog: # Don't run if there is no data loaded in return # Use Workflow's execute method to run the workflow. # our callback_slot will be called when the workflow has executed its operations # image is an additional keyword-argument that is fed into the first operation in the workflow # (the invert operation needs an "image" argument) self._workflow.execute(callback_slot=self.results_ready, image=self._catalog_viewer.image) def results_ready(self, *results): """Update the results view widget with the processed data. This is called when the workflow's execute method has finished running is operations. """ # print(results) # results is a tuple that will look like: # ({"output_name": output_value"}, ...) # This will only contain more than one dictionary if using Workflow.execute_all output_image = results[0][ "output_image"] # We want the output_image from the last operation self.output_image = output_image self._results_viewer.setImage( output_image) # Update the result view widget def update_blur(self): self.correct_image() def update_color(self): self.correct_image() # TODO: move this into our custom widget def correct_image(self): rgb2 = self.output_image * 255.0 rgg = Image.fromarray(np.uint8(rgb2)).convert('RGB') blur = self._results_sliders.blur_slider.slider.value() color = self._results_sliders.color_slider.slider.value() enhancer = ImageEnhance.Color(rgg) new1 = enhancer.enhance(color) new = np.array(new1.filter(ImageFilter.GaussianBlur(radius=blur))) # new = np.array(rgg.filter(ImageFilter.BLUR(radius = value))) self._results_viewer.setImage(new)
class StartWindow(QMainWindow): def __init__(self, webcam): super().__init__() self.webcam = webcam self.central_widget = QWidget() self.btnCapture = QPushButton("Capture", self.central_widget) self.btnExit = QPushButton("Exit", self.central_widget) self.nameText = QLabel("Please Enter Your Name: ", self.central_widget) self.userName = QLineEdit(self.central_widget) self.statusBar = QLabel("Status:", self.central_widget) self.statusUpdate = QLabel("Ready to Capture", self.central_widget) self.image_view = ImageView() self.layout = QVBoxLayout(self.central_widget) self.layout.addWidget(self.image_view) self.layout.addWidget(self.nameText) self.layout.addWidget(self.userName) self.layout.addWidget(self.statusBar) self.layout.addWidget(self.statusUpdate) self.layout.addWidget(self.btnCapture) self.layout.addWidget(self.btnExit) self.setCentralWidget(self.central_widget) #Create a timer object. Every timer timeout results in imageviewer being updated self.update_timer = QTimer() self.update_timer.timeout.connect(self.update_movie) #Connect the capture button with start_movie() self.btnCapture.clicked.connect(self.start_movie) self.btnExit.clicked.connect(self.exitApp) self.userName.textChanged.connect(self.btnChange) self.btnCapture.setEnabled(False) def btnChange(self): #Only enable the capture button when a name is entered if self.userName.text(): self.btnCapture.setEnabled(True) else: self.btnCapture.setEnabled(False) def update_movie(self): #Update the image viewer with the last frame of the self.webcam object self.image_view.setImage(self.webcam.last_frame.T) def start_movie(self): #Start the MovieThread if user clicks "Capture" button #Set timer to timeout every 30 ms #Disable buttons during capturing self.nameEntered = self.userName.text().replace(' ', '_') self.userName.setText('') self.btnCapture.setEnabled(False) self.btnExit.setEnabled(False) self.statusUpdate.setText("Capturing...") self.movie_thread = MovieThread(self.webcam, self.nameEntered, self.btnCapture, self.btnExit, self.statusUpdate) self.movie_thread.start() self.update_timer.start(30) def exitApp(self): #Upon exiting the application, run vid2faces.py on deepspeed4 ssh = self.webcam.close_camera() self.btnCapture.setEnabled(False) self.btnExit.setEnabled(False) self.statusUpdate.setText( "Converting videos into images on deepspeed4") stdin, stdout, stderr = ssh.exec_command( 'python3 {}vid2faces.py'.format(SERVER_HOME_DIR)) print("Output from vid2faces.py:") for line in stdout.readlines(): print(line.replace('\n', '').replace('\t', '')) if not stderr: for line in stderr.readlines(): print(line.replace('\n', '').replace('\t', '')) raise ValueError("Remote execution throws the exception above") self.close()
class VNAPowerSweepWidget(VNAFitWidget): def __init__(self): super(VNAPowerSweepWidget, self).__init__() self.power_start = QDoubleSpinBox() self.power_stop = QDoubleSpinBox() self.power_step = QDoubleSpinBox() self.form_layout.addRow("Power Start", self.power_start) self.form_layout.addRow("Power Stop", self.power_stop) self.form_layout.addRow("Power Stop", self.power_step) self.start_sweep_button = QPushButton("Start Sweep") self.start_sweep_button.clicked.connect(self.start_sweep) self.button_layout.addWidget(self.start_sweep_button) self.traces_plot = ImageView() self.plot_layout.addWidget(self.traces_plot) self.qints_plot = PlotWidget() self.plot_layout.addWidget(self.qints_plot) self.current_power = None self.traces = [] self.qints = [] def start_sweep(self): powers = arange(self.power_start.value(), self.power_stop.value(), self.power_step.value()) vna = self.get_vna() for i, power in enumerate(powers): self.current_power = power vna.set_power(power) self.grab_trace() self.fit_trace() self.save_trace() self.traces.append(self.mags) self.traces_plot.setImage(array(self.traces)) self.qints.append(self.fit_params['qint'].value) self.qints_plot.clear() self.qints_plot.plot(self.powers[:i], self.qints) self.current_power = None g = self.get_h5group() g['freqs'] = vna.get_xaxis() g['powers'] = powers g['qints'] = self.qints g['traces'] = self.traces dataserver_helpers.set_scale(g, 'powers', 'qints') dataserver_helpers.set_scale(g, 'powers', 'traces') dataserver_helpers.set_scale(g, 'powers', 'freqs', dim=1) def get_h5group(self): if self.current_power is not None: dataset = str(self.dataset_edit.text()) self.dataset_edit.setText(dataset + "/%.1f" % self.current_power) g = super(VNAPowerSweepWidget, self).get_h5group() self.dataset_edit.setText(dataset) else: return super(VNAPowerSweepWidget, self).get_h5group() def grab_trace(self): super(VNAPowerSweepWidget, self).grab_trace() power_array = self.get_h5group(use_power_child=False)
class Ui_MainWindow(object): def __init__(self): self.timer = QtCore.QTimer() self.number_of_clicked = 0 self.worker = Pool(1) self.result_space = None def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(1000, 700) MainWindow.setMaximumSize(QtCore.QSize(1000, 700)) setConfigOption('background', 'w') self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) self.horizontalLayout.setObjectName("horizontalLayout") self.formLayout_settings = QtWidgets.QFormLayout() self.formLayout_settings.setObjectName("formLayout_settings") self.label_neighbours_rule = QtWidgets.QLabel(self.centralwidget) self.label_neighbours_rule.setObjectName("label_neighbours_rule") self.formLayout_settings.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_neighbours_rule) self.comboBox_neighbours_rule = QtWidgets.QComboBox(self.centralwidget) self.comboBox_neighbours_rule.setObjectName("comboBox_neighbours_rule") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.comboBox_neighbours_rule.addItem("") self.formLayout_settings.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.comboBox_neighbours_rule) self.label_border_condition = QtWidgets.QLabel(self.centralwidget) self.label_border_condition.setObjectName("label_border_condition") self.formLayout_settings.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_border_condition) self.comboBox_border_condition = QtWidgets.QComboBox( self.centralwidget) self.comboBox_border_condition.setObjectName( "comboBox_border_condition") self.comboBox_border_condition.addItem("") self.comboBox_border_condition.addItem("") self.formLayout_settings.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.comboBox_border_condition) self.label_size_of_space = QtWidgets.QLabel(self.centralwidget) self.label_size_of_space.setObjectName("label_size_of_space") self.formLayout_settings.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_size_of_space) self.lineEdit_size_of_space = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_size_of_space.setObjectName("lineEdit_size_of_space") self.formLayout_settings.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.lineEdit_size_of_space) self.label_number_of_grains = QtWidgets.QLabel(self.centralwidget) self.label_number_of_grains.setObjectName("label_number_of_grains") self.formLayout_settings.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_number_of_grains) self.lineEdit_number_of_grains = QtWidgets.QLineEdit( self.centralwidget) self.lineEdit_number_of_grains.setObjectName( "lineEdit_number_of_grains") self.formLayout_settings.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.lineEdit_number_of_grains) self.label_inclusions_number = QtWidgets.QLabel(self.centralwidget) self.label_inclusions_number.setObjectName("label_inclusions_number") self.formLayout_settings.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_inclusions_number) self.lineEdit_inclusions_number = QtWidgets.QLineEdit( self.centralwidget) self.lineEdit_inclusions_number.setObjectName( "lineEdit_inclusions_number") self.formLayout_settings.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.lineEdit_inclusions_number) self.label_min_radius = QtWidgets.QLabel(self.centralwidget) self.label_min_radius.setObjectName("label_min_radius") self.formLayout_settings.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_min_radius) self.lineEdit_min_radius = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_min_radius.setObjectName("lineEdit_min_radius") self.formLayout_settings.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.lineEdit_min_radius) self.label_max_radius = QtWidgets.QLabel(self.centralwidget) self.label_max_radius.setObjectName("label_max_radius") self.formLayout_settings.setWidget(6, QtWidgets.QFormLayout.LabelRole, self.label_max_radius) self.lineEdit_max_radius = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_max_radius.setObjectName("lineEdit_max_radius") self.formLayout_settings.setWidget(6, QtWidgets.QFormLayout.FieldRole, self.lineEdit_max_radius) self.pushButton_import_from_csv = QtWidgets.QPushButton( self.centralwidget) self.pushButton_import_from_csv.setObjectName( "pushButton_import_from_csv") self.pushButton_import_from_csv.clicked.connect( self.io_open_file_name_dialog) self.formLayout_settings.setWidget(12, QtWidgets.QFormLayout.LabelRole, self.pushButton_import_from_csv) self.pushButton_export_to_csv = QtWidgets.QPushButton( self.centralwidget) self.pushButton_export_to_csv.setObjectName("pushButton_export_to_csv") self.pushButton_export_to_csv.clicked.connect(self.io_open_save_dialog) self.formLayout_settings.setWidget(13, QtWidgets.QFormLayout.LabelRole, self.pushButton_export_to_csv) self.pushButton_export_to_png = QtWidgets.QPushButton( self.centralwidget) self.pushButton_export_to_png.setObjectName("pushButton_export_to_png") self.pushButton_export_to_png.clicked.connect( self.io_open_save_dialog_image) self.formLayout_settings.setWidget(14, QtWidgets.QFormLayout.LabelRole, self.pushButton_export_to_png) self.line_horizontal = QtWidgets.QFrame(self.centralwidget) self.line_horizontal.setFrameShape(QtWidgets.QFrame.HLine) self.line_horizontal.setFrameShadow(QtWidgets.QFrame.Sunken) self.line_horizontal.setObjectName("line") self.formLayout_settings.setWidget(15, QtWidgets.QFormLayout.SpanningRole, self.line_horizontal) self.pushButton_init_space = QtWidgets.QPushButton(self.centralwidget) self.pushButton_init_space.setMaximumSize(QtCore.QSize(16777215, 40)) self.pushButton_init_space.setObjectName("pushButton_init_space") self.pushButton_init_space.clicked.connect( self.controller_init_ca_algo) self.formLayout_settings.setWidget(17, QtWidgets.QFormLayout.FieldRole, self.pushButton_init_space) self.pushButton_start_stop = QtWidgets.QPushButton(self.centralwidget) self.pushButton_start_stop.setObjectName("pushButton_start_stop") self.pushButton_start_stop.clicked.connect( self.controller_init_image_timer) self.formLayout_settings.setWidget(18, QtWidgets.QFormLayout.FieldRole, self.pushButton_start_stop) self.pushButton_step = QtWidgets.QPushButton(self.centralwidget) self.pushButton_step.setObjectName("pushButton_step") self.pushButton_step.clicked.connect(self.controller_one_step) self.formLayout_settings.setWidget(19, QtWidgets.QFormLayout.FieldRole, self.pushButton_step) self.pushButton_clear_space = QtWidgets.QPushButton(self.centralwidget) self.pushButton_clear_space.setObjectName("pushButton_clear_space") self.pushButton_clear_space.clicked.connect(self.view_clear_space) self.formLayout_settings.setWidget(20, QtWidgets.QFormLayout.FieldRole, self.pushButton_clear_space) self.label = QtWidgets.QLabel(self.centralwidget) self.label.setText("") self.label.setObjectName("label") self.formLayout_settings.setWidget(9, QtWidgets.QFormLayout.LabelRole, self.label) self.label_probability = QtWidgets.QLabel(self.centralwidget) self.label_probability.setObjectName("label_probability") self.formLayout_settings.setWidget(8, QtWidgets.QFormLayout.LabelRole, self.label_probability) self.lineEdit_prob_threshold = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_prob_threshold.setObjectName("lineEdit_prob_threshold") self.formLayout_settings.setWidget(8, QtWidgets.QFormLayout.FieldRole, self.lineEdit_prob_threshold) self.horizontalLayout.addLayout(self.formLayout_settings) self.line_vertical = QtWidgets.QFrame(self.centralwidget) self.line_vertical.setFrameShape(QtWidgets.QFrame.VLine) self.line_vertical.setFrameShadow(QtWidgets.QFrame.Sunken) self.line_vertical.setObjectName("line_2") self.horizontalLayout.addWidget(self.line_vertical) self.graphicsView = ImageView(self.centralwidget) self.graphicsView.setMinimumSize(QtCore.QSize(501, 501)) self.graphicsView.setMaximumSize(QtCore.QSize(501, 501)) self.graphicsView.setObjectName("graphicsView") self.graphicsView.ui.histogram.hide() self.graphicsView.ui.roiBtn.hide() self.graphicsView.ui.menuBtn.hide() self.graphicsView.show() self.horizontalLayout.addWidget(self.graphicsView) # Other stuff MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 1000, 26)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.controller_init_ca_algo() def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label_neighbours_rule.setText( _translate("MainWindow", "Neighbours rule")) self.comboBox_neighbours_rule.setItemText( 0, _translate("MainWindow", "VONNEUMANN")) self.comboBox_neighbours_rule.setItemText( 1, _translate("MainWindow", "MOORE")) self.comboBox_neighbours_rule.setItemText( 2, _translate("MainWindow", "HEXAGONAL_LEFT")) self.comboBox_neighbours_rule.setItemText( 3, _translate("MainWindow", "HEXAGONAL_RIGHT")) self.comboBox_neighbours_rule.setItemText( 4, _translate("MainWindow", "PENTAGONAL_LEFT")) self.comboBox_neighbours_rule.setItemText( 5, _translate("MainWindow", "PENTAGONAL_RIGHT")) self.label_border_condition.setText( _translate("MainWindow", "Border rule")) self.comboBox_border_condition.setItemText( 0, _translate("MainWindow", "ABSORBING")) self.comboBox_border_condition.setItemText( 1, _translate("MainWindow", "SNAKELIKE")) self.label_size_of_space.setText( _translate("MainWindow", "Size of space")) self.lineEdit_size_of_space.setText(_translate("MainWindow", "100")) self.label_number_of_grains.setText( _translate("MainWindow", "Number of grains")) self.lineEdit_number_of_grains.setText(_translate("MainWindow", "120")) self.label_inclusions_number.setText( _translate("MainWindow", "Number of incl.")) self.lineEdit_inclusions_number.setText(_translate("MainWindow", "0")) self.label_min_radius.setText(_translate("MainWindow", "Min radius")) self.lineEdit_min_radius.setText(_translate("MainWindow", "1")) self.label_max_radius.setText(_translate("MainWindow", "Max radius")) self.lineEdit_max_radius.setText(_translate("MainWindow", "6")) self.pushButton_import_from_csv.setText( _translate("MainWindow", "Import csv")) self.pushButton_export_to_csv.setText( _translate("MainWindow", "Export csv")) self.pushButton_export_to_png.setText( _translate("MainWindow", "Export png")) self.pushButton_init_space.setText( _translate("MainWindow", "Init space")) self.pushButton_start_stop.setText( _translate("MainWindow", "Start/Stop")) self.pushButton_step.setText(_translate("MainWindow", "Step")) self.pushButton_clear_space.setText( _translate("MainWindow", "Clear space")) self.label_probability.setText(_translate("MainWindow", "Probability")) self.lineEdit_prob_threshold.setText(_translate("MainWindow", "60")) def view_generate_pg_colormap(self): self.pos = np.linspace( 0.0, 1.0, self._ca_algo.number_of_reserved_ids + int(self.lineEdit_number_of_grains.text())) self.cmap = ColorMap(pos=self.pos, color=self._ca_algo.color_id) self.graphicsView.setColorMap(self.cmap) def view_display_image(self): self.graphicsView.setImage( self._ca_algo.space.T, levels=(0.0, self._ca_algo.number_of_reserved_ids + float(self.lineEdit_number_of_grains.text()))) def view_clear_space(self): self.result_space = None self.timer.stop() self._ca_algo.space = self._ca_algo.space_clear self.graphicsView.clear() def controller_init_ca_algo(self): self._ca_algo = None self.result_space = None self._ca_algo = CellularAutomata( int(self.lineEdit_number_of_grains.text()), int(self.lineEdit_inclusions_number.text()), int(self.lineEdit_min_radius.text()), int(self.lineEdit_max_radius.text()), int(self.lineEdit_size_of_space.text()), int(self.lineEdit_size_of_space.text()), str(self.comboBox_border_condition.currentText()), str(self.comboBox_neighbours_rule.currentText())) self._ca_algo.add_random() self._ca_algo.add_inclusions() self.view_generate_pg_colormap() self.view_display_image() def controller_init_image_timer(self): self.number_of_clicked += 1 if self.number_of_clicked % 2: self.pushButton_start_stop.setStyleSheet("background-color: green") self.timer.timeout.connect(self.controller_update_func) self.timer.start(50) else: self.pushButton_start_stop.setStyleSheet("background-color: none") self.timer.stop() def controller_update_func(self): if self.result_space is None: self.result_space = self.worker.apply_async(self._ca_algo.one_step) return if self.result_space.ready(): self._ca_algo.space = self.result_space.get() self.view_display_image() self.result_space = self.worker.apply_async(self._ca_algo.one_step) def controller_one_step(self): self.timer.stop() self.controller_update_func() def io_open_save_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self.centralwidget, "Save to CSV file", "", "CSV Files (*.csv)", options=options) if fileName: pd.DataFrame(self._ca_algo.space).to_csv(fileName) def io_open_file_name_dialog(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getOpenFileName(self.centralwidget, "Open CSV file", "", "CSV Files (*.csv)", options=options) if fileName: self._ca_algo.space = pd.read_csv(fileName, index_col=0).astype(int).values self.view_display_image() def io_open_save_dialog_image(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog fileName, _ = QFileDialog.getSaveFileName(self.centralwidget, "Save to PNG file", "", "PNG Files (*.png)", options=options) if fileName: self.graphicsView.export(fileName)
class TrackViewer(QWidget): def __init__(self, image_path, locs_path, pixel_size_um=0.16, frame_interval=0.00548, gui_size=600, cmap='plasma', start_frame=0, stop_frame=500, parent=None): super(TrackViewer, self).__init__(parent=parent) self.image_path = image_path self.locs_path = locs_path self.pixel_size_um = pixel_size_um self.frame_interval = frame_interval self.gui_size = gui_size self.cmap = cmap self.start_frame = start_frame self.stop_frame = stop_frame self.initData() self.initUI() def initData(self): # Load the set of localizations self.all_locs = pd.read_csv(self.locs_path) # Adjust the positions for off-by-1/2 pixel errors self.all_locs[['y', 'x']] = self.all_locs[['y', 'x']] + 0.5 # Load the images over which to plot localizations self.imageReader = ImageReader(self.image_path) # Set the default subregion to the full field of view self.subregion_kwargs = self.imageReader._process_subregion() # Adjust frame limits self.stop_frame = min(self.stop_frame, self.imageReader.n_frames-1) # Get the first image self.image = self.imageReader.get_frame(self.start_frame) # Set the number of overlay graphs. Due to the structure # of pyqtgraph, overlay is fastest when we have separate # graphs for each color of trajectory. self.n_graphs = 20 self.modulus = 7691 cmap = cm.get_cmap("plasma", self.n_graphs) self.graph_colors = np.array( [mpl_colors.rgb2hex(cmap(i)[:3]) for i in range(self.n_graphs)] ) # Assign each trajectory to one of the graphs. We'll do # this each time we retrack the localizations self.locs = self.all_locs.loc[ np.logical_and( self.all_locs['frame']>=self.start_frame, self.all_locs['frame']<=self.stop_frame, ), : ].copy() self.hash_tracks() def initUI(self): self.win = QWidget() L_master = QGridLayout(self.win) # Left window, for image win_left = QWidget(self.win) L_left = QGridLayout(win_left) L_master.addWidget(win_left, 0, 0, 1, 2) # Right window, for widgets win_right = QWidget(self.win) L_right = QGridLayout(win_right) L_master.addWidget(win_right, 0, 2, 1, 1) ## IMAGE / TRAJECTORY OVERLAY # ImageView, for showing image self.imageView = ImageView(parent=win_left) L_left.addWidget(self.imageView, 0, 0) self.imageView.setImage(self.image) # GraphItems, for overlaying trajectories in various # colors self.graphItems = [GraphItem() for j in range(self.n_graphs)] for j in range(self.n_graphs): self.graphItems[j].setParentItem(self.imageView.imageItem) self.graphItems[j].scatter.setPen(color=self.graph_colors[j], width=4.0) # ScatterPlotItem, for occasional overlay of search radii self.scatterPlotItem = ScatterPlotItem() self.scatterPlotItem.setParentItem(self.imageView.imageItem) ## WIDGETS widget_align = Qt.AlignTop # Frame slider self.frame_slider = IntSlider(minimum=self.start_frame, interval=1, maximum=self.stop_frame, init_value=self.start_frame, name='Frame', parent=win_right) self.frame_slider.assign_callback(self.frame_slider_callback) L_right.addWidget(self.frame_slider, 0, 0, alignment=widget_align) # Button to toggle trajectory overlay self.B_overlay_state = True self.B_overlay = QPushButton("Overlay trajectories", parent=win_right) self.B_overlay.clicked.connect(self.B_overlay_callback) L_right.addWidget(self.B_overlay, 7, 0, alignment=widget_align) # Menu to select the tracking option methods = keys_to_str(TRACK_SLIDER_CONFIG.keys()) self.M_method = LabeledQComboBox(methods, "Method", init_value="diffusion", parent=win_right) L_right.addWidget(self.M_method, 1, 0, alignment=widget_align) self.M_method.assign_callback(self.M_method_callback) # Five semantically-flexible FloatSliders to set tracking # method parameters self.floatSliders = [] self.n_sliders = 5 for j in range(self.n_sliders): slider = FloatSlider(parent=win_right, min_width=100) L_right.addWidget(slider, 2+j, 0, alignment=widget_align) slider.assign_callback(getattr(self, "slider_%d_callback" % j)) self.floatSliders.append(slider) # Button to change ROI self.B_change_roi = QPushButton("Change ROI", parent=win_right) L_right.addWidget(self.B_change_roi, 8, 0, alignment=widget_align) self.B_change_roi.clicked.connect(self.B_change_roi_callback) # Button to change frame limits self.B_frame_limits = QPushButton("Change frame limits", parent=win_right) L_right.addWidget(self.B_frame_limits, 9, 0, alignment=widget_align) self.B_frame_limits.clicked.connect(self.B_frame_limits_callback) # Button to toggle search radius overlay self.B_search_radius_state = False self.B_search_radius = QPushButton("Show search radii", parent=win_right) L_right.addWidget(self.B_search_radius, 9, 1, alignment=widget_align) self.B_search_radius.clicked.connect(self.B_search_radius_callback) ## KEYBOARD SHORTCUTS # Tab right/left through frames self.left_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Left), self.win) self.right_shortcut = QShortcut(QKeySequence(QtGui_Qt.Key_Right), self.win) self.left_shortcut.activated.connect(self.tab_prev_frame) self.right_shortcut.activated.connect(self.tab_next_frame) ## INITIALIZATION # overlay the first set of localizations self.change_track_method(self.M_method.currentText()) self.retrack() self.hash_tracks() self.update_tracks() # Resize and show GUI self.win.resize(self.gui_size*2, self.gui_size) self.win.show() ## CORE FUNCTIONS def set_track_params(self, reset=False, **kwargs): """ Modify or reset the current set of tracking parameters. args ---- reset : bool. If True, reset the set of tracking parameters back to defaults for the current tracking method **kwargs : kwargs to the current tracking method """ if reset: method = self.M_method.currentText() for k in [j for j in TRACK_SLIDER_CONFIG[method].keys() if j not in kwargs.keys()]: kwargs[k] = TRACK_SLIDER_CONFIG[method][k]['init_value'] self.track_params = { 'method': method, 'pixel_size_um': self.pixel_size_um, 'frame_interval': self.frame_interval, **kwargs } else: for k in kwargs.keys(): self.track_params[k] = kwargs[k] def update_image(self, frame_index=None, autoRange=False, autoLevels=False, autoHistogramRange=False): """ Get a new frame from the reader and update the central ImageView. args ---- frame_index : int, desired frame autoRange : bool, reset the ImageView window autoLevels : bool, reset LUTs autoHistogramRange : bool, reset LUT selection window """ if frame_index is None: frame_index = self.frame_slider.value() self.image = self.imageReader.get_subregion(frame_index, **self.subregion_kwargs) self.imageView.setImage(self.image, autoRange=autoRange, autoLevels=autoLevels, autoHistogramRange=autoHistogramRange) def load_tracks_in_frame_limits(self): """ Given the current set of frame limits and/or ROI limits, take only localizations in the frame limits/ ROI and assign them to self.locs. """ # Take only localizations in the ROI self.locs = self.all_locs.loc[ ( np.array(self.all_locs['frame']>=self.frame_slider.minimum) & \ np.array(self.all_locs['frame']<=self.frame_slider.maximum) & \ np.array(self.all_locs['y']>=self.subregion_kwargs['y0']) & \ np.array(self.all_locs['y']<=self.subregion_kwargs['y1']) & \ np.array(self.all_locs['x']>=self.subregion_kwargs['x0']) & \ np.array(self.all_locs['x']<=self.subregion_kwargs['x1']) ), : ].copy() # Adjust for the start pos self.locs['y'] = self.locs['y'] - self.subregion_kwargs['y0'] self.locs['x'] = self.locs['x'] - self.subregion_kwargs['x0'] def hash_tracks(self): """ Save the current value of self.locs as an ndarray with the name self.tracks. This facilitates much more rapid indexing, since ndarrays have faster operations than pandas.DataFrames. This also hashes the trajectory index to a graph index that determines in which color the trajectory will render. """ self.locs['graph_index'] = (self.locs['trajectory'] * self.modulus) % self.n_graphs self.tracks = np.asarray(self.locs[ ['frame', 'trajectory', 'y', 'x', 'graph_index'] ].sort_values(by=['trajectory', 'frame'])) def overlay_search_radii(self): """ On top of each localization in the current frame, overlay the search radii. """ if self.B_search_radius_state: # Get all localizations in the current frame frame_index = self.frame_slider.value() pos = self.tracks[self.tracks[:,0]==frame_index, 2:4] if 'search_radius' in self.track_params.keys() and \ 'pixel_size_um' in self.track_params.keys(): sr_pxl = self.track_params['search_radius']/self.track_params['pixel_size_um'] self.scatterPlotItem.setData( pos=pos, symbol='o', pxMode=False, pen={'color': '#FFFFFF', 'width': 2}, size=sr_pxl*2, brush=None, ) else: self.scatterPlotItem.setData([]) else: self.scatterPlotItem.setData([]) def retrack(self): """ Run tracking on the set of localizations with the current settings. """ self.locs = track(self.locs, **self.track_params) def change_track_method(self, method): """ Change the current tracking method, updating the sliders etc. """ # Reset the tracking params self.set_track_params(reset=True, method=method) # Reconfigure the sliders n_params = len(TRACK_SLIDER_CONFIG[method]) for j, name in enumerate(TRACK_SLIDER_CONFIG[method].keys()): self.floatSliders[j].show() self.floatSliders[j].configure( minimum=TRACK_SLIDER_CONFIG[method][name]['minimum'], maximum=TRACK_SLIDER_CONFIG[method][name]['maximum'], init_value=TRACK_SLIDER_CONFIG[method][name]['init_value'], name=name, interval=TRACK_SLIDER_CONFIG[method][name]['interval'], return_int=(TRACK_SLIDER_CONFIG[method][name]['type']=='int'), ) for j in range(n_params, len(self.floatSliders)): self.floatSliders[j].hide() # Retrack and render localizations self.load_tracks_in_frame_limits() self.retrack() self.hash_tracks() self.update_tracks() def update_tracks(self): """ Get the current set of trajectories from self.tracks and overlay onto the raw image. """ if self.B_overlay_state: # Get the current frame index frame_index = self.frame_slider.value() # Get all spots before or in the current frame T = self.tracks[self.tracks[:,0]<=frame_index, :] # Find the minimum trajectory index present in this frame try: t = T[T[:,0]==frame_index, 1].min() except ValueError: for gi in range(self.n_graphs): self.graphItems[gi].setData(pen=None) return # Get all trajectories that coincide with this frame T = T[np.where(T[:,1]==t)[0][0]:,:] # Iterate over each graph group for gi in range(self.n_graphs): # Take the trajectories that correspond to this graph G = T[T[:,4]==gi, :] # No points left - hide everything if len(G.shape)<2 or G.shape[0] < 1: self.graphItems[gi].setData( pen=None ) else: # Figure out which points correspond to the same trajectory indicator = (G[1:,1] - G[:-1,1]) == 0 connect_indices = np.where(indicator)[0] adj = np.asarray([connect_indices, connect_indices+1]).T # Perform the overlay self.graphItems[gi].setData( pos=G[:,2:4], adj=adj, pen={'color': self.graph_colors[gi], 'width': pen_width}, symbol='o', #symbolPen={'color': self.graph_colors[gi], 'width': pen_width}, brush=None, pxMode=False, size=symbol_sizes['o'] ) else: for gi in range(self.n_graphs): self.graphItems[gi].setData(pen=None) ## WIDGET CALLBACKS def frame_slider_callback(self): """ Respond to a user change in the frame slider. """ frame_index = self.frame_slider.value() self.update_image(frame_index=frame_index) self.update_tracks() self.overlay_search_radii() def tab_next_frame(self): """ Go to the next frame. Signaled by the right arrow key. """ next_idx = int(self.frame_slider.value()) if next_idx < self.frame_slider.maximum: next_idx += 1 self.frame_slider.setValue(next_idx) def tab_prev_frame(self): """ Go to the previous frame. Signaled by the left arrow key. """ prev_idx = int(self.frame_slider.value()) if prev_idx > self.frame_slider.minimum: prev_idx -= 1 self.frame_slider.setValue(prev_idx) def B_overlay_callback(self): """ Toggle the trajectory overlay. """ self.B_overlay_state = not self.B_overlay_state self.update_tracks() def M_method_callback(self): """ Change the current tracking method. """ m = self.M_method.currentText() self.change_track_method(m) self.retrack() self.hash_tracks() self.update_tracks() def slider_callback(self, slider_index): """ Respond to a user change in one of the sliders that sets tracking parameters. """ # Get the identity of the slider that changed name = self.floatSliders[slider_index].name # Get the new value of this slider value = self.floatSliders[slider_index].value() # Set this value in the tracking params self.track_params[name] = value # Retrack and rerender self.retrack() self.hash_tracks() self.update_tracks() # Special case: if this slider is the search radius, # also change the search radius icon if name == 'search_radius': self.overlay_search_radii() def slider_0_callback(self): self.slider_callback(0) def slider_1_callback(self): self.slider_callback(1) def slider_2_callback(self): self.slider_callback(2) def slider_3_callback(self): self.slider_callback(3) def slider_4_callback(self): self.slider_callback(4) def B_change_roi_callback(self): """ Change the current ROI used for tracking. """ # Make a maximum intensity projection if not hasattr(self, "max_int_proj"): print("Projecting...") self.max_int_proj = self.imageReader.max_int_proj() # Prompt the user to choose an ROI y_slice, x_slice = PromptSelectROI( self.max_int_proj, parent=self) # If the user has accepted, reset the ROI if not y_slice is None: y0 = y_slice.start y1 = y_slice.stop x0 = x_slice.start x1 = x_slice.stop self.subregion_kwargs = {'y0': y0, 'y1': y1, 'x0': x0, 'x1': x1} # Reslice the locs self.update_image(autoRange=True, autoLevels=True, autoHistogramRange=True) self.load_tracks_in_frame_limits() self.retrack() self.hash_tracks() self.update_tracks() self.overlay_search_radii() def B_frame_limits_callback(self): """ Change the current frame limits. """ # Prompt the user to select a start and stop frame labels = [ 'Start frame (min 0)', 'Stop frame (max %d)' % (self.imageReader.n_frames-1) ] defaults = [ self.start_frame, self.stop_frame ] choices = getTextInputs(labels, defaults, title="Select frame limits") # Reconfigure the slider self.frame_slider.configure( minimum=int(choices[0]), maximum=int(choices[1]), interval=1, name='Frame', init_value=int(choices[0]), ) # Take the tracks in the frame limits self.load_tracks_in_frame_limits() # Retrack etc. self.update_image(frame_index=int(choices[0]), autoRange=True, autoLevels=True, autoHistogramRange=True) self.retrack() self.hash_tracks() self.update_tracks() self.overlay_search_radii() def B_search_radius_callback(self): """ Toggle overlaying the search radius onto the raw image. """ self.B_search_radius_state = not self.B_search_radius_state self.overlay_search_radii()