|
1 | 1 | import sys |
| 2 | +import os |
2 | 3 | import typing as ty |
3 | 4 | from collections import defaultdict, Counter |
4 | 5 | from pathlib import Path |
@@ -149,6 +150,73 @@ def dicom_collection_read_metadata( |
149 | 150 | return collated |
150 | 151 |
|
151 | 152 |
|
| 153 | +def get_dicom_tag( |
| 154 | + file: ty.Union[str, os.PathLike[ty.Any], ty.BinaryIO], |
| 155 | + target_tag: ty.Tuple[int, int], |
| 156 | +) -> ty.Union[str, bytes, None]: |
| 157 | + """A basic function to read a DICOM file and extract the value of a specific tag. |
| 158 | + This is a low-level function that does not use any external libraries. |
| 159 | + It is not a replacement for pydicom, but can be used to extract specific tags |
| 160 | + without loading the entire DICOM file into memory. |
| 161 | +
|
| 162 | + Parameters |
| 163 | + ---------- |
| 164 | + filepath : str or os.PathLike |
| 165 | + The path to the DICOM file. |
| 166 | + target_tag : tuple[int, int] |
| 167 | + The DICOM tag to extract, specified as a tuple of (group, element). |
| 168 | + For example, (0x0010, 0x0010) for PatientName. |
| 169 | +
|
| 170 | + Returns |
| 171 | + ------- |
| 172 | + str or bytes or None |
| 173 | + The value of the specified DICOM tag, decoded as a string if possible. |
| 174 | + If the tag is not found or cannot be decoded, returns None. |
| 175 | + """ |
| 176 | + if isinstance(file, (str, os.PathLike)): |
| 177 | + filepath = file |
| 178 | + file_stream = open(filepath, "rb") |
| 179 | + close_stream = True |
| 180 | + elif hasattr(file, "read"): |
| 181 | + file_stream = file # type: ignore[assignment] |
| 182 | + close_stream = False |
| 183 | + else: |
| 184 | + raise TypeError("file must be a path-like object or a binary stream") |
| 185 | + |
| 186 | + try: |
| 187 | + file_stream.seek(132) # Skip preamble and 'DICM' if at file start |
| 188 | + |
| 189 | + while True: |
| 190 | + tag_bytes = file_stream.read(4) |
| 191 | + if len(tag_bytes) < 4: |
| 192 | + break |
| 193 | + |
| 194 | + group = int.from_bytes(tag_bytes[:2], "little") |
| 195 | + element = int.from_bytes(tag_bytes[2:], "little") |
| 196 | + tag = (group, element) |
| 197 | + |
| 198 | + vr = file_stream.read(2).decode() |
| 199 | + |
| 200 | + if vr in {"OB", "OW", "OF", "SQ", "UT", "UN"}: |
| 201 | + file_stream.read(2) # reserved |
| 202 | + length = int.from_bytes(file_stream.read(4), "little") |
| 203 | + else: |
| 204 | + length = int.from_bytes(file_stream.read(2), "little") |
| 205 | + |
| 206 | + value = file_stream.read(length) |
| 207 | + |
| 208 | + if tag == target_tag: |
| 209 | + try: |
| 210 | + return value.decode().strip() |
| 211 | + except UnicodeDecodeError: |
| 212 | + return value |
| 213 | + finally: |
| 214 | + if close_stream: |
| 215 | + file_stream.close() |
| 216 | + |
| 217 | + return None # Not found |
| 218 | + |
| 219 | + |
152 | 220 | # class Vnd_Siemens_Vision(DicomImage): |
153 | 221 | # ext = ".ima" |
154 | 222 |
|
|
0 commit comments