A desktop medical image viewer for DICOM and NIfTI formats built with Tkinter. Designed for quick inspection of volumetric medical imaging data with windowing, multi-axis navigation, and metadata exploration.
- DICOM — load a directory of DICOM files with automatic filtering (
pydicom.misc.is_dicom) and InstanceNumber sorting - NIfTI — load
.nii/.nii.gzfiles with 3D+ volume support
- Multi-axis NIfTI — simultaneous axial, sagittal, and coronal panels with independent slice navigation
- Single-axis DICOM — slice slider with keyboard navigation (Left/Right, Home/End)
- Zoom — scroll wheel zoom per panel (0.1x–20x)
- Pan — middle-click drag
- Window/Level presets — Brain, Bone, Lung, Abdomen, Soft Tissue
- Manual Center/Width sliders — real-time adjustment
- DICOM defaults — reads WindowCenter/WindowWidth from DICOM tags
- Colormaps — gray, hot, jet, bone via toolbar dropdown
- NIfTI — reoriented to RAS canonical (
nib.as_closest_canonical) withrot90for correct radiological display - DICOM — horizontal flip (LPS) for standard radiological convention
- Menu bar — File (Open Dir, Open File, Exit), View (Metadata, Reset Zoom, Theme, Font Size, Font Weight), Tools (Window Presets)
- Dark / Light theme — selectable from View menu,
ttkclam-based - Font configuration — size (8–20) and weight (normal/bold) from View menu
- Status bar — pixel coordinates, intensity value, dimensions, zoom level under cursor
- Info bar — patient/image metadata (format-aware: Patient ID, Modality for DICOM; Dimensions, Voxel Size, Orientation for NIfTI)
- DICOM tag browser with sequence expansion and VR column
- NIfTI header key/value display
- Search/filter across name, value, and VR fields
- Right-click to copy value to clipboard
- Horizontal and vertical scrollbars
| Shortcut | Action |
|---|---|
Ctrl+O |
Open DICOM directory |
Ctrl+Shift+O |
Open NIfTI file |
Ctrl+M |
Show metadata |
Ctrl+0 |
Reset zoom |
Ctrl+Q |
Exit |
Left / Right |
Previous / next slice |
Home / End |
First / last slice |
viewer/
├── __init__.py
├── __main__.py # CLI entry point (argparse)
├── app.py # Tk root setup, theme init, resize debounce
├── controllers/
│ └── viewer.py # Main controller — wires model, views, callbacks
├── models/
│ ├── base.py # ImageVolume ABC
│ ├── dicom.py # DicomVolume (filtering, sorting, LRU cache, LPS)
│ └── nifti.py # NiftiVolume (RAS reorientation, multi-axis)
├── views/
│ ├── canvas.py # Single image canvas with zoom/pan/cursor tracking
│ ├── info_bar.py # Data-driven info bar
│ ├── menubar.py # File/View/Tools menus
│ ├── metadata.py # Metadata window with search and copy
│ ├── multi_canvas.py # 3-panel multi-axis view with per-panel zoom/pan
│ └── toolbar.py # Toolbar with controls and W/L sliders
└── utils/
├── image.py # Resize utilities
├── normalization.py # Min-max, windowing, colormaps
└── theme.py # Dark/light palette, font management
tests/
├── test_dicom.py # DicomVolume unit tests (synthetic DICOM)
├── test_nifti.py # NiftiVolume unit tests (in-memory NIfTI)
├── test_normalization.py # Normalization/windowing/colormap tests
└── test_image.py # Resize utility tests
- Python 3.10 or higher
- Tk/Tcl (included with most Python installations)
Clone and install with pip:
git clone https://github.com/jpabloglez/image-viewer.git
cd image-viewer
pip install -e .Or install dependencies directly:
pip install -r requirements.txt# Open a DICOM directory
python -m viewer -d /path/to/dicom/directory
# Open a NIfTI file
python -m viewer -i /path/to/file.nii.gz
# Launch without arguments (use File menu or toolbar to open)
python -m viewer
# Enable debug logging
python -m viewer -d /path/to/dicom --log-level DEBUG# Install test dependencies
pip install -e ".[test]"
# Run tests
python -m pytest tests/ -v
# Run with coverage
python -m pytest tests/ -v --cov=viewer --cov-report=term-missing
# Lint
pip install ruff
ruff check viewer/ tests/See tests/README.md for details on test modules and data strategy.
GitHub Actions runs on every push/PR to main:
- Lint —
ruff checkon viewer and tests - Test —
pytestwith coverage on Python 3.10, 3.11, 3.12
The application follows an MVC pattern:
- Models (
ImageVolumeABC) handle file I/O and return raw numpy arrays — no Tkinter dependency - Views are Tkinter widgets that display data and emit callbacks — no model knowledge
- Controller (
ViewerController) wires models to views, manages state, and orchestrates the render pipeline:model.get_slice()→ normalize/window → colormap → PIL Image → canvas display
File loading runs on background threads with root.after() for Tkinter-safe UI updates. DICOM pixel data is cached with @lru_cache(maxsize=64).
This project is licensed under the MIT License — see the LICENSE file for details.
