Пример #1
0
def array_to_qimage(arr, copy=False):
    """Convert NumPy array to QImage object"""
    # https://gist.githubusercontent.com/smex/5287589/raw/toQImage.py
    if arr is None:
        return QImage()
    if len(arr.shape) not in (2, 3):
        raise NotImplementedError("Unsupported array shape %r" % arr.shape)
    data = arr.data
    ny, nx = arr.shape[:2]
    stride = arr.strides[0]  # bytes per line
    color_dim = None
    if len(arr.shape) == 3:
        color_dim = arr.shape[2]
    if arr.dtype == np.uint8:
        if color_dim is None:
            qimage = QImage(data, nx, ny, stride, QImage.Format_Indexed8)
#            qimage.setColorTable([qRgb(i, i, i) for i in range(256)])
            qimage.setColorCount(256)
        elif color_dim == 3:
            qimage = QImage(data, nx, ny, stride, QImage.Format_RGB888)
        elif color_dim == 4:
            qimage = QImage(data, nx, ny, stride, QImage.Format_ARGB32)
        else:
            raise TypeError("Invalid third axis dimension (%r)" % color_dim)
    elif arr.dtype == np.uint32:
        qimage = QImage(data, nx, ny, stride, QImage.Format_ARGB32)
    else:
        raise NotImplementedError("Unsupported array data type %r" % arr.dtype)
    if copy:
        return qimage.copy()
    return qimage
