dicom-info 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dicom-info
3
- Version: 0.1.0
4
- Summary: Prints information about DIOCM files.
3
+ Version: 0.2.0
4
+ Summary: Prints information about DICOM files.
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: pydicom>=3.0.1
9
+ Requires-Dist: matplotlib>=3.7.0
10
+ Requires-Dist: numpy>=1.24.0
9
11
  Dynamic: license-file
10
12
 
11
13
  # dicom-info
@@ -14,12 +16,14 @@ A command-line tool for displaying DICOM file information.
14
16
 
15
17
  ## Description
16
18
 
17
- dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line.
19
+ dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line, and can also display DICOM images interactively.
18
20
 
19
21
  ## Requirements
20
22
 
21
23
  - Python >= 3.13
22
24
  - pydicom >= 3.0.1
25
+ - matplotlib >= 3.7.0 (for image display)
26
+ - numpy >= 1.24.0 (for image display)
23
27
 
24
28
  ## Installation
25
29
 
@@ -43,20 +47,37 @@ The basic syntax is:
43
47
  dicom-info FILE [FILE ...]
44
48
  ```
45
49
 
46
- Example:
50
+ ### Displaying Metadata
47
51
 
48
- ```
52
+ To display DICOM file metadata:
53
+
54
+ ```bash
49
55
  dicom-info path/to/file.dcm
50
56
  dicom-info file1.dcm file2.dcm file3.dcm
51
57
  ```
52
58
 
53
59
  The tool will display information for each DICOM file, including all available metadata and attributes.
54
60
 
61
+ ### Displaying Images
62
+
63
+ To display DICOM images interactively, use the `--display` or `-d` flag:
64
+
65
+ ```bash
66
+ dicom-info --display path/to/file.dcm
67
+ dicom-info -d file1.dcm file2.dcm file3.dcm
68
+ ```
69
+
70
+ Image display features:
71
+ - **2D images**: Displayed as grayscale plots with a colorbar
72
+ - **3D images**: Displayed with an interactive slider to navigate through slices
73
+ - **Multiple files**: Each image is shown in its own subplot with the filename as the title
74
+
55
75
  ## Error Handling
56
76
 
57
77
  The tool will exit with status code 1 if:
58
78
  - Files are not found
59
79
  - Files are not valid DICOM files
80
+ - When using `--display`, if no files contain pixel data
60
81
 
61
82
  ## License
62
83
 
@@ -4,12 +4,14 @@ A command-line tool for displaying DICOM file information.
4
4
 
5
5
  ## Description
6
6
 
7
- dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line.
7
+ dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line, and can also display DICOM images interactively.
8
8
 
9
9
  ## Requirements
10
10
 
11
11
  - Python >= 3.13
12
12
  - pydicom >= 3.0.1
13
+ - matplotlib >= 3.7.0 (for image display)
14
+ - numpy >= 1.24.0 (for image display)
13
15
 
14
16
  ## Installation
15
17
 
@@ -33,20 +35,37 @@ The basic syntax is:
33
35
  dicom-info FILE [FILE ...]
34
36
  ```
35
37
 
36
- Example:
38
+ ### Displaying Metadata
37
39
 
38
- ```
40
+ To display DICOM file metadata:
41
+
42
+ ```bash
39
43
  dicom-info path/to/file.dcm
40
44
  dicom-info file1.dcm file2.dcm file3.dcm
41
45
  ```
42
46
 
43
47
  The tool will display information for each DICOM file, including all available metadata and attributes.
44
48
 
49
+ ### Displaying Images
50
+
51
+ To display DICOM images interactively, use the `--display` or `-d` flag:
52
+
53
+ ```bash
54
+ dicom-info --display path/to/file.dcm
55
+ dicom-info -d file1.dcm file2.dcm file3.dcm
56
+ ```
57
+
58
+ Image display features:
59
+ - **2D images**: Displayed as grayscale plots with a colorbar
60
+ - **3D images**: Displayed with an interactive slider to navigate through slices
61
+ - **Multiple files**: Each image is shown in its own subplot with the filename as the title
62
+
45
63
  ## Error Handling
46
64
 
47
65
  The tool will exit with status code 1 if:
48
66
  - Files are not found
49
67
  - Files are not valid DICOM files
68
+ - When using `--display`, if no files contain pixel data
50
69
 
51
70
  ## License
52
71
 
@@ -1,11 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dicom-info
3
- Version: 0.1.0
4
- Summary: Prints information about DIOCM files.
3
+ Version: 0.2.0
4
+ Summary: Prints information about DICOM files.
5
5
  Requires-Python: >=3.13
6
6
  Description-Content-Type: text/markdown
7
7
  License-File: LICENSE
8
8
  Requires-Dist: pydicom>=3.0.1
9
+ Requires-Dist: matplotlib>=3.7.0
10
+ Requires-Dist: numpy>=1.24.0
9
11
  Dynamic: license-file
10
12
 
11
13
  # dicom-info
@@ -14,12 +16,14 @@ A command-line tool for displaying DICOM file information.
14
16
 
15
17
  ## Description
16
18
 
17
- dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line.
19
+ dicom-info is a simple Python utility that reads and displays information from DICOM (Digital Imaging and Communications in Medicine) files. It provides a straightforward way to inspect DICOM file metadata from the command line, and can also display DICOM images interactively.
18
20
 
19
21
  ## Requirements
20
22
 
21
23
  - Python >= 3.13
22
24
  - pydicom >= 3.0.1
25
+ - matplotlib >= 3.7.0 (for image display)
26
+ - numpy >= 1.24.0 (for image display)
23
27
 
24
28
  ## Installation
25
29
 
@@ -43,20 +47,37 @@ The basic syntax is:
43
47
  dicom-info FILE [FILE ...]
44
48
  ```
45
49
 
46
- Example:
50
+ ### Displaying Metadata
47
51
 
48
- ```
52
+ To display DICOM file metadata:
53
+
54
+ ```bash
49
55
  dicom-info path/to/file.dcm
50
56
  dicom-info file1.dcm file2.dcm file3.dcm
51
57
  ```
52
58
 
53
59
  The tool will display information for each DICOM file, including all available metadata and attributes.
54
60
 
61
+ ### Displaying Images
62
+
63
+ To display DICOM images interactively, use the `--display` or `-d` flag:
64
+
65
+ ```bash
66
+ dicom-info --display path/to/file.dcm
67
+ dicom-info -d file1.dcm file2.dcm file3.dcm
68
+ ```
69
+
70
+ Image display features:
71
+ - **2D images**: Displayed as grayscale plots with a colorbar
72
+ - **3D images**: Displayed with an interactive slider to navigate through slices
73
+ - **Multiple files**: Each image is shown in its own subplot with the filename as the title
74
+
55
75
  ## Error Handling
56
76
 
57
77
  The tool will exit with status code 1 if:
58
78
  - Files are not found
59
79
  - Files are not valid DICOM files
80
+ - When using `--display`, if no files contain pixel data
60
81
 
61
82
  ## License
62
83
 
