dicom-web-query

Query and retrieve DICOM objects via DICOMweb REST API. Also use when the user needs to work with DICOMweb servers, retrieve imaging studies via REST, or perform web-based DICOM operations. For traditional DICOM queries, see pacs-workflow.

DICOMweb Query

You are a DICOMweb expert. Your role is to help users interact with DICOMweb-enabled servers for imaging data retrieval.

DICOMweb Overview

RESTful DICOM Services

ServiceMethodDescription
QIDO-RSGETQuery DICOM images (Query-based ID Retrieve)
WADO-RSGETRetrieve DICOM objects (Web Access to DICOM)
STOW-RSPOSTStore DICOM objects (Store Over the Web)
WADO-URIGETRetrieve via URI (legacy)

Base URL Structure

https://pacs.example.com/dicomweb

QIDO-RS (Query)

Study Search

import requests

BASE_URL = "https://pacs.example.com/dicomweb"

def qido_studies(filters=None, include_fields=None):
    """
    Query for studies using QIDO-RS.
    
    Args:
        filters: Dict of DICOM tags to filter
        include_fields: Specific tags to return
    """
    params = {}
    
    if filters:
        for key, value in filters.items():
            params[f"includefield={key}"] = value
    
    response = requests.get(
        f"{BASE_URL}/studies",
        params=params
    )
    
    return response.json()

Common Query Parameters

ParameterDICOM TagDescription
00100010PatientNamePatient name
00100020PatientIDPatient ID
00080020StudyDateStudy date (YYYYMMDD)
00080030StudyTimeStudy time
00080050AccessionNumberAccession number
00080060ModalityImaging modality
00081030StudyDescriptionStudy description
00200010StudyInstanceUIDStudy UID

Query by Patient

def find_studies_by_patient(patient_id):
    """Find all studies for a patient."""
    params = {
        "PatientID": patient_id,
        "includefield": "00080020,00080030,00080060"
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

Query by Date Range

def find_studies_by_date(start_date, end_date, modality=None):
    """Find studies within date range."""
    params = {
        "StudyDate": f"{start_date}-{end_date}"
    }
    
    if modality:
        params["Modality"] = modality
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

Query by Modality

def find_ct_studies(limit=100):
    """Find CT studies."""
    params = {
        "Modality": "CT",
        "limit": limit
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    return response.json()

WADO-RS (Retrieve)

Retrieve Study

def retrieve_study(study_uid, format="application/dicom+json"):
    """
    Retrieve study metadata.
    
    Args:
        study_uid: Study Instance UID
        format: Response format
    """
    headers = {"Accept": format}
    
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}",
        headers=headers
    )
    
    return response.json()

Retrieve as DICOM (ZIP)

def download_study_dicom(study_uid, output_path=None):
    """Download complete study as DICOM ZIP."""
    headers = {"Accept": "application/zip"}
    
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/archive",
        headers=headers,
        stream=True
    )
    
    if output_path:
        with open(output_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                f.write(chunk)
    
    return response.content

Retrieve Series

def retrieve_series(study_uid, series_uid):
    """Retrieve specific series."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Retrieve Single Instance

def retrieve_instance(study_uid, series_uid, instance_uid):
    """Retrieve single DICOM instance metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Retrieve Pixel Data

def retrieve_image_pixels(study_uid, series_uid, instance_uid, frame=1):
    """
    Retrieve image pixel data.
    
    Args:
        study_uid: Study Instance UID
        series_uid: Series Instance UID
        instance_uid: SOP Instance UID
        frame: Frame number (1-indexed for multi-frame)
    """
    url = f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/frames/{frame}"
    
    response = requests.get(
        url,
        headers={"Accept": "image/jpeg"}
    )
    
    return response.content  # JPEG image data

WADO-RS Metadata

Study Metadata

def get_study_metadata(study_uid):
    """Get complete study metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Series Metadata

def get_series_metadata(study_uid, series_uid):
    """Get series metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Instance Metadata

def get_instance_metadata(study_uid, series_uid, instance_uid):
    """Get single instance metadata."""
    response = requests.get(
        f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/instances/{instance_uid}/metadata",
        headers={"Accept": "application/dicom+json"}
    )
    return response.json()

Thumbnail Retrieval

def get_thumbnail(study_uid, series_uid=None):
    """
    Retrieve study or series thumbnail.
    
    Args:
        study_uid: Study Instance UID
        series_uid: Optional series UID
    """
    if series_uid:
        url = f"{BASE_URL}/studies/{study_uid}/series/{series_uid}/thumbnail"
    else:
        # Get first series thumbnail
        url = f"{BASE_URL}/studies/{study_uid}/thumbnail"
    
    response = requests.get(
        url,
        headers={"Accept": "image/jpeg"}
    )
    
    return response.content

STOW-RS (Store)

Store DICOM File

def store_dicom(file_path, study_uid=None):
    """
    Store DICOM file to server.
    
    Args:
        file_path: Path to DICOM file
        study_uid: Optional existing study UID to add to
    """
    with open(file_path, "rb") as f:
        data = f.read()
    
    headers = {
        "Content-Type": "application/dicom",
        "Accept": "application/dicom+json"
    }
    
    response = requests.post(
        f"{BASE_URL}/studies{'/' + study_uid if study_uid else ''}/instances",
        headers=headers,
        data=data
    )
    
    return response.json()

Pagination

Limit Results

def qido_with_pagination(filters, limit=100, offset=0):
    """Query with pagination."""
    params = {
        "limit": limit,
        "offset": offset,
        **filters
    }
    
    response = requests.get(f"{BASE_URL}/studies", params=params)
    
    # Check for more results
    total = response.headers.get("X-Total-Count", "unknown")
    
    return {
        "results": response.json(),
        "total": total,
        "has_more": (offset + limit) < int(total) if total.isdigit() else True
    }

Common Patterns

Find and Download Study

def find_and_download(patient_id, output_dir):
    """Find patient's latest study and download."""
    # Find studies
    studies = find_studies_by_patient(patient_id)
    
    if not studies:
        return None
    
    # Get most recent
    latest = studies[0]
    study_uid = latest["0020000D"]["Value"][0]
    
    # Download
    download_path = f"{output_dir}/{study_uid}.zip"
    download_study_dicom(study_uid, download_path)
    
    return download_path

Bulk Retrieve by Date

def download_studies_by_date(start_date, end_date, modality, output_dir):
    """Download all studies for date range."""
    studies = find_studies_by_date(start_date, end_date, modality)
    
    downloaded = []
    for study in studies:
        study_uid = study["0020000D"]["Value"][0]
        try:
            path = f"{output_dir}/{study_uid}.zip"
            download_study_dicom(study_uid, path)
            downloaded.append(path)
        except Exception as e:
            print(f"Failed to download {study_uid}: {e}")
    
    return downloaded

Error Handling

ErrorCauseSolution
404 Not FoundStudy/series doesn't existVerify UID
401 UnauthorizedAuth requiredAdd credentials
403 ForbiddenInsufficient permissionsCheck user roles
500 Server ErrorServer issueRetry later

Authentication

Basic Auth

from requests.auth import HTTPBasicAuth

def authenticated_request(url, auth):
    """Make authenticated request."""
    response = requests.get(
        url,
        auth=HTTPBasicAuth(auth["username"], auth["password"])
    )
    return response

Bearer Token

def token_auth_request(url, token):
    """Make request with bearer token."""
    headers = {"Authorization": f"Bearer {token}"}
    response = requests.get(url, headers=headers)
    return response

Related Skills

  • pacs-workflow: For PACS-specific operations
  • filesystem-imaging: For local file handling
  • radiology-context: For configuration

Examples

Example 1: Query and Download CT Study

# Find CT studies from last week
studies = find_studies_by_date("20260325", "20260403", "CT")

# Download first result
if studies:
    study_uid = studies[0]["0020000D"]["Value"][0]
    download_study_dicom(study_uid, "ct_study.zip")

Example 2: Get Study Metadata

metadata = get_study_metadata("1.2.840.12345.67890")
for item in metadata:
    print(f"{item['0020000D']['vr']}: {item['00080018']['Value']}")

Example 3: Retrieve Image for Viewing

# Get thumbnail
thumb = get_thumbnail("1.2.840.12345.67890")

# Save as JPEG
with open("thumbnail.jpg", "wb") as f:
    f.write(thumb)