Showing posts with label Dicom. Show all posts
Showing posts with label Dicom. Show all posts

Thursday, November 4, 2021

Niffler: A DICOM Framework for Machine Learning and Processing Pipelines

Niffler is an open source DICOM framework for machine learning and processing pipelines. This is an introductory presentation for Niffler.
 

Friday, June 26, 2020

Niffler: A DICOM Framework for Machine Learning Pipelines against Real-Time Radiology Images

I have been developing Niffler for quite some time. It is a DICOM network framework for machine learning pipelines. It retrieves the DICOM images from the PACS real-time and retrospectively based on the queries. It has been running for 19 months in our lab stable now, and has powered several machine learning research.

It extracts and stores metadata from the PACS in real-time, and also retrieves studies retrospectively for each specific studies on-demand.

Now it is time to make this project public for the broader scientific community. Please find it at https://github.com/Emory-HITI/Niffler. This is still an alpha release. But we are working around the clock to make it usable by anyone in the universe of radiology!

 For more details and citation, please follow our pre-print: 

Kathiravelu, Pradeeban, Ashish Sharma, Saptarshi Purkayastha, Priyanshu Sinha, Alexandre Cadrin-Chenevert, Imon Banerjee, and Judy Wawira Gichoya. Developing and Deploying Machine Learning Pipelines against Real-Time Image Streams from the PACS. arXiv preprint arXiv:2004.07965 (2020).

We will keep you updated! Please feel free to send me if you have any questions or suggestions for improvement. I am also up for collaborations, as always! :)

Tuesday, May 12, 2020

Using DCM4CHE FindSCU tool to get only certain DICOM fields

DCM4CHE is a powerful framework. It consists of a FindScu tool that we can use to query, find, and present data and metadata.

This post discusses how to find StudyInstanceUIDs, given PatientID and StudyDate, for example.

1. First, make sure the Source PACS (SRCAET) is configured to accept queries from the destination (BMIPACS) AET and port, where you are running these queries from.


2. Go to the dcm4che-5.19.0/etc/findscu folder and create an XSLT template.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>

  <xsl:template match="/NativeDicomModel">
    <xsl:text>"</xsl:text>
    <xsl:apply-templates select="DicomAttribute[@tag='0020000D']"/>
    <xsl:text>"</xsl:text>
    <xsl:text>,</xsl:text>
    <xsl:text>"</xsl:text>
    <xsl:apply-templates select="DicomAttribute[@tag='00100020']"/>
    <xsl:text>"</xsl:text>
    <xsl:text>&#xA;</xsl:text>
    
  </xsl:template>

  <xsl:template match="DicomAttribute">
    <xsl:apply-templates select="Value"/>
  </xsl:template>

  <xsl:template match="Value">
    <xsl:if test="@number != 1">\</xsl:if>
    <xsl:value-of select="text()"/>
  </xsl:template>

</xsl:stylesheet> 
  
Please note that above DicomAttribute[@tag='0020000D'] indicates StudyInstanceUID.

3. Save it (we save it as stid.csv.xsl.

4. Now, go to dcm4che-5.19.0/bin and run the below:


./findscu -c SRCAET@XXX.XXX.XXX.XXX:PPP -b BMIPACS:4242 -m PatientID=AAAA -m StudyDate=20180423 -r StudyInstanceUID -x ../etc/findscu/stid.csv.xsl --out-cat --out-file test.csv --out-dir .

In the above query, make sure to replace:
SRCAET with the relevant AE Title of the PACS host,
XXX.XXX.XXX.XXX with its relevant host IP address,
and PPP with its port.
Similarly, BMIPACS:4242 with the respective local AET and port.
 
Replace AAAA with the correct PatientID and StudyDate with an actual study date.

out-dir indicates the output directory. We are storing it to the present directory in this example.

Now, run it.

5. The output will return the StudyInstanceUID and PatientID in test1.csv as below in the specified output directory:
"xxx.xxx.xxx.xxx.xxx.xxx", "AAAA"

Wednesday, April 15, 2020

C-MOV with DCM4CHE

The correct DCM4CHE Commands for the query-based retrieval at study level, with using only an AccessionNumber.

#1) Start the DCM4CHE StoreScp. The storescp process starts a simple DICOM listener.

./storescp --accept-unknown --directory /storage_location_in_the_filesystem  --filepath {00100020}/{0020000D}/{0020000E}/{00080018}.dcm  -b BMIPACS:4244

BMIPACS is the storage AE Title locally, which listens to port 4244. You can set this to be any available port. Once started, this StoreScp accepts any incomping DICOM messages.

#2) Run FindScu to find the StudyInstanceUID from the AccessionNumber, since this extraction is in study level.

./findscu -c  SRC_AET@XX.XX.XX.XX:PPP -b BMIPACS:4244 -m AccessionNumber=YYYY -r StudyInstanceUID

SRC_AET is the source AE title. XX.XX.XX.XX represents the source's IP address and PPP represents its port. We find the StudyInstanceUID of the Accessions with AccessionNumber YYYY in this command.

From the output, we get the StudyInstanceUID from the below line.
....
(0020,000D) UI [ZZZZ] StudyInstance
....

#3) Run MoveScu to get the images using the StudyInstanceUID (retrieved in #2) and AccessionNumber. 

./movescu -c  SRC_AET@XX.XX.XX.XX:PPP -b BMIPACS:4244 -m AccessionNumber=YYYY -m StudyInstanceUID=ZZZZ --dest BMIPACS


Retrieved images can also be moved between servers or box, using rclone.

$ rclone copy local_src box:dest_in_box --local-no-check-updated

or with nohup,

$ nohup rclone copy local_src box:dest_in_box --local-no-check-updated &

Wednesday, March 25, 2020

PNG conversion for jpeg compressed DICOM images

Due to an existing bug in pydicom, the png conversion of (tested with jpeg lossless compression) compressed dcm images were failing.

A few steps to fix the issue (tested on Mac).


2. Close and open your terminal.

3. pip install pydicom image numpy pandas pypng

4. conda install -c conda-forge -y gdcm

5. Now run your python script again and see it works.

Thursday, December 5, 2019

Running Kheops on a Mac OS host

This post discusses how to run Kheops from the on Mac OS, with Docker.

$ git clone git@github.com:OsiriX-Foundation/kheopsDocker.git

$ cd kheopsDocker/

$ sh script_run_docker.sh

Install newman

$ sudo npm install -g newman

/usr/local/bin/newman -> /usr/local/lib/node_modules/newman/bin/newman.js
+ newman@4.5.6
added 160 packages from 196 contributors in 5.481s


   ╭────────────────────────────────────────────────────────────────╮
   │                                                                │
   │       New minor version of npm available! 6.9.0 → 6.13.2       │
   │   Changelog: https://github.com/npm/cli/releases/tag/v6.13.2   │
   │               Run npm install -g npm to update!                │
   │                                                                │
   ╰────────────────────────────────────────────────────────────────╯



Replace line 10 in script_run_docker.sh

with

newman run postman/kheops-Get_tokens.json -e postman/kheops_localhost.postman_environment.json -g postman/Workspace.postman_globals.json --export-globals postman/Workspace.postman_globals.json

Save the file.

If you are using Docker for Mac, you may encounter the error listed in https://github.com/docker/for-mac/issues/2396

Creating pacsldap ... error
Creating db       ...

ERROR: for pacsldap  Cannot start service ldap: b'Mounts denied: \r\nThe paths /etc/localtime and /usr/share/zoneinfo/Europe/Zurich\r\nare not shared from OS X and are not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\Creating db       ... done

ERROR: for ldap  Cannot start service ldap: b'Mounts denied: \r\nThe paths /etc/localtime and /usr/share/zoneinfo/Europe/Zurich\r\nare not shared from OS X and are not known to Docker.\r\nYou can configure shared paths from Docker -> Preferences... -> File Sharing.\r\nSee https://docs.docker.com/docker-for-mac/osxfs/#namespaces for more info.\r\n.'
ERROR: Encountered errors while bringing up the project.

As a work-around, remove all the occurrences of the below line from kheops/docker-compose.yml.
      - /etc/localtime:/etc/localtime:ro

$ sudo mkdir /dcm4chee-arc

$ sudo chmod 777 /dcm4chee-arc

Share /usr/share/zoneinfo/Europe/ and /dcm4chee-arc from the Preferences of Docker Desktop in your Mac host machine.

Save the file and then re-run

$ sh script_run_docker.sh

$ sh script_run_docker.sh
Stopping db       ... done
Stopping pacsldap ... done
Removing pacsarc  ... done
Removing db       ... done
Removing pacsldap ... done
Removing network kheops_default
Removing volume kheops_dcm4chee-ldap-ldap
Removing volume kheops_dcm4chee-ldap-slapdd
Removing volume kheops_dcm4chee-db-data
Removing volume kheops_dcm4chee-arc-wildfly
Removing volume kheops_kheops-db-data
Creating network "kheops_default" with the default driver
Creating volume "kheops_dcm4chee-ldap-ldap" with default driver
Creating volume "kheops_dcm4chee-ldap-slapdd" with default driver
Creating volume "kheops_dcm4chee-db-data" with default driver
Creating volume "kheops_dcm4chee-arc-wildfly" with default driver
Creating volume "kheops_kheops-db-data" with default driver
Creating db       ... done
Creating pacsldap ... done
Creating pacsarc  ... done
Creating kheopszipper           ... done
Creating kheopsauthorization    ... done
Creating pacsauthorizationproxy ... done
Creating kheopsui               ... done
Creating kheopsdicomwebproxy    ... done
Creating kheopsreverseproxy     ... done
CONTAINER ID        IMAGE                                              COMMAND                  CREATED             STATUS              PORTS                                                                                                                                              NAMES
d5caa14fac54        osirixfoundation/kheops-reverse-proxy:ci-test      "./etc/nginx/conf.d/…"   47 seconds ago      Up 45 seconds       0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp, 0.0.0.0:8042->8042/tcp, 0.0.0.0:8048->8048/tcp                                                           kheopsreverseproxy
67552e9c9469        osirixfoundation/kheops-dicomweb-proxy:master      "./replaceSecretsAnd…"   48 seconds ago      Up 46 seconds       0.0.0.0:8049->8080/tcp                                                                                                                             kheopsdicomwebproxy
92710a77dd25        osirixfoundation/kheops-ui:master                  "./docker-entrypoint…"   48 seconds ago      Up 46 seconds       80/tcp, 3000/tcp                                                                                                                                   kheopsui
c9fa33eb693d        osirixfoundation/kheops-authorization:dev          "./replaceSecretsAnd…"   50 seconds ago      Up 47 seconds       8080/tcp                                                                                                                                           kheopsauthorization
0f267500942f        osirixfoundation/kheops-zipper:master              "./replaceSecretsAnd…"   50 seconds ago      Up 48 seconds       8080/tcp                                                                                                                                           kheopszipper
c95c6e4380c6        osirixfoundation/pacs-authorization-proxy:master   "/bin/sh -c '. $NGIN…"   50 seconds ago      Up 48 seconds       0.0.0.0:82->80/tcp                                                                                                                                 pacsauthorizationproxy
89c52e69534a        osirixfoundation/kheops-dcm4chee-arc-psql:latest   "/script_entrypoint.…"   51 seconds ago      Up 49 seconds       0.0.0.0:2575->2575/tcp, 0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp, 0.0.0.0:8787->8787/tcp, 0.0.0.0:9990->9990/tcp, 0.0.0.0:11112->11112/tcp   pacsarc
78bf11251bf7        dcm4che/slapd-dcm4chee:2.4.44-15.0                 "/docker-entrypoint.…"   52 seconds ago      Up 50 seconds       389/tcp                                                                                                                                            pacsldap
f4628a1a1c44        osirixfoundation/kheops-database:demo              "verify-secret-env-v…"   52 seconds ago      Up 50 seconds       5432/tcp                                                                                                                                           db
newman

kheops-Get_tokens

→ well-known
  GET https://keycloak.kheops.online/auth/realms/travis/.well-known/openid-configuration [200 OK, 2.62KB, 657ms]

→ get token user 1 (titi)
  POST https://keycloak.kheops.online/auth/realms/travis/protocol/openid-connect/token [200 OK, 2.01KB, 325ms]

→ get token user 2 (toto)
  POST https://keycloak.kheops.online/auth/realms/travis/protocol/openid-connect/token [200 OK, 2.01KB, 314ms]

→ get token user 3 (tata)
  POST https://keycloak.kheops.online/auth/realms/travis/protocol/openid-connect/token [200 OK, 2.01KB, 332ms]

┌─────────────────────────┬─────────────────────┬────────────────────┐
│                         │            executed │             failed │
├─────────────────────────┼─────────────────────┼────────────────────┤
│              iterations │                   1 │                  0 │
├─────────────────────────┼─────────────────────┼────────────────────┤
│                requests │                   4 │                  0 │
├─────────────────────────┼─────────────────────┼────────────────────┤
│            test-scripts │                   4 │                  0 │
├─────────────────────────┼─────────────────────┼────────────────────┤
│      prerequest-scripts │                   0 │                  0 │
├─────────────────────────┼─────────────────────┼────────────────────┤
│              assertions │                   0 │                  0 │
├─────────────────────────┴─────────────────────┴────────────────────┤
│ total run duration: 1790ms                                         │
├────────────────────────────────────────────────────────────────────┤
│ total data received: 7.53KB (approx)                               │
├────────────────────────────────────────────────────────────────────┤
│ average response time: 407ms [min: 314ms, max: 657ms, s.d.: 144ms] │
└────────────────────────────────────────────────────────────────────┘

Then go to http://localhost:8042 from your favorite browser.

Register yourself when it redirects to a page such as https://keycloak.kheops.online/auth/realms/travis/protocol/openid-connect/auth?client_id=loginConnect&redirect_uri=.........

It will take you back to http://localhost:8042/inbox after the registration. Now Kheops is ready to use!
Kheops Web UI

I can even tweet from the Kheops Web UI with a link to the created album!
"My KHEOPS shared album. http://localhost/view/ttuArLPrFv834JQ88KaQGY #KHEOPS"
Apparently, the link is broken. It should be: http://localhost:8042/view/ttuArLPrFv834JQ88KaQGY
But I see the parallels with the concept of Óbidos (MEDIator) replicasets in the given UUID ttuArLPrFv834JQ88KaQGY.
One can also create a sharing URL with upload/edit/delete capabilities (again, the port is not correctly used. But that is a front-end bug they can fix. For our purposes, we just need to add the port in the shared URL).

I can also view the images that I uploaded from OHIF viewer directly, by clicking that link. It uses their cloud hosted instance of the OHIF viewer to view the image stored locally, as can be seen from the URL.







We also observe the Docker containers initialized by this integrated project are - 1) db, 2) pacsldap, 3) pacsarc, 4) pacsauthorizationproxy, 5) kheopszipper, 6) kheopsauthorization, 7) kheopsdicomwebproxy, 8) kheopsui, and 9) kheopsreverseproxy. This system uses the managed Keycloak instance in the cloud for its authorization.

