Skip to content

PYMEAcquire camera dtype support#1624

Merged
David-Baddeley merged 7 commits into
python-microscopy:masterfrom
barentine:camdtype
Jan 14, 2026
Merged

PYMEAcquire camera dtype support#1624
David-Baddeley merged 7 commits into
python-microscopy:masterfrom
barentine:camdtype

Conversation

@barentine
Copy link
Copy Markdown
Member

Addresses issue encountered in #1463 and #1551 , which is that PYMEAcquire currently forces uint16. This PR allows cameras to use float (e.g. reading voltages off an NIDaq for my raster scanning project) or different int bit depths. This is split out from #1551 to simplify review.

Is this a bugfix or an enhancement?
enhancement
Proposed changes:

  • add dtype attribute to camera base class
  • use camera dtype in frameWrangler
  • store dtype as backend_kwarg in SpoolController.
  • add dtype as input to HDFBackend. Note that MemoryBackend already takes dtype as a parameter, and ClusterBackend already handles other data types through PZFFormat.dumps.
  • use numpy min/max in histogram update if we aren't working with uint16

Notes

  • I have not worked with or tested the tiff acquisition backend.
  • this PR can be tested with the following simulated camera class, which just casts the typical fakeCam frames to float.
FakeFloatCamera

class FakeFloatCamera(FakeCamera):
    numpy_frames=1
    order= 'C'
    dtype= 'float32'

    def __init__(self, XVals, YVals, noiseMaker, zPiezo, zOffset=50.0, fluors=None, laserPowers=[0,50], xpiezo=None, ypiezo=None, illumFcn = 'ConstIllum', pixel_size_nm=70.):
        super().__init__(XVals, YVals, noiseMaker, zPiezo, zOffset, fluors, laserPowers, xpiezo=xpiezo, ypiezo=ypiezo, illumFcn=illumFcn, pixel_size_nm=pixel_size_nm)
        self._saturation_threshold = np.finfo(np.float32).max

    def ExtractColor(self, chSlice, mode):
        try:
            d = self.compT.getIm().astype('float32')  # get image from completed computation thread
            # print d.nbytes, chSlice.nbytes
            memcpy(chSlice.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
                   d.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)), chSlice.nbytes)
            # chSlice[:,:] = d
            # self.compTOld = None #set computation thread to None such that we get an error if we try and obtain the same result twice
        except AttributeError:  # triggered if called with None
            logger.error("Grabbing problem: probably called with 'None' thread")

@David-Baddeley
Copy link
Copy Markdown
Contributor

If we are adding support for non uint16 data types then it should be across all backends (or, at the very least, we should make sure we raise an informative error if the backend doesn't support it). We should also have a mechanism for disabling un-supported backends so they don't show in the GUI (or, alternatively potentially doing type coercion - but that has some potential data loss issues). At a minimum I think it we need to build some data-type awareness into the Backend base class - e.g. add a supported_dtypes attribute to the base class (defaults to ['uint16',]), make dtype a base class kwarg, and raise an exception in the base class constructor if dtype not in supported_dtypes. Derived classes (currently memory and HDF backends) can override supported_dtypes as appropriate.

Because (for now) it's just your special case that uses non uint16 dtypes, we might not need to worry to much about the UI, but the supported_dtypes will define the interface we need to check to see if a given backend is offered.

@David-Baddeley
Copy link
Copy Markdown
Contributor

David-Baddeley commented Jan 8, 2026

Suggest we also use actual numpy dtype objects rather than strings for the dtypes (we can cast in the constructor, so it will accept both) so that comparisons between, e.g. 'u2' and, 'uint16', and '<u2' etc evaluate correctly.

Comment thread PYME/DSView/displaySettingsPanel.py Outdated

_min, _max = minmax_u16(dsa)
# get min and max efficiently if it is uint16
if str(dsa.dtype) == 'uint16':
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit wary about string comparisons on dtypes - maybe

Suggested change
if str(dsa.dtype) == 'uint16':
if dsa.dtype == np.uint16:

@barentine
Copy link
Copy Markdown
Member Author

Thanks for the comments, @David-Baddeley !

I changed the type of the dtype attributes from str to dtype. Note that PZFFormat.py makes string comparisons if we want to tweak that at some point.

I also added supported_dtypes attribute for the backends. At the moment I would say it is more restrictive than it needs to be for e.g. MemoryBackend, but at least gets the framework there.

As an aside - if Time/Blinking series is selected we probably want to grey out 'Memory' backend in the UI unless we change its support.

@David-Baddeley David-Baddeley merged commit 9b35037 into python-microscopy:master Jan 14, 2026
3 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants