示例#1
0
class PreviewWidget(GraphicsLayoutWidget):
    def __init__(self):
        super(PreviewWidget, self).__init__()
        self.setMinimumHeight(250)
        self.setMinimumWidth(250)
        self.view = self.addViewBox(lockAspect=True, enableMenu=False)
        self.imageitem = ImageItem()
        self.textitem = TextItem(anchor=(0.5, 0))
        self.textitem.setFont(QFont("Zero Threes"))
        self.imgdata = None

        self.imageitem.setOpts(axisOrder="row-major")

        self.view.addItem(self.imageitem)
        self.view.addItem(self.textitem)
        self.textitem.hide()
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # def textItemBounds(axis, frac=1.0, orthoRange=None):
        #     b = self.textitem.boundingRect()
        #     sx, sy = self.view.viewPixelSize()
        #     x, y = sx*b.width(), sy*b.height()
        #     if axis == 0: return (-x/2, x/2)
        #     if axis == 1: return (0, y)
        #
        # self.textitem.dataBounds = textItemBounds

    def sizeHint(self):
        return QSize(250, 250)

    def preview_header(self, header: NonDBHeader):
        try:
            data = header.meta_array()[0]
            self.setImage(data)
        except IndexError:
            self.imageitem.clear()
            self.setText("UNKNOWN DATA FORMAT")

    def setImage(self, imgdata):
        self.imageitem.clear()
        self.textitem.hide()
        self.imgdata = imgdata
        self.imageitem.setImage(np.log(self.imgdata * (self.imgdata > 0) +
                                       (self.imgdata < 1)),
                                autoLevels=True)
        self.imageitem.setTransform(
            QTransform(1, 0, 0, -1, 0, self.imgdata.shape[-2]))
        self.view.autoRange()

    def setText(self, text):
        self.textitem.setText(text)
        self.imageitem.clear()
        self.textitem.setVisible(True)
        self.view.autoRange()
