Synchronize medical imaging studies between storage modalities
Project description
dicomsync
Synchronize medical imaging studies between storage modalities
- Copy and query medical imaging studies via CLI or python
- Avoids copying duplicate studies by querying
- Supports DICOM file folders, zipped studies and XNAT pre-archive
Installation
pip install dicomsync
Usage
An example of sending some dicom to some place.
TLDR;
$ dicomsync init
$ dicomsync place add dicom_root my_studies /tmp/dicom_root # add dicom place
$ dicomsync find my_studies:patient1* # show studies
$ dicomsync place add zipped_root zipped /tmp/dicom_zipped # add zipped place
$ dicomsync send my_studies:* zipped # send matching studies
$ dicomsync place add xnat_pre_archive the_xnat_project https://xnat.health-ri.nl <xnat_project_name> <xnat_username>
$ export 'XNAT_PASS=<xnat_password>' # set xnat password
Creating and querying a place
Say you have some folders containing dicom files in patient/study structure:
/tmp/dicom_root + patient1 + study1 + file1.dcm
| | | file2.dcm
| | | file3.dcm
| |
| + study2 + file1.dcm
| | | file2.dcm
| |
| + study3 + file1.dcm
| | file2.dcm
|
+ patient2 + study1 + file1.dcm
| file3.dcm
Add this location to dicomsync as a place called my_studies
$ dicomsync init
$ dicomsync place add dicom_root my_studies /tmp/dicom_root
# you can now see this as a place
$ dicomsync place list
key place
---------- --------------------------------
my_studies Root folder at '/tmp/dicom_root'
You can show all patients and studies my_studies by using the find command:
$ dicomsync find my_studies:*
found 4:
my_studies:patient2/study1
my_studies:patient1/study3
my_studies:patient1/study2
my_studies:patient1/study1
The find query format is <place>:<patient>/<study>. You can use an astrisk * as a
wildcard that matches any characters:
# All studies for patient1
$ dicomsync find my_studies:patient1*
found 3:
my_studies:patient1/study3
my_studies:patient1/study2
my_studies:patient1/study1
# only studies called study1 for any patient:
(base2) sjoerd@xps159500:/tmp$ dicomsync find my_studies:*study1
found 2:
my_studies:patient2/study1
my_studies:patient1/study1
# only a single study
$ dicomsync find my_studies:patient1/study2
found 1:
my_studies:patient1/study2
Sending studies
This follows on from the usage example above: we have a dicom_root place called
my_studies.
Now we create a second place and send some studies to it. This will be a zipped_root.
A folder that saves studies in the format /<patient>/<study>.zip
dicomsync place add zipped_root zipped /tmp/dicom_zipped
# there are now two places
dicomsync place list
key place
---------- -----------------------------------------------
my_studies Root folder at '/tmp/dicom_root'
zipped Zipped DICOM Root folder at '/tmp/dicom_zipped'
Now we send a single study my_studies:patient1/study2 to zipped using the send command:
$ dicomsync send my_studies:patient1/study2 zipped
dicomsync.cli.send INFO: Sending all studies matching StudyQuery 'my_studies:patient1/study2' to 'zipped'
dicomsync.cli.send INFO: found 1 studies matching StudyQuery 'my_studies:patient1/study2'.
dicomsync.cli.send INFO: Found '0' duplicate studies.Sending the rest: patient1/study2 (DICOMStudyFolder)
dicomsync.cli.send INFO: processing patient1/study2 (DICOMStudyFolder)
dicomsync.local INFO: Creating zip archive for /tmp/dicom_root/patient1/study2 in /tmp/dicom_zipped/patient1/study2.zip
The study has been sent. You can use '*:*' to find all studies in all places:
$ dicomsync find '*:*'
found 5:
my_studies:patient2/study1
my_studies:patient1/study3
my_studies:patient1/study2
my_studies:patient1/study1
zipped:patient1/study2
If you try to send the same study again, it will not be sent as it already exists:
$ dicomsync send my_studies:patient1/study2 zipped
2025-10-06 13:22:22 dicomsync.cli.send INFO: Sending all studies matching StudyQuery 'my_studies:patient1/study2' to 'zipped'
2025-10-06 13:22:22 dicomsync.cli.send INFO: found 1 studies matching StudyQuery 'my_studies:patient1/study2'.
2025-10-06 13:22:22 dicomsync.cli.send INFO: Found 1 duplicate studies.
2025-10-06 13:22:22 dicomsync.cli.send INFO: All studies were duplicates. Not sending anything.
You can send all studies. Duplicates will not be sent:
$ dicomsync send my_studies:* zipped
2025-10-06 13:23:42 dicomsync.cli.send INFO: Sending all studies matching StudyQuery 'my_studies:*/*' to 'zipped'
2025-10-06 13:23:42 dicomsync.cli.send INFO: found 4 studies matching StudyQuery 'my_studies:*/*'.
2025-10-06 13:23:42 dicomsync.cli.send INFO: Found '1' duplicate studies.Sending the rest: patient2/study1 (DICOMStudyFolder)
patient1/study3 (DICOMStudyFolder)
patient1/study1 (DICOMStudyFolder)
2025-10-06 13:23:42 dicomsync.cli.send INFO: processing patient2/study1 (DICOMStudyFolder)
2025-10-06 13:23:42 dicomsync.local INFO: Creating zip archive for /tmp/dicom_root/patient2/study1 in /tmp/dicom_zipped/patient2/study1.zip
2025-10-06 13:23:42 dicomsync.cli.send INFO: processing patient1/study3 (DICOMStudyFolder)
2025-10-06 13:23:42 dicomsync.local INFO: Creating zip archive for /tmp/dicom_root/patient1/study3 in /tmp/dicom_zipped/patient1/study3.zip
2025-10-06 13:23:42 dicomsync.cli.send INFO: processing patient1/study1 (DICOMStudyFolder)
2025-10-06 13:23:42 dicomsync.local INFO: Creating zip archive for /tmp/dicom_root/patient1/study1 in /tmp/dicom_zipped/patient1/study1.zip
XNAT place
To add an XNAT pre-archive as a place:
$ dicomsync place add xnat_pre_archive the_xnat_project https://xnat.health-ri.nl <xnat_project_name> <xnat_username>
# set your xnat password in environment. Note the extra space in front of the command to avoid logging the command
$ export 'XNAT_PASS=<xnat_password>'
Using dicomsync in scripts
See the /examples folder for examples
Development
To set up for development of dicomsync:
- git clone from github
- Install dependencies:
poetry install
- Add pre-commit hooks:
pre-commit install
- To check code before committing:
pre-commit run
Design notes
Choices and intentions for this library. Guideline for development.
The goal of dicomsync is to make it easier to transfer medical imaging studies between different places. Some practical examples:
- Avoiding duplicate uploads. Make it easy to do a
sendoperation multiple times (after errors) without worry. Operations should be idempotent by default and minimize work by default. - Logging. Starting and leaving a multi-day upload, coming back and knowing what happened.
The main objects are ImagingStudy and Place. dicomsync works with imaging studies
that can exists in different places. Initial places can be XNAT, Local folder,
local zipfile. Each place tends to have its own ImagingStudy subclass
First version simplifications
Rabbit holes avoided in the first version
- No universal intermediate data structures. It would be great to have a universal
dicomsync internal data structure. For ImagingStudy subtype you define
a
to_dicomsync()andfrom_dicomsync()methods. However, this is way too involved for now. We will stick withto_specific_place()methods for each individual place.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dicomsync-1.3.0.tar.gz.
File metadata
- Download URL: dicomsync-1.3.0.tar.gz
- Upload date:
- Size: 93.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
023ca6befb270199d034a41a16ac92a41c98bd8f1d4ce328f9b74699f0cdb344
|
|
| MD5 |
7d3e7b787be74b9f5ea7e2a7309945af
|
|
| BLAKE2b-256 |
992b3476c40da1015611a75f968a2854a37bbed8dae0dc3bba07c3d5ed283966
|
File details
Details for the file dicomsync-1.3.0-py3-none-any.whl.
File metadata
- Download URL: dicomsync-1.3.0-py3-none-any.whl
- Upload date:
- Size: 31.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ca992a99fcef809d72e3bf354145a33d0b839320d86c01bc8ad9bf41fd234459
|
|
| MD5 |
a371ef8026445758e95480846f0e65a6
|
|
| BLAKE2b-256 |
b2d8a460469c13ecf230096f3c76a5fafc800a420221513fe47892208608a74b
|