DICOM implementation written in Swift

Overview

DcmSwift

DcmSwift is a (partial, work in progress) DICOM implementation written in Swift. It aims to provide minimal support for the DICOM standard, focusing primarily on the DICOM file format implementation. Other aspects of the standard like networking and imaging will certainly be addressed later.

Requirements

  • MacOS 10.13
  • Xcode 12.4
  • Swift 5.3

Dependencies

  • IBM-Swift/BlueSocket (networking)
  • pointfreeco/swift-html (HTML rendering of DICOM SR)

Dependencies are managed by SPM.

Disclamer

DcmSwift is not a medical imaging nor diagnosis oriented library and is not intented to be used as such. It focuses on the computing aspects of the DICOM standard, and provide a powerful set of tools to deal with DICOM files at the atomic level. The authors of this source code cannot be held responsible for its misuses or malfunctions, as it is defined in the license below.

Overview

DcmSwift is written in Swift 5.3 and mainly rely on the Foundation core library, in order to stay as compliant as possible with most of the common Swift toolchains.

A minimal DICOM specification is embed within the DicomSpec class itself. It provide a large set of tools to manipulate UIDs, SOP Classes, VRs, Tags and more DICOM specific identifiers.

With the DicomFile class you can read/write standard DICOM files (even some broken ones!). It provides an abstract layer through the DataSet class and several tools to manipulate inner data. Such objects can be exported to several formats (raw data, XML, JSON) and translated to several Transfer Syntaxes.

The library also comes with a set of helpers to ease the manipulation of DICOM specific data type like dates, times, endianness, etc. The whole API want to stay as minimal as it is possible (despite the whole DICOM standard wildness), and still giving you a decent set of features to deal with it in a standard and secure way.

DcmSwift is widely used in the DicomiX application for macOS, which is available here. The app is mainly developed as a showcase of concepts implemented by the DcmSwift library.

Use DcmSwift in your project

DcmSwift relies on SPM so all you have to do is to declare it as a dependency of your target in your Package.swift file:

dependencies: [
    .package(name: "DcmSwift", url: "http://gitlab.dev.opale.pro/rw/DcmSwift.git", from:"0.0.1"),
]

...