Wednesday, October 2, 2019

Running a PixelMed DICOM Server

PixelMed has a reference Java implementation of the DICOM specifications. It is quite easy to set up and run PixelMed. This blog post summarizes the steps to get it run quickly.

First, download the source, binary, and dependencies of PixelMed DICOM server. At the time of writing, the latest version can be found here.

$ mkdir pixelmed-src

$ cd pixelmed-src

$ wget http://www.dclunie.com/pixelmed/software/20190626_current/pixelmedjavadicom_sourcerelease.20190626.tar.bz2

$ tar -xvf pixelmedjavadicom_sourcerelease.20190626.tar.bz2


Extract the binary.

$ mkdir ../pixelmed

$ cd ../pixelmed

$ wget http://www.dclunie.com/pixelmed/software/20190626_current/pixelmedjavadicom_binaryrelease.20190626.tar.bz2

$ tar -xvf pixelmedjavadicom_binaryrelease.20190626.tar.bz2

Extract the source and copy testserver.properties from com.pixelmed.server package to the root folder of the binary. The package also has the core class of the DICOM Server.

Adopt the testserver.properties for your needs.

# Where to store the database support files
Application.DatabaseFileName=/tmp/testserverdb

# Where to store the images stored in the database
Application.SavedImagesFolderName=/tmp/testserverimages