示例#2
0
class SliceableGraphicsView(GraphicsView, SlicingView):
    sigToggleHorizontalSlice = Signal(bool)
    sigToggleVerticalSlice = Signal(bool)
    sigToggleDepthSlice = Signal(bool)
    sigMakePrimary = Signal(object, object)
    sigCrosshairMoved = Signal()

    SUPPORTED_NDIM = 2

    def __init__(self, slice_direction, parent=None, xlink=None, ylink=None):
        super(SliceableGraphicsView, self).__init__(parent=parent)

        self.slice_direction = slice_direction

        self.setContentsMargins(0, 0, 0, 0)

        # Add axes
        self.view = SliceableAxes(slice_direction)
        self.view.axes["left"]["item"].setZValue(10)
        self.view.axes["top"]["item"].setZValue(10)
        self.setCentralItem(self.view)

        for sig in [
                'sigToggleVerticalSlice', 'sigToggleHorizontalSlice',
                'sigToggleDepthSlice', 'sigMakePrimary'
        ]:
            if hasattr(self.view, sig):
                getattr(self.view, sig).connect(getattr(self, sig))

        # Add imageitem
        self.image_item = ImageItem(axisOrder='row-major')
        self.image_item.setOpts()
        self.view.addItem(self.image_item)

        # add crosshair
        self.crosshair = BetterCrosshairROI((0, 0),
                                            parent=self.view,
                                            resizable=False)
        self.crosshair.sigMoved.connect(self.sigCrosshairMoved)
        self.view.getViewBox().addItem(self.crosshair)

        # find top-level parent NDImageView
        while not isinstance(parent, NDImageView):
            parent = parent.parent()

        # Initialize lut, levels
        self.image_item.setLevels(parent.levels, update=True)
        self.image_item.setLookupTable(parent.lut, update=True)

        # Link axes
        if ylink:
            self.view.vb.setYLink(ylink)
        if xlink:
            self.view.vb.setXLink(xlink)

    def setData(self, data):
        # Constrain squareness when units match
        is_square = data.dims[-2].split('(')[-1] == data.dims[-1].split(
            '(')[-1]
        self.view.vb.setAspectLocked(is_square)

        xvals = data.coords[data.dims[-1]]
        yvals = data.coords[data.dims[-2]]
        xmin = float(xvals.min())
        xmax = float(xvals.max())
        ymin = float(yvals.min())
        ymax = float(yvals.max())

        # Position the image according to coords
        shape = data.shape
        a = [(0, shape[-2]), (shape[-1], shape[-2]), (shape[-1], 0), (0, 0)]

        # b = [(ymin, xmax), (ymax, xmax), (ymax, xmin), (ymin, xmin)]
        if self.slice_direction in ['horizontal', 'depth']:
            b = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]
        elif self.slice_direction == 'vertical':
            b = [(xmax, ymax), (xmin, ymax), (xmin, ymin), (xmax, ymin)]

        quad1 = QPolygonF()
        quad2 = QPolygonF()
        for p, q in zip(a, b):
            quad1.append(QPointF(*p))
            quad2.append(QPointF(*q))

        transform = QTransform()
        QTransform.quadToQuad(quad1, quad2, transform)

        # Bind coords from the xarray to the timeline axis
        # super(SliceableGraphicsView, self).setImage(img, autoRange, autoLevels, levels, axes, np.asarray(img.coords[img.dims[0]]), pos, scale, transform, autoHistogramRange, levelMode)
        self.image_item.setImage(np.asarray(data), autoLevels=False)
        self.image_item.setTransform(transform)

        # Label the image axes
        self.view.setLabel('left', data.dims[-2])
        self.view.setLabel('bottom', data.dims[-1])

    def resetCrosshair(self):
        transform = self.image_item.viewTransform()
        new_pos = transform.map(self.image_item.boundingRect().center())
        self.crosshair.setPos(new_pos)
        # self.crosshair.sigMoved.emit(new_pos)

    def updateImage(self, autoHistogramRange=True):
        ## Redraw image on screen
        if self.image is None:
            return

        image = self.getProcessedImage()

        if autoHistogramRange:
            self.ui.histogram.setHistogramRange(self.levelMin, self.levelMax)

        # Transpose image into order expected by ImageItem
        if self.imageItem.axisOrder == 'col-major':
            axorder = ['t', 'x', 'y', 'c']
        else:
            axorder = ['t', 'y', 'x', 'c']
        axorder = [
            self.axes[ax] for ax in axorder if self.axes[ax] is not None
        ]
        ax_swap = [image.dims[ax_index] for ax_index in axorder]
        image = image.transpose(*ax_swap)

        # Select time index
        if self.axes['t'] is not None:
            self.ui.roiPlot.show()
            image = image[self.currentIndex]

        self.imageItem.updateImage(np.asarray(image))

    def updateCrosshair(self, x, y):
        self.crosshair.setPos(x, y)

    def quickMinMax(self, data):
        """
        Estimate the min/max values of *data* by subsampling. MODIFIED TO USE THE 99TH PERCENTILE instead of max.
        """
        if data is None:
            return 0, 0

        sl = slice(None, None, max(1, int(data.size // 1e6)))
        data = np.asarray(data[sl])

        levels = (np.nanmin(data),
                  np.nanpercentile(
                      np.where(data < np.nanmax(data), data, np.nanmin(data)),
                      99))

        return [levels]
示例#3
0
class PreviewWidget(GraphicsLayoutWidget):
    def __init__(self):
        super(PreviewWidget, self).__init__()
        self.setMinimumHeight(250)
        self.setMinimumWidth(250)
        self.view = self.addViewBox(lockAspect=True, enableMenu=False)
        self.imageitem = ImageItem()
        self.textitem = TextItem(anchor=(0.5, 0))
        self.textitem.setFont(QFont("Zero Threes"))
        self.imgdata = None

        self.imageitem.setOpts(axisOrder="row-major")

        self.view.addItem(self.imageitem)
        self.view.addItem(self.textitem)
        self.textitem.hide()
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # def textItemBounds(axis, frac=1.0, orthoRange=None):
        #     b = self.textitem.boundingRect()
        #     sx, sy = self.view.viewPixelSize()
        #     x, y = sx*b.width(), sy*b.height()
        #     if axis == 0: return (-x/2, x/2)
        #     if axis == 1: return (0, y)
        #
        # self.textitem.dataBounds = textItemBounds

    def sizeHint(self):
        return QSize(250, 250)

    @threads.method(threadkey="preview", showBusy=False)
    def preview(self, data):
        if isinstance(data, NonDBHeader):
            self.preview_header(data)
        else:
            self.preview_catalog(data)

    @staticmethod
    def guess_stream_field(catalog: BlueskyRun):
        # TODO: use some metadata (techniques?) for guidance about how to get a preview

        streams = bluesky_utils.streams_from_run(catalog)
        if "primary" in streams:
            streams.remove("primary")
            streams.insert(0, "primary")

        for stream in streams:
            descriptor = bluesky_utils.descriptors_from_stream(
                catalog, stream)[0]
            fields = bluesky_utils.fields_from_descriptor(descriptor)
            for field in fields:
                field_ndims = bluesky_utils.ndims_from_descriptor(
                    descriptor, field)
                if field_ndims > 1:
                    return stream, field

    def preview_catalog(self, catalog: BlueskyRun):
        threads.invoke_in_main_thread(self.setText, "LOADING...")
        try:
            stream, field = self.guess_stream_field(catalog)
            data = getattr(catalog, stream).to_dask()[field].squeeze()
            for i in range(len(data.shape) - 2):
                data = data[0]
            threads.invoke_in_main_thread(self.setImage,
                                          np.asarray(data.compute()))
        except Exception as ex:
            msg.logError(ex)
            threads.invoke_in_main_thread(self.imageitem.clear)
            threads.invoke_in_main_thread(self.setText, "UNKNOWN DATA FORMAT")

    def preview_header(self, header: NonDBHeader):
        try:
            data = header.meta_array()[0]
            threads.invoke_in_main_thread(self.setImage, data)
        except IndexError:
            threads.invoke_in_main_thread(self.imageitem.clear)
            threads.invoke_in_main_thread(self.setText, "UNKNOWN DATA FORMAT")

    def setImage(self, imgdata):
        self.imageitem.clear()
        self.textitem.hide()
        self.imgdata = imgdata
        self.imageitem.setImage(np.log(self.imgdata * (self.imgdata > 0) +
                                       (self.imgdata < 1)),
                                autoLevels=True)
        self.imageitem.setTransform(
            QTransform(1, 0, 0, -1, 0, self.imgdata.shape[-2]))
        self.view.autoRange()

    def setText(self, text):
        self.textitem.setText(text)
        self.imageitem.clear()
        self.textitem.setVisible(True)
        self.view.autoRange()
示例#4
0
class SliceableGraphicsView(GraphicsView):
    sigToggleHorizontalSlice = Signal(bool)
    sigToggleVerticalSlice = Signal(bool)
    sigMakePrimary = Signal(object, object)

    def __init__(self):
        super(SliceableGraphicsView, self).__init__()

        self.setContentsMargins(0, 0, 0, 0)

        # Add axes
        self.view = SliceableAxes()
        self.view.axes["left"]["item"].setZValue(10)
        self.view.axes["top"]["item"].setZValue(10)
        self.setCentralItem(self.view)
        self.view.sigToggleVerticalSlice.connect(self.sigToggleVerticalSlice)
        self.view.sigToggleHorizontalSlice.connect(self.sigToggleHorizontalSlice)
        self.view.sigMakePrimary.connect(self.sigMakePrimary)

        # Add imageitem
        self.image_item = ImageItem()
        self.view.addItem(self.image_item)

        # add crosshair
        self.crosshair = BetterCrosshairROI((0, 0), parent=self.view, resizable=False)
        self.view.getViewBox().addItem(self.crosshair)

    def setData(self, data):

        xvals = data.coords[data.dims[-1]]
        yvals = data.coords[data.dims[-2]]
        xmin = float(xvals.min())
        xmax = float(xvals.max())
        ymin = float(yvals.min())
        ymax = float(yvals.max())

        # Position the image according to coords
        shape = data.shape
        a = [(0, shape[-1]), (shape[-2] - 1, shape[-1]), (shape[-2] - 1, 1), (0, 1)]

        # b = [(ymin, xmax), (ymax, xmax), (ymax, xmin), (ymin, xmin)]
        b = [(xmax, ymin), (xmax, ymax), (xmin, ymax), (xmin, ymin)]

        quad1 = QPolygonF()
        quad2 = QPolygonF()
        for p, q in zip(a, b):
            quad1.append(QPointF(*p))
            quad2.append(QPointF(*q))

        transform = QTransform()
        QTransform.quadToQuad(quad1, quad2, transform)

        # Bind coords from the xarray to the timeline axis
        # super(SliceableGraphicsView, self).setImage(img, autoRange, autoLevels, levels, axes, np.asarray(img.coords[img.dims[0]]), pos, scale, transform, autoHistogramRange, levelMode)
        self.image_item.setImage(np.asarray(data), autoLevels=False)
        self.image_item.setTransform(transform)

        # Label the image axes
        self.view.setLabel('left', data.dims[-2])
        self.view.setLabel('bottom', data.dims[-1])

    def resetCrosshair(self):
        transform = self.image_item.viewTransform()
        new_pos = transform.map(self.image_item.boundingRect().center())
        self.crosshair.setPos(new_pos)
        self.crosshair.sigMoved.emit(new_pos)

    def updateImage(self, autoHistogramRange=True):
        ## Redraw image on screen
        if self.image is None:
            return

        image = self.getProcessedImage()

        if autoHistogramRange:
            self.ui.histogram.setHistogramRange(self.levelMin, self.levelMax)

        # Transpose image into order expected by ImageItem
        if self.imageItem.axisOrder == 'col-major':
            axorder = ['t', 'x', 'y', 'c']
        else:
            axorder = ['t', 'y', 'x', 'c']
        axorder = [self.axes[ax] for ax in axorder if self.axes[ax] is not None]
        ax_swap = [image.dims[ax_index] for ax_index in axorder]
        image = image.transpose(*ax_swap)

        # Select time index
        if self.axes['t'] is not None:
            self.ui.roiPlot.show()
            image = image[self.currentIndex]

        self.imageItem.updateImage(np.asarray(image))

    def quickMinMax(self, data):
        """
        Estimate the min/max values of *data* by subsampling. MODIFIED TO USE THE 99TH PERCENTILE instead of max.
        """
        if data is None:
            return 0, 0

        sl = slice(None, None, max(1, int(data.size // 1e6)))
        data = np.asarray(data[sl])

        levels = (np.nanmin(data), np.nanpercentile(np.where(data < np.nanmax(data), data, np.nanmin(data)), 99))

        return [levels]
示例#5
0
class PreviewWidget(GraphicsLayoutWidget):
    def __init__(self):
        super(PreviewWidget, self).__init__()
        self.setMinimumHeight(250)
        self.setMinimumWidth(250)
        self.view = self.addViewBox(lockAspect=True, enableMenu=False)
        self.imageitem = ImageItem()
        self.textitem = TextItem(anchor=(0.5, 0))
        self.textitem.setFont(QFont("Zero Threes"))
        self.imageitem.setOpts(axisOrder="row-major")

        self.view.addItem(self.imageitem)
        self.view.addItem(self.textitem)
        self.textitem.hide()
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # def textItemBounds(axis, frac=1.0, orthoRange=None):
        #     b = self.textitem.boundingRect()
        #     sx, sy = self.view.viewPixelSize()
        #     x, y = sx*b.width(), sy*b.height()
        #     if axis == 0: return (-x/2, x/2)
        #     if axis == 1: return (0, y)
        #
        # self.textitem.dataBounds = textItemBounds

    def sizeHint(self):
        return QSize(250, 250)

    @threads.method(threadkey="preview", showBusy=False)
    def preview(self, data):
        if isinstance(data, NonDBHeader):
            self.preview_header(data)
        else:
            self.preview_catalog(data)

    def preview_catalog(self, catalog: BlueskyRun):
        threads.invoke_in_main_thread(self.setText, "LOADING...")
        try:
            stream, field = bluesky_utils.guess_stream_field(catalog)
            data = bluesky_utils.preview(catalog, stream, field)
            threads.invoke_in_main_thread(self.setImage, data)
        except Exception as ex:
            msg.logError(ex)
            threads.invoke_in_main_thread(self.imageitem.clear)
            threads.invoke_in_main_thread(self.setText, "UNKNOWN DATA FORMAT")

    def preview_header(self, header: NonDBHeader):
        try:
            data = header.meta_array()[0]
            threads.invoke_in_main_thread(self.setImage, data)
        except IndexError:
            threads.invoke_in_main_thread(self.imageitem.clear)
            threads.invoke_in_main_thread(self.setText, "UNKNOWN DATA FORMAT")

    def setImage(self, imgdata):
        self.imageitem.clear()
        self.textitem.hide()
        self.imageitem.setImage(np.log(imgdata * (imgdata > 0) +
                                       (imgdata < 1)),
                                autoLevels=True)
        self.imageitem.setTransform(
            QTransform(1, 0, 0, -1, 0, imgdata.shape[-2]))
        self.view.autoRange()

    def setText(self, text):
        self.textitem.setText(text)
        self.imageitem.clear()
        self.textitem.setVisible(True)
        self.view.autoRange()
示例#6
0
class PreviewWidget(GraphicsLayoutWidget):
    def __init__(self):
        super(PreviewWidget, self).__init__()
        self.setMinimumHeight(250)
        self.setMinimumWidth(250)
        self.view = self.addViewBox(lockAspect=True, enableMenu=False)
        self.imageitem = ImageItem()
        self.textitem = TextItem(anchor=(0.5, 0))
        self.textitem.setFont(QFont("Zero Threes"))
        self.imgdata = None

        self.imageitem.setOpts(axisOrder="row-major")

        self.view.addItem(self.imageitem)
        self.view.addItem(self.textitem)
        self.textitem.hide()
        self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)

        # def textItemBounds(axis, frac=1.0, orthoRange=None):
        #     b = self.textitem.boundingRect()
        #     sx, sy = self.view.viewPixelSize()
        #     x, y = sx*b.width(), sy*b.height()
        #     if axis == 0: return (-x/2, x/2)
        #     if axis == 1: return (0, y)
        #
        # self.textitem.dataBounds = textItemBounds

    def sizeHint(self):
        return QSize(250, 250)

    def preview(self, data):
        if isinstance(data, NonDBHeader):
            self.preview_header(data)
        else:
            self.preview_catalog(data)

    def preview_catalog(self, catalog: BlueskyRun):
        try:
            dask_array = catalog.primary.to_dask()
            fields = dask_array.keys()
            # Filter out seq num and uid
            field = next(field for field in fields if not field in ["seq_num", "uid"])
            data = dask_array[field]
            for i in range(len(data.shape) - 2):
                data = data[0]
            self.setImage(np.asarray(data.compute()))
        except IndexError:
            self.imageitem.clear()
            self.setText("UNKNOWN DATA FORMAT")

    def preview_header(self, header: NonDBHeader):
        try:
            data = header.meta_array()[0]
            self.setImage(data)
        except IndexError:
            self.imageitem.clear()
            self.setText("UNKNOWN DATA FORMAT")

    def setImage(self, imgdata):
        self.imageitem.clear()
        self.textitem.hide()
        self.imgdata = imgdata
        self.imageitem.setImage(np.log(self.imgdata * (self.imgdata > 0) + (self.imgdata < 1)), autoLevels=True)
        self.imageitem.setTransform(QTransform(1, 0, 0, -1, 0, self.imgdata.shape[-2]))
        self.view.autoRange()

    def setText(self, text):
        self.textitem.setText(text)
        self.imageitem.clear()
        self.textitem.setVisible(True)
        self.view.autoRange()