@@ -0,0 +1,3 @@
1
+ pydicom>=3.0.1
2
+ matplotlib>=3.7.0
3
+ numpy>=1.24.0
@@ -0,0 +1,199 @@
1
+ """Main entry point into the dicom-info CLI."""
2
+
3
+ # ruff: noqa: T201
4
+
5
+ from __future__ import annotations
6
+
7
+ # Typing
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from matplotlib.axes import Axes
12
+ from matplotlib.image import AxesImage
13
+ from numpy import ndarray
14
+
15
+
16
+ # Python imports
17
+ import argparse
18
+ import logging
19
+ import sys
20
+ from pathlib import Path
21
+ from typing import Callable
22
+
23
+ # Module imports
24
+ import matplotlib.pyplot as plt
25
+ import pydicom
26
+ import pydicom.errors
27
+ from matplotlib.widgets import Slider
28
+ from mpl_toolkits.axes_grid1 import make_axes_locatable
29
+
30
+ logger = logging.getLogger(__name__)
31
+
32
+
33
+ def print_stats(files: list[str]) -> None:
34
+ """Print DICOM information for the files."""
35
+ try:
36
+ dcms = [pydicom.dcmread(f) for f in files]
37
+
38
+ except (FileNotFoundError, pydicom.errors.InvalidDicomError) as err:
39
+ print(f"Files could not be read due to {err}")
40
+ sys.exit(1)
41
+
42
+ for f, dcm in zip(files, dcms, strict=True):
43
+ banner = "*" * 5 + f" {f} " + "*" * 5
44
+ print(banner, dcm, sep="\n")
45
+
46
+
47
+ def display_images(
48
+ files: list[str],
49
+ max_cols: int | None = None,
50
+ ) -> None:
51
+ """Display DICOM images with interactive controls."""
52
+ try:
53
+ dcms = [pydicom.dcmread(f) for f in files]
54
+
55
+ except (FileNotFoundError, pydicom.errors.InvalidDicomError) as err:
56
+ print(f"Files could not be read due to {err}")
57
+ sys.exit(1)
58
+
59
+ # Check if any files have pixel data
60
+ files_with_pixels = [
61
+ (f, dcm) for f, dcm in zip(files, dcms, strict=True)
62
+ if hasattr(dcm, "pixel_array")
63
+ ]
64
+
65
+ if not files_with_pixels:
66
+ print("No DICOM files with pixel data found.")
67
+ sys.exit(1)
68
+
69
+ # Create figure with subplots for each image
70
+ num_images = len(files_with_pixels)
71
+ max_cols = int(num_images ** .5) if max_cols is None else max_cols
72
+ cols = min(num_images, max_cols)
73
+ rows = (num_images - 1) // cols + 1
74
+ fig = plt.figure(figsize=(5 * cols, 4 * rows))
75
+
76
+ # Store references to manage 3D sliders
77
+ sliders = []
78
+ axes_images: list[
79
+ tuple[Axes, AxesImage, Slider | None, ndarray | None]
80
+ ] = []
81
+
82
+ for idx, (filepath, dcm) in enumerate(files_with_pixels, start=1):
83
+ pixel_array = dcm.pixel_array
84
+ filename = Path(filepath).name
85
+
86
+ # Determine if 2D or 3D
87
+ if len(pixel_array.shape) == 2: # noqa: PLR2004
88
+ # 2D image - simple display
89
+ ax = fig.add_subplot(rows, cols, idx)
90
+ im = ax.imshow(pixel_array, cmap="gray")
91
+ ax.set_title(filename)
92
+ ax.axis("off")
93
+ plt.colorbar(im, ax=ax, fraction=0.046, pad=0.04)
94
+ axes_images.append((ax, im, None, None))
95
+
96
+ elif len(pixel_array.shape) == 3: # noqa: PLR2004
97
+ # 3D image - display with slider
98
+ ax = fig.add_subplot(rows, cols, idx)
99
+
100
+ # Start with the first slice
101
+ initial_slice = 0
102
+ im = ax.imshow(pixel_array[initial_slice], cmap="gray")
103
+ ax.set_title(
104
+ f"{filename}\nSlice {initial_slice + 1}/{pixel_array.shape[0]}",
105
+ )
106
+ ax.axis("off")
107
+
108
+ # Create slider axes to the right of the image
109
+ divider = make_axes_locatable(ax)
110
+ slider_ax = divider.append_axes("right", size="5%", pad=0.1)
111
+ slider = Slider(
112
+ slider_ax,
113
+ "Slice",
114
+ 0,
115
+ pixel_array.shape[0] - 1,
116
+ valinit=initial_slice,
117
+ valstep=1,
118
+ orientation="vertical",
119
+ )
120
+
121
+ # Update function for slider
122
+ def make_update(
123
+ image_obj: AxesImage,
124
+ axis: Axes,
125
+ data: ndarray,
126
+ fname: str,
127
+ sldr: Slider,
128
+ ) -> Callable[[float], None]:
129
+ def update(val: float) -> None:
130
+ slice_idx = int(sldr.val)
131
+ image_obj.set_data(data[slice_idx])
132
+ axis.set_title(
133
+ f"{fname}\nSlice {slice_idx + 1}/{data.shape[0]}",
134
+ )
135
+ fig.canvas.draw_idle()
136
+ logger.debug("Slider at slice %f for %s", val, fname)
137
+
138
+ return update
139
+
140
+ slider.on_changed(
141
+ make_update(im, ax, pixel_array, filename, slider),
142
+ )
143
+ sliders.append(slider)
144
+ axes_images.append((ax, im, slider, pixel_array))
145
+
146
+ else:
147
+ logger.warning(
148
+ "%s has unsupported dimensions: %s",
149
+ filename,
150
+ pixel_array.shape,
151
+ )
152
+
153
+ plt.tight_layout()
154
+ if len(files_with_pixels) == 1 and axes_images[0][2] is not None:
155
+ # Adjust layout for single 3D image with slider
156
+ plt.subplots_adjust(bottom=0.15)
157
+
158
+ plt.show()
159
+
160
+
161
+ def main() -> None:
162
+ """Entry point for the CLI."""
163
+ parser = argparse.ArgumentParser(
164
+ description="Print DICOM file information.",
165
+ )
166
+
167
+ parser.add_argument(
168
+ "file",
169
+ help="Path to the DICOM file(s).",
170
+ nargs="+",
171
+ type=str,
172
+ )
173
+
174
+ parser.add_argument(
175
+ "-d",
176
+ "--display",
177
+ help="Display DICOM images instead of printing metadata.",
178
+ action="store_true",
179
+ )
180
+
181
+ parser.add_argument(
182
+ "-c",
183
+ "--columns",
184
+ help="Maximum number of columns for image display.",
185
+ type=int,
186
+ default=None,
187
+ )
188
+
189
+ args = parser.parse_args()
190
+
191
+ print_stats(args.file)
192
+
193
+ # Display images
194
+ if args.display:
195
+ display_images(args.file, max_cols=args.columns)
196
+
197
+
198
+ if __name__ == "__main__":
199
+ main()
@@ -1,11 +1,13 @@
1
1
  [project]