Пример #2
0
class QwtPlotImage(QwtPlotItem):

    def __init__(self, parent, title =QwtText()):

        QwtPlotItem.__init__(self)
        self.plot = parent
        self.display_type = "hippo"
        self.ValueAxis =  None
        self.ComplexColorMap = None
        self._flags_array = None
        self._nan_flags_array = None
        self._image_for_display = None
        self._display_flags = False
        self.Qimage = None
        self.r_cmax = None
        self.r_cmin = None
        self.i_cmax = None
        self.i_cmin = None
        self.raw_image = None
        self.dimap = None
        self.complex = False
        self.log_scale = False
        self.log_y_scale = False
        self.transform_offset = 0.0
        self.flag_colour = 0
        self.nan_colour = 255
        self.lock_image_real = False
        self.lock_image_imag = False
        self.setTitle(title)
    # __init__()
    
    def setDisplayType(self, display_type):
      self.display_type = display_type
      if HAS_TIMBA:
        _dprint(2,'display type set to ', self.display_type);
    # setDisplayType

    def setFlagColour(self, flag_colour):
      self.flag_colour = flag_colour

    def setLockImage(self, real = True, lock_image=False):
      if real:
        self.lock_image_real = lock_image
      else:
        self.lock_image_imag = lock_image

    # When set to True, the log_scale parameter will cause
    # the displayed image to be scaled logarithmically.
    def setLogScale(self, log_scale = True):
      self.log_scale = log_scale
      if self.log_scale == False:
        self.dimap = None
        self.transform_offset = 0.0

    # When set to True, the log_y_scale parameter will cause
    # the output Y coordinate axis to be scaled properly for a Log plot
    # (used by QwtColorBar.py)
    def setLogYScale(self, log_y_scale = True):
      self.log_y_scale = log_y_scale

    def setFlagsArray(self, flags_array):
      self._flags_array = flags_array
      
    def setNanFlagsArray(self, flags_array):
      self._nan_flags_array = flags_array

    def setDisplayFlag(self, display_flags):
        self._display_flags = display_flags
    # setDisplayFlag

    def removeFlags(self):
      self._flags_array = None
      self._nan_flags_array = None
    # removeFlags

    def getRealImageRange(self):
      try:
        if self.raw_image.dtype == numpy.complex64 or self.raw_image.dtype == numpy.complex128:
          real_array =  self.raw_image.real
        else:
          real_array = self.raw_image
        return (self.r_cmin, self.r_cmax, real_array.min(),real_array.max())
      except:
        return (self.r_cmin, self.r_cmax, None, None)
    # getRealImageRange

    def getImagImageRange(self):
      try:
        if self.raw_image.dtype == numpy.complex64 or self.raw_image.dtype == numpy.complex128:
          imag_array =  self.raw_image.imag
        return (self.i_cmin, self.i_cmax,imag_array.min(), imag_array.max())
      except:
        return (self.i_cmin, self.i_cmax, None, None)
    # getRealImageRange


    
    def defineImageRange(self, limits, real=True):
       min = limits[0]
       max = limits[1]
       if abs(max - min) < 0.00005:
         if max == 0.0 or min == 0.0:
           min = -0.1
           max = 0.1
         else:
           min = 0.9 * min
           max = 1.1 * max
       if min > max:
         temp = max
         max = min
         min = temp
       if real:
         if not self.lock_image_real:
           self.r_cmin = min
           self.r_cmax = max
       else:
         if not self.lock_image_imag:
           self.i_cmin = min
           self.i_cmax = max

    def setImageRange(self, image):
      self.raw_image = image
      if image.dtype == numpy.complex64 or image.dtype == numpy.complex128:
        self.complex = True
        imag_array =  image.imag
        real_array =  image.real
        try:
          min = real_array.min()
          max = real_array.max()
        except:
          min = 0.0
          max = 0.0
        if abs(max - min) < 0.00005:
          if max == 0.0 or min == 0.0:
            min = -0.1
            max = 0.1
          else:
            min = 0.9 * min
            max = 1.1 * max
        if min > max:
          temp = max
          max = min
          min = temp
        if not self.lock_image_real:
          self.r_cmin = min
          self.r_cmax = max

        try:
          min = imag_array.min()
          max = imag_array.max()
        except:
          min = 0.0
          max = 0.0
        if abs(max - min) < 0.00005:
          if max == 0.0 or min == 0.0:
            min = -0.1
            max = 0.1
          else:
            min = 0.9 * min
            max = 1.1 * max
        if min > max:
          temp = max
          max = min
          min = temp
        if not self.lock_image_imag:
          self.i_cmin = min
          self.i_cmax = max
      else:
        self.complex = False
        try:
          min = image.min()
          max = image.max()
        except:
          min = 0.0
          max = 0.0
        if abs(max - min) < 0.00005:
          if max == 0.0 or min == 0.0:
            min = -0.1
            max = 0.1
          else:
            min = 0.9 * min
            max = 1.1 * max
        if min > max:
          temp = max
          max = min
          min = temp
        if not self.lock_image_real:
          self.r_cmin = min
          self.r_cmax = max
    # setImageRange

    def updateImage(self, image):
        self.setImage(image)
        self.raw_image = image

    def setFlaggedImageRange(self):
      (nx,ny) = self.raw_image.shape
      num_elements = nx * ny
      if self._flags_array is None:
        if not self._nan_flags_array is None:
          flags_array = self._nan_flags_array.copy()
        else:
          flags_array = numpy.zeros((nx,ny),int);
      else:
        flags_array = self._flags_array.copy()
        if not self._nan_flags_array is None:
          flags_array = flags_array + self._nan_flags_array
      flattened_flags = numpy.reshape(flags_array,(num_elements,))
      if self.raw_image.dtype == numpy.complex64 or self.raw_image.dtype == numpy.complex128:
        real_array =  self.raw_image.real
        imag_array =  self.raw_image.imag
        flattened_real_array = numpy.reshape(real_array.copy(),(num_elements,))
        flattened_imag_array = numpy.reshape(imag_array.copy(),(num_elements,))
        real_flagged_array = numpy.compress(flattened_flags == 0, flattened_real_array)
        imag_flagged_array = numpy.compress(flattened_flags == 0, flattened_imag_array)
        flagged_image = numpy.zeros(shape=real_flagged_array.shape,dtype=self.raw_image.dtype)
        flagged_image.real = real_flagged_array
        flagged_image.imag = imag_flagged_array
      else:
        flattened_array = numpy.reshape(self.raw_image.copy(),(num_elements,))
        flagged_image = numpy.compress(flattened_flags == 0, flattened_array)
      self.setImageRange(flagged_image)
    # setFlaggedImageRange

    def convert_to_log(self, incoming_image):
      self.transform_offset = 0.0
      transform_image = incoming_image
      image_min = incoming_image.min()
      if image_min <= 0.0:
        image_min = -1.0 * image_min
        self.transform_offset = 0.001 + image_min
        transform_image = self.transform_offset + incoming_image
      scale_min = transform_image.min()
      scale_max = transform_image.max()
      if scale_min == scale_max:
       scale_min = scale_min - 0.5 * scale_min
       scale_max = scale_max + 0.5 * scale_min
      self.dimap = ImageScaler(1, 256, scale_min, scale_max, True)
      if HAS_TIMBA:
        _dprint(3, 'doing log transform of ', transform_image)
      temp_image = self.dimap.iTransform(transform_image)
      if HAS_TIMBA:
        _dprint(3, 'log transformed image ', temp_image)
      return temp_image

    def getTransformOffset(self):
      return self.transform_offset

    def convert_limits(self, limits):
      if not self.dimap is None:
        first_limit = self.dimap.transform(self.transform_offset + limits[0])
        second_limit = self.dimap.transform(self.transform_offset + limits[1])
      else:
        first_limit = None
        second_limit = None
      return [first_limit, second_limit]


    def to_QImage(self, image):
