示例#1
0
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)))
示例#2
0
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)
示例#3
0
 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')
示例#5
0
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)
示例#6
0
    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)
示例#7
0
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
示例#8
0
    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())
示例#10
0
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()
示例#11
0
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)
示例#12
0
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)
示例#13
0
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)
示例#14
0
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()
示例#15
0
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!')
示例#16
0
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)
示例#17
0
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_()
示例#18
0
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()
示例#19
0
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()
示例#20
0
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)
示例#21
0
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))
示例#22
0
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)
示例#23
0
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)
示例#24
0
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()
示例#25
0
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)
示例#26
0
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)
示例#27
0
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()