Advertisement
Guest User

Untitled

a guest
Nov 16th, 2012
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 7.21 KB | None | 0 0
  1. import devices.depot
  2. import events
  3. import util.datadoc
  4. import util.logger
  5. import util.threads
  6.  
  7. import numpy
  8. import threading
  9. import time
  10.  
  11. ## Unique ID for identifying saver instances
  12. uniqueID = 0
  13.  
  14.  
  15.  
  16. ## This class simply records all data received during an experiment and then
  17. # saves it to disk later.
  18. class DataSaver:
  19.     ## \param cameras List of CameraHandler instances for the cameras that
  20.     #         will be generating images
  21.     # \param numReps How many times the experiment will be repeated.
  22.     # \param cameraToImagesPerRep Maps camera handlers to how many images to
  23.     #        expect for that camera in a single repeat of the experiment.
  24.     # \param runThread Thread that is executing the experiment. When it exits,
  25.     #        we know to stop expecting more images.
  26.     # \param savePath Path to save the incoming data to.
  27.     # \param pixelSizeZ Size of the Z "pixel" (i.e. distance between Z slices).
  28.     # \param titles List of strings to insert into the MRC file's header.
  29.     #        Per the file format, each string can be up to 80 characters long
  30.     #        and there can be up to 10 of them.
  31.     def __init__(self, cameras, numReps, cameraToImagesPerRep, runThread,
  32.             savePath, pixelSizeZ, titles):
  33.         self.cameras = cameras
  34.         self.numReps = numReps
  35.         self.cameraToImagesPerRep = cameraToImagesPerRep
  36.         self.runThread = runThread
  37.         global uniqueID
  38.         ## Unique ID for our instance
  39.         self.uniqueID = uniqueID
  40.         uniqueID += 1
  41.         ## Set to True once we're done with all saving activity.
  42.         self.isSavingComplete = False
  43.         # Find the maximum image size (in pixels) in X and Y. While we're at it,
  44.         # assign a number to each camera, for indexing into our data array
  45.         # later.
  46.         maxWidth, maxHeight = 0, 0
  47.         ## Maps a camera handler to the index into self.data for that camera.
  48.         self.handleToIndex = {}
  49.         for i, camera in enumerate(self.cameras):
  50.             width, height = camera.getImageSize()
  51.             maxWidth = max(width, maxWidth)
  52.             maxHeight = max(height, maxHeight)
  53.             self.handleToIndex[camera] = i
  54.         self.indexToHandle = dict([(value, key) for key, value in self.handleToIndex.iteritems()])
  55.  
  56.         ## Array of pixel data, in WTZYX order. Because different cameras
  57.         # may have different image sizes, we pad with zeros to fill in the
  58.         # extra space.
  59.         self.data = numpy.zeros(
  60.                 (len(self.cameras), self.numReps,
  61.                     max(self.cameraToImagesPerRep.values()),
  62.                     maxHeight, maxWidth),
  63.                 dtype = numpy.uint16)
  64.  
  65.         ## Filehandle we will write the data to.
  66.         self.filehandle = open(savePath, 'wb')
  67.         ## Lock on writing to the file.
  68.         self.fileLock = threading.Lock()
  69.  
  70.         pixelSizeXY = devices.depot.getHandlersOfType(devices.depot.OBJECTIVE)[0].getPixelSize()
  71.         ## MRC header object for the data.
  72.         self.header = util.datadoc.makeHeaderFor(self.data, pixelSizeXY, pixelSizeZ)
  73.         self.header.NumTitles = len(titles)
  74.         self.header.title[:len(titles)] = titles
  75.         with self.fileLock:
  76.             util.datadoc.writeMrcHeader(self.header, self.filehandle)
  77.         ## Number of bytes to allocate for each image in the file.
  78.         # \todo Assuming unsigned 16-bit integer here.
  79.         self.imageBytes = (maxWidth * maxHeight * 2)
  80.  
  81.         ## List of how many images we've received, on a per-camera basis.
  82.         self.imagesReceived = [0] * len(self.cameras)
  83.         ## List of functions that receive image data and feed it into
  84.         # self.imagesReceived.
  85.         self.lambdas = []
  86.  
  87.  
  88.     ## Subscribe to the new-camera-image events for the cameras we care about.
  89.     # Save the functions we generate for handling the subscriptions, so we can
  90.     # unsubscribe later.
  91.     def startCollecting(self):
  92.         for camera in self.cameras:
  93.             func = lambda data, camera = camera: self.onImage(self.handleToIndex[camera], data)
  94.             self.lambdas.append(func)
  95.             events.subscribe('new image %s' % camera.name, func)
  96.  
  97.  
  98.     ## Wait for the runThread to finish, then wait a bit longer in case some
  99.     # images are laggardly, before we close our filehandle.
  100.     def executeAndSave(self):
  101.         self.runThread.join()
  102.        
  103.         # Check if we've received all images; if we haven't, then give them
  104.         # a little time to arrive.
  105.         waitStart = time.time()
  106.         while time.time() - waitStart < .5:
  107.             amDone = True
  108.             for camera in self.cameras:
  109.                 total = self.imagesReceived[self.handleToIndex[camera]]
  110.                 target = self.cameraToImagesPerRep[camera] * self.numReps
  111.                 if total != target:
  112.                     # There exists a camera for which we do not have all
  113.                     # images yet.
  114.                     amDone = False
  115.                     break
  116.             if amDone:
  117.                 break
  118.             time.sleep(.01)
  119.         for i, camera in enumerate(self.cameras):
  120.             events.unsubscribe('new image %s' % camera.name, self.lambdas[i])
  121.         with self.fileLock:
  122.             # Finish writing.
  123.             self.filehandle.close()
  124.  
  125.         for camera in self.cameras:
  126.             expected = self.cameraToImagesPerRep[camera] * self.numReps
  127.             received = self.imagesReceived[self.handleToIndex[camera]]
  128.             if expected != received:
  129.                 print "Only got %d out of %d images for camera %s" % (received, expected, camera.name)
  130.                 util.logger.log.warn("Missing images for camera %s: only got %d out of %d" % (camera.name, received, expected))
  131.  
  132.         self.isSavingComplete = True
  133.  
  134.  
  135.     ## Receive new data and add it to our array.
  136.     @util.threads.callInNewThread
  137.     def onImage(self, cameraIndex, imageData):
  138.         with self.fileLock:
  139.             # Calculate the time and Z indices for the new image.
  140.             imagesPerRep = self.cameraToImagesPerRep[self.indexToHandle[cameraIndex]]
  141.             numImages = self.imagesReceived[cameraIndex]
  142.             timepoint = int(numImages / imagesPerRep)
  143.             zIndex = numImages - timepoint * imagesPerRep
  144.             height, width = imageData.shape
  145.             try:
  146.                 self.data[cameraIndex, timepoint, zIndex, :height, :width] = imageData
  147.             except Exception, e:
  148.                 print "Bad index",self.uniqueID,numImages,(cameraIndex, timepoint, zIndex, height, width), self.data.shape
  149.                 util.logger.log.error("Saver %d tried invalid index: %s compare %s" % (self.uniqueID, (cameraIndex, timepoint, zIndex, height, width), self.data.shape))
  150.  
  151.             # Determine the offset for the image in the file. 1024 is the
  152.             # size of the image header.
  153.             offset = 1024 + self.imageBytes * (cameraIndex * max(self.cameraToImagesPerRep.values()) + self.imagesReceived[cameraIndex])
  154.             self.filehandle.seek(offset)
  155.             self.filehandle.write(self.data[cameraIndex, timepoint, zIndex])
  156.             self.imagesReceived[cameraIndex] += 1
  157.  
  158.  
  159.     ## Return True if saving is complete.
  160.     def getAmDone(self):
  161.         return self.isSavingComplete
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement