Source code for ptyrad.io.generic
"""
Generic file handling (load/save) for raw, npy, tif formats
"""
import os
import numpy as np
from tifffile import imread, imwrite
[docs]
def load_raw(file_path, shape, dtype=np.float32, offset=0, gap=1024):
"""Loads a raw binary file containing interleaved image data and gaps.
This implementation uses a custom `numpy.dtype` with `np.fromfile` for
fast I/O performance, extracting only the valid data regions and skipping
the specified byte gaps between frames. Note that custom processed raw
data might have a gap of 0.
Args:
file_path (str): The path to the raw binary file.
shape (tuple of int): The expected shape of the data in the format
(N, height, width), where N is the number of frames.
dtype (data-type, optional): The NumPy data type of the image pixels.
Defaults to np.float32.
offset (int, optional): The number of bytes to skip at the beginning
of the file. Defaults to 0.
gap (int, optional): The number of gap bytes to skip between each
image frame. Defaults to 1024.
Returns:
numpy.ndarray: An array of the extracted data with the specified shape
and dtype.
Raises:
ValueError: If the actual file size does not match the expected size
calculated from the inputs.
"""
# shape = (N, height, width)
# np.fromfile with custom dtype is faster than the np.read and np.frombuffer
# This implementaiton is also roughly 2x faster (10sec vs 20sec) than load_hdf5 with a 128x128x128x128 (1GB) EMPAD dataset
# Note that for custom processed empad2 raw there might be no gap between the images
N, height, width = shape
# Verify file size first
expected_size = offset + N * (height * width * dtype().itemsize + gap)
actual_size = os.path.getsize(file_path)
if actual_size != expected_size:
raise ValueError(f"Mismatch in expected ({expected_size} bytes = offset + N * (height * width * 4 + gap)) vs. actual ({actual_size} bytes) file size! Check your loading configurations!")
# Define the custom dtype to include both data and gap
custom_dtype = np.dtype([
('data', dtype, (height, width)),
('gap', np.uint8, gap) # uint8 means 1 byte per gap element
])
# Read the entire file using the custom dtype
with open(file_path, 'rb') as f:
f.seek(offset)
raw_data = np.fromfile(f, dtype=custom_dtype, count=N)
# Extract just the 'data' part (ignoring the gaps)
data = raw_data['data']
return data
[docs]
def load_tif(file_path):
"""Loads an image array from a TIFF file.
Args:
file_path (str): The path to the TIFF file.
Returns:
numpy.ndarray: The loaded image data.
Raises:
FileNotFoundError: If the specified file does not exist.
"""
# Check if the file exists
if not os.path.exists(file_path):
raise FileNotFoundError(f"The specified file '{file_path}' does not exist. Please check your file path and working directory.")
data = imread(file_path)
return data
[docs]
def load_npy(file_path):
"""Loads an array from a binary NumPy .npy file.
Args:
file_path (str): The path to the .npy file.
Returns:
numpy.ndarray: The loaded array data.
Raises:
FileNotFoundError: If the specified file does not exist.
"""
# Check if the file exists
if not os.path.exists(file_path):
raise FileNotFoundError(f"The specified file '{file_path}' does not exist. Please check your file path and working directory.")
data = np.load(file_path)
return data
[docs]
def write_tif(file_path, data):
"""Saves a NumPy array as a TIFF file.
The file is saved with ImageJ compatibility enabled to ensure proper
handling of hyperstacks and metadata in common microscopy viewers.
Args:
file_path (str): The destination path for the TIFF file.
data (numpy.ndarray): The array data to save.
"""
imwrite(file_path, data, imagej=True)
[docs]
def write_npy(file_path, data):
"""Saves a NumPy array to a binary .npy file.
Args:
file_path (str): The destination path for the .npy file.
data (numpy.ndarray): The array data to save.
"""
np.save(file_path, data)