示例#1
0
class SkyImagePlotItem(QwtPlotItem, QObject):
    """SkyImagePlotItem is a 2D image in l,m coordimnates"""
    def __init__(self, nx=0, ny=0, l0=0, m0=0, dl=1, dm=1, image=None):
        QwtPlotItem.__init__(self)
        # name, if any
        self.name = self.filename = None
        # internal init
        self._qo = QObject()
        self._image = self._imgminmax = None
        self._nvaluecalls = 0
        self._value_time = self._value_time0 = None
        self._lminmax = (0, 0)
        self._mminmax = (0, 0)
        self._cache_qimage = {}
        self._cache_mapping = self._cache_imap = self._cache_interp = None
        self._psfsize = 0, 0, 0
        # set image, if specified
        if image is not None:
            nx, ny = image.shape
            self.setImage(image)
        # set coordinates, if specified
        if nx and ny:
            self.setImageCoordinates(nx, ny, l0, m0, dl, dm)
        # set default colormap and intensity map
        self.colormap = Colormaps.GreyscaleColormap
        self.imap = Colormaps.LinearIntensityMap()

    def emit(self, *args):
        self._qo.emit(*args)

    def connect(self, *args):
        QObject.connect(self._qo, *args)

    def clearDisplayCache(self):
        """Clears all display caches."""
        self._cache_qimage = {}
        self._cache_interp = self._cache_imap = None

    def setColorMap(self, cmap=None, emit=True):
        """Changes the colormap. If called with no arguments, clears colormap-dependent caches"""
        self._cache_qimage = {}
        if cmap:
            self.colormap = cmap
        if emit:
            self.emit(SIGNAL("repaint"))

    def updateCurrentColorMap(self):
        self._cache_qimage = {}
        self.emit(SIGNAL("repaint"))

    def setIntensityMap(self, imap=None, emit=True):
        """Changes the intensity map. If called with no arguments, clears intensity map-dependent caches"""
        self._cache_qimage = {}
        self._cache_imap = None
        if imap:
            self.imap = imap
        if emit:
            self.emit(SIGNAL("repaint"))

    def colorMap(self):
        return self.colormap

    def intensityMap(self):
        return self.imap

    def setImageCoordinates(self, nx, ny, x0, y0, l0, m0, dl, dm):
        """Sets up image coordinates. Pixel x0,y0 is centered at location l0,m0 in the plot, pixel size is dl,dm, image size is (nx,ny)"""
        dprint(2, "image coordinates are", nx, ny, x0, y0, l0, m0, dl, dm)
        self._nx, self._ny = nx, ny
        self._l0, self._m0 = l0, m0
        self._dl, self._dm = dl, dm
        self._x0, self._y0 = x0, y0
        self._lminmax = (l0 - dl * (x0 + 0.5), l0 + (nx - x0 - 0.5) * dl)
        if dl < 0:
            self._lminmax = (self._lminmax[1], self._lminmax[0])
        self._mminmax = (m0 - dm * (y0 + 0.5), m0 + (ny - y0 - 0.5) * dm)
        self._bounding_rect = QRectF(self._lminmax[0], self._mminmax[0],
                                     nx * abs(dl), ny * abs(dm))
        self._bounding_rect_pix = QRect(0, 0, nx, ny)
        dprint(2, "image extents are", self._lminmax, self._mminmax)

    def imageDims(self):
        """Returns image dimensions as mx,ny"""
        return self._nx, self._ny

    def referencePixel(self):
        return self._x0, self._y0

    def lmToPix(self, l, m):
        """Converts l,m coordimnates to float (so possibly fractional) pixel coordinates."""
        return self._x0 + (l - self._l0) / self._dl, self._y0 + (
            m - self._m0) / self._dm

    def pixToLm(self, x, y):
        """Converts pixel coordinates to lm coordinates."""
        return self._l0 + (x - self._x0) * self._dl, self._m0 + (
            y - self._y0) * self._dm

    def getExtents(self):
        """Returns image extent, as (l0,l1),(m0,m1)"""
        return self._lminmax, self._mminmax

    def boundingRect(self):
        """Returns bouding rectangle of image, in lm coordinates."""
        return self._bounding_rect

    def currentRect(self):
        """Returns currently visible rectange, in lm coordinates. Coordinates may be outside of image range."""
        return self._current_rect

    def currentRectPix(self):
        """Returns currently visible rectange, in pixel coordinates. Pixel coordinates are bounded to 0,0 and nx-1,ny-1."""
        return self._current_rect_pix

    def setImage(self, image, key=None, minmax=None):
        """Sets image array.
        If key is not None, sets this as the image key (for use with the pixmap cache.)
        If minmax is not None, then stores this as the (presumably cached or precomputed) min/max values.
        """
        self._image = image
        self._imgminmax = minmax
        self._image_key = key
        # clear intermediate caches
        self._prefilter = self._cache_interp = self._cache_imap = None
        # if key is None, also clear QImage cache -- it only works when we have images identified by keys
        if key is None:
            self._cache_qimage = {}

    def image(self):
        """Returns image array."""
        return self._image

    def imagePixel(self, x, y):
        if numpy.ma.isMA(self._image):
            return self._image.data[x, y], self._image.mask[x, y]
        else:
            return self._image[x, y], False

    def imageMinMax(self):
        if not self._imgminmax:
            dprint(3, "computing image min/max")
            rdata, rmask = self.optimalRavel(self._image)
            try:
                self._imgminmax = measurements.extrema(
                    rdata,
                    labels=rmask,
                    index=None if rmask is None else False)[:2]
            except:
                # when all data is masked, some versions of extrema() throw an exception
                self._imgminmax = numpy.nan, numpy.nan
            dprint(3, self._imgminmax)
        return self._imgminmax

    def draw(self, painter, xmap, ymap, rect):
        """Implements QwtPlotItem.draw(), to render the image on the given painter."""
        xp1, xp2, xdp, xs1, xs2, xds = xinfo = xmap.p1(), xmap.p2(
        ), xmap.pDist(), xmap.s1(), xmap.s2(), xmap.sDist()
        yp1, yp2, ydp, ys1, ys2, yds = yinfo = ymap.p1(), ymap.p2(
        ), ymap.pDist(), ymap.s1(), ymap.s2(), ymap.sDist()
        dprint(5, "draw:", rect, xinfo, yinfo)
        self._current_rect = QRectF(QPointF(xs2, ys1), QSizeF(xds, yds))
        self._current_rect_pix = QRect(
            QPoint(*self.lmToPix(xs1, ys1)),
            QPoint(*self.lmToPix(xs2, ys2))).intersected(
                self._bounding_rect_pix)
        dprint(5, "draw:", self._current_rect_pix)
        # put together tuple describing current mapping
        mapping = xinfo, yinfo
        # if mapping has changed w.r.t. cache (i.e. zoom has changed), discard all cached QImages
        if mapping != self._cache_mapping:
            dprint(2, "does not match cached mapping, cache is:",
                   self._cache_mapping)
            dprint(2, "and we have:", mapping)
            self.clearDisplayCache()
            self._cache_mapping = mapping
        t0 = time.time()
        # check cached QImage for current image key.
        qimg = self._cache_qimage.get(self._image_key)
        if qimg:
            dprint(5, "QImage found in cache, reusing")
        # else regenerate image
        else:
            # check for cached intensity-mapped data
            if self._cache_imap is not None:
                dprint(5, "intensity-mapped data found in cache, reusing")
            else:
                if self._cache_interp is not None:
                    dprint(5, "interpolated data found in cache, reusing")
                else:
                    image = self._image.transpose(
                    ) if self._data_fortran_order else self._image
                    spline_order = 2
                    xsamp = abs(xmap.sDist() / xmap.pDist()) / abs(self._dl)
                    ysamp = abs(ymap.sDist() / ymap.pDist()) / abs(self._dm)
                    if max(xsamp, ysamp) < .33 or min(xsamp, ysamp) > 2:
                        spline_order = 1
                    dprint(2,
                           "regenerating drawing cache, sampling factors are",
                           xsamp, ysamp, "spline order is", spline_order)
                    self._cache_imap = None
                    if self._prefilter is None and spline_order > 1:
                        self._prefilter = interpolation.spline_filter(
                            image, order=spline_order)
                        dprint(2, "spline prefiltering took",
                               time.time() - t0, "secs")
                        t0 = time.time()
                    # make arrays of plot coordinates
                    # xp[0],yp[0] corresponds to pixel 0,0, where 0,0 is the upper-left corner of the plot
                    # the maps are in a funny order (w.r.t. meaning of p1/p2/s1/s2), so the indices here are determined empirically
                    # We also adjust by half-pixel, to get the world coordinate of the pixel _center_
                    xp = xmap.s1() - (xmap.sDist() / xmap.pDist()) * (
                        0.5 + numpy.arange(int(xmap.pDist())))
                    yp = ymap.s2() - (ymap.sDist() / ymap.pDist()) * (
                        0.5 + numpy.arange(int(ymap.pDist())))
                    # now convert plot coordinates into fractional image pixel coordinates
                    xi = self._x0 + (xp - self._l0) / self._dl
                    yi = self._y0 + (yp - self._m0) / self._dm
                    # interpolate image data
                    ###        # old code for nearest-neighbour interpolation
                    ###        # superceded by interpolation below (we simply round pixel coordinates to go to NN when oversampling)
                    ###        xi = xi.round().astype(int)
                    ###        oob_x = (xi<0)|(xi>=self._nx)
                    ###        xi[oob_x] = 0
                    ###        yi = yi.round().astype(int)
                    ###        oob_y = (yi<0)|(yi>=self._ny)
                    ###        yi[oob_y] = 0
                    ###        idx = (xi[:,numpy.newaxis]*self._ny + yi[numpy.newaxis,:]).ravel()
                    ###        interp_image = self._image.ravel()[idx].reshape((len(xi),len(yi)))
                    ###        interp_image[oob_x,:] = 0
                    ###        interp_image[:,oob_y] = 0
                    ###        self._qimage_cache = self.colormap.colorize(interp_image,self._img_range)
                    ###        self._qimage_cache_attrs = (rect,xinfo,yinfo)

                    # if either axis is oversampled by a factor of 3 or more, switch to nearest-neighbour interpolation by rounding pixel values
                    if xsamp < .33:
                        xi = xi.round()
                    if ysamp < .33:
                        yi = yi.round()
                    # make [2,nx,ny] array of interpolation coordinates
                    xy = numpy.zeros((2, len(xi), len(yi)))
                    xy[0, :, :] = xi[:, numpy.newaxis]
                    xy[1, :, :] = yi[numpy.newaxis, :]
                    # interpolate. Use NAN for out of range pixels...
                    # for fortran order, tranpose axes for extra speed (flip XY around then)
                    if self._data_fortran_order:
                        xy = xy[-1::-1, ...]
                    if spline_order > 1:
                        interp_image = interpolation.map_coordinates(
                            self._prefilter,
                            xy,
                            order=spline_order,
                            cval=numpy.nan,
                            prefilter=False)
                    else:
                        interp_image = interpolation.map_coordinates(
                            image, xy, order=spline_order, cval=numpy.nan)
                    # ...and put a mask on them (Colormap.colorize() will make these transparent).
                    mask = ~numpy.isfinite(interp_image)
                    self._cache_interp = numpy.ma.masked_array(
                        interp_image, mask)
                    dprint(2, "interpolation took", time.time() - t0, "secs")
                    t0 = time.time()
                # ok, we have interpolated data in _cache_interp
                self._cache_imap = self.imap.remap(self._cache_interp)
                dprint(2, "intensity mapping took", time.time() - t0, "secs")
                t0 = time.time()
            # ok, we have intensity-mapped data in _cache_imap
            qimg = self.colormap.colorize(self._cache_imap)
            dprint(2, "colorizing took", time.time() - t0, "secs")
            t0 = time.time()
            # cache the qimage
            self._cache_qimage[self._image_key] = qimg.copy()
        # now draw the image
        t0 = time.time()
        painter.drawImage(xp1, yp2, qimg)
        dprint(2, "drawing took", time.time() - t0, "secs")

    def setPsfSize(self, maj, min, pa):
        self._psfsize = maj, min, pa

    def getPsfSize(self):
        return self._psfsize
