""" Copyright © 2025 Howard Hughes Medical Institute, Authored by Carsen Stringer , Michael Rariden and Marius Pachitariu. """ import os import numpy as np import cv2 import tifffile import logging from tqdm import tqdm import re try: import nd2 ND2 = True except: ND2 = False try: import nrrd NRRD = True except: NRRD = False io_logger = logging.getLogger(__name__) def load_dax(filename): ### modified from ZhuangLab github: ### https://github.com/ZhuangLab/storm-analysis/blob/71ae493cbd17ddb97938d0ae2032d97a0eaa76b2/storm_analysis/sa_library/datareader.py#L156 inf_filename = os.path.splitext(filename)[0] + ".inf" if not os.path.exists(inf_filename): io_logger.critical( f"ERROR: no inf file found for dax file {filename}, cannot load dax without it" ) return None ### get metadata image_height, image_width = None, None # extract the movie information from the associated inf file size_re = re.compile(r"frame dimensions = ([\d]+) x ([\d]+)") length_re = re.compile(r"number of frames = ([\d]+)") endian_re = re.compile(r" (big|little) endian") with open(inf_filename, "r") as inf_file: lines = inf_file.read().split("\n") for line in lines: m = size_re.match(line) if m: image_height = int(m.group(2)) image_width = int(m.group(1)) m = length_re.match(line) if m: number_frames = int(m.group(1)) m = endian_re.search(line) if m: if m.group(1) == "big": bigendian = 1 else: bigendian = 0 # set defaults, warn the user that they couldn"t be determined from the inf file. if not image_height: io_logger.warning("could not determine dax image size, assuming 256x256") image_height = 256 image_width = 256 ### load image img = np.memmap(filename, dtype="uint16", shape=(number_frames, image_height, image_width)) if bigendian: img = img.byteswap() img = np.array(img) return img def imread(filename): """ Read in an image file with tif or image file type supported by cv2. Args: filename (str): The path to the image file. Returns: numpy.ndarray: The image data as a NumPy array. Raises: None Raises an error if the image file format is not supported. Examples: >>> img = imread("image.tif") """ # ensure that extension check is not case sensitive ext = os.path.splitext(filename)[-1].lower() if ext == ".tif" or ext == ".tiff" or ext == ".flex": with tifffile.TiffFile(filename) as tif: ltif = len(tif.pages) try: full_shape = tif.shaped_metadata[0]["shape"] except: try: page = tif.series[0][0] full_shape = tif.series[0].shape except: ltif = 0 if ltif < 10: img = tif.asarray() else: page = tif.series[0][0] shape, dtype = page.shape, page.dtype ltif = int(np.prod(full_shape) / np.prod(shape)) io_logger.info(f"reading tiff with {ltif} planes") img = np.zeros((ltif, *shape), dtype=dtype) for i, page in enumerate(tqdm(tif.series[0])): img[i] = page.asarray() img = img.reshape(full_shape) return img elif ext == ".dax": img = load_dax(filename) return img elif ext == ".nd2": if not ND2: io_logger.critical("ERROR: need to 'pip install nd2' to load in .nd2 file") return None elif ext == ".nrrd": if not NRRD: io_logger.critical( "ERROR: need to 'pip install pynrrd' to load in .nrrd file") return None else: img, metadata = nrrd.read(filename) if img.ndim == 3: img = img.transpose(2, 0, 1) return img elif ext != ".npy": try: img = cv2.imread(filename, -1) #cv2.LOAD_IMAGE_ANYDEPTH) if img.ndim > 2: img = img[..., [2, 1, 0]] return img except Exception as e: io_logger.critical("ERROR: could not read file, %s" % e) return None else: try: dat = np.load(filename, allow_pickle=True).item() masks = dat["masks"] return masks except Exception as e: io_logger.critical("ERROR: could not read masks from file, %s" % e) return None def imsave(filename, arr): """ Saves an image array to a file. Args: filename (str): The name of the file to save the image to. arr (numpy.ndarray): The image array to be saved. Returns: None """ ext = os.path.splitext(filename)[-1].lower() if ext == ".tif" or ext == ".tiff": tifffile.imwrite(filename, data=arr, compression="zlib") else: if len(arr.shape) > 2: arr = cv2.cvtColor(arr, cv2.COLOR_BGR2RGB) cv2.imwrite(filename, arr)