RSNA DICOM Anonymizer
Project description
RSNA DICOM Anonymizer V17
Installation
Select the correct binary download for your platform from the available releases
Windows
- Download and extract zip to desired application directory.
- Execute Anonymizer.exe and override User Account Control to allow the program to "Run anyway".
- Logs will be written to
~/AppData/Local/Anonymizer
Mac OSX
- Download and extract zip file to desired application directory.
- Mount the disk by clicking the
Anonymizer_17.*.dmgfile where * is the relevant version. - In the finder window presented, drag the Anonymzier icon to Applications folder.
- Wait for the Application to be decompressed and copied.
- Open a terminal (
/Applications/Utilities/Terminal) - To remove extended attributes, in the terminal, execute the command:
xattr -rc /Applications/Anonymizer_17.*.app. - Double click the application icon to execute.
- Logs will be written to
~/Library/Logs/Anonymizer/anonymizer.log
Linux
- Download and extract zip file to desired application directory.
- Open terminal cd to application directory
chmod +x Anonymizer_17.*where * is the relevant version- Execute
./Anonymizer_17.* - Logs will be written to
~/Logs/Anonymizer/anonymizer.log
Documentation
Sponsors
Free code signing on Windows provided by SignPath.io, certificate by SignPath Foundation
Development
Setup
- Setup python enviroment (>3.10) which includes Tkinter, recommend using pyenv with MacOS & Linux
- Ensure python is installed with Tkinter:
python -m tkinter, a small GUI window should open - Install virtual environment & package manager:
pip install pipenv - Clone repository
- Setup virtual enviroment and install all dependencies listed in Pipfile:
pipenv install --dev
Unit Testing
For model and controller with coverage
1. Create tests/controller/.env file with your AWS_USERNAME and AWS_PASSWORD
2. pipenv shell
3. coverage run -m pytest tests
4. coverage report --omit="tests/*"
Build executables
- If building on OSX ensure create-dmg is installed:
brew install create-dmg pipenv shellcd srcpython build.py- Executable will be in
src/dist
Github Actions CI/CD
- See:
.github/workflows/build.yml - Triggered by push and pull request on master branch
- Includes executable build step using
build.py
Code Metrics
Using radon
1. pipenv shell
2. radon raw -i "tests,docs,prototyping" . > radon_results.txt
3. python src/radon_raw_totals.py
Translations
Languages for 17.1: en_US, de, es, fr
Ensure gettext is installed:
- Windows: Install instructions or
choco install gettext - Mac OSX:
brew install gettext - Linux:
sudo apt-get install gettext
Extracting messages from source files:
cd src/anonymizer/assets/locales/ ./extract_translations.sh
Updating translations:
cd src/anonymizer/assets/locales/ ./update_translations.sh
Software Architecture
Full class diagram here
Model
Two python classes pickled to files in project directory:
1. ProjectModel
./ProjectModel.pkl when project settings change
2. AnonymizerModel
./private/AnonymizerModel.pkl every 30 secs if files were stored
classDiagram
class DICOMNode {
<<model.project>>
+ ip
+ port
+ aet
+ local
}
class NetworkTimeouts {
<<model.project>>
+ tcp_connection
+ acse
+ dimse
+ network
}
class LoggingLevels {
<<model.project>>
+ anonymizer
+ pynetdicom
+ pydicom
}
class AWSCognito {
<<model.project>>
+ account_id
+ region_name
+ app_client_id
+ user_pool_id
+ identity_pool_id
+ s3_bucket
+ s3_prefix
+ username
+ password
}
class ProjectModel {
<<model.project>>
+ MODEL_VERSION
+ PRIVATE_DIR
+ PUBLIC_DIR
+ PHI_EXPORT_DIR
+ QUARANTINE_DIR
+ RSNA_ROOT_ORG_UID
+ IMPLEMENTATION_CLASS_UID
+ IMPLEMENTATION_VERSION_NAME
+ site_id
+ project_name
+ uid_root
+ storage_dir
+ modalities
+ storage_classes
+ transfer_syntaxes
+ anonymizer_script_path
+ export_to_aws
}
ProjectModel --* "1" LoggingLevels: logging_levels
ProjectModel --* "1" NetworkTimeouts: network_timeouts
ProjectModel --* "1" AWSCognito: aws_cognito
ProjectModel --* "1" DICOMNode: scu
ProjectModel --* "1" DICOMNode: scp
ProjectModel --* "*" DICOMNode: remote_scps
class Series {
<<model.anonymizer>>
+ series_uid
+ series_desc
+ modality
+ instance_count
}
class Study {
<<model.anonymizer>>
+ source
+ study_uid
+ study_date
+ anon_date_delta
+ accession_number
+ study_desc
+ target_instance_count
}
Study "1" --* "*" Series: series
class PHI{
<<model.anonymizer>>
+ patient_name
+ patient_id
+ sex
+ dob
+ ethnic_group
}
PHI "1" --* "*" Study: studies
class AnonymizerModel {
<<model.anonymizer>>
+ MODEL_VERSION
+ MAX_PATIENTS
+ default_anon_pt_id
- _version
- _site_id
- _uid_root
- _uid_prefix
- _tag_keep
- _patient_id_lookup
- _uid_lookup
- _acc_no_lookup
- _patients
- _studies
- _series
- _instances
}
AnonymizerModel "1" --* "*" PHI: _phi_lookup
Controller
1. ProjectController
Main control class, descendent of pynetdicom.ApplicationEntity handling all DICOM file and network i/o.
2. AnonymizerController
Provides API & worker threads to anonymize queued DICOM files incoming from network or file system.
classDiagram
class ProjectModel {
<<model.project>>
}
class AnonymizerModel {
<<model.anonymizer>>
}
class ApplicationEntity {
<<pynetdicom.ae>>
}
class Dataset {
<<pydicom.Dataset>>
}
class PresentationContext {
<<pynetdicom.presentation>>
}
class Event {
<<pynetdicom.events>>
}
Event "1" ..> "1" Dataset
class Association {
<<pynetdicom.association>>
}
Association "1" ..> "*" Dataset
class AnonymizerController {
<<controller.anonymizer>>
}
AnonymizerController --* AnonymizerModel
AnonymizerController --> ProjectModel
AnonymizerController "1" ..> "*" Dataset
class InstanceUIDHierarchy {
<<controller.project>>
+ uid
+ number
}
class SeriesUIDHierarchy {
<<controller.project>>
}
SeriesUIDHierarchy "1" --* "*" InstanceUIDHierarchy: instances
class StudyUIDHierarchy {
<<controller.project>>
}
StudyUIDHierarchy "1" --* "*" SeriesUIDHierarchy: series
class EchoRequest {
<<controller.project>>
}
class EchoResponse {
<<controller.project>>
}
class FindStudyRequest {
<<controller.project>>
}
class FindStudyResponse {
<<controller.project>>
}
FindStudyResponse "1" --* "1" Dataset: status
FindStudyResponse "1" --* "1" Dataset: study_result
class MoveStudiesRequest {
<<controller.project>>
}
MoveStudiesRequest "1" --* "*" StudyUIDHierarchy: studies
class ExportPatientsRequest {
<<controller.project>>
}
class ExportPatientsResponse {
<<controller.project>>
}
class ProjectController {
<<controller.project>>
}
ApplicationEntity <|-- ProjectController
ProjectController "1" --* "1" ProjectModel
ProjectController "1" --* "1" AnonymizerController
ProjectController "1" ..> "*" Dataset
ProjectController "1" ..> "*" PresentationContext
ProjectController "1" ..> "*" Event
ProjectController "1" ..> "*" Association
ProjectController "1" ..> "1" EchoRequest
ProjectController "1" ..> "1" EchoResponse
ProjectController "1" ..> "*" StudyUIDHierarchy
ProjectController "1" ..> "1" FindStudyRequest
ProjectController "1" ..> "*" FindStudyResponse
ProjectController "1" ..> "1" MoveStudiesRequest
ProjectController "1" ..> "1" ExportPatientsRequest
ProjectController "1" ..> "*" ExportPatientsResponse
View
Python standard library for GUI: Tkinter (interface to Tk toolkit written in C) enhanced using UI library CustomTkinter.
UI colors and fonts are set by ctk.ThemeManager from assets/themes/rsna_theme.json which handles appearance modes: System, Light & Dark.
1. Anonymizer
Main application class (ctk.CTk) with context sensitive menu (project open or closed)
2. WelcomeDialog
First view on fresh program start when no project open
3. HTMLView
Render html help files with simplified tag set using tkhtmlview library
4. SettingsDialog
Configures ProjectModel => DICOMNodeDialog, AWSCognitoDialog, NetworkTimeoutsDialog, ModalitiesDialog, SOPClassesDialog, TransferSyntaxesDialog, LoggingLevelsDialog
5. Dashboard
Displays project metrics and provides buttons for QueryView & ExportView
6. QueryView
Query remote scp and import studies using C-MOVE at specified level
7. ImportStudiesDialog
Display status of current C-MOVE import operation triggered from QueryView
8. ImportFilesDialog
Display status of file import operation triggered from menu File/Import Files or File/Import Directory
9. ExportView
Export anonymized studies to remote scp or AWS
classDiagram
class ProjectController {
<<controller.project>>
}
class AnonymizerController {
<<controller.anonymizer>>
}
class Tk {
<<tkinter>>
}
class CTk {
<<customtkinter>>
}
class WelcomeView {
<<view.welcome>>
}
class HTMLScrolledText {
<<tkhtmlview>>
}
class RenderHTML {
<<tkhtmlview>>
}
class HTMLView {
}
HTMLView "1" --* "1" HTMLScrolledText
HTMLView "1" --* "1" RenderHTML
class DICOMNodeDialog {
<<view.dicom_node_dialog>>
}
class AWSCognitoDialog {
<<aws_cognito_dialog>>
}
class NetworkTimeoutsDialog {
<<view.settings.network_timeouts.dialog>>
}
class ModalitiesDialog {
<<view.settings.modalities_dialog>>
}
class SOPClassesDialog {
<<view.settings.sop_classes_dialog>>
}
class TransferSyntaxesDialog {
<<view.settings.transfer_syntaxes_dialog>>
}
class LoggingLevelsDialog {
<<view.settings.logging_levels_dialog>>
}
class SettingsDialog {
<<view.settings.settings_dialog>>
}
SettingsDialog "1" ..> "*" JavaAnonymizerExportedStudy
SettingsDialog "1" --* "1" DICOMNodeDialog: local_server
SettingsDialog "1" --* "1" DICOMNodeDialog: query_server
SettingsDialog "1" --* "1" DICOMNodeDialog: export_server
SettingsDialog "1" --* "1" AWSCognitoDialog: aws_cognito
SettingsDialog "1" --* "1" ModalitiesDialog: modalities
SettingsDialog "1" --* "1" NetworkTimeoutsDialog: network_timeouts
SettingsDialog "1" --* "1" SOPClassesDialog: sop_classes
SettingsDialog "1" --* "1" TransferSyntaxesDialog: transfer_syntaxes
SettingsDialog "1" --* "1" LoggingLevelsDialog: logging_levels
class Dashboard {
<<view.dashboard>>
}
Dashboard "1" --> "1" ProjectController
class QueryView {
<<view.query_retrieve_import>>
}
QueryView "1" --> "1" ProjectController
QueryView "1" --> "1" Dashboard
class ImportStudiesDialog {
<<import_studies_dialog>>
}
ImportStudiesDialog "1" --> "1" ProjectController
class ImportFilesDialog {
<<view.import_files_dialog>>
}
ImportFilesDialog "1" --> "1" AnonymizerController
class ExportView {
<<view.export>>
}
ExportView "1" --> "1" ProjectController
ExportView "1" --> "1" Dashboard
Tk <|-- CTk
class Anonymizer {
<<anonymizer>>
}
CTk <|-- Anonymizer
Anonymizer "1" --* "1" ProjectController: controller
Anonymizer "1" --* "1" WelcomeView: welcome_view
Anonymizer "1" --* "1" QueryView: query_view
Anonymizer "1" --* "1" ExportView: export_view
Anonymizer "1" --* "1" Dashboard: dashboard
Anonymizer "1" --* "*" HTMLView: help_views
Anonymizer "1" --* "1" SettingsDialog
Anonymizer "1" --* "1" ImportStudiesDialog
Anonymizer "1" --* "1" ImportFilesDialog
Project details
Release history Release notifications | RSS feed
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 rsna_anonymizer-17.3.2.tar.gz.
File metadata
- Download URL: rsna_anonymizer-17.3.2.tar.gz
- Upload date:
- Size: 10.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
15a51f0b6e09b066ed29d6fb1d94184f0784bf91fb4ddfecaff762fbdb6a819b
|
|
| MD5 |
2bbfa047c27fcc19e230344b11285b5d
|
|
| BLAKE2b-256 |
03cdd6dc1bea29eba2fa8914261cb14da441272b061df06312c181409dc46385
|
Provenance
The following attestation bundles were made for rsna_anonymizer-17.3.2.tar.gz:
Publisher:
release.yaml on RSNA/anonymizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rsna_anonymizer-17.3.2.tar.gz -
Subject digest:
15a51f0b6e09b066ed29d6fb1d94184f0784bf91fb4ddfecaff762fbdb6a819b - Sigstore transparency entry: 164803969
- Sigstore integration time:
-
Permalink:
RSNA/anonymizer@319f89c04c32441b7110182f9772d6258b753203 -
Branch / Tag:
refs/tags/17.3.2 - Owner: https://github.com/RSNA
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@319f89c04c32441b7110182f9772d6258b753203 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rsna_anonymizer-17.3.2-py3-none-any.whl.
File metadata
- Download URL: rsna_anonymizer-17.3.2-py3-none-any.whl
- Upload date:
- Size: 10.8 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3499a1ccc93aa7107da1e3698ba3597c2e6d2ba33b451697e9a5fae0910ca801
|
|
| MD5 |
cb99db05ba717e13cbb8f2f3355b828f
|
|
| BLAKE2b-256 |
ea080e993b3312991536356a7d2199d6c95dae71da360bd7964356643ec266fa
|
Provenance
The following attestation bundles were made for rsna_anonymizer-17.3.2-py3-none-any.whl:
Publisher:
release.yaml on RSNA/anonymizer
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rsna_anonymizer-17.3.2-py3-none-any.whl -
Subject digest:
3499a1ccc93aa7107da1e3698ba3597c2e6d2ba33b451697e9a5fae0910ca801 - Sigstore transparency entry: 164803970
- Sigstore integration time:
-
Permalink:
RSNA/anonymizer@319f89c04c32441b7110182f9772d6258b753203 -
Branch / Tag:
refs/tags/17.3.2 - Owner: https://github.com/RSNA
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@319f89c04c32441b7110182f9772d6258b753203 -
Trigger Event:
push
-
Statement type: