Skip to main content

Fast temporary DICOM viewer for local and remote research workflows

Project description

dcmview

dcmview is a fast, temporary DICOM viewer for research and development work. Point it at one or more DICOM files from the command line or Python, and it starts a local browser viewer for images, tags, cine playback, and rectangular ROI annotations. Stop the process and the server is gone.

The main problem it solves is remote-server inspection. Medical imaging research often happens where the data already live: an SSH session, a shared compute server, or a locked-down institutional network. Viewing those images usually means choosing between slow notebook plots, setting up a web viewer on the server, opening firewall ports, or uploading data and annotations into a third-party cloud tool. dcmview keeps the workflow local to the machine with the files: start the viewer, forward the loopback port over SSH when needed, and inspect the study in seconds.

dcmview is intended for developer and research inspection, not clinical diagnosis.

Why use it?

  • Inspect DICOM files where they already are, including remote servers.
  • Avoid notebook-based frame rendering for multi-frame studies.
  • Keep data off third-party viewers when all you need is quick review.
  • Open a browser UI with familiar viewer tools: pan, zoom, scroll, window/level, flips, rotation, tags, and cine playback.
  • Load, edit, and export rectangular ROI annotations without modifying the source DICOM files.
  • Use the same tool from a shell command, a Python script, or a notebook.
  • Run as an ephemeral server with no database, config file, or persistent state.

Install

On supported Linux platforms, the Python package bundles the dcmview binary:

python -m pip install --user dcmview-py
dcmview --help

The package installs both dcmview and dcmview-py; dcmview is the primary command.

On macOS, use the published Homebrew tap or download a prebuilt archive from GitHub Releases.

Source builds are available for contributors and unsupported platforms:

cargo install --path .

Build prerequisites for source installs:

  • Rust stable 1.75+
  • Node.js 18+ and npm at build time
  • ssh on PATH only when using SSH forwarding helpers

Quick Start

Open one file:

dcmview ./scan.dcm

Scan a study directory recursively:

dcmview ./study_dir

Run without opening a browser, useful on a remote server:

dcmview --no-browser ./study_dir

When ready, dcmview prints a URL:

dcmview: server running at http://127.0.0.1:<port>

Press Ctrl+C to stop the server.

Remote Server Workflow

The safest default is to keep dcmview bound to loopback on the remote machine and access it through SSH port forwarding.

On the remote server:

dcmview --no-browser --port 8888 /path/to/dicom_or_study_dir

On your local machine:

ssh -L 8888:localhost:8888 user@remote-server

Then open:

http://localhost:8888

You can also let dcmview use an auto-assigned port by omitting --port; copy the printed port into your SSH command. The optional --tunnel flags are available for environments where the dcmview process can start the SSH helper itself.

The HTTP server is unauthenticated. It binds to 127.0.0.1 by default. If you bind to 0.0.0.0 or another public interface, use your own network access controls.

Python Usage

dcmview-py is a small subprocess wrapper around the Rust binary. It is useful when a script or notebook has already selected the cases to inspect.

from dcmview_py import view

# Blocking call; returns when dcmview exits.
view(["./scan.dcm"], browser=False, timeout=300)

# Non-blocking call.
handle = view(["./study_dir"], browser=False, block=False)
print(handle.url)
handle.stop()

Context manager:

from dcmview_py import view

with view(["./study_dir"], browser=False, block=False) as handle:
    print(handle.url)

The module CLI mirrors the Rust options:

python -m dcmview_py --no-browser --timeout 120 ./study_dir

Viewer Features

The embedded browser viewer includes:

  • File tabs labeled from PatientID, Modality, and StudyDate when present.
  • Canvas-based image viewing with pan, zoom, scroll, window/level, reset, horizontal/vertical flips, and 90-degree rotation.
  • Window presets including DICOM defaults, full dynamic range, and common CT presets.
  • Multi-frame controls with previous/next, cine playback, FPS selection, loop, and sweep.
  • Lazy DICOM tag browsing with filtering, sequence expansion, binary length display, resizable columns, and click-to-copy values.
  • Rectangular ROI annotation display and editing, including draw, select, move, resize, delete, frame scoping, and CSV export.

Common shortcuts:

Action Shortcut
Previous/next frame Left/Right arrows or [ / ]
Play/pause cine Space
Window/level tool W
Pan tool P
Zoom tool Z
Scroll tool S
ROI tool R
Reset viewport Double-click

Right-drag always zooms, middle-drag always pans, the wheel scrolls frames, and Ctrl/Cmd+wheel zooms.

Annotations

