Beispiel #1
0
class Stack:
  "A class to load and manipulate images generated during single molecule experiments"

  defaultROI = {}
  defaultDonorROI = 'donor'
  defaultAcceptorROI = 'acceptor'

  @classmethod
  def fromfile(cls, imgfile, camfile=''):
    img = fileIO.loadimg(imgfile)
    camfile = camfile or fileIO.to_cam_filename(imgfile)
    metadata = fileIO.loadcam(camfile)
    metadata['filename'] = imgfile
    stack = cls(img, metadata)
    # Keeping the .filename attribute for backward compatibility, but should move to
    # metadata lookup
    stack.filename = metadata['filename']
    return stack

  # def __init__(self, filename, camFile='', deepcopy=False):
  def __init__(self, data, metadata={}, roi={}):
    self._img = copy.copy(data)
    self.metadata = dict(metadata)
    self._donorROIName = Stack.defaultDonorROI
    self._acceptorROIName = Stack.defaultAcceptorROI
    self._figure = Figure()
    self._roi = roi
    if not roi:
      self.addROI(*self.defaultROI.values())
    self._frame_iter = cycle(range(self.frames))
    if metadata:
      self.origin = (self.metadata['roileft'],self.metadata['roibottom'])
      if self._img.shape != (self.metadata['frames'],self.metadata['height'],self.metadata['width']):
        raise StackError, ".img file and .cam file dimensions do not agree"

  def copy(self, deep=True):
    newstack = copy.copy(self)
    if deep:
      newstack._img = copy.copy(self._img)
    return newstack

  @property
  def frames(self):
    return self._img.shape[0]

  def __len__(self):
    return self.frames

  @property
  def time(self):
      return np.arange(1,self.frames+1)*self.metadata['exposurems']/1000.

  @property
  def height(self):
    return self._img.shape[1]

  @property
  def width(self):
    return self._img.shape[2]

  @property
  def donor(self):
    if not self._roi.has_key(self._donorROIName):
        raise StackError, "ROI called %s hasn't been defined yet" % self._donorROIName
    return self.counts(self._roi[self._donorROIName])

  @property
  def acceptor(self):
    if not self._roi.has_key(self._acceptorROIName):
        raise StackError, "ROI called %s hasn't been defined yet" % self._acceptorROIName
    return self.counts(self._roi[self._acceptorROIName])
    
  @property
  def roi(self):
    return self._roi

  @classmethod
  def setDefaultROI(cls, *args):
    for _roi in args:
        cls.defaultROI[_roi.name]=_roi

  def toBackground(self,zfilter='median'):
    width, height = self.width, self.height
    if zfilter == 'median':
      self._img = np.median( self._img, axis=0, overwrite_input=True ).reshape((1,height,width))
    elif zfilter == 'mean':
      self._img = np.mean( self._img, axis=0 ).reshape((1,height,width))
    elif zfilter == 'min':
      self._img = np.min( self._img, axis=0 ).reshape((1,height,width))
    else:
      raise ValueError, "Filter type can only be median, mean, or min"
    return self

  def addROI(self, *ROIs):
    for roi in ROIs:
      try:
        roi = ROI.copy(roi)
        key = roi.name
        roi = roi.toRelative(self.origin)
        if roi.right > self.width:
          raise StackError(
            "ROI 'right' {0} is outside right edge of image {1}: \n {2}".format(roi.right,self.width,roi)
          )
        if roi.top > self.height:
          raise StackError, "ROI 'top' is outside top edge of image: {0}\n {1}".format(roi.top,roi)
        self._roi[key] = roi
      except AttributeError:
        raise TypeError, "Must use objects with ROI interface"

  def showROI(self,*args):
    for roi in args:
      self._roi[roi].draw()

  def show(self, frame=None, **kwargs):
    if not isinstance(frame, int) and frame is not None:
      raise ValueError('First argument frame must be an integer')
    self._figure = kwargs.pop('figure', self._figure)
    if frame is None:
      frame = next(self._frame_iter)
    else:
      self._frame_iter = dropwhile(lambda n: n<=frame, cycle(range(self.frames)))
    self._figure.show()
    self._figure.makeCurrent()
    plt.title('Frame %d' % frame)
    self[frame].show(**kwargs)
    return frame
 
  def setDonorROI(self, roi_name):
    if not self._roi.has_key(roi_name):
        raise KeyError, "Image.Stack does not have an ROI named %s" % roi_name
    self._donorROIName = roi_name
    return self

  def setAcceptorROI(self, roi_name):
    if not self._roi.has_key(roi_name):
        raise KeyError, "Image.Stack does not have an ROI named %s" % roi_name
    self._acceptorROIName = roi_name
    return self
    
  def counts(self, roi=None):
    if roi:
      if self._roi.has_key(str(roi)):
        roi = self._roi[roi]
      roi = roi.toRelative(self.origin)
      return self[:,roi.bottom:roi.top,roi.left:roi.right].counts()
    else:
      return np.sum( np.sum(self._img,axis=1), axis=1 )

  def attime(self,time):
      if isinstance(time,slice):
        start,step = None,None
      exposurems = self.metadata['exposurems'] 
      if time.start:
        start = time.start/exposurems
      if time.step:
        step = time.step/exposurems
      time = slice(start,time.stop/exposurems,step)
      return self[time/exposurems]
      
  def __getitem__(self,key):
    if isinstance(key,int): # Single frame
      return Frame(self._img[key], self._roi)
    else: # It's a slice
      temp = self.copy(deep=False)
      temp._img = temp._img[key]
      if isinstance(temp._img.shape,tuple) and len(temp._img.shape) > 2:
        temp.frames = temp._img.shape[0]
      else:
        temp.frames = 1
      return temp
    raise IndexError, "Invalid index: %s" % str(key)

  def append(self, stack):
    temp = copy.copy(self)
    temp._img = np.append( temp._img, stack._img, axis=0 )
    return temp

  def __sub__(self, stack):
    return self.__add__(stack.__neg__())

  def __add__(self, stack):
    temp = self.copy()
    if hasattr(stack,'_img'):
      try:
        temp._img = temp._img + stack._img
      except ValueError:
        raise StackError("Couldn't add images: check sizes are the same")
    else:
      temp._img = temp._img + stack
    return temp

  def __neg__(self):
    temp = self.copy()
    temp._img = -temp._img
    return temp

  def __eq__(self, other):
    return np.all(self._img == other._img)

  def __ne__(self, other):
    return not self==other

  def __repr__(self):
    return "Stack %dx%dx%d" % (self.frames, self.height, self.width)

  def __iter__(self):
    for i in range(self.frames):
      yield self[i]