# convert to 8 bit image
      image_for_display = None
      if image.dtype == numpy.complex64 or image.dtype == numpy.complex128:
        self.complex = True
        real_array =  image.real
        if self.log_scale:
          temp_array = self.convert_to_log(real_array)
          temp_array = self.convert_to_log(real_array)
          if not self.r_cmin is None and not self.r_cmax is None:
            limits = self.convert_limits([self.r_cmin,self.r_cmax])
          else:
            limits = [self.r_cmin, self.r_cmax] 
          byte_image = bytescale(temp_array,limits)
        else:
          limits = [self.r_cmin,self.r_cmax]
          byte_image = bytescale(real_array,limits)
        (nx,ny) = real_array.shape
        image_for_display = numpy.empty(shape=(nx*2,ny),dtype=byte_image.dtype);
        image_for_display[:nx,:] = byte_image
        imag_array =  image.imag
        if self.log_scale:
          temp_array = self.convert_to_log(imag_array)
          if not self.i_cmin is None and not self.i_cmax is None:
            limits = self.convert_limits([self.i_cmin,self.i_cmax])
          else:
            limits = [self.i_cmin, self.i_cmax] 
          byte_image = bytescale(temp_array,limits)
        else:
          limits = [self.i_cmin,self.i_cmax]
          byte_image = bytescale(imag_array,limits)
        image_for_display[nx:,:] = byte_image
      else:
        if self.log_scale:
          temp_array = self.convert_to_log(image)
          if not self.r_cmin is None and not self.r_cmax is None:
            limits = self.convert_limits([self.r_cmin,self.r_cmax])
          else:
            limits = [self.r_cmin, self.r_cmax] 
          #print 'to_QImage log limits = ', limits
          image_for_display = bytescale(temp_array,limits)
        else:
          limits = [self.r_cmin,self.r_cmax]
          #print 'to_QImage real limits = ', limits
          image_for_display = bytescale(image,limits)
      # turn image into a QImage, and return result   
      if not self._nan_flags_array is None:
        if self.complex:
          image_for_display[:nx,:] = numpy.where(self._nan_flags_array,1,image_for_display[:nx,:])
          image_for_display[nx:,:] = numpy.where(self._nan_flags_array,1,image_for_display[nx:,:])
        else:
          image_for_display = numpy.where(self._nan_flags_array,1,image_for_display)
      self._image_for_display = image_for_display
      result = convertToQImage(image_for_display,True).mirrored(0, 1)
      # always suppress NaNs
      if not self._nan_flags_array is None:
        result.setColor(1, qRgb(self.nan_colour, self.nan_colour, self.nan_colour))
      return result

    def toGrayScale(self, Qimage):
      for i in range(0, 256):
        Qimage.setColor(i, qRgb(i, i, i))

    def toHippo(self, Qimage):
      dv = 255.0
      vmin = 1.0
      for i in range(2, 256):
        r = 1.0
        g = 1.0
        b = 1.0
        v = 1.0 * i
        if (v < (vmin + 0.25 * dv)):
          r = 0;
          if dv != 0:
            g = 4 * (v - vmin) / dv;
        elif (v < (vmin + 0.5 * dv)):
          r = 0;
          if dv != 0:
            b = 1 + 4 * (vmin + 0.25 * dv - v) / dv;
        elif (v < (vmin + 0.75 * dv)):
          b = 0;
          if dv != 0:
            r = 4 * (v - vmin - 0.5 * dv) / dv;
        else: 
          b = 0;
          if dv != 0:
            g = 1 + 4 * (vmin + 0.75 * dv - v) / dv;
          else:
            r = 0
        red   = int ( r * 255. )
        green = int ( g * 255. )
        blue  = int ( b * 255. )