# Name to use for external TCP access to database (such a server will not be started if this property is absent)
Application.DatabaseServerName=testserverdb

# The root URL for the WebServer - if missing will default to the fully qualified local host name (which may not always be available, or appropriate for NAT'd external connections from outside the firewall)
WebServer.RootURL=http://192.168.14.45:7091/


# The name of the stylesheet for the WebServer
WebServer.StylesheetPath=stylesheet.css


# The name of the request type to use for displaying instances for the WebServer
# One of INSTANCELIST, IMAGEDISPLAY, or APPLETDISPLAY
WebServer.RequestTypeToUseForInstances=INSTANCELIST


# WebServer.ListeningPort should be set to whatever port the web server listens
# on for incoming connections.
WebServer.ListeningPort=7091


# WebServer.NumberOfWorkers should be set to a sufficient number to manage simulataneous incoming connections
WebServer.NumberOfWorkers=5


# Dicom.StorageSCUCompressionLevel determines what types of compressed Transfer Syntaxes are
# proposed:
#    0 = none
#    1 = propose deflate
#    2 = propose deflate and bzip2 (if bzip2 codec is available)
Dicom.StorageSCUCompressionLevel=0


# Dicom.ListeningPort should be set to whatever port this DicomImageViewer application is to
# listen on to accept incoming associations.
Dicom.ListeningPort=4008

