import os
from pathlib import Path
import textwrap
from scipy.ndimage import median_filter
from tifffile import TiffFile
import tifffile
from ncempy.io import dm as ncempy_dm
from ncempy.io.emdVelox import fileEMDVelox
from itertools import takewhile
from skimage import io as skio
import numpy as np
import io
import sys
import json
[docs]def read_image(f: os.PathLike) -> tuple[np.ndarray, dict]:
"""Uses Tifffile or ncempy.io load an image and read the scale if there is one.
Args:
f (str): file to read
Raises:
NotImplementedError: If unknown scale type is given, or Tif series is given.
RuntimeError: If uknown file type is given, or number of pages in tif is wrong
Returns:
tuple: (image, mdata), image given as 2D or 3D numpy array,
mdata has keys:
filepath: str
filename: str
scale: nm/pixel
defocus_values: nm
scale_unit: str
defocus_unit: str
beam_energy: float
"""
f = Path(f)
if not f.exists():
raise FileNotFoundError(str(f.absolute()))
metadata = {
"filepath": str(f.absolute()),
"filename": f.stem + "".join(f.suffixes),
}
defocus = None
defocus_unit = None
beam_energy = None
if f.suffix in [".tif", ".tiff"]:
with TiffFile(f, mode="r") as tif:
if tif.imagej_metadata is not None and "unit" in tif.imagej_metadata:
res = tif.pages[0].tags["XResolution"].value
if res[0] == 0:
scale = None
else:
scale = res[1] / res[0] # to nm/pixel
if tif.imagej_metadata["unit"] == "nm":
pass
elif tif.imagej_metadata["unit"] in ["um", "µm", "micron"]:
scale *= 1e3
elif tif.imagej_metadata["unit"] in ["mm", "millimeter"]:
scale *= 1e6
else:
print(f'unknown scale type: {tif.imagej_metadata["unit"]}')
raise NotImplementedError
else:
scale = None
if len(tif.series) != 1:
raise NotImplementedError(
"Not sure how to deal with multi-series stack"
)
if len(tif.pages) > 1: # load as stack
out_im = []
for page in tif.pages:
out_im.append(page.asarray())
out_im = np.array(out_im)
elif len(tif.pages) == 1: # single image
out_im = tif.pages[0].asarray()
else:
raise RuntimeError(
f"Found an unexpected number of pages: {len(tif.pages)}"
)
elif f.suffix in [".dm3", ".dm4", ".dm5"]:
with ncempy_dm.fileDM(f) as im:
dset = im.getDataset(0)
mdata = im.getMetadata(0)
if any(["def" in i for i in mdata.keys()]):
print("possibly found defocus metadata in dm file. update load!")
assert dset["pixelUnit"][0] == dset["pixelUnit"][1]
assert dset["pixelSize"][0] == dset["pixelSize"][1]
if dset["pixelUnit"][0] == "nm":
scale = dset["pixelSize"][0]
elif dset["pixelUnit"][0] == "µm":
scale = dset["pixelSize"][0] * 1000
else:
print(f"unknown scale type {dset['pixelUnit'][0]}")
raise NotImplementedError
if 'Microscope Info Voltage' in mdata:
beam_energy = mdata['Microscope Info Voltage']
out_im = dset["data"]
elif f.suffix in [".emd"]: # TODO test but make this for dmx as well?
with fileEMDVelox(f) as emd:
out_im, mdata = emd.get_dataset(0)
defocus = float(emd.metaDataJSON["Optics"]["Defocus"]) * 1e9 # nm
defocus_unit = "nm"
metadata["AcquisitionTime"] = str(mdata["AcquisitionTime"].time())
metadata["AcquisitionDate"] = str(mdata["AcquisitionTime"].date())
assert mdata["pixelUnit"][0] == mdata["pixelUnit"][1]
assert mdata["pixelSize"][0] == mdata["pixelSize"][1]
if mdata["pixelUnit"][0] == "nm":
scale = mdata["pixelSize"][0]
elif mdata["pixelUnit"][0] == "µm":
scale = mdata["pixelSize"][0] * 1000
else:
print(f"unknown scale type {mdata['pixelUnit'][0]}")
raise NotImplementedError
raise NotImplementedError("look for beam energy")
elif f.suffix in [".png", ".jpg", ".jpeg"]:
out_im = skio.imread(f)
scale = None
else:
raise RuntimeError(f"Unknown filetype given: {f.suffix}")
metadata["scale"] = scale
metadata["scale_unit"] = "nm"
metadata["defocus_values"] = defocus
metadata["defocus_unit"] = defocus_unit
metadata["beam_energy"] = beam_energy
return out_im, metadata
[docs]def read_json(file):
"""
read json
"""
with open(file, "r") as f:
d = json.load(f)
return d