示例#2
0
 def processEvents(self):
     QObject.emit(self, SIGNAL("signalProcessEvents"))
示例#3
0
文件: SkyImage.py 项目: ska-sa/tigger
class SkyImagePlotItem(QwtPlotItem, QObject):
    """SkyImagePlotItem is a 2D image in l,m coordimnates"""

    def __init__(self, nx=0, ny=0, l0=0, m0=0, dl=1, dm=1, image=None):
        QwtPlotItem.__init__(self)
        # name, if any
        self.name = self.filename = None
        # internal init
        self._qo = QObject()
        self._image = self._imgminmax = None
        self._nvaluecalls = 0
        self._value_time = self._value_time0 = None
        self._lminmax = (0, 0)
        self._mminmax = (0, 0)
        self._cache_qimage = {}
        self._cache_mapping = self._cache_imap = self._cache_interp = None
        self._psfsize = 0, 0, 0
        # set image, if specified
        if image is not None:
            nx, ny = image.shape
            self.setImage(image)
        # set coordinates, if specified
        if nx and ny:
            self.setImageCoordinates(nx, ny, l0, m0, dl, dm)
        # set default colormap and intensity map
        self.colormap = Colormaps.GreyscaleColormap
        self.imap = Colormaps.LinearIntensityMap()

    def emit(self, *args):
        self._qo.emit(*args)

    def connect(self, *args):
        QObject.connect(self._qo, *args)

    def clearDisplayCache(self):
        """Clears all display caches."""
        self._cache_qimage = {}
        self._cache_interp = self._cache_imap = None

    def setColorMap(self, cmap=None, emit=True):
        """Changes the colormap. If called with no arguments, clears colormap-dependent caches"""
        self._cache_qimage = {}
        if cmap:
            self.colormap = cmap
        if emit:
            self.emit(SIGNAL("repaint"))

    def updateCurrentColorMap(self):
        self._cache_qimage = {}
        self.emit(SIGNAL("repaint"))

    def setIntensityMap(self, imap=None, emit=True):
        """Changes the intensity map. If called with no arguments, clears intensity map-dependent caches"""
        self._cache_qimage = {}
        self._cache_imap = None
        if imap:
            self.imap = imap
        if emit:
            self.emit(SIGNAL("repaint"))

    def colorMap(self):
        return self.colormap

    def intensityMap(self):
        return self.imap

    def setImageCoordinates(self, nx, ny, x0, y0, l0, m0, dl, dm):
        """Sets up image coordinates. Pixel x0,y0 is centered at location l0,m0 in the plot, pixel size is dl,dm, image size is (nx,ny)"""
        dprint(2, "image coordinates are", nx, ny, x0, y0, l0, m0, dl, dm)
        self._nx, self._ny = nx, ny
        self._l0, self._m0 = l0, m0
        self._dl, self._dm = dl, dm
        self._x0, self._y0 = x0, y0
        self._lminmax = (l0 - dl * (x0 + 0.5), l0 + (nx - x0 - 0.5) * dl)
        if dl < 0:
            self._lminmax = (self._lminmax[1], self._lminmax[0])
        self._mminmax = (m0 - dm * (y0 + 0.5), m0 + (ny - y0 - 0.5) * dm)
        self._bounding_rect = QRectF(self._lminmax[0], self._mminmax[0], nx * abs(dl), ny * abs(dm))
        self._bounding_rect_pix = QRect(0, 0, nx, ny)
        dprint(2, "image extents are", self._lminmax, self._mminmax)

    def imageDims(self):
        """Returns image dimensions as mx,ny"""
        return self._nx, self._ny

    def referencePixel(self):
        return self._x0, self._y0

    def lmToPix(self, l, m):
        """Converts l,m coordimnates to float (so possibly fractional) pixel coordinates."""
        return self._x0 + (l - self._l0) / self._dl, self._y0 + (m - self._m0) / self._dm

    def pixToLm(self, x, y):
        """Converts pixel coordinates to lm coordinates."""
        return self._l0 + (x - self._x0) * self._dl, self._m0 + (y - self._y0) * self._dm

    def getExtents(self):
        """Returns image extent, as (l0,l1),(m0,m1)"""
        return self._lminmax, self._mminmax

    def boundingRect(self):
        """Returns bouding rectangle of image, in lm coordinates."""
        return self._bounding_rect

    def currentRect(self):
        """Returns currently visible rectange, in lm coordinates. Coordinates may be outside of image range."""
        return self._current_rect

    def currentRectPix(self):
        """Returns currently visible rectange, in pixel coordinates. Pixel coordinates are bounded to 0,0 and nx-1,ny-1."""
        return self._current_rect_pix

    def setImage(self, image, key=None, minmax=None):
        """Sets image array.
        If key is not None, sets this as the image key (for use with the pixmap cache.)
        If minmax is not None, then stores this as the (presumably cached or precomputed) min/max values.
        """
        self._image = image
        self._imgminmax = minmax
        self._image_key = key
        # clear intermediate caches
        self._prefilter = self._cache_interp = self._cache_imap = None
        # if key is None, also clear QImage cache -- it only works when we have images identified by keys
        if key is None:
            self._cache_qimage = {}

    def image(self):
        """Returns image array."""
        return self._image

    def imagePixel(self, x, y):
        if numpy.ma.isMA(self._image):
            return self._image.data[x, y], self._image.mask[x, y]
        else:
            return self._image[x, y], False

    def imageMinMax(self):
        if not self._imgminmax:
            dprint(3, "computing image min/max")
            rdata, rmask = self.optimalRavel(self._image)
            try:
                self._imgminmax = measurements.extrema(rdata, labels=rmask, index=None if rmask is None else False)[:2]
            except:
                # when all data is masked, some versions of extrema() throw an exception
                self._imgminmax = numpy.nan, numpy.nan
            dprint(3, self._imgminmax)
        return self._imgminmax

    def draw(self, painter, xmap, ymap, rect):
        """Implements QwtPlotItem.draw(), to render the image on the given painter."""
        xp1, xp2, xdp, xs1, xs2, xds = xinfo = xmap.p1(), xmap.p2(), xmap.pDist(), xmap.s1(), xmap.s2(), xmap.sDist()
        yp1, yp2, ydp, ys1, ys2, yds = yinfo = ymap.p1(), ymap.p2(), ymap.pDist(), ymap.s1(), ymap.s2(), ymap.sDist()
        dprint(5, "draw:", rect, xinfo, yinfo)
        self._current_rect = QRectF(QPointF(xs2, ys1), QSizeF(xds, yds))
        self._current_rect_pix = QRect(QPoint(*self.lmToPix(xs1, ys1)), QPoint(*self.lmToPix(xs2, ys2))).intersected(
            self._bounding_rect_pix)
        dprint(5, "draw:", self._current_rect_pix)
        # put together tuple describing current mapping
        mapping = xinfo, yinfo
        # if mapping has changed w.r.t. cache (i.e. zoom has changed), discard all cached QImages
        if mapping != self._cache_mapping:
            dprint(2, "does not match cached mapping, cache is:", self._cache_mapping)
            dprint(2, "and we have:", mapping)
            self.clearDisplayCache()
            self._cache_mapping = mapping
        t0 = time.time()
        # check cached QImage for current image key.
        qimg = self._cache_qimage.get(self._image_key)
        if qimg:
            dprint(5, "QImage found in cache, reusing")
        # else regenerate image
        else:
            # check for cached intensity-mapped data
            if self._cache_imap is not None:
                dprint(5, "intensity-mapped data found in cache, reusing")
            else:
                if self._cache_interp is not None:
                    dprint(5, "interpolated data found in cache, reusing")
                else:
                    image = self._image.transpose() if self._data_fortran_order else self._image
                    spline_order = 2
                    xsamp = abs(xmap.sDist() / xmap.pDist()) / abs(self._dl)
                    ysamp = abs(ymap.sDist() / ymap.pDist()) / abs(self._dm)
                    if max(xsamp, ysamp) < .33 or min(xsamp, ysamp) > 2:
                        spline_order = 1
                    dprint(2, "regenerating drawing cache, sampling factors are", xsamp, ysamp, "spline order is",
                           spline_order)
                    self._cache_imap = None
                    if self._prefilter is None and spline_order > 1:
                        self._prefilter = interpolation.spline_filter(image, order=spline_order)
                        dprint(2, "spline prefiltering took", time.time() - t0, "secs")
                        t0 = time.time()
                    # make arrays of plot coordinates
                    # xp[0],yp[0] corresponds to pixel 0,0, where 0,0 is the upper-left corner of the plot
                    # the maps are in a funny order (w.r.t. meaning of p1/p2/s1/s2), so the indices here are determined empirically
                    # We also adjust by half-pixel, to get the world coordinate of the pixel _center_
                    xp = xmap.s1() - (xmap.sDist() / xmap.pDist()) * (0.5 + numpy.arange(int(xmap.pDist())))
                    yp = ymap.s2() - (ymap.sDist() / ymap.pDist()) * (0.5 + numpy.arange(int(ymap.pDist())))
                    # now convert plot coordinates into fractional image pixel coordinates
                    xi = self._x0 + (xp - self._l0) / self._dl
                    yi = self._y0 + (yp - self._m0) / self._dm
                    # interpolate image data
                    ###        # old code for nearest-neighbour interpolation
                    ###        # superceded by interpolation below (we simply round pixel coordinates to go to NN when oversampling)
                    ###        xi = xi.round().astype(int)
                    ###        oob_x = (xi<0)|(xi>=self._nx)
                    ###        xi[oob_x] = 0
                    ###        yi = yi.round().astype(int)
                    ###        oob_y = (yi<0)|(yi>=self._ny)
                    ###        yi[oob_y] = 0
                    ###        idx = (xi[:,numpy.newaxis]*self._ny + yi[numpy.newaxis,:]).ravel()
                    ###        interp_image = self._image.ravel()[idx].reshape((len(xi),len(yi)))
                    ###        interp_image[oob_x,:] = 0
                    ###        interp_image[:,oob_y] = 0
                    ###        self._qimage_cache = self.colormap.colorize(interp_image,self._img_range)
                    ###        self._qimage_cache_attrs = (rect,xinfo,yinfo)

                    # if either axis is oversampled by a factor of 3 or more, switch to nearest-neighbour interpolation by rounding pixel values
                    if xsamp < .33:
                        xi = xi.round()
                    if ysamp < .33:
                        yi = yi.round()
                    # make [2,nx,ny] array of interpolation coordinates
                    xy = numpy.zeros((2, len(xi), len(yi)))
                    xy[0, :, :] = xi[:, numpy.newaxis]
                    xy[1, :, :] = yi[numpy.newaxis, :]
                    # interpolate. Use NAN for out of range pixels...
                    # for fortran order, tranpose axes for extra speed (flip XY around then)
                    if self._data_fortran_order:
                        xy = xy[-1::-1, ...]
                    if spline_order > 1:
                        interp_image = interpolation.map_coordinates(self._prefilter, xy, order=spline_order,
                                                                     cval=numpy.nan, prefilter=False)
                    else:
                        interp_image = interpolation.map_coordinates(image, xy, order=spline_order, cval=numpy.nan)
                    # ...and put a mask on them (Colormap.colorize() will make these transparent).
                    mask = ~numpy.isfinite(interp_image)
                    self._cache_interp = numpy.ma.masked_array(interp_image, mask)
                    dprint(2, "interpolation took", time.time() - t0, "secs")
                    t0 = time.time()
                # ok, we have interpolated data in _cache_interp
                self._cache_imap = self.imap.remap(self._cache_interp)
                dprint(2, "intensity mapping took", time.time() - t0, "secs")
                t0 = time.time()
            # ok, we have intensity-mapped data in _cache_imap
            qimg = self.colormap.colorize(self._cache_imap)
            dprint(2, "colorizing took", time.time() - t0, "secs")
            t0 = time.time()
            # cache the qimage
            self._cache_qimage[self._image_key] = qimg.copy()
        # now draw the image
        t0 = time.time()
        painter.drawImage(xp1, yp2, qimg)
        dprint(2, "drawing took", time.time() - t0, "secs")

    def setPsfSize(self, maj, min, pa):
        self._psfsize = maj, min, pa

    def getPsfSize(self):
        return self._psfsize
示例#4
0
 def processEvents(self):
     QObject.emit(self, SIGNAL("signalProcessEvents"))