# Dicom.CalledAETitle should be set to whatever this DicomImageViewer application is to
# call itself when accepting an association.
Dicom.CalledAETitle=BMIPACS


# Dicom.CallingAETitle should be set to whatever this DicomImageViewer application is to
# call itself when initiating an association.
Dicom.CallingAETitle=BMIPACS

# Dicom.PrimaryDeviceType should be set to the type of device, e.g., WSD, ARCHIVE.
Dicom.PrimaryDeviceType=ARCHIVE


# Dicom.RemoteAEs is a space or comma separated list of all the available remote AEs;
# each AE may be named anything unique (in this file) without a space or comma; the name
# does not need to be the same as the actual AE title.
#
#Dicom.RemoteAEs=osirix helgray
#
# Each remote AE (listed in Dicom.RemoteAEs) needs to be described by three
# properties:
# Dicom.RemoteAEs.XXXXX.CalledAETitle
# Dicom.RemoteAEs.XXXXX.HostNameOrIPAddress
# Dicom.RemoteAEs.XXXXX.Port
#
# where XXXXX is the name of the AE displayed to the user and used in this file
#

Extract the dependencies and copy the lib folder to the root folder of the binary.



$ mkdir ../pixelmed-dep

$ cd ../pixelmed-dep

$ wget http://www.dclunie.com/pixelmed/software/20190626_current/pixelmedjavadicom_dependencyrelease.20190626.tar.bz2

$ tar -xvf pixelmedjavadicom_dependencyrelease.20190626.tar.bz2

$ cp -r lib/ ../pixelmed
 

$ cd ../pixelmed

$ java -server -Djava.awt.headless=true -Xms128m -Xmx512m -cp ./pixelmed.jar:./lib/additional/hsqldb.jar:./lib/additional/commons-compress-1.12.jar:./lib/additional/vecmath1.2-1.14.jar:./lib/additional/commons-codec-1.3.jar:./lib/additional/jmdns.jar:./lib/additional/aiviewer.jar com.pixelmed.server.DicomAndWebStorageServer testserver.properties

[main] INFO com.pixelmed.server.DicomAndWebStorageServer properties=
{Dicom.PrimaryDeviceType=ARCHIVE, Application.SavedImagesFolderName=tmp, Dicom.CallingAETitle=STORESCP, Dicom.CalledAETitle=STORESCP, Dicom.ListeningPort=11112}

Now, you may see the web view of the PixelMed DICOM viewer from http://192.168.14.45:7091 from your browser.

Tuesday, October 23, 2018

Installing Standalone Viewer of OHIF


You can install an OHIF Simple Viewer by following the below steps:

1) Install Meteor
$ curl https://install.meteor.com/ | sh


2) Clone the Viewers repository
$ git clone https://github.com/OHIF/Viewers.git

 
3) Go to the SimpleViewer directory
$ cd Viewers/StandaloneViewer/StandaloneViewer/


4) Run the Standalone viewer
$ METEOR_PACKAGE_DIRS="../../Packages" meteor --settings ../../config/dcm4cheeDIMSE.json

Make sure that your JSON file consisting of the URLs of DICOM images (i.e., series instances) is placed inside the directory Viewers/StandaloneViewer/StandaloneViewer/public
as in
Viewers/StandaloneViewer/StandaloneViewer/public/big.json
 
5) Access the viewer from the browser.
You may access it from localhost:3000 followed by the url of a JSON file consisting of Dicom images.
http://localhost:3000/?url=https://raw.githubusercontent.com/OHIF/Viewers/master/StandaloneViewer/StandaloneViewer/private/testData/PTCTStudy.json
or access your local json files consisting of the DICOM instances, as in,
http://localhost:3000/?url=big.json