class PixelSpace(ImageView): def __init__(self, *args, **kwargs): # Add axes self.axesItem = PlotItem() self.axesItem.axes["left"]["item"].setZValue(10) self.axesItem.axes["top"]["item"].setZValue(10) if "view" not in kwargs: kwargs["view"] = self.axesItem self._transform = QTransform() self._raw_image = None super(PixelSpace, self).__init__(*args, **kwargs) self.imageItem.sigImageChanged.connect(self.updateAxes) def transform(self, img=None): # Build Quads shape = img.shape a = [(0, shape[-2] - 1), (shape[-1] - 1, shape[-2] - 1), (shape[-1] - 1, 0), (0, 0)] b = [(0, 1), (shape[-1] - 1, 1), (shape[-1] - 1, shape[-2]), (0, shape[-2])] 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) for item in self.view.items: if isinstance(item, ImageItem): item.setTransform(transform) self._transform = transform return img, transform def setImage(self, img, *args, **kwargs): if img is None: return if getattr(self, "displaymode", DisplayMode.raw) == DisplayMode.raw: self._raw_image = img if not kwargs.get("transform", None): img, transform = self.transform(img) self.updateAxes() super(PixelSpace, self).setImage(img, *args, transform=transform, **kwargs) else: super(PixelSpace, self).setImage(img, *args, **kwargs) def setTransform(self): self.setImage(self._raw_image) # this should loop back around to the respective transforms def updateAxes(self): self.axesItem.setLabel("bottom", "x (px)") # , units='s') self.axesItem.setLabel("left", "z (px)")
class XArrayView(ImageView): def __init__(self, *args, **kwargs): # Add axes self.axesItem = PlotItem() self.axesItem.axes["left"]["item"].setZValue(10) self.axesItem.axes["top"]["item"].setZValue(10) if "view" not in kwargs: kwargs["view"] = self.axesItem super(XArrayView, self).__init__(*args, **kwargs) self.view.invertY(False) def setImage(self, img, **kwargs): if hasattr(img, 'coords'): if 'transform' not in kwargs: xvals = img.coords[img.dims[-2]] yvals = img.coords[img.dims[-1]] xmin = float(xvals.min()) xmax = float(xvals.max()) ymin = float(yvals.min()) ymax = float(yvals.max()) # Position the image according to coords shape = img.shape a = [(0, shape[-2]), (shape[-1] - 1, shape[-2]), (shape[-1] - 1, 1), (0, 1)] b = [(ymin, xmin), (ymax, xmin), (ymax, xmax), (ymin, xmax)] 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) kwargs['transform'] = transform if 'xvals' not in kwargs: kwargs['xvals'] = np.asarray(img.coords[img.dims[0]]) # Set the timeline axis label from dims self.ui.roiPlot.setLabel('bottom', img.dims[0]) # Label the image axes self.axesItem.setLabel('left', img.dims[-2]) self.axesItem.setLabel('bottom', img.dims[-1]) # Add a bit more size self.ui.roiPlot.setMinimumSize(QSize(0, 70)) # Bind coords from the xarray to the timeline axis super(XArrayView, self).setImage(img, **kwargs) def updateImage(self, autoHistogramRange=True): if hasattr(self.image, 'dims'): ## 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)) else: super(XArrayView, self).updateImage(autoHistogramRange) 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]
class SAXSViewerPlugin(DynImageView, QWidgetPlugin): def __init__(self, header: NonDBHeader = None, field: str = 'primary', toolbar: QToolBar = None, *args, **kwargs): # Add q axes self.axesItem = PlotItem() self.axesItem.setLabel('bottom', u'q (Å⁻¹)') # , units='s') self.axesItem.setLabel('left', u'q (Å⁻¹)') self.axesItem.axes['left']['item'].setZValue(10) self.axesItem.axes['top']['item'].setZValue(10) if 'view' not in kwargs: kwargs['view'] = self.axesItem super(SAXSViewerPlugin, self).__init__(**kwargs) self.axesItem.invertY(False) # Setup axes reset button self.resetAxesBtn = QPushButton('Reset Axes') sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(1) sizePolicy.setHeightForWidth( self.resetAxesBtn.sizePolicy().hasHeightForWidth()) self.resetAxesBtn.setSizePolicy(sizePolicy) self.resetAxesBtn.setObjectName("resetAxes") self.ui.gridLayout.addWidget(self.resetAxesBtn, 2, 1, 1, 1) self.resetAxesBtn.clicked.connect(self.autoRange) # Setup LUT reset button self.resetLUTBtn = QPushButton('Reset LUT') sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(1) sizePolicy.setHeightForWidth( self.resetLUTBtn.sizePolicy().hasHeightForWidth()) # self.resetLUTBtn.setSizePolicy(sizePolicy) # self.resetLUTBtn.setObjectName("resetLUTBtn") self.ui.gridLayout.addWidget(self.resetLUTBtn, 3, 1, 1, 1) self.resetLUTBtn.clicked.connect(self.autoLevels) # Hide ROI button and rearrange self.ui.roiBtn.setParent(None) self.ui.gridLayout.addWidget(self.ui.menuBtn, 1, 1, 1, 1) self.ui.gridLayout.addWidget(self.ui.graphicsView, 0, 0, 3, 1) # Setup coordinates label self.coordinatesLbl = QLabel('--COORDINATES WILL GO HERE--') self.ui.gridLayout.addWidget(self.coordinatesLbl, 3, 0, 1, 1, alignment=Qt.AlignHCenter) # Setup mask layer self.maskimage = pg.ImageItem(opacity=.25) self.view.addItem(self.maskimage) # Setup calibration layer self.calibrantimage = pg.ImageItem(opacity=.25) self.view.addItem(self.calibrantimage) # Empty ROI for later use self.maskROI = pg.PolyLineROI([], closed=True, movable=False, pen=pg.mkPen(color='r', width=2)) self.maskROI.handlePen = pg.mkPen(color='r', width=2) self.maskROI.handleSize = 10 self.view.addItem(self.maskROI) # Connect toolbar handlers self.toolbar = toolbar if self.toolbar: self.toolbar.modegroup.triggered.connect(self.redraw) # Setup results cache self.results = [] # Set header if header: self.setHeader(header, field) def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field # make lazy array from document data = None try: data = header.meta_array(field) except IndexError: msg.logMessage( 'Header object contained no frames with field ' '{field}' '.', msg.ERROR) if data: kwargs['transform'] = QTransform(0, -1, 1, 0, 0, data.shape[-2]) super(SAXSViewerPlugin, self).setImage(img=data, *args, **kwargs) def setMaskImage(self, mask): if mask is not None: self.maskimage.setImage(mask, lut=np.array([[0, 0, 0, 0], [255, 0, 0, 255]])) self.maskimage.setTransform( QTransform(0, -1, 1, 0, 0, mask.shape[-2])) else: self.maskimage.clear() def setCalibrantImage(self, data): if data is not None: self.calibrantimage.setImage(data, lut=calibrantlut) self.calibrantimage.setTransform(QTransform(0, 1, 1, 0, 0, 0)) else: self.calibrantimage.clear() def redraw(self): if not self.parent().currentWidget() == self: return # Don't redraw when not shown for result in self.results: try: if self.toolbar.cakeaction.isChecked(): self.setImage(result['cake'].value) break elif self.toolbar.remeshaction.isChecked(): self.setImage(result['remesh'].value ) # TODO: add checkbox to toolbar break elif 'inpaint' in result: self.setImage(result['inpaint'].value) break except TypeError: continue else: # if self.toolbar.rawaction.isChecked(): self.setHeader(self.header, self.field) def setResults(self, results): self.results = results self.redraw()
class EFIViewerPlugin(Crosshair, QCoordinates, DynImageView, QWidgetPlugin): def __init__(self, header: NonDBHeader = None, field: str = 'primary', toolbar: QToolBar = None, *args, **kwargs): # Add axes self.axesItem = PlotItem() # self.axesItem.setLabel('bottom', u'q ()') # , units='s') # self.axesItem.setLabel('bottom', u'q ()') # , units='s') # self.axesItem.setLabel('left', u'q ()') self.axesItem.axes['left']['item'].setZValue(10) self.axesItem.axes['top']['item'].setZValue(10) if 'view' not in kwargs: kwargs['view'] = self.axesItem super(EFIViewerPlugin, self).__init__(**kwargs) self.axesItem.invertY(True) # Setup axes reset button self.resetAxesBtn = QPushButton('Reset Axes') sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(1) sizePolicy.setHeightForWidth( self.resetAxesBtn.sizePolicy().hasHeightForWidth()) self.resetAxesBtn.setSizePolicy(sizePolicy) self.resetAxesBtn.setObjectName("resetAxes") self.ui.gridLayout.addWidget(self.resetAxesBtn, 2, 1, 1, 1) self.resetAxesBtn.clicked.connect(self.autoRange) # Setup LUT reset button self.resetLUTBtn = QPushButton('Reset LUT') sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(1) sizePolicy.setHeightForWidth( self.resetLUTBtn.sizePolicy().hasHeightForWidth()) # self.resetLUTBtn.setSizePolicy(sizePolicy) # self.resetLUTBtn.setObjectName("resetLUTBtn") self.ui.gridLayout.addWidget(self.resetLUTBtn, 3, 1, 1, 1) self.resetLUTBtn.clicked.connect(self.autoLevels) # Hide ROI button and rearrange # self.ui.roiBtn.setParent(None) # self.ui.gridLayout.addWidget(self.ui.menuBtn, 1, 1, 1, 1) # self.ui.gridLayout.addWidget(self.ui.graphicsView, 0, 0, 3, 1) # Setup coordinates label # self.coordinatesLbl = QLabel('--COORDINATES WILL GO HERE--') # self.ui.gridLayout.addWidget(self.coordinatesLbl, 3, 0, 1, 1, alignment=Qt.AlignHCenter) # Set header if header: self.setHeader(header, field) def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field # make lazy array from document data = None try: data = header.meta_array(field) except IndexError: msg.logMessage( f'Header object contained no frames with field {field}.', msg.ERROR) for ii in header: msg.logMessage('header types = '.format(type(ii))) if data: # data = np.squeeze(data) #test for 1D spectra if data.ndim > 1: # kwargs['transform'] = QTransform(0, -1, 1, 0, 0, data.shape[-2]) # NOTE PAE: for setImage: # use setImage(xVals=timeVals) to set the values on the slider for 3D data try: # Retrieve the metadata for pixel scale and units descriptorsTee = itertools.tee( header.descriptors, 1)[0] # tee the descriptors generator once _ = next(descriptorsTee) # start document headerTitle, md = next( descriptorsTee) # descriptor document with metadata scale0 = (md['PhysicalSizeX'], md['PhysicalSizeY']) units0 = (md['PhysicalSizeXUnit'], md['PhysicalSizeYUnit']) except: scale0 = (1, 1) units0 = ('', '') msg.logMessage( 'EFIviewer: No pixel size or units detected') super(EFIViewerPlugin, self).setImage(img=data, scale=scale0, *args, **kwargs) self.axesItem.setLabel('bottom', text='X', units=units0[0]) self.axesItem.setLabel('left', text='Y', units=units0[1])