--annotations loads an EMBED-style ROI CSV into memory:

dcmview --annotations ./embed_annotations.csv ./study_dir

dcmview never modifies the input CSV or DICOM files. Viewer edits are kept in memory and can be downloaded with Export ROIs.

Required columns:

  • anon_dicom_path
  • ROI_coords

Optional columns:

  • num_ROI; when present, it must equal len(ROI_coords)
  • ROI_frames; when omitted or [], ROIs apply to all frames

ROI_coords is a JSON array of [ymin, xmin, ymax, xmax] boxes. ROI_frames is a JSON array of frame-index lists. JSON-valued fields must be CSV-quoted.

anon_dicom_path,num_ROI,ROI_coords,ROI_frames
/path/to/dbt_case.dcm,2,"[[120,340,220,430],[400,510,480,590]]","[[0,1,2],[5,6]]"
/path/to/ffdm_case.dcm,1,"[[80,150,190,260]]","[]"

Matching uses normalized path equality against loaded DICOM paths. Frame indices are zero-based and must be less than NumberOfFrames.

CLI Reference

dcmview [OPTIONS] <PATH> [PATH ...]
Option Default Description
<PATH>... required DICOM files or directories to inspect
-p, --port <u16> 0 Bind port; 0 auto-assigns an available port
--host <addr> 127.0.0.1 Bind address
--no-browser false Do not open the browser automatically
--timeout <seconds> none Exit after this many seconds without API/browser requests
--no-recursive false Scan only the top level of input directories
--tunnel false Start an SSH local port-forward helper
--tunnel-host <host> none SSH target used with --tunnel
--tunnel-port <u16> 0 Forwarded local port; 0 uses the server port
--annotations <csv> none Load EMBED-style ROI annotations

Examples:

dcmview ./scan.dcm
dcmview --no-recursive ./study_dir
dcmview --host 127.0.0.1 --port 8888 --no-browser ./study_dir
dcmview --timeout 300 ./study_dir
dcmview --annotations ./embed_annotations.csv ./study_dir

HTTP API

The browser UI uses a small local HTTP API. It is also useful for scripts that need the same decoded frame, tag, or annotation data while the server is running.

