Ptufile is a Python library to
- read data and metadata from PicoQuant PTU and related files (PHU, PCK, PCO, PFS, PUS, PQRES, PQDAT, and SPQR), and
- write TCSPC histograms to T3 image mode PTU files.
PTU files contain time correlated single photon counting (TCSPC) measurement data and instrumentation parameters.
Author: | Christoph Gohlke |
---|---|
License: | BSD-3-Clause |
Version: | 2025.9.9 |
DOI: | 10.5281/zenodo.10120021 |
Install the ptufile package and all dependencies from the Python Package Index:
python -m pip install -U "ptufile[all]"
See Examples for using the programming interface.
Source code and support are available on GitHub.
This revision was tested with the following requirements and dependencies (other versions may work):
- CPython 3.11.9, 3.12.10, 3.13.7, 3.14.0rc 64-bit
- NumPy 2.3.3
- Xarray 2025.9.0 (recommended)
- Matplotlib 3.10.6 (optional)
- Tifffile 2025.9.9 (optional)
- Numcodecs 0.16.2 (optional)
- Python-dateutil 2.9.0 (optional)
- Cython 3.1.3 (build)
2025.9.9
- Log error when decoding image with invalid line or frame masks.
2025.7.30
- Add option to specify pixel time for decoding images.
- Add functions to read and write PicoQuant BIN files.
- Drop support for Python 3.10.
2025.5.10
- Mark Cython extension free-threading compatible.
- Support Python 3.14.
2025.2.20
- Rename PqFileMagic to PqFileType (breaking).
- Rename PqFile.magic to PqFile.type (breaking).
- Add PQDAT and SPQR file types.
2025.2.12
- Add options to specify file open modes to PqFile and PtuFile.read_records.
- Add convenience properties to PqFile and PtuFile.
- Cache records read from file.
2025.1.13
- Fall back to file size if TTResult_NumberOfRecords is zero (#2).
2024.12.28
- Add imwrite function to encode TCSPC image histogram in T3 PTU format.
- Add enums for more PTU tag values.
- Add PqFile.datetime property.
- Read TDateTime tag as datetime instead of struct_time (breaking).
- Rename PtuFile.type property to record_type (breaking).
- Fix reading PHU missing HistResDscr_HWBaseResolution tag.
- Warn if tags are not 8-byte aligned in file.
2024.12.20
- Support bi-directional sinusoidal scanning (WIP).
2024.11.26
- Support bi-directional scanning (FLIMbee scanner).
- Drop support for Python 3.9.
2024.10.10
- Also trim leading channels without photons (breaking).
- Add property to identify channels with photons.
2024.9.14
- …
Refer to the CHANGES file for older revisions.
PicoQuant GmbH is a manufacturer of photonic components and instruments.
The PicoQuant unified file formats are documented at the PicoQuant-Time-Tagged-File-Format-Demos.
The following features are currently not implemented due to the lack of test files or documentation: PT2 and PT3 files, decoding images from T2 and SPQR formats, bidirectional per frame, and deprecated image reconstruction.
Compatibility with PTU files written by non-PicoQuant software (for example, Leica LAS X or Abberior Imspector) is limited, as is decoding line, bidirectional, and sinusoidal scanning.
Other modules for reading or writing PicoQuant files are Read_PTU.py, readPTU_FLIM, fastFLIM, PyPTU, PTU_Reader, PTU_Writer, FlimReader, tangy, tttrlib, picoquantio, ptuparser, phconvert, trattoria (wrapper of trattoria-core, tttr-toolbox), PAM <https://gitlab.com/PAM-PIE/PAM/-/blob/master/functions/readin/Read_PTU.m>, and napari-flim-phasor-plotter.
Read properties and tags from any type of PicoQuant unified tagged file:
>>> pq = PqFile('tests/data/Settings.pfs')
>>> pq.type
<PqFileType.PFS: ...>
>>> pq.guid
UUID('86d428e2-cb0b-4964-996c-04456ba6be7b')
>>> pq.tags
{...'CreatorSW_Name': 'SymPhoTime 64', 'CreatorSW_Version': '2.1'...}
>>> pq.close()
Read metadata from a PicoQuant PTU FLIM file:
>>> ptu = PtuFile('tests/data/FLIM.ptu')
>>> ptu.type
<PqFileType.PTU: ...>
>>> ptu.record_type
<PtuRecordType.PicoHarpT3: 66307>
>>> ptu.measurement_mode
<PtuMeasurementMode.T3: 3>
>>> ptu.measurement_submode
<PtuMeasurementSubMode.IMAGE: 3>
Decode TTTR records from the PTU file to numpy.recarray
:
>>> decoded = ptu.decode_records()
>>> decoded.dtype
dtype([('time', '<u8'), ('dtime', '<i2'), ('channel', 'i1'), ('marker', 'u1')])
Get global times of frame changes from markers:
>>> decoded['time'][(decoded['marker'] & ptu.frame_change_mask) > 0]
array([1571185680], dtype=uint64)
Decode TTTR records to overall delay-time histograms per channel:
>>> ptu.decode_histogram(dtype='uint8')
array([[ 5, 7, 7, ..., 10, 9, 2]], shape=(2, 3126), dtype=uint8)
Get information about the FLIM image histogram in the PTU file:
>>> ptu.shape
(1, 256, 256, 2, 3126)
>>> ptu.dims
('T', 'Y', 'X', 'C', 'H')
>>> ptu.coords
{'T': ..., 'Y': ..., 'X': ..., 'H': ...}
>>> ptu.dtype
dtype('uint16')
>>> ptu.active_channels
(0, 1)
Decode parts of the image histogram to numpy.ndarray
using slice notation.
Slice step sizes define binning, -1 being used to integrate along axis:
>>> ptu[:, ..., 0, ::-1]
array([[[103, ..., 38],
...
[ 47, ..., 30]]],
shape=(1, 256, 256), dtype=uint16)
Alternatively, decode the first channel and integrate all histogram bins
into a xarray.DataArray
, keeping reduced axes:
>>> ptu.decode_image(channel=0, dtime=-1, asxarray=True)
<xarray.DataArray (T: 1, Y: 256, X: 256, C: 1, H: 1)> ...
array([[[[[103]],
...
[[ 30]]]]], shape=(1, 256, 256, 1, 1), dtype=uint16)
Coordinates:
* T (T) float64... 0.05625
* Y (Y) float64... -0.0001304 ... 0.0001294
* X (X) float64... -0.0001304 ... 0.0001294
* C (C) uint8... 0
* H (H) float64... 0.0
Attributes...
frequency: 19999200.0
...
Write the TCSPC histogram and metadata to a PicoHarpT3 image mode PTU file:
>>> imwrite(
... '_test.ptu',
... ptu[:],
... ptu.global_resolution,
... ptu.tcspc_resolution,
... # optional metadata
... pixel_time=ptu.pixel_time,
... record_type=PtuRecordType.PicoHarpT3,
... comment='Written by ptufile.py',
... tags={'File_RawData_GUID': [ptu.guid]},
... )
Read back the TCSPC histogram from the file:
>>> tcspc_histogram = imread('_test.ptu')
>>> import numpy
>>> numpy.array_equal(tcspc_histogram, ptu[:])
True
Close the file handle:
>>> ptu.close()
Preview the image and metadata in a PTU file from the console:
python -m ptufile tests/data/FLIM.ptu