def __init__(self, name, detector, dataflow, emitter, sem_stream, **kwargs): """ sem_stream (SEMStream): an SEM stream with the same emitter """ super(MomentOfInertiaLiveStream, self).__init__(name, detector, dataflow, emitter, **kwargs) # Initialise to some typical value: small so that it's fast self.repetition.value = (9, 9) # Fuzzing should not be needed del self.fuzzing # B/C is fixed to min/max, and histogram is pretty much useless del self.auto_bc del self.auto_bc_outliers del self.histogram # Region of interest as left, top, right, bottom (in ratio from the # whole area of the emitter => between 0 and 1) that defines the region # to be acquired for the MoI compution. # This is expected to be centered to the lens pole position. self.detROI = model.TupleContinuous((0, 0, 1, 1), range=((0, 0, 0, 0), (1, 1, 1, 1)), cls=(int, long, float), setter=self._setDetROI) # The background data (typically, an acquisition without ebeam). # It is subtracted from the acquisition data. # If set to None, baseline is used self.background = model.VigilantAttribute(None, setter=self._setBackground) # Future of the acquisition self._acq_stream = MomentOfInertiaMDStream("MoI acq", sem_stream, self) self._acquire_f = None
class MomentOfInertiaLiveStream(CCDSettingsStream): """ Special stream to acquire AR view and display moment of inertia live. Also provides spot size information. Needs a SEMStream. Note: internally it uses MomentOfInertiaSyncStream to actually acquire data. """ def __init__(self, name, detector, dataflow, emitter, sem_stream, **kwargs): """ sem_stream (SEMStream): an SEM stream with the same emitter """ super(MomentOfInertiaLiveStream, self).__init__(name, detector, dataflow, emitter, **kwargs) # Initialise to some typical value: small so that it's fast self.repetition.value = (9, 9) # Fuzzing should not be needed del self.fuzzing # B/C is fixed to min/max, and histogram is pretty much useless del self.auto_bc del self.auto_bc_outliers del self.histogram # Region of interest as left, top, right, bottom (in ratio from the # whole area of the emitter => between 0 and 1) that defines the region # to be acquired for the MoI compution. # This is expected to be centered to the lens pole position. self.detROI = model.TupleContinuous((0, 0, 1, 1), range=((0, 0, 0, 0), (1, 1, 1, 1)), cls=(int, long, float), setter=self._setDetROI) # The background data (typically, an acquisition without ebeam). # It is subtracted from the acquisition data. # If set to None, baseline is used self.background = model.VigilantAttribute(None, setter=self._setBackground) # Future of the acquisition self._acq_stream = MomentOfInertiaMDStream("MoI acq", sem_stream, self) self._acquire_f = None def _setDetROI(self, roi): """ Setter for the .detROI VA Synchronises the detROI VA with the VA of the acquisition stream """ self._acq_stream.detROI.value = roi return roi def _setBackground(self, data): """ Setter for the .background VA Synchronises the background VA with the VA of the acquisition stream """ self._acq_stream.background.value = data return data def _projectMoI2RGB(self, data, valid): """ Project a 2D spatial DataArray into a RGB representation data (DataArray): 2D DataArray valid (numpy.ndarray of bool) return (DataArray): 3D DataArray """ # Note: NaN values will become 0 (and 255 after inversion) rgbim = img.DataArray2RGB(data) # Inverse the contrast, because the smallest the MoI, the brighter the # pixel should be. rgbim = 255 - rgbim # Make non valid/clipping pixels reddish. for (x, y), v in numpy.ndenumerate(valid): if not v: if math.isnan(data[x, y]): # We don't want it too bright rgbim[x, y] = [64, 0, 0] else: val = rgbim[x, y, 0] rgbim[x, y] = [min(val + 64, 255), val // 4, val // 4] rgbim.flags.writeable = False md = self._find_metadata(data.metadata) md[model.MD_DIMS] = "YXC" # RGB format return model.DataArray(rgbim, md) def _updateImage(self): if self.raw: moi, valid = self.raw[1:3] # 2nd and 3rd data are useful for us self.image.value = self._projectMoI2RGB(moi, valid) def _on_acq_done(self, future): # Pretty much the same as _onNewData(), but also relaunch an acquisition try: logging.debug("MoI acquisition finished") try: self.raw = future.result() # sem, moi, valid, spot int., raw CCD center if not future.cancelled(): self._shouldUpdateImage() except CancelledError: pass except Exception: logging.exception("Failed to acquire data") # start the next acquisition if self.is_active.value: self._acquire_f = self._acq_stream.acquire() self._acquire_f.add_done_callback(self._on_acq_done) def _onActive(self, active): """ Called when the Stream is activated or deactivated by setting the is_active attribute """ if active: # Convert the .acquire() future into a live acquisition if not self.should_update.value: logging.warning("Trying to activate stream while it's not " "supposed to update") self._bg_image = self.background.value # approx. the index of the center image self._acquire_f = self._acq_stream.acquire() self._acquire_f.add_done_callback(self._on_acq_done) else: self._acquire_f.cancel() def getRawValue(self, pos): """ Return the raw value at the given position and the maxima pos (int, int): position on the array return: (0<float or None): raw value of the moment of inertia (None or tuple of floats): min/max raw values raises: IndexError if pos is incorrect """ raw = self.raw if len(raw) >= 3: data = raw[1].view(numpy.ndarray) # To ensure we get floats return data[pos], (numpy.nanmin(data), numpy.nanmax(data)) else: # Nothing yet return None, None # TODO: take as argument the pixel position? def getImageCCD(self): """ Return the CCD image at the center return (DataArray or None): raw CCD data """ raw = self.raw if len(raw) < 5: # Nothing yet return None data = raw[4] # TODO: find spot center and crop around it? Also apply background subtraction? rgbim = img.DataArray2RGB(data) rgbim.flags.writeable = False md = self._find_metadata(data.metadata) md[model.MD_DIMS] = "YXC" # RGB format return model.DataArray(rgbim, md) # TODO: take as argument the pixel position? def getSpotIntensity(self): """ return (0<=float<=1): spot intensity """ raw = self.raw if len(raw) >= 4: return raw[3][()] else: # Nothing yet return None