# the following call will use the previous computations to
# set up a hippo-like color display
        Qimage.setColor(i, qRgb(red, green, blue))

    def setImage(self, image):
# convert to QImage
      self.Qimage = self.to_QImage(image)
# set color scale a la HippoDraw Scale
      if self.display_type == "hippo":
        self.toHippo(self.Qimage)

# set color scale to Grayscale
      if self.display_type == "grayscale":
        self.toGrayScale(self.Qimage)

# compute flagged image if required
      if not self._flags_array is None or not self._nan_flags_array is None:
        self.setFlagQimage()

    def setFlagQimage(self):
      (nx,ny) = self._image_for_display.shape
      image_for_display = self._image_for_display.copy()
      if not self._flags_array is None:
        if self.complex:
          image_for_display[:nx//2,:] = numpy.where(self._flags_array,0,self._image_for_display[:nx//2,:])
          image_for_display[nx//2:,:] = numpy.where(self._flags_array,0,self._image_for_display[nx//2:,:])
        else:
          image_for_display = numpy.where(self._flags_array,0,self._image_for_display)

      if not self._nan_flags_array is None:
        if self.complex:
          image_for_display[:nx//2,:] = numpy.where(self._nan_flags_array,1,image_for_display[:nx//2,:])
          image_for_display[nx//2:,:] = numpy.where(self._nan_flags_array,1,image_for_display[nx//2:,:])
        else:
          image_for_display = numpy.where(self._nan_flags_array,1,image_for_display)

      self.flags_Qimage = convertToQImage(image_for_display,True).mirrored(0, 1)

# set color scale a la HippoDraw Scale
      if self.display_type == "hippo":
        self.toHippo(self.flags_Qimage)

# set color scale to Grayscale
      if self.display_type == "grayscale":
        self.toGrayScale(self.flags_Qimage)

# set zero to black to display flag image pixels in black 
      self.flags_Qimage.setColor(0, qRgb(self.flag_colour, self.flag_colour, self.flag_colour))
      self.flags_Qimage.setColor(1, qRgb(self.nan_colour, self.nan_colour, self.nan_colour))

    def setBrentjensImage(self, image):
      absmin = abs(image.min())
      MaxAbs = abs(image.max())
      if (absmin > MaxAbs):
        MaxAbs = absmin
      self.ValueAxis.calcTransferFunction(-MaxAbs, MaxAbs, 0, self.ComplexColorMap.getNumberOfColors()-1)

      if image.min() != image.max():
# get real and imaginary arrays
        real_image = image.real
        imag_image = image.imag
        shape = image.shape
        Ncol = self.ComplexColorMap.getNumberOfColors()
        bits_per_pixel = 32
        self.Qimage = QImage(shape[0], shape[1], bits_per_pixel, Ncol)
        for i in range(shape[0]):
          for j in range(shape[1]):
            colre = int(self.ValueAxis.worldToAxis(real_image[i,j]))
            colim = int(self.ValueAxis.worldToAxis(imag_image[i,j]))
            if(colre < Ncol and colim < Ncol): 
              value = self.ComplexColorMap.get_color_value(colre,colim)
              self.Qimage.setPixel(i,j,value)
            else:
              if HAS_TIMBA:
               _dprint(2, "*************************************");
               _dprint(2, "colre: ", colre);
               _dprint(2, "colim: ", colim);
               _dprint(2, "real : ", real_image[i,j]);
               _dprint(2, "imag : ", imag_image[i,j]);
               _dprint(2, "Ncol: ", Ncol);
               _dprint(2, "*************************************");
        self.Qimage.mirror(0,1)

    def setData(self, data_array, xScale = None, yScale = None):
        '*** setting data **** '
        self.complex = False
        shape = data_array.shape
        if HAS_TIMBA:
          _dprint(3, 'array shape is ', shape)
        shape0 = shape[0]
        if data_array.dtype == numpy.complex64 or data_array.dtype == numpy.complex128:
          self.complex = True
          shape0 = 2 * shape[0]
        if xScale:
            self.xMap = ImageScaler(0, shape0 - 1, xScale[0], xScale[1])
            self.xMap_draw = ImageScaler(0, shape0 - 1, xScale[0], xScale[1])
            temp_scale = (xScale[0],xScale[1])
            self.plot.setAxisScale(QwtPlot.xBottom, *temp_scale)
            if HAS_TIMBA:
             _dprint(3, 'xScale is ', xScale)
        else:
            self.xMap = ImageScaler(0, shape0, 0, shape0 )
            self.xMap_draw = ImageScaler(0, shape0, 0, shape0 )
            self.plot.setAxisScale(QwtPlot.xBottom, 0, shape0)
        if yScale:
            if HAS_TIMBA:
             _dprint(3, 'yScale is ', yScale)
             _dprint(3, 'self.log_y_scale is ', self.log_y_scale)

            self.yMap = ImageScaler(0, shape[1]-1, yScale[0], yScale[1],self.log_y_scale)
            self.yMap_draw = ImageScaler(0, shape[1]-1, yScale[0], yScale[1],self.log_y_scale)
            temp_scale = (yScale[0],yScale[1])
            if HAS_TIMBA:
             _dprint(3, 'Called setAxisScale(QwtPlot.yLeft) with ', temp_scale)
            self.plot.setAxisScale(QwtPlot.yLeft, *temp_scale)
        else:
            self.yMap = ImageScaler(0, shape[1], 0, shape[1])
            self.yMap_draw = ImageScaler(0, shape[1], 0, shape[1])
            self.plot.setAxisScale(QwtPlot.yLeft, 0, shape[1])
        self.setImage(data_array)
        self.raw_image = data_array
    # setData()    

    def update_yMap_draw(self, d1, d2):
      self.yMap_draw.setDblRange(d1, d2, self.log_y_scale)

    def update_xMap_draw(self, d1, d2):
      self.xMap_draw.setDblRange(d1, d2, self.log_y_scale)

    def get_xMap_draw_coords(self):
      return (self.xMap_draw.d1(), self.xMap_draw.d2())

    def get_yMap_draw_coords(self):
      return (self.yMap_draw.d1(), self.yMap_draw.d2())

    def draw(self, painter, xMap, yMap,rect):
        """Paint image to zooming to xMap, yMap

        Calculate (x1, y1, x2, y2) so that it contains at least 1 pixel,
        and copy the visible region to scale it to the canvas.

        NOTE: we ignore the system-provided Maps and use our own 
        'draw' maps
        """
        if self.Qimage is None:
          return

#       #_dprint(3, 'incoming x map ranges ',xMap.s1(), ' ', xMap.s2())
#       #_dprint(3, 'incoming y map ranges ',yMap.s1(), ' ', yMap.s2())
#       print 'incoming x map ranges ',xMap.s1(), ' ', xMap.s2()
#       print 'incoming y map ranges ',yMap.s1(), ' ', yMap.s2()
#       print 'incoming x map draw ranges ',self.xMap_draw.d1(), ' ', self.xMap_draw.d2()
#       print 'incoming y map draw ranges ',self.yMap_draw.d1(), ' ', self.yMap_draw.d2()
#       print 'incoming self x map ranges ',self.xMap.d1(), ' ', self.xMap.d2()
#       print 'incoming self y map ranges ',self.yMap.d1(), ' ', self.yMap.d2()

        # calculate y1, y2
        y1 = y2 = self.Qimage.height()
        if HAS_TIMBA:
         _dprint(3, 'image height ', self.Qimage.height())
#        y1 = y2 = self.Qimage.height() - 1
#       print 'starting image height ', y1
        y1 *= (self.yMap.d2() - self.yMap_draw.d2())
        y1 /= (self.yMap.d2() - self.yMap.d1())
#       y1 = max(0, int(y1-0.5))
        y1 = max(0, (y1-0.5))
        if HAS_TIMBA:
          _dprint(3, 'float y1 ', y1)
        y1 = int(y1 + 0.5)
        y2 *= (self.yMap.d2() - self.yMap_draw.d1())
        y2 /= (self.yMap.d2() - self.yMap.d1())
        if HAS_TIMBA:
         _dprint(3, 'float y2 ', y2)
#       y2 = min(self.Qimage.height(), int(y2+0.5))
        y2 = min(self.Qimage.height(), (y2+0.5))
        y2 = int(y2)
        if HAS_TIMBA:
         _dprint(3, 'int y1, y2 ', y1, ' ',y2)
        # calculate x1, x2 - these are OK
        x1 = x2 = self.Qimage.width() 
#       print 'starting image width ', x1
        x1 *= (xMap.s1() - self.xMap.d1())
        x1 /= (self.xMap.d2() - self.xMap.d1())
        if HAS_TIMBA:
         _dprint(3, 'float x1 ', x1)
#       x1 = max(0, int(x1-0.5))
        x1 = max(0, int(x1))
        x2 *= (self.xMap_draw.d2() - self.xMap.d1())
        x2 /= (self.xMap.d2() - self.xMap.d1())
        if HAS_TIMBA:
         _dprint(3, 'float x2 ', x2)
        x2 = min(self.Qimage.width(), int(x2+0.5))
        if HAS_TIMBA:
         _dprint(3, 'int x1, x2 ', x1, ' ',x2)
        # copy
        xdelta = x2-x1
        ydelta = y2-y1
#       print 'xdelta ydelta ', xdelta, ' ', ydelta
#       print 'x1 y1 ', x1, ' ', y1
        # these tests seem necessary for the dummy 'scalar' displays
        if xdelta < 0:
          xdelta = self.Qimage.height()
        if ydelta < 0:
          ydelta = self.Qimage.height()
        image = None
        if self._display_flags:
          image = self.flags_Qimage.copy(x1, y1, xdelta, ydelta)
        else:
          image = self.Qimage.copy(x1, y1, xdelta, ydelta)
        # zoom
        image = image.scaled(xMap.p2()-xMap.p1()+1, yMap.p1()-yMap.p2()+1)
        # draw
        painter.drawImage(xMap.p1(), yMap.p2(), image)