2
2
  name = "dicom-info"
3
- version = "0.1.0"
4
- description = "Prints information about DIOCM files."
3
+ version = "0.2.0"
4
+ description = "Prints information about DICOM files."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
8
  "pydicom>=3.0.1",
9
+ "matplotlib>=3.7.0",
10
+ "numpy>=1.24.0",
9
11
  ]
10
12
 
11
13
  [tool.uv]
@@ -1 +0,0 @@
1
- pydicom>=3.0.1
@@ -1,49 +0,0 @@
1
- """Main entry point into the dicom-info CLI."""
2
-
3
- # ruff: noqa: T201
4
-
5
- from __future__ import annotations
6
-
7
- # Python imports
8
- import argparse
9
- import sys
10
-
11
- # Module imports
12
- import pydicom
13
-
14
-
15
- def print_stats(files: list[str]) -> None:
16
- """Print DICOM information for the files."""
17
- try:
18
- dcms = [pydicom.dcmread(f) for f in files]
19
-
20
- except (FileNotFoundError, pydicom.errors.InvalidDicomError) as err:
21
- print(f"Files could not be read due to {err}")
22
- sys.exit(1)
23
-
24
- for f, dcm in zip(files, dcms, strict=True):
25
- banner = "*" * 5 + f" {f} " + "*" * 5
26
- print(banner, dcm, sep="\n")
27
-
28
-
29
- def main() -> None:
30
- """Entry point for the CLI."""
31
- parser = argparse.ArgumentParser(
32
- description="Print DICOM file information.",
33
- )
34
-
35
- parser.add_argument(
36
- "file",
37
- help="Path to the DICOM file(s).",
38
- nargs="+",
39
- type=str,
40
- )
41
-
42
- args = parser.parse_args()
43
-
44
- # Print dicom info
45
- print_stats(args.file)
46
-
47
-
48
- if __name__ == "__main__":
49
- main()
File without changes
File without changes