Static frontend assets are served at / and /assets/*.

Method Path Description
GET /api/files File registry and server metadata
GET /api/file/:index/info Frame metadata for one file
GET /api/file/:index/frame/:frame Display frame; supported image transfer syntaxes return PNG
GET /api/file/:index/frame/:frame/raw Decoded frame sample bytes for client-side rendering
GET /api/file/:index/tags Lazy DICOM tag tree
GET /api/file/:index/annotations Current in-memory ROI annotations
PUT /api/file/:index/annotations Replace in-memory ROI annotations for one file
GET /api/annotations/export.csv Download current annotations as EMBED CSV

Files

GET /api/files returns:

{
  "files": [
    {
      "index": 0,
      "path": "/path/to/scan.dcm",
      "label": "PATIENT - MG - 20240101",
      "has_pixels": true,
      "frame_count": 60,
      "rows": 3000,
      "columns": 2500,
      "transfer_syntax_uid": "1.2.840.10008.1.2.4.50",
      "default_window": { "center": 200.0, "width": 4000.0 }
    }
  ],
  "tunnelled": false,
  "tunnel_host": null,
  "server_start_ms": 1714300000000
}

GET /api/file/:index/info returns frame_count, rows, columns, transfer_syntax, has_pixels, and default_window.

Display Frames

GET /api/file/:index/frame/:frame returns image/png for supported display paths.

Query parameters:

Param Description
wc Window center; used with ww in default mode
ww Window width; used with wc in default mode
mode default or full_dynamic

Window selection:

  • default: explicit wc and ww, then DICOM Window Center/Width, then 1st/99th percentile fallback.
  • full_dynamic: true min/max of the current frame; ignores DICOM defaults and query window values.

Transfer syntax behavior:

Transfer syntax Display behavior
JPEG Baseline / Extended Decoded server-side and PNG-encoded
JPEG Lossless / Lossless SV1 Decoded server-side and PNG-encoded
JPEG 2000 lossless/lossy Decoded server-side and PNG-encoded
Implicit LE / Explicit LE / Explicit BE Windowed server-side and PNG-encoded
JPEG-LS / RLE / other 422 {"error": "unsupported transfer syntax: ..."}

Response headers include X-Cache: HIT or X-Cache: MISS.

Raw Frames

GET /api/file/:index/frame/:frame/raw returns application/octet-stream plus metadata headers. This is a decoded sample transport for the frontend, not a copy of the original DICOM Pixel Data element for compressed syntaxes.

Supported raw paths:

  • Uncompressed frames: native sample bytes, normalized to little-endian by dicom-object.
  • JPEG Baseline / Extended: decoded to 8-bit grayscale samples.
  • JPEG Lossless: decoded to 8-bit or 16-bit grayscale samples when supported by the codec stack.
  • Grayscale JPEG 2000: decoded to 8-bit or 16-bit samples.

JPEG-LS, RLE, unsupported syntaxes, and multi-component JP2 raw decoding return 422 or a decode error.

Tags and Annotations

GET /api/file/:index/tags returns an array of DICOM tag nodes. Pixel data and other binary VRs are represented by byte length, not by full values. Long numeric arrays and sequences may be truncated with truncated and total fields.

GET /api/file/:index/annotations returns:

{
  "num_roi": 2,
  "roi_coords": [[120, 340, 220, 430], [400, 510, 480, 590]],
  "roi_frames": [[0, 1, 2], [5, 6]]
}

PUT /api/file/:index/annotations replaces one file's in-memory annotations and returns the canonicalized payload. Invalid coordinates or frame mappings return 400 {"error": "..."}.

Development

Frontend:

npm --prefix frontend ci
npm --prefix frontend run dev
npm --prefix frontend run build

Backend:

cargo fmt --all
cargo fmt --all -- --check
cargo check
cargo build
cargo build --release

Tests:

cargo test

Integration tests use real DICOM fixtures and cover discovery, display-frame decoding, raw-frame transport, cache headers, tag serialization, annotations, and tunnel fallback.

Architecture summary:

  • Backend: Rust, Axum, Tokio
  • DICOM: dicom-rs, dicom-pixeldata, jpeg2k
  • Pixel pipeline: server-side display PNGs, raw sample transport for interactive rendering, LRU caches
  • Frontend: Svelte 5, Vite, TypeScript, embedded via rust-embed
  • Distribution: one executable with no runtime frontend assets

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

dcmview_py-0.1.1-py3-none-manylinux_2_28_x86_64.whl (5.2 MB view details)

Uploaded Python 3manylinux: glibc 2.28+ x86-64

dcmview_py-0.1.1-py3-none-macosx_11_0_arm64.whl (4.7 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

dcmview_py-0.1.1-py3-none-macosx_10_13_x86_64.whl (4.9 MB view details)

Uploaded Python 3macOS 10.13+ x86-64

File details

Details for the file dcmview_py-0.1.1-py3-none-manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for dcmview_py-0.1.1-py3-none-manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 4afa048ba9926807bcb7d523b969f810dd79745c801e2adcaf3b7f8f3e9edeac
MD5 5ed4ae6d06e88e3b73ee72e7c2918851
BLAKE2b-256 3d646d25022da9c701047a5b293cc180b5c3d88039cfe1ebf24e8c5c6312bd9b

See more details on using hashes here.

Provenance

The following attestation bundles were made for dcmview_py-0.1.1-py3-none-manylinux_2_28_x86_64.whl:

Publisher: release.yml on beatrice-b-m/dcmview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dcmview_py-0.1.1-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for dcmview_py-0.1.1-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 4191442ecd68e0c8e05c4f4fbb3fe1a2387857557b1f1c250ab42ae49a84fdee
MD5 73fe46e8205dac0f7b3f949ed7219dfe
BLAKE2b-256 dbd024a87b0eb6eed5231223e35bbbaa572d3faf377a3b8e0aeb66449791af4d

See more details on using hashes here.

Provenance

The following attestation bundles were made for dcmview_py-0.1.1-py3-none-macosx_11_0_arm64.whl:

Publisher: release.yml on beatrice-b-m/dcmview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file dcmview_py-0.1.1-py3-none-macosx_10_13_x86_64.whl.

File metadata

File hashes

Hashes for dcmview_py-0.1.1-py3-none-macosx_10_13_x86_64.whl
Algorithm Hash digest
SHA256 b3628de965a52006df19a670ad2298e48cabf2ef58fc8337b584f79eb8203e89
MD5 bf5e0bda9afe8fc3a1f7c5219da675c3
BLAKE2b-256 0535b87fc3c436ebe32b01bb0fb8258e699f25a2f2d31a88bebc0b1df35b9062

See more details on using hashes here.

Provenance

The following attestation bundles were made for dcmview_py-0.1.1-py3-none-macosx_10_13_x86_64.whl:

Publisher: release.yml on beatrice-b-m/dcmview

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page