Skip to content
Snippets Groups Projects
Commit bdb70082 authored by Tamino Huxohl's avatar Tamino Huxohl
Browse files

format and comment interfile module

parent 8e0a76c4
No related branches found
No related tags found
No related merge requests found
...@@ -58,6 +58,7 @@ class Interfile: ...@@ -58,6 +58,7 @@ class Interfile:
Interfile data type wrapping the header and the image Interfile data type wrapping the header and the image
into an object. into an object.
""" """
header: Dict[str, str] header: Dict[str, str]
image: np.ndarray image: np.ndarray
...@@ -70,6 +71,7 @@ class _InterfileKeys: ...@@ -70,6 +71,7 @@ class _InterfileKeys:
""" """
Data class defining keys for an Interfile header. Data class defining keys for an Interfile header.
""" """
placeholder: str = "{_}" placeholder: str = "{_}"
_dim: str = f"matrix size [{placeholder}]" _dim: str = f"matrix size [{placeholder}]"
...@@ -88,6 +90,8 @@ class _InterfileKeys: ...@@ -88,6 +90,8 @@ class _InterfileKeys:
def spacing(self, index: int) -> str: def spacing(self, index: int) -> str:
return self._spacing.replace(self.placeholder, str(index)) return self._spacing.replace(self.placeholder, str(index))
InterfileKeys = _InterfileKeys() InterfileKeys = _InterfileKeys()
...@@ -103,7 +107,9 @@ def type_by_format(number_format: str, bytes_per_pixel: int) -> type: ...@@ -103,7 +107,9 @@ def type_by_format(number_format: str, bytes_per_pixel: int) -> type:
if number_format == "float" and bytes_per_pixel == 4: if number_format == "float" and bytes_per_pixel == 4:
return np.float32 return np.float32
raise ValueError("Unknown mapping from format {number_format} with {bytes_per_pixel} bytes to numpy type") raise ValueError(
"Unknown mapping from format {number_format} with {bytes_per_pixel} bytes to numpy type"
)
def parse_interfile_header_str(header: str) -> Dict[str, str]: def parse_interfile_header_str(header: str) -> Dict[str, str]:
...@@ -139,13 +145,17 @@ def load_interfile(filename: str) -> Interfile: ...@@ -139,13 +145,17 @@ def load_interfile(filename: str) -> Interfile:
Load an INTERFILE header and its image as a numpy array. Load an INTERFILE header and its image as a numpy array.
:param filename: the filename of the INTERFILE header file :param filename: the filename of the INTERFILE header file
:return: the header as a dict and the image as a numpy array :return: the interfile image
""" """
header = parse_interfile_header(filename) header = parse_interfile_header(filename)
dim_x = int(header[InterfileKeys.dim(1)]) dim_x = int(header[InterfileKeys.dim(1)])
dim_y = int(header[InterfileKeys.dim(2)]) dim_y = int(header[InterfileKeys.dim(2)])
dim_z = int(header[InterfileKeys.dim(3)]) if InterfileKeys.dim(3) in header else int(header[InterfileKeys.n_projections]) dim_z = (
int(header[InterfileKeys.dim(3)])
if InterfileKeys.dim(3) in header
else int(header[InterfileKeys.n_projections])
)
bytes_per_pixel = int(header[InterfileKeys.bytes_per_pixel]) bytes_per_pixel = int(header[InterfileKeys.bytes_per_pixel])
num_format = header[InterfileKeys.number_format] num_format = header[InterfileKeys.number_format]
...@@ -170,6 +180,16 @@ def load_interfile_img(filename: str) -> np.ndarray: ...@@ -170,6 +180,16 @@ def load_interfile_img(filename: str) -> np.ndarray:
def write_interfile(filename: str, interfile: Interfile): def write_interfile(filename: str, interfile: Interfile):
"""
Write an interfile image.
Note that custom extensions to the filename are removed and the defaults
from this module are used (.hv for the header and .v for the image).
This method also sanitizes the header by making sure that
dimensions match the image.
:param filename: the filename where the interfile image is written to
:param interfile: the interfile saved
"""
filename = os.path.splitext(filename)[0] filename = os.path.splitext(filename)[0]
filename_data = f"{filename}{EXTENSION_INTERFILE_IMAGE}" filename_data = f"{filename}{EXTENSION_INTERFILE_IMAGE}"
filename_header = f"{filename}{EXTENSION_INTERFILE_HEADER}" filename_header = f"{filename}{EXTENSION_INTERFILE_HEADER}"
...@@ -195,11 +215,29 @@ if __name__ == "__main__": ...@@ -195,11 +215,29 @@ if __name__ == "__main__":
import argparse import argparse
import math import math
parser = argparse.ArgumentParser(description="Modify an interfile image") parser = argparse.ArgumentParser(
parser.add_argument("--interfile", type=str, required=True, help="the interfile input") description="Modify an interfile image",
parser.add_argument("--out", type=str, help="the interfile output - if not set the input file will be overwritte") formatter_class=argparse.ArgumentDefaultsHelpFormatter,
parser.add_argument("--cmd", choices=["fill", "crop", "pad", "flip"], help="the modification to perform") )
parser.add_argument("--param", type=int, required=True, help="the parameters for the modification [fill: value, crop & pad: target size of z dimension]") parser.add_argument(
"--interfile", type=str, required=True, help="the interfile input"
)
parser.add_argument(
"--out",
type=str,
help="the interfile output - if not set the input file will be overwritte",
)
parser.add_argument(
"--cmd",
choices=["fill", "crop", "pad", "flip"],
help="the modification to perform",
)
parser.add_argument(
"--param",
type=int,
required=True,
help="the parameters for the modification [fill: value, crop & pad: target size of z dimension]",
)
args = parser.parse_args() args = parser.parse_args()
args.out = args.interfile if args.out is None else args.out args.out = args.interfile if args.out is None else args.out
...@@ -211,16 +249,22 @@ if __name__ == "__main__": ...@@ -211,16 +249,22 @@ if __name__ == "__main__":
if args.cmd == "pad": if args.cmd == "pad":
target_size = args.param target_size = args.param
real_size = interfile.image.shape[0] real_size = interfile.image.shape[0]
assert target_size > real_size, f"Cannot pad from a larger size {real_size} to a smaller size {target_size}" assert (
target_size > real_size
), f"Cannot pad from a larger size {real_size} to a smaller size {target_size}"
diff = target_size - real_size diff = target_size - real_size
pad_lower = math.ceil(diff / 2) pad_lower = math.ceil(diff / 2)
pad_upper = math.floor(diff / 2) pad_upper = math.floor(diff / 2)
interfile.image = np.pad(interfile.image, ((pad_lower, pad_upper), (0, 0), (0, 0))) interfile.image = np.pad(
interfile.image, ((pad_lower, pad_upper), (0, 0), (0, 0))
)
if args.cmd == "crop": if args.cmd == "crop":
target_size = args.param target_size = args.param
real_size = interfile.image.shape[0] real_size = interfile.image.shape[0]
assert target_size < real_size, f"Cannot crop from a smaller size {real_size} to a larger size {target_size}" assert (
target_size < real_size
), f"Cannot crop from a smaller size {real_size} to a larger size {target_size}"
diff = target_size / 2 diff = target_size / 2
center = real_size // 2 center = real_size // 2
...@@ -231,7 +275,3 @@ if __name__ == "__main__": ...@@ -231,7 +275,3 @@ if __name__ == "__main__":
interfile.image = interfile.image[:, :, ::-1] interfile.image = interfile.image[:, :, ::-1]
write_interfile(args.out, interfile) write_interfile(args.out, interfile)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment