Skip to content

Commit adb29ce

Browse files
feat: update read() to parse both dat formats
1 parent 3f3c66a commit adb29ce

File tree

1 file changed

+54
-18
lines changed

1 file changed

+54
-18
lines changed

rawtools/dat.py

Lines changed: 54 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,18 @@ def determine_bit_depth(fp, dims):
6161
logging.warning(f"Unable to determine bit-depth of volume '{fp}'. Expected at <{expected_filesize}> bytes but found <{file_size}> bytes. Defaulting to unsigned 16-bit.")
6262
return 'uint16'
6363

64-
def __parse_object_filename(line):
65-
pattern = r"^ObjectFileName\:\s+(?P<filename>.*\.raw)$"
64+
def __parse_object_filename(line, dat_format):
65+
if (dat_format == "Dragonfly"):
66+
pattern = r"\s+<ObjectFileName>(?P<filename>.*\.raw)<\/ObjectFileName>$"
67+
elif (dat_format == "NSI"):
68+
pattern = r"^ObjectFileName\:\s+(?P<filename>.*\.raw)$"
6669

6770
match = re.match(pattern, line, flags=re.IGNORECASE)
6871
if match is not None:
6972
logging.debug(f"Match: {match}")
7073
return match.group('filename')
7174

72-
def __parse_resolution(line):
75+
def __parse_resolution(line, dat_format):
7376
"""Get the x, y, z dimensions of a volume.
7477
7578
Args:
@@ -80,8 +83,11 @@ def __parse_resolution(line):
8083
8184
"""
8285
# logging.debug(line.strip())
83-
pattern_old = r'\s+<Resolution X="(?P<x>\d+)"\s+Y="(?P<y>\d+)"\s+Z="(?P<z>\d+)"'
84-
pattern = r'Resolution\:\s+(?P<x>\d+)\s+(?P<y>\d+)\s+(?P<z>\d+)'
86+
if (dat_format == "Dragonfly"):
87+
pattern = r'\s+<Resolution X="(?P<x>\d+)"\s+Y="(?P<y>\d+)"\s+Z="(?P<z>\d+)"'
88+
elif (dat_format == "NSI"):
89+
pattern_old = r'\s+<Resolution X="(?P<x>\d+)"\s+Y="(?P<y>\d+)"\s+Z="(?P<z>\d+)"'
90+
pattern = r'Resolution\:\s+(?P<x>\d+)\s+(?P<y>\d+)\s+(?P<z>\d+)'
8591

8692
# See if the DAT file is the newer version
8793
match = re.match(pattern, line, flags=re.IGNORECASE)
@@ -103,7 +109,7 @@ def __parse_resolution(line):
103109
raise Exception(f"Unable to extract dimensions from DAT file. Found dimensions: '{dims}'.")
104110
return dims
105111

106-
def __parse_slice_thickness(line):
112+
def __parse_slice_thickness(line, dat_format):
107113
"""Get the x, y, z dimensions of a volume.
108114
109115
Args:
@@ -113,31 +119,56 @@ def __parse_slice_thickness(line):
113119
(float, float, float): x, y, z real-world thickness in mm. Otherwise, returns None.
114120
115121
"""
116-
pattern = r'\w+\:\s+(?P<xth>\d+\.\d+)\s+(?P<yth>\d+\.\d+)\s+(?P<zth>\d+\.\d+)'
122+
if (dat_format == "Dragonfly"):
123+
pattern = r"\s+<Spacing\s+X=\"(?P<xth>\d+\.\d+)\"\s+Y=\"(?P<yth>\d+\.\d+)\"\s+Z=\"(?P<zth>\d+\.\d+)\""
124+
elif (dat_format == "NSI"):
125+
pattern = r'\w+\:\s+(?P<xth>\d+\.\d+)\s+(?P<yth>\d+\.\d+)\s+(?P<zth>\d+\.\d+)'
126+
117127
match = re.match(pattern, line, flags=re.IGNORECASE)
118128
if match is not None:
119129
logging.debug(f"Match: {match}")
120-
df = match.groupdict()
121130
dims = [ match.group('xth'), match.group('yth'), match.group('zth') ]
122-
dims = [ float(s) for s in dims ]
131+
if (dat_format == "Dragonfly"):
132+
# Change Dragonfly thickness units to match NSI format
133+
dims = [ (float(s)*1000) for s in dims ]
134+
elif (dat_format == "NSI"):
135+
dims = [ float(s) for s in dims ]
123136
if not dims or len(dims) != 3:
124137
raise Exception(f"Unable to extract slice thickness from DAT file: '{line}'. Found slice thickness: '{dims}'.")
125138
return dims
126139

127-
def __parse_format(line):
128-
pattern = r"^Format\:\s+(?P<format>\w+)$"
140+
def __parse_format(line, dat_format):
141+
if (dat_format == "Dragonfly"):
142+
pattern = r"\s+<Format>(?P<format>\w+)<\/Format>$"
143+
elif (dat_format == "NSI"):
144+
pattern = r"^Format\:\s+(?P<format>\w+)$"
145+
129146
match = re.match(pattern, line, flags=re.IGNORECASE)
130147
if match is not None:
131148
logging.debug(f"Match: {match}")
132149
return match.group('format')
133150

134-
def __parse_object_model(line):
135-
pattern = r"^ObjectModel\:\s+(?P<object_model>\w+)$"
151+
def __parse_object_model(line, dat_format):
152+
if (dat_format == "Dragonfly"):
153+
pattern = r"\s+<Unit>(?P<object_model>\w+)<\/Unit>$"
154+
elif (dat_format == "NSI"):
155+
pattern = r"^ObjectModel\:\s+(?P<object_model>\w+)$"
156+
136157
match = re.match(pattern, line, flags=re.IGNORECASE)
137158
if match is not None:
138159
logging.debug(f"Match: {match}")
139160
return match.group('object_model')
140161

162+
def __is_dragonfly_dat_format(line):
163+
pattern = r"^<\?xml\sversion=\"1\.0\"\?>$"
164+
match = re.match(pattern, line, flags=re.IGNORECASE)
165+
if match is not None:
166+
logging.debug(f"Match: {match}")
167+
return True
168+
169+
170+
171+
141172
def read(fp):
142173
"""Read a .DAT file
143174
Args:
@@ -146,24 +177,29 @@ def read(fp):
146177
dict: contents of .DAT file
147178
"""
148179
data = {}
180+
dat_format = "NSI"
149181
with open(fp, 'r') as ifp:
150182
# Parse the individual lines
151183
for line in ifp.readlines():
152184
line = line.strip()
153-
if (object_filename := __parse_object_filename(line)) is not None:
185+
# Determine if format is NSI .dat or Dragonfly .dat
186+
if (__is_dragonfly_dat_format(line)):
187+
dat_format = "Dragonfly"
188+
189+
if (object_filename := __parse_object_filename(line, dat_format)) is not None:
154190
data['ObjectFileName'] = object_filename
155191

156-
if (resolution := __parse_resolution(line)) is not None:
192+
if (resolution := __parse_resolution(line, dat_format)) is not None:
157193
data['xdim'], data['ydim'], data['zdim'] = resolution
158194
data['dimensions'] = resolution
159195

160-
if (thicknesses := __parse_slice_thickness(line)) is not None:
196+
if (thicknesses := __parse_slice_thickness(line, dat_format)) is not None:
161197
data['x_thickness'], data['y_thickness'], data['z_thickness'] = thicknesses
162198

163-
if (file_format := __parse_format(line)) is not None:
199+
if (file_format := __parse_format(line, dat_format)) is not None:
164200
data['Format'] = file_format
165201

166-
if (object_model := __parse_object_model(line)) is not None:
202+
if (object_model := __parse_object_model(line, dat_format)) is not None:
167203
data['model'] = object_model
168204

169205

0 commit comments

Comments
 (0)