84 lines
2.7 KiB
Python
84 lines
2.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
``NDArrayPreview`` --- Standalone Preview for ImagePlugin
|
|
*********************************************************
|
|
|
|
This module provides a standalone object to receive images to ophyd from the
|
|
AreaDetector's ImagePlugin.
|
|
|
|
Created on Wed Jan 29 2025
|
|
|
|
@author: mohacsi_i
|
|
"""
|
|
import numpy as np
|
|
from ophyd import Device, Component, EpicsSignal, Kind, Staged
|
|
from ophyd.areadetector import NDDerivedSignal
|
|
|
|
|
|
from bec_lib import bec_logger
|
|
|
|
logger = bec_logger.logger
|
|
|
|
|
|
class NDArrayPreview(Device):
|
|
"""Wrapper class around AreaDetector's NDStdArray plugins
|
|
|
|
This is a standalone class to display images from AreaDetector's
|
|
ImagePlugin without using a parent device. It also offers BEC exposed
|
|
methods to transfer image and change image array Kind-ness.
|
|
|
|
NOTE: As an explicit request, it can toggle data recording
|
|
"""
|
|
|
|
# Subscriptions for plotting image
|
|
USER_ACCESS = ["image", "savemode"]
|
|
SUB_MONITOR = "device_monitor_2d"
|
|
_default_sub = SUB_MONITOR
|
|
|
|
# Status attributes
|
|
array_size_x = Component(EpicsSignal, "ArraySize0_RBV", kind=Kind.config)
|
|
array_size_y = Component(EpicsSignal, "ArraySize1_RBV", kind=Kind.config)
|
|
array_size_z = Component(EpicsSignal, "ArraySize2_RBV", kind=Kind.config)
|
|
ndimensions = Component(EpicsSignal, "NDimensions_RBV", kind=Kind.config)
|
|
array_data = Component(EpicsSignal, "ArrayData", kind=Kind.omitted)
|
|
shaped_image = Component(
|
|
NDDerivedSignal,
|
|
derived_from="array_data",
|
|
shape=("array_size_z", "array_size_y", "array_size_x"),
|
|
num_dimensions="ndimensions",
|
|
kind=Kind.omitted,
|
|
)
|
|
|
|
def read(self):
|
|
"""Stream out data on every read()"""
|
|
if self._staged == Staged.yes:
|
|
image = self.shaped_image.get()
|
|
self._run_subs(sub_type=self.SUB_MONITOR, value=image)
|
|
return super().read()
|
|
|
|
def savemode(self, save=False):
|
|
"""Toggle save mode for the shaped image"""
|
|
# pylint: disable=protected-access
|
|
if save:
|
|
self.shaped_image._kind = Kind.normal
|
|
else:
|
|
self.shaped_image._kind = Kind.omitted
|
|
|
|
def image(self):
|
|
"""Fallback method in case image streaming fills up the BEC"""
|
|
array_size = (self.array_size_z.get(), self.array_size_y.get(), self.array_size_x.get())
|
|
if array_size == (0, 0, 0):
|
|
raise RuntimeError("Invalid image; ensure array_callbacks are on")
|
|
|
|
if array_size[-1] == 0:
|
|
array_size = array_size[:-1]
|
|
|
|
image = self.array_data.get()
|
|
return np.array(image).reshape(array_size)
|
|
|
|
|
|
# Automatically connect to SAMCAM at PXIII if directly invoked
|
|
if __name__ == "__main__":
|
|
img = NDArrayPreview("X06DA-SAMCAM:image1:", name="samimg")
|
|
img.wait_for_connection()
|