A portable, self-contained DICOM/HL7 workstation for PACS administrators.
No installation required — just run PacsAdminTool.exe or PacsAdminToolWeb.exe.
Can also be run locally with Python and pip, or deployed as a Docker container.
Note: This tool was built for my own professional use. The repository is not actively maintained and issues may not be addressed.
| Tab | Functionality |
|---|---|
| Dashboard | AE connectivity health check (batch C-ECHO all presets at once), service status, recent audit activity |
| C-FIND / Q-R | Patient/Study/Series/Image level C-FIND with query builder; multi-select results with checkboxes; bulk C-MOVE or C-GET retrieve |
| C-STORE | Send single files or entire folder trees to any Storage SCP |
| DICOM Receiver | Embedded DICOM SCP — receive C-STORE and C-ECHO; auto-saves files; inspect tags or delete files per-row; auto-purges files older than 24 h |
| DMWL | Full Modality Worklist query with export to CSV |
| Storage Commit | Send N-ACTION storage commitment requests; receive async N-EVENT-REPORT responses with committed/failed breakdown |
| IOCM | Send Instance Availability Notifications (delete/change notifications) |
| Inspector & Editor | Upload any .dcm file; browse all tags in a searchable tree; inline tag editor lets you change any value, then download the modified file or send it directly via C-STORE; Diff sub-tab compares two DICOM files tag-by-tag |
| DICOM Validator | Upload any .dcm file for conformance checking; validates file meta (group 0002), core UIDs, mandatory patient/study tags, pixel data consistency, UID collisions, and retired SOP classes; findings categorised as Error / Warning / Info |
| SR Viewer | Parse any DICOM Structured Report as an interactive collapsible tree; colour-coded type/relationship badges; DCM-coded concepts link to the NEMA PS3.16 standard; filter bar |
| KOS Creator | Build a DICOM Key Object Selection document (XDS-I manifest) from existing DICOM files |
| DICOMize | Convert non-DICOM files to DICOM: PDF → Encapsulated PDF, images (JPEG/PNG/BMP/TIFF/WebP) → Secondary Capture, video (MP4/MOV) → Encapsulated Video; download or send directly to a PACS via C-STORE |
| Anonymizer | Strip PHI from one or more DICOM files using a Basic or Full profile; download result as a ZIP |
| UID Remapper | Generate fresh Study / Series / Instance UIDs for a batch of DICOM files while preserving internal referential integrity; preview the old→new UID mapping before downloading as ZIP |
| DICOMDIR | Read a DICOMDIR file and browse the Patient → Study → Series → Instance hierarchy; or generate a standards-compliant DICOMDIR from uploaded files/folder and download as ZIP |
| DICOMweb | QIDO-RS study/series/instance queries, STOW-RS multi-file upload, WADO-RS retrieve (packaged as ZIP); preset-based server configuration with Basic Auth and Bearer Token support |
| HL7 | Send/receive HL7 v2 messages over MLLP; built-in templates for ORM, ORU, ADT, SIU, OML, QBP |
| Settings | Manage local AE title/port, remote AE presets, HL7 defaults, language, log level |
| Logs | Live view of application logs from ~/.pacs_admin_tool/logs/ |
Both the desktop GUI and the web UI check GitHub Releases on startup and show a styled notification banner when a newer version is available.
Web UI — a blue slide-in banner appears below the header bar:
- Shows the new version number and a link to the release page
- On
.exebuilds: a "Download Update" button downloads the new binary in the background; once ready it becomes "Install & Restart" — the swap and restart happen automatically
Desktop GUI — a matching blue banner appears between the header and tabs:
- Same Download / Install & Restart / View Release buttons
- Fires 3 seconds after startup in a background thread; the UI is never blocked
Auto-update details:
- Works for frozen PyInstaller executables (
PacsAdminTool.exe,PacsAdminToolWeb.exe) - Windows: a detached batch script performs the swap after the process exits, then relaunches
- Linux/macOS: the binary is replaced in-place and
os.execv()restarts the process - Docker and source-code installs: only the "View Release ↗" link is shown (no auto-update)
- Uses only Python standard-library modules (
urllib,json,threading) — no extra packages required - Results are cached for 1 hour; GitHub is never contacted more than once per hour
Both the desktop GUI and the web server can show a system tray icon (notification area, bottom-right on Windows) when pystray and Pillow are installed. The app works without them, but system tray functionality will be disabled.
Desktop GUI:
- Closing the window (X button) minimises the app to the tray instead of quitting
- Double-click the tray icon or use the right-click menu to restore the window
- Right-click menu: Show Window, Open Data Folder, Exit
Web Server:
- The tray icon is the only visual indicator that the server is running (no console window)
- Right-click menu: Open in Browser, Open Data Folder, Exit
- No need to use Task Manager to stop the server — just click Exit in the tray menu
# 1. Install dependencies
pip install pydicom pynetdicom hl7
# 2. (Optional) Install system tray support
pip install pystray Pillow
# 3. Run
python main.py# 1. Install dependencies
pip install pydicom pynetdicom hl7 flask flask-socketio simple-websocket
# 2. (Optional) Install system tray support
pip install pystray Pillow
# 3. Run the web server
python webmain.py
# 4. Open in your browser
# http://localhost:5000To allow other PCs on your network to connect:
python webmain.py --host 0.0.0.0
# Others can reach it at http://<your-ip>:5000To use a different port:
python webmain.py --port 8080To enable debug mode with auto-reload:
python webmain.py --debugThe web server is available as a pre-built Docker image on the GitHub Container Registry. No Python installation required — Docker is all you need.
Copy the docker-compose.yml from this repository and run:
docker compose up -dThen open http://localhost:5000 in your browser.
Config and logs are stored in a named Docker volume (pacs_data) so they survive container restarts and upgrades.
To upgrade to a newer version:
docker compose pull
docker compose up -dservices:
pacsadmintool:
image: ghcr.io/bobvmierlo/pacsadmintool:latest
container_name: pacsadmintool
restart: unless-stopped
ports:
- "5000:5000" # Web UI — change the left number to use a different host port
# e.g. "8080:5000" to reach the UI at http://your-server:8080
# Uncomment these if you want the built-in listeners to accept traffic
# from outside the container. The left (host) port must match what you
# configure in the Settings tab inside the app.
#- "11112:11112" # DICOM Storage SCP (C-STORE / C-ECHO receiver)
#- "2575:2575" # HL7 MLLP listener
volumes:
- pacs_data:/data # Named volume — stores config.json and log files.
# Replace with a bind-mount if you prefer a specific path
# on the host, e.g.: - /opt/pacsadmin:/data
environment:
# PACS_DATA_DIR is already set to /data inside the image.
# Only override this if you change the volume mount point above.
# - PACS_DATA_DIR=/data
volumes:
pacs_data:Key settings at a glance:
| Option | What it does |
|---|---|
"5000:5000" |
Maps host port → container port. Change the left number to expose the UI on a different host port. |
"11112:11112" |
Required if you want to receive DICOM via C-STORE from external systems. Must match the SCP port in Settings. |
"2575:2575" |
Required if you want to receive HL7 messages from external systems. Must match the HL7 listener port in Settings. |
pacs_data:/data |
Named volume for persistent storage. Swap for a bind-mount (e.g. /opt/pacsadmin:/data) if you need direct host-filesystem access to your config or logs. |
restart: unless-stopped |
The container restarts automatically after a reboot or crash. Use no to disable, or always to restart even after a manual docker stop. |
docker run -d \
--name pacsadmintool \
--restart unless-stopped \
-p 5000:5000 \
-v pacs_data:/data \
ghcr.io/bobvmierlo/pacsadmintool:latest# One command:
build.batThis produces dist\PacsAdminTool.exe — a single executable with no external dependencies.
pip install -r requirements.txt
pyinstaller pacs_tool.spec --clean --noconfirmchmod +x build.sh
./build.sh
# Output: dist/PacsAdminToolSettings are saved to ~/.pacs_admin_tool/config.json automatically.
Logs are written to ~/.pacs_admin_tool/logs/ with automatic 7-day rotation.
When running in Docker the data directory is /data inside the container
(controlled by the PACS_DATA_DIR environment variable). Mount a volume there
to keep your config and logs across restarts — see the Docker section above.
Add your PACS, RIS, and modality AE entries in the Settings tab. They become available as a dropdown in every other tab.
The UI supports English and Dutch. Switch languages in the Settings tab.
- Supports Patient Root and Study Root query models
- Query levels: PATIENT, STUDY, SERIES, IMAGE
- Wildcard matching supported (e.g.
SMITH*for patient name) - Results shown in a table with checkboxes for bulk selection
- Retrieve one or more C-FIND results to a destination AE
- Destination must have your local AE registered as a known source
- Bulk mode: select multiple rows and retrieve sequentially
- Alternative to C-MOVE — pulls files directly into the application without requiring an inbound port on the destination
- Not supported by all PACS systems (optional per DICOM standard)
- See the Help tab for a full C-MOVE vs C-GET comparison
- Sends files using all common Storage SOP classes
- Recursive folder scanning supported
- Queries ModalityWorklistInformationFind SOP
- Filters: patient, date, modality, AE title, accession
- CSV export of results
- Sends N-ACTION with a list of SOP Class/Instance UID pairs
- Async N-EVENT-REPORT arrives on a separate inbound association (requires the DICOM Receiver to be running)
- Displays committed/failed counts and per-UID failure reason codes
- Sends Instance Availability Notification (N-CREATE)
- Marks instances as UNAVAILABLE for delete notifications
- Per PS3.4 Annex KK
- Runs a DICOM SCP in the background
- Accepts C-STORE (all common modalities), C-ECHO, and Storage Commitment N-EVENT-REPORT callbacks
- Saves received files to a configurable directory
- Per-file Inspect (full tag browser) and Delete actions in the Files on Disk table
- Auto-purge: files older than 24 hours are deleted on startup and nightly at 01:00 to prevent patient data from lingering
Two sub-tabs in one panel — no PACS connection required.
Inspect & Edit sub-tab:
- Upload any
.dcmfile and browse all tags in a searchable, collapsible tree - Sequence (SQ) elements expand inline; binary data shown as byte-length summary
- Click Edit Tags… to open the inline Tag Editor: change any tag value in-place, track pending changes, then Download Modified File or Send to PACS via C-STORE
Diff sub-tab:
- Select two
.dcmfiles (File A and File B) and click Compare - Results table shows every tag with its status: Changed / Only in A / Only in B / Identical
- Toggle "Show differences only" to filter out identical tags
- Colour-coded rows: yellow = changed, red-tint = only in A, green-tint = only in B
Performs a DICOM conformance check on any .dcm file without sending it to a PACS.
Check categories:
| Category | What is verified |
|---|---|
| File Meta | Group 0002 present, MediaStorageSOPClassUID matches SOPClassUID, transfer syntax present |
| Core UIDs | SOPClassUID, SOPInstanceUID, StudyInstanceUID, SeriesInstanceUID — all present, valid UID format |
| Patient module | Type 2 tags PatientName, PatientID, PatientBirthDate, PatientSex present (blank is allowed) |
| Study module | StudyDate, StudyTime, AccessionNumber, ReferringPhysicianName — presence and format |
| Pixel data | PixelData present for image SOP classes; consistency of Rows, Columns, BitsAllocated, SamplesPerPixel |
| UID collisions | Detects Study/Series/Instance UIDs that are identical (copy-paste errors) |
| SOP class | Flags retired SOP classes |
| Private tags | Reports the number of private (odd-group) tags present |
Severity levels:
- Error — non-conformant; likely to cause import/routing failures in a PACS
- Warning — technically valid but unusual; may cause interoperability issues
- Info — informational only; no action required
Requirements: Only pydicom (already bundled) — no extra packages.
Generates fresh Study / Series / Instance UIDs for a batch of DICOM files while preserving internal referential integrity.
Why you need this: Most PACS systems reject a C-STORE when the StudyInstanceUID already exists in their database. The UID Remapper creates brand-new UIDs so the study can be re-imported as a fresh study.
Remap levels:
| Level | Tags replaced |
|---|---|
| Study only | StudyInstanceUID (0020,000D) |
| Study + Series | StudyInstanceUID + SeriesInstanceUID (0020,000E) |
| Study + Series + Instance | All three + SOPInstanceUID (0008,0018) + file meta MediaStorageSOPInstanceUID |
Workflow:
- Select one or more DICOM files
- Choose a remap level (default: all UIDs)
- Optionally set a custom UID prefix (default
2.25.— UUID-based, globally unique without a registered root) - Click Preview to inspect the old→new UID mapping before committing
- Click Download Remapped ZIP to get the modified files
- C-STORE via the C-STORE tab or upload via DICOMweb STOW-RS
All files from the same original study receive the same new StudyInstanceUID, maintaining referential integrity across a multi-file set.
- Upload one or more DICOM files
- Basic profile: removes patient identifiers and institution details
- Full profile: additionally removes study/series descriptions and other potentially identifying fields
- Replacement patient name and ID can be specified
- Output downloaded as a ZIP archive
- Reader: upload a DICOMDIR file to browse the Patient → Study → Series → Instance hierarchy
- Generator: upload individual DICOM files or an entire folder; a standards-compliant DICOM File Set (DICOMDIR + organised instance files) is generated using
pydicom.FileSetand returned as a downloadable ZIP
Implements all three DICOMweb services defined in DICOM PS3.18 as an HTTP-based alternative to the traditional C-FIND / C-STORE / C-MOVE workflow.
Server configuration:
- Base URL, authentication type (None / Basic / Bearer Token), and credentials are entered once per session or loaded from a named preset
- Presets are saved in
config.jsonunderdicomweb_presetsand are available in the dropdown on every session start
Sub-tabs:
| Sub-tab | Service | What it does |
|---|---|---|
| QIDO-RS | Query based on ID for DICOM Objects | Queries studies, series, or instances using standard DICOM attributes as URL parameters; results rendered in a sortable table |
| STOW-RS | Store Over the Web | Uploads one or more local .dcm files to the server using a multipart/related; type=application/dicom POST |
| WADO-RS | Web Access to DICOM Objects | Retrieves one or more DICOM instances from the server; the server packages the multipart/related response into a .zip file for convenient download |
Authentication:
- None — plain HTTP/HTTPS, no credentials
- Basic — HTTP Basic Auth (username + password encoded as Base64)
- Bearer Token —
Authorization: Bearer <token>header (OAuth 2.0 / API keys)
Requirements: requests library (pip install requests); already included in requirements.txt and requirements-web.txt.
Parses any DICOM Structured Report and renders it as an interactive collapsible tree.
Supported SR SOP classes:
- Basic Text SR, Enhanced SR, Comprehensive SR, Comprehensive 3D SR
- X-Ray Radiation Dose SR, Patient Radiation Dose SR, Enhanced X-Ray Radiation Dose SR
- Mammography CAD SR, Chest CAD SR, Colon CAD SR
- Simplified Adult Echo SR
- Acquisition Context SR, Implantation Plan SR
- Key Object Selection (KOS)
- And any other SR SOP class (generic fallback)
Supported content item types: CONTAINER, NUM (with units), TEXT, CODE, IMAGE, UIDREF, PNAME, DATE, TIME, DATETIME, SCOORD, SCOORD3D, TCOORD, COMPOSITE, WAVEFORM
Tree viewer features:
CONTAINERitems render as collapsible sections with a ▼/▶ toggle; click to expand/collapse- Colour-coded type badges (amber for NUM, purple for CODE, green for TEXT, …)
- Relationship badges (CONTAINS, HAS OBS CONTEXT, INFERRED FROM, …)
- Concept names coded in the DCM scheme are clickable links to NEMA PS3.16 (opens in a new tab)
- Filter bar — live search across concept names and values; results shown as a flat table
- Expand all / Collapse all controls
- "View Raw DICOM Tags" button opens the full attribute list for low-level inspection
Converts non-DICOM files into valid DICOM objects using only the libraries already bundled in the tool (pydicom + Pillow — no extra packages required).
Sub-tabs:
| Sub-tab | Input | Output DICOM SOP Class | Notes |
|---|---|---|---|
| Any PDF file | Encapsulated PDF Storage (1.2.840.10008.5.1.4.1.1.104.1) |
PDF bytes stored verbatim | |
| Images | JPEG, PNG, BMP, TIFF, WebP, JFIF … | Secondary Capture Image Storage (1.2.840.10008.5.1.4.1.1.7) |
Converted to 8-bit RGB; one .dcm per image |
| Video | MP4, MOV … | Video Photographic Image Storage (1.2.840.10008.5.1.4.1.1.77.1.2.1) |
MPEG-4 AVC/H.264 transfer syntax; video stored verbatim |
Shared patient & study data card sits above the sub-tabs and is shared across all three. Fill it in once per session. Click the header to collapse it after filling.
- Study Instance UID — enter to link to an existing study, or click Generate (RFC 4122 /
2.25.…style UID) - Download DICOM — converts and triggers a browser download (
.dcmor.zipfor multiple images) - Send to PACS — converts then immediately C-STOREs to the specified remote AE; supports presets from Settings
Builds a DICOM Key Object Selection document (SOP 1.2.840.10008.5.1.4.1.1.88.59) that can be used as an XDS-I manifest when publishing a study to an IHE XDS domain.
Workflow:
- Load one or more DICOM files — the tool extracts patient, study, series, and instance metadata automatically.
- Review and edit the study/patient fields and the referenced instance list if needed.
- Choose a document title (Of Interest, For Referring Provider, XDS-I Manifest, etc.).
- Create & Save the KOS as a
.dcmfile, or Create & Send it directly via C-STORE.
Instance list format (one line per instance, # lines are comments):
# SeriesUID | SOPClassUID | SOPInstanceUID
1.2.3.4.5|1.2.840.10008.5.1.4.1.1.2|1.2.3.4.5.6.7
The generated KOS includes:
ContentSequencewith IMAGE items and observer contextCurrentRequestedProcedureEvidenceSequence(required by DICOM PS3.3 C.17.6)- Proper file meta with Explicit VR Little Endian transfer syntax
- All nine CID 7010 document titles, including
XDS-I Manifest (DCM:113500)
| Template | Description |
|---|---|
| ORM^O01 | Radiology order |
| OML^O21 | Lab order |
| ORU^R01 | Observation/report result |
| ADT^A04 | Patient registration |
| ADT^A08 | Patient update |
| ADT^A23 | Delete visit |
| SIU^S12 | Schedule appointment |
| SIU^S15 | Cancel appointment |
| QBP^Q22 | Patient demographics query (PDQ) |
All templates are editable before sending. Raw HL7 text can also be pasted directly.
Desktop version:
- Python 3.10+
- pynetdicom
- pydicom
- hl7
Optional:
- pystray (system tray icon)
- Pillow (icon rendering for tray)
- PyInstaller (build only)
Web version (additional):
- flask
- flask-socketio
- simple-websocket
- requests (DICOMweb tab)
The compiled .exe has no runtime requirements.
| Service | Default Port |
|---|---|
| Local DICOM SCP | 11112 |
| HL7 MLLP Listener | 2575 |
| Web UI | 5000 |
Configure in Settings tab or ~/.pacs_admin_tool/config.json.
The web UI bundles the following JavaScript libraries (all served locally, no CDN):
| Library | Version | Author / Project | License | Link |
|---|---|---|---|---|
| dwv (DICOM Web Viewer) | 0.33 | Yves Martelli | GPL-3.0 | github.com/ivmartel/dwv |
| Konva | 8.4.2 | Anton Lavrenov | MIT | github.com/konvajs/konva |
| JSZip | 3.10.1 | Stuart Knightley | MIT / GPL-3.0 | github.com/Stuk/jszip |
| magic-wand-tool | 1.1.7 | Ryasnoy Paul | MIT | github.com/Tamersoul/magic-wand-js |
| Socket.IO (client) | 4.7.2 | Guillermo Rauch | MIT | github.com/socketio/socket.io |
The DICOM decoder Web Worker scripts bundled under web/static/decoders/ originate from the dwv project and its upstream sources:
decoders/pdfjs/— JPEG 2000 / JPEG Baseline decoders adapted from Mozilla PDF.js (Apache-2.0)decoders/rii-mango/— JPEG Lossless decoder from rii-mango/jpeg-lossless-decoder-js (MIT)decoders/dwv/— RLE decoder from the dwv project (GPL-3.0)