.target(
    name: "YourTarget",
    dependencies: [
        "DcmSwift"
    ]

If you are using Xcode, you can add this package by repository address.

DICOM files

Read a DICOM file

Read a file:

let dicomFile = DicomFile(forPath: filepath)

Get a DICOM dataset attribute:

let patientName = dicomFile.dataset.string(forTag: "PatientName")

Write a DICOM file

Set a DICOM dataset attribute:

dicomFile.dataset.set(value:"John^Doe", forTagName: "PatientName")

Once modified, write the dataset to a file again:

dicomFile.write(atPath: newPath)

DataSet

Read dataset

You can load a DataSet object manually using DicomInputStream:

let inputStream = DicomInputStream(filePath: filepath)

do {
    if let dataset = try inputStream.readDataset() {
        // ...
    }
} catch {
    Logger.error("Error")
}

DicomInputStream can also be initialized with URL or Data object.

Create DataSet from scratch

Or you can create a totally genuine DataSet instance and start adding some element to it:

let dataset = DataSet()

dataset.set(value:"John^Doe", forTagName: "PatientName")
dataset.set(value:"12345678", forTagName: "PatientID")

print(dataset.toData().toHex())

Add an element, here a sequence, to a dataset:

dataset.add(element: DataSequence(withTag: tag, parent: nil))

DICOMDIR

Get all files indexed by a DICOMDIR file:

if let dicomDir = DicomDir(forPath: dicomDirPath) {
    print(dicomDir.index)
}

List patients indexed in the DICOMDIR:

if let dicomDir = DicomDir(forPath: dicomDirPath) {
    print(dicomDir.patients)
}

Get files indexed by a DICOMDIR file for a specific PatientID:

if let dicomDir = DicomDir(forPath: dicomDirPath) {
    if let files = dicomDir.index(forPatientID: "198726783") {
        print(files)
    }
}

DICOM SR

Load and print SR Tree:

if let dicomFile = DicomFile(forPath: dicomSRPath) {
    if let doc = dicomFile.structuredReportDocument {
        print(doc)
    }
}

Load and print SR as HTML:

if let dicomFile = DicomFile(forPath: dicomSRPath) {
    if let doc = dicomFile.structuredReportDocument {
        print(doc.html)
    }
}

Networking

DICOM ECHO

Create a calling AE, aka your local client (port is totally random and unused):

let callingAE = DicomEntity(
    title: callingAET,
    hostname: "127.0.0.1",
    port: 11112)

Create a called AE, aka the remote AE you want to connect to:

let calledAE = DicomEntity(
    title: calledAET,
    hostname: calledHostname,
    port: calledPort)

Create a DICOM client:

let client = DicomClient(
    callingAE: callingAE,
    calledAE: calledAE)

Run C-ECHO SCU service:

if client.echo() {
    print("ECHO \(calledAE) SUCCEEDED")
} else {
    print("ECHO \(callingAE) FAILED")
}

See source code of embbeded binaries for more network related examples (DcmFind, DcmStore).

Using binaries

The DcmSwift package embbed some binaries known as DcmPrint, DcmAnonymize, DcmEcho, etc. which you can build as follow:

swift build

To build release binaries:

swift build -c release

Binaries can be found in .build/release directory. For example:

.build/release/DcmPrint /my/dicom/file.dcm

Unit Tests

Before running the tests suite, you need to download test resources with this embedded script:

./test.sh

Run the command:

swift test

Documentation

Documentation can be generated using jazzy:

jazzy \
  --module DcmSwift \
  --swift-build-tool spm \
  --build-tool-arguments -Xswiftc,-swift-version,-Xswiftc,5

Or with swift doc:

swift doc generate \
    --module-name DcmSwift Sources/DcmSwift/Data \
    --minimum-access-level private \
    --output docs --format html

Side notes

For testing/debuging networking

Very useful DCMTK arguments for storescp program that show a lot of logs:

storescp 11112 --log-level trace

Another alternative is storescp program from dcm4chee (5.x), but without the precision DCMTK offers.

storescp -b [email protected]:11112

DCMTK proposes also a server, for testing cfind program:

dcmqrscp 11112 --log-level trace -c /path/to/config/dcmqrscp.cfg

All the executables from both DCMTK and dcm4chee are very good reference for testing DICOM features.

Contributors

License

MIT License

Copyright (c) 2019 - OPALE [email protected]

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

You might also like...
OAuth2 framework for macOS and iOS, written in Swift.
OAuth2 framework for macOS and iOS, written in Swift.

OAuth2 OAuth2 frameworks for macOS, iOS and tvOS written in Swift 5.0. ⤵️ Installation 🛠 Usage 🖥 Sample macOS app (with data loader examples) 📖 Tec

Replacement for Apple's Reachability re-written in Swift with closures
Replacement for Apple's Reachability re-written in Swift with closures

Reachability.swift Reachability.swift is a replacement for Apple's Reachability sample, re-written in Swift with closures. It is compatible with iOS (

A generic network layer written in swift

SwiftyNet 1.0.0 A generic network layer written in swift. you can use it as an abstraction layer above Alamofire with generic returned types. Installa

Tiny http server engine written in Swift programming language.

What is Swifter? Tiny http server engine written in Swift programming language. Branches * stable - lands on CocoaPods and others. Supports the latest

MQTT for iOS and macOS written with Swift

CocoaMQTT MQTT v3.1.1 client library for iOS/macOS/tvOS written with Swift 5 Build Build with Xcode 11.1 / Swift 5.1 Installation CocoaPods Install us

Http Request wrapper written in Swift

Net Net is a HttpRequest wrapper written in Swift Features GET, POST, PUT, DELETE method Powerful request params: nested params, number, string, dic,

A simple GCD based HTTP client and server, written in 'pure' Swift
A simple GCD based HTTP client and server, written in 'pure' Swift

SwiftyHTTP Note: I'm probably not going to update this any further - If you need a Swift networking toolset for the server side, consider: Macro.swift

DispatchSource based socket framework written in pure Swift

SwiftDSSocket Overview SwiftDSSocket is a purely swift based asynchronous socket framework built atop DispatchSource. Function signatures are pretty m

A simple HTTP server written in Swift

http4swift http4swift is a tiny HTTP server library for Nest-compatible applications. This project is unstable, and the API might be changed at anytim

Comments
  • how to draw 16 bits image color gray  ?

    how to draw 16 bits image color gray ?

    • The image on the left is drawn from the function imageFromPixels(size:pixels:width:height:) in the file DicomImage.swift
    • The image on the right is using the ImageJ tool to open it.

    You can also see the color difference between the two pictures. After I investigate on google it may be necessary to set the bitmapInfo parameter to set the correct color ? Do you have any solution for the above problem?

        if let cgim = CGImage(
            width: width,
            height: height,
            bitsPerComponent: self.bitsAllocated,//self.bitsStored,
            bitsPerPixel: self.bitsAllocated,
            bytesPerRow: self.bytesPerRow, // -> bytes not bits
            space: self.colorSpace,
            bitmapInfo: bitmapInfo,
            provider: providerRef!,
            decode: nil,
            shouldInterpolate: true,
            intent: .defaultIntent
        ) {
            return cgim
        }
    

    Data: Resources/DICOM/MR_ImplicitVRLittleEndian_MULTI_MONOCHROME2_Philips_RTNC1_33.dcm

    image

    opened by nghiaphamsg 0
  • Multivalue for WindowCenter and WindowWidth

    Multivalue for WindowCenter and WindowWidth

    The string values for WindowCenter and Width can have multivalues separated by . The string to int in DicomImage could take this into account eg if let v = self.dataset.string(forTag: "WindowWidth") { self.windowWidth = Int(v.split(separator: "\")[0]) ?? self.windowWidth } if let v = self.dataset.string(forTag: "WindowCenter") { self.windowCenter = Int(v.split(separator: "\")[0]) ?? self.windowCenter }

    opened by anoukstein 0
Releases(v0.0.1)
Owner
OPALE
Medical imaging and clinical data integrator
OPALE
Swift implementation of WalletConnect v.2 protocol for native iOS applications

Wallet Connect v.2 - Swift Swift implementation of WalletConnect v.2 protocol for native iOS applications. Requirements iOS 13 XCode 13 Swift 5 Usage

WalletConnect 142 Jan 4, 2023
A slim implementation of a websocket server using Swift and Vapor 4.0.

Swift Websocket Server Example using Vapor 4.0 This project includes a minimum working example for a websocket server written in Swift. To interact wi

Adrian Hupka 5 Sep 22, 2022
Swift implementation of libp2p, a modular & extensible networking stack

Swift LibP2P The Swift implementation of the libp2p networking stack Table of Contents Overview Disclaimer Install Usage Example API Contributing Cred

null 19 Dec 18, 2022
WebSocket implementation for use by Client and Server

WebSocket ⚠️ This module contains no networking. To create a WebSocket Server, see WebSocketServer. To create a WebSocket Client, see WebSocketClient.

Zewo Graveyard 63 Jan 29, 2022
iOS implementation of OmniEdge VPN

Overview This repository contains the open source OmniEdge Evalution Version iOS Client code. No

OmniEdge 50 Nov 22, 2022
This is a completely fresh implementation of the iCepa app.

iCepa Restart This is a completely fresh implementation of the iCepa app. It is a testbed for Network Extension experiments for advanced VPN-style app

The iCepa Project 684 Dec 12, 2022
Cross-platform JsonRPC client implementation with HTTP and WebSocket support

JsonRPC.swift Cross-platform JsonRPC client implementation with HTTP and WebSocket support Getting started Installation Package Manager Add the follow

Tesseract 5 Oct 19, 2022
Swift Express is a simple, yet unopinionated web application server written in Swift

Documentation <h5 align="right"><a href="http://demo.swiftexpress.io/">Live ?? server running Demo <img src="https://cdn0.iconfinder.com/data/icons/

Crossroad Labs 850 Dec 2, 2022
Easy to use OAuth 2 library for iOS, written in Swift.

Heimdallr Heimdallr is an OAuth 2.0 client specifically designed for easy usage. It currently supports the resource owner password credentials grant f

trivago N.V. 628 Oct 17, 2022
Network abstraction layer written in Swift.

Moya 14.0.0 A Chinese version of this document can be found here. You're a smart developer. You probably use Alamofire to abstract away access to URLS

Moya 14.4k Jan 1, 2023