Understanding DICOM, OHIF Viewer, and PACS
1. Introduction
In medical imaging, the seamless storage, management, and viewing of images are essential for accurate diagnosis and effective patient care. This document explains the roles of DICOM, OHIF Viewer, and PACS in this process and details how these components work together.
2. What is DICOM?
Definition
- DICOM (Digital Imaging and Communications in Medicine) is the global standard for managing medical imaging information. It ensures that medical images and associated data, such as patient information, are stored, transmitted, and accessed in a consistent manner across different systems and devices.
Key Features
File Format: DICOM files encapsulate both the image data (e.g., CT scans, MRIs) and metadata (e.g., patient details, imaging parameters).
Communication Protocol: DICOM also defines the protocols for transferring images across networks, allowing various medical systems to exchange and interpret the data correctly.
3. What is OHIF Viewer?
Definition
- OHIF (Open Health Imaging Foundation) Viewer is an open-source, web-based application designed to view and interact with DICOM images. It provides a user-friendly interface for healthcare professionals to analyze medical images directly from their web browsers.
Key Features
Web-Based Accessibility: OHIF Viewer can be accessed via any modern web browser, making it a flexible solution for both on-site and remote image viewing.
DICOM Compatibility: The viewer is tailored to work with DICOM files, enabling users to view, annotate, and manipulate medical images in a standard format.
Integration with PACS: OHIF Viewer can be connected to a PACS server, allowing it to fetch, display, and interact with images stored on the server.
4. What is PACS?
Definition
- PACS (Picture Archiving and Communication System) is a medical imaging technology used for the storage, retrieval, management, and distribution of DICOM images. PACS serves as the central repository in a healthcare environment, where all medical images are archived.
Key Components
Image Storage: PACS stores large volumes of DICOM images and associated metadata.
Image Retrieval: Users can search, retrieve, and view images stored within PACS using various interfaces.
Communication with OHIF: PACS communicates with viewers like OHIF through DICOMweb protocols, allowing seamless access to stored images.
Common PACS Servers
Orthanc: A lightweight, open-source PACS server that is easy to set up and integrates well with OHIF Viewer.
DCM4CHEE: An open-source PACS server widely used in large-scale environments, known for its robust features and scalability.
Conquest: A free, open-source DICOM server that is simpler but effective for smaller installations.
5. Connecting OHIF Viewer to a PACS Server
Implementation 1: Using OHIF and Orthanc by MAPDR (work fine but this is a modified image by someone but works fine when you upload data tin orthanc it shows in ohif )
Overview: This implementation demonstrates how to set up an OHIF Viewer connected to an Orthanc PACS server using Docker and Docker Compose. The steps are based on a tutorial by MAPDR, which can be found on YouTube and GitHub.
Prerequisites:
Ubuntu 20.04 Server (or similar Linux distribution)
Docker and Docker Compose installed
Steps:
Clone the GitHub Repository:
- Clone the repository that contains the necessary configurations and files for OHIF and Orthanc.
git clone https://github.com/hyper4saken/ohif-orthanc.git
- Change into the directory containing the project files.
cd ohif-orthanc
Review the Folder Structure:
The cloned folder contains:
A Python DICOM importer script.
A
docker-compose.yml
file to deploy OHIF and Orthanc.Configuration files for Nginx, OHIF, and Orthanc.
Docker Compose Setup:
The
docker-compose.yml
file defines two services:Orthanc (PACS Server): Uses the
jodogne/orthanc-plugins
Docker image. It exposes ports 8042 for web access and 4242 for DICOM communication.OHIF Viewer: Uses the
ohif/viewer
Docker image. It exposes port 3000 for web access.
version: "3"
services:
pacs:
container_name: orthanc
image: jodogne/orthanc-plugins
ports:
- 8042:8042
- 4242:4242
volumes:
- ./orthanc.json:/etc/orthanc/orthanc.json:ro
- ./orthanc_db:/var/lib/orthanc/db/
restart: always
networks:
- pacs
ohif_viewer:
container_name: ohif
image: ohif/viewer
ports:
- 3000:80
environment:
- APP_CONFIG:/usr/share/nginx/html/app-config.js
volumes:
- ./nginx_ohif.conf:/etc/nginx/conf.d/default.conf:ro
- ./ohif.js:/usr/share/nginx/html/app-config.js:ro
- ./logo.png:/usr/share/nginx/html/logo.png:ro
restart: always
networks:
- pacs
networks:
pacs:
Orthanc Configuration:
- The Orthanc configuration file
orthanc.json
includes settings for DICOM AE titles, user credentials, and other parameters. Adjust these settings to your environment and ensure strong password practices.
- The Orthanc configuration file
Deploying the Containers:
- Pull and run the Docker containers using Docker Compose:
docker-compose up -d
- This command pulls the necessary images and starts the OHIF and Orthanc containers in detached mode.
Access OHIF Viewer:
- Open a web browser and navigate to
http://server-ip:3000
(replaceserver-ip
with your server's IP address) to access the OHIF Viewer.
- Open a web browser and navigate to
Importing DICOM Files:
- You can import DICOM files directly into OHIF or send images to the Orthanc server using any DICOM-compatible software by specifying the AE Title and Port number of your Orthanc server.
Outcome: By following these steps, you’ll have an OHIF Viewer connected to an Orthanc PACS server, allowing you to view and manage medical images through a web interface.
Implementation 2: Using Orthanc with PostgreSQL via Docker (work fine but it has buit in viewer but didnt i connect to ohif )
Overview: This implementation focuses on setting up Orthanc with a PostgreSQL database using Docker, as per the instructions provided on the Orthanc official documentation. This setup persists the database, ensuring that the medical imaging data is retained across container restarts.
Prerequisites:
- Docker installed on your system
Steps:
Start the PostgreSQL Container:
- Run the official PostgreSQL Docker container with environment variables for the PostgreSQL username and password.
docker run --name some-postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=pgpassword --rm postgres
Create the Orthanc Database:
- In a separate terminal shell, create a database within the PostgreSQL container to host the Orthanc data.
docker run -it --link some-postgres:postgres --rm postgres sh -c 'echo "CREATE DATABASE orthanc;" | exec psql -h "$POSTGRES_PORT_5432_TCP_ADDR" -p "$POSTGRES_PORT_5432_TCP_PORT" -U postgres'
- Enter the password (
pgpassword
) when prompted.
Retrieve PostgreSQL Container IP and Port:
- Fetch the IP address and port of the running PostgreSQL container to configure Orthanc.
docker inspect --format '{{ .NetworkSettings.IPAddress }}' some-postgres
docker inspect --format '{{ .NetworkSettings.Ports }}' some-postgres
Get the Default Orthanc Configuration File:
- Download the default Orthanc configuration file from the Orthanc Docker image.
docker run --rm --entrypoint=cat jodogne/orthanc-plugins:1.12.4 /etc/orthanc/orthanc.json > /tmp/orthanc.json
Modify the Orthanc Configuration:
- Add a PostgreSQL section to the Orthanc configuration file (
/tmp/orthanc.json
). Adjust theHost
andPort
values based on the output from the previous step.
- Add a PostgreSQL section to the Orthanc configuration file (
"PostgreSQL" : {
"EnableIndex" : true,
"EnableStorage" : true,
"Host" : "172.17.0.38",
"Port" : 5432,
"Database" : "orthanc",
"Username" : "postgres",
"Password" : "pgpassword"
}
my config:
Start the Orthanc Container:
- Run the Orthanc container with the modified configuration file. This container will be linked to the PostgreSQL database.
docker run -p 4242:4242 -p 8042:8042 --rm -v /tmp/orthanc.json:/etc/orthanc/orthanc.json:ro j
Implementation 3 :Connecting OHIF Viewer to a PACS Server Using DCM4CHEE ( Didnt work try to upload studies but didnt show in OHIF )
Implementation Steps
Clone the DCM4CHEE Repository:
git clone https://github.com/dcm4che/dcm4chee-arc-light.git cd dcm4chee-arc-light
Modify the Docker Compose Configuration: In the
docker
directory, update thedocker-compose.yml
file as needed:version: "3" services: ldap: image: dcm4che/slapd-dcm4chee:2.6.6-32.0 logging: driver: json-file options: max-size: "10m" ports: - "389:389" environment: STORAGE_DIR: /storage/fs1 volumes: - /var/local/dcm4chee-arc/ldap:/var/lib/openldap/openldap-data - /var/local/dcm4chee-arc/slapd.d:/etc/openldap/slapd.d db: image: dcm4che/postgres-dcm4chee:16.2-32 logging: driver: json-file options: max-size: "10m" ports: - "5432:5432" environment: POSTGRES_DB: pacsdb POSTGRES_USER: pacs POSTGRES_PASSWORD: pacs volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/db:/var/lib/postgresql/data arc: image: dcm4che/dcm4chee-arc-psql:5.32.0 logging: driver: json-file options: max-size: "10m" ports: - "8080:8080" - "8443:8443" - "9990:9990" - "9993:9993" - "11112:11112" - "2762:2762" - "2575:2575" - "12575:12575" environment: POSTGRES_DB: pacsdb POSTGRES_USER: pacs POSTGRES_PASSWORD: pacs WILDFLY_CHOWN: /storage WILDFLY_WAIT_FOR: ldap:389 db:5432 depends_on: - ldap - db volumes: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro - /var/local/dcm4chee-arc/wildfly:/opt/wildfly/standalone - /var/local/dcm4chee-arc/storage:/storage
Start the DCM4CHEE Services:
docker-compose -f docker-compose.yml up -d
Clone and Run OHIF Viewer:
git clone https://github.com/OHIF/Viewers.git cd Viewers docker build -t ohif/viewer . docker run -p 3000:80 ohif/viewer
Modify OHIF Viewer Configuration: Edit
platform/viewer/public/config/servers.json
to connect to DCM4CHEE PACS:/** @type {AppTypes.Config} */ window.config = { routerBasename: '/', // whiteLabeling: {}, extensions: [], modes: [], customizationService: {}, showStudyList: true, // some windows systems have issues with more than 3 web workers maxNumberOfWebWorkers: 3, // below flag is for performance reasons, but it might not work for all servers showWarningMessageForCrossOrigin: true, showCPUFallbackMessage: true, showLoadingIndicator: true, experimentalStudyBrowserSort: false, strictZSpacingForVolumeViewport: true, groupEnabledModesFirst: true, maxNumRequests: { interaction: 100, thumbnail: 75, // Prefetch number is dependent on the http protocol. For http 2 or // above, the number of requests can be go a lot higher. prefetch: 25, }, // filterQueryParam: false, defaultDataSourceName: 'dicomweb', /* Dynamic config allows user to pass "configUrl" query string this allows to load config without recompiling application. The regex will ensure valid configuration source */ // dangerouslyUseDynamicConfig: { // enabled: true, // // regex will ensure valid configuration source and default is /.*/ which matches any character. To use this, setup your own regex to choose a specific source of configuration only. // // Example 1, to allow numbers and letters in an absolute or sub-path only. // // regex: /(0-9A-Za-z.]+)(\/[0-9A-Za-z.]+)*/ // // Example 2, to restricts to either hosptial.com or othersite.com. // // regex: /(https:\/\/hospital.com(\/[0-9A-Za-z.]+)*)|(https:\/\/othersite.com(\/[0-9A-Za-z.]+)*)/ // regex: /.*/, // }, dataSources: [ { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', sourceName: 'dicomweb', configuration: { friendlyName: 'AWS S3 Static wado server', name: 'aws', wadoUriRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb', qidoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb', wadoRoot: 'https://d33do7qe4w26qo.cloudfront.net/dicomweb', qidoSupportsIncludeField: false, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: false, supportsWildcard: true, staticWado: true, singlepart: 'bulkdata,video', // whether the data source should use retrieveBulkData to grab metadata, // and in case of relative path, what would it be relative to, options // are in the series level or study level (some servers like series some study) bulkDataURI: { enabled: true, relativeResolution: 'studies', }, omitQuotationForMultipartRequest: true, }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', sourceName: 'ohif2', configuration: { friendlyName: 'AWS S3 Static wado secondary server', name: 'aws', wadoUriRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb', qidoRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb', wadoRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb', qidoSupportsIncludeField: false, supportsReject: false, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: false, supportsWildcard: true, staticWado: true, singlepart: 'bulkdata,video', // whether the data source should use retrieveBulkData to grab metadata, // and in case of relative path, what would it be relative to, options // are in the series level or study level (some servers like series some study) bulkDataURI: { enabled: true, relativeResolution: 'studies', }, omitQuotationForMultipartRequest: true, }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', sourceName: 'ohif3', configuration: { friendlyName: 'AWS S3 Static wado secondary server', name: 'aws', wadoUriRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', qidoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', wadoRoot: 'https://d3t6nz73ql33tx.cloudfront.net/dicomweb', qidoSupportsIncludeField: false, supportsReject: false, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: false, supportsWildcard: true, staticWado: true, singlepart: 'bulkdata,video', // whether the data source should use retrieveBulkData to grab metadata, // and in case of relative path, what would it be relative to, options // are in the series level or study level (some servers like series some study) bulkDataURI: { enabled: true, relativeResolution: 'studies', }, omitQuotationForMultipartRequest: true, }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', sourceName: 'local5000', configuration: { friendlyName: 'Static WADO Local Data', name: 'DCM4CHEE', qidoRoot: 'http://localhost:5000/dicomweb', wadoRoot: 'http://localhost:5000/dicomweb', qidoSupportsIncludeField: false, supportsReject: true, supportsStow: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: false, supportsWildcard: true, staticWado: true, singlepart: 'video', bulkDataURI: { enabled: true, relativeResolution: 'studies', }, }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy', sourceName: 'dicomwebproxy', configuration: { friendlyName: 'dicomweb delegating proxy', name: 'dicomwebproxy', }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomjson', sourceName: 'dicomjson', configuration: { friendlyName: 'dicom json', name: 'json', }, }, { namespace: '@ohif/extension-default.dataSourcesModule.dicomlocal', sourceName: 'dicomlocal', configuration: { friendlyName: 'dicom local', }, }, ], httpErrorHandler: error => { // This is 429 when rejected from the public idc sandbox too often. console.warn(error.status); // Could use services manager here to bring up a dialog/modal if needed. console.warn('test, navigate to https://ohif.org/'); }, // whiteLabeling: { // /* Optional: Should return a React component to be rendered in the "Logo" section of the application's Top Navigation bar */ // createLogoComponentFn: function (React) { // return React.createElement( // 'a', // { // target: '_self', // rel: 'noopener noreferrer', // className: 'text-purple-600 line-through', // href: '/', // }, // React.createElement('img', // { // src: './assets/customLogo.svg', // className: 'w-8 h-8', // } // )) // }, // }, hotkeys: [ { commandName: 'incrementActiveViewport', label: 'Next Viewport', keys: ['right'], }, { commandName: 'decrementActiveViewport', label: 'Previous Viewport', keys: ['left'], }, { commandName: 'rotateViewportCW', label: 'Rotate Right', keys: ['r'] }, { commandName: 'rotateViewportCCW', label: 'Rotate Left', keys: ['l'] }, { commandName: 'invertViewport', label: 'Invert', keys: ['i'] }, { commandName: 'flipViewportHorizontal', label: 'Flip Horizontally', keys: ['h'], }, { commandName: 'flipViewportVertical', label: 'Flip Vertically', keys: ['v'], }, { commandName: 'scaleUpViewport', label: 'Zoom In', keys: ['+'] }, { commandName: 'scaleDownViewport', label: 'Zoom Out', keys: ['-'] }, { commandName: 'fitViewportToWindow', label: 'Zoom to Fit', keys: ['='] }, { commandName: 'resetViewport', label: 'Reset', keys: ['space'] }, { commandName: 'nextImage', label: 'Next Image', keys: ['down'] }, { commandName: 'previousImage', label: 'Previous Image', keys: ['up'] }, // { // commandName: 'previousViewportDisplaySet', // label: 'Previous Series', // keys: ['pagedown'], // }, // { // commandName: 'nextViewportDisplaySet', // label: 'Next Series', // keys: ['pageup'], // }, { commandName: 'setToolActive', commandOptions: { toolName: 'Zoom' }, label: 'Zoom', keys: ['z'], }, // ~ Window level presets { commandName: 'windowLevelPreset1', label: 'W/L Preset 1', keys: ['1'], }, { commandName: 'windowLevelPreset2', label: 'W/L Preset 2', keys: ['2'], }, { commandName: 'windowLevelPreset3', label: 'W/L Preset 3', keys: ['3'], }, { commandName: 'windowLevelPreset4', label: 'W/L Preset 4', keys: ['4'], }, { commandName: 'windowLevelPreset5', label: 'W/L Preset 5', keys: ['5'], }, { commandName: 'windowLevelPreset6', label: 'W/L Preset 6', keys: ['6'], }, { commandName: 'windowLevelPreset7', label: 'W/L Preset 7', keys: ['7'], }, { commandName: 'windowLevelPreset8', label: 'W/L Preset 8', keys: ['8'], }, { commandName: 'windowLevelPreset9', label: 'W/L Preset 9', keys: ['9'], }, ], servers: { dicomWeb: [ { name: 'DCM4CHEE', wadoUriRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/wado', qidoRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/rs', wadoRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/rs', qidoSupportsIncludeField: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: true, }, ], }, servers: { dicomWeb: [ { name: 'DCM4CHEE', wadoUriRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/wado', qidoRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/rs', wadoRoot: 'http://192.168.131.131:8080/dcm4chee-arc/aets/DCM4CHEE/rs', qidoSupportsIncludeField: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: true, }, ], }, };
Test the Connection: Open the OHIF Viewer in your browser at
http://localhost:3000
and ensure it connects to the DCM4CHEE PACS.
Subscribe to my newsletter
Read articles from Muhammad Hassan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Muhammad Hassan
Muhammad Hassan
Hey there! I'm currently working as an Associate DevOps Engineer, and I'm diving into popular DevOps tools like Azure Devops,Linux, Docker, Kubernetes,Terraform and Ansible. I'm also on the learning track with AWS certifications to amp up my cloud game. If you're into tech collaborations and exploring new horizons, let's connect!