Skip to content

Commit

Permalink
Dev 0.5.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cbedetti committed May 2, 2017
1 parent e319f22 commit 2e6aa8a
Show file tree
Hide file tree
Showing 13 changed files with 973 additions and 329 deletions.
699 changes: 678 additions & 21 deletions LICENSE.txt

Large diffs are not rendered by default.

49 changes: 12 additions & 37 deletions README.md
Expand Up @@ -4,65 +4,40 @@ Dcm2Bids helps you to convert DICOM files of a study to [Brain Imaging Data Stru

Learn more about BIDS and read the [specifications][bids-spec].

# Install
## Install

```
git clone https://github.com/cbedetti/Dcm2Bids
```

Add the installation directory to your PYTHONPATH and the `scripts` directory to your PATH.

### Software dependencies
#### Software dependencies

- [dcm2niix][dcm2niix-github] with `dcm2niibatch` compiled
- dcm2niix

### Python dependencies
DICOM to NIfTI conversion is done with `dcm2niix` converter. See their [github][dcm2niix-github] for source or [NITRC][dcm2niix-nitrc] for compiled versions.

The file `environment.yml` contains the python dependencies. It's possible to create a `dcm2bids` python environment with [conda][conda].
## Usage

```
conda config --add channels conda-forge
conda env create -f environment.yml
dcm2bids [-h] -d DICOM_DIR -p PARTICIPANT [-s SESSION] -c CONFIG
[--dry_dcm2niix] [-y]
```

# Usage

```
dcm2bids [-h] -o BIDS_DIR -d DICOM_DIR -p PARTICIPANT [-s SESSION] -c CONFIG [-n]
```

You need to build the config file of your study to let `dcm2bids` associate your acquisitions with the right dicoms through dicom header fields. Every study is different and this step needs a little bit of work.
You need to build the config file of your study to let `dcm2bids` associate your acquisitions with the right dicoms through bids sidecar created by dcm2niix. Every study is different and this step needs a little bit of work.

The configuration uses the `json` format and one example is provided in the `example` directory.

### batch options

```
{
"batch_options": {
"isGz": true,
"isFlipY": false,
"isVerbose": false,
"isCreateBIDS": true,
"isOnlySingleFile": false
}
}
```

This is the options needed for `dcm2niibatch`. Keep them like that.

### descriptions
#### Descriptions

The description field is a list of dictionnary. Each dictionnary describes one acquisition.

# Output

The script creates a batch file and save it in the `code` directory of your BIDS folder. It execute automatically `dcm2niibatch` after that. It is possible to do a dry run if you add the option `-n`.

# DICOM to NIfTI conversion
#### Output

Conversion is done with `dcm2niibatch`, a tool from dcm2niix converter. See their [github][dcm2niix-github] for source or [NITRC][dcm2niix-nitrc] for compiled versions.
dcm2bids create `sub-<PARTICIPANT>` directories in the folder the script is launched.

Acquisitions with no or more than one fitting descriptions are kept in `tmp_dcm2niix` directory. Users can review these missing acquistions to change the configuration file accordingly.

[bids]: http://bids.neuroimaging.io/
[bids-spec]: http://bids.neuroimaging.io/#download
Expand Down
62 changes: 0 additions & 62 deletions dcm2bids/batch.py

This file was deleted.

101 changes: 48 additions & 53 deletions dcm2bids/dcm2bids.py
@@ -1,31 +1,24 @@
# -*- coding: utf-8 -*-


import glob
import os
import json
from .batch import Batch
from .dcmparser import Dcmparser
from .structure import Acquisition, Participant


def load_json(filename):
with open(filename, 'r') as f:
data = json.load(f)
return data
from builtins import input
from .dcm2niix import Dcm2niix
from .sidecarparser import Sidecarparser
from .structure import Participant
from .utils import load_json, make_directory_tree, splitext_


class Dcm2bids(object):
"""
"""

def __init__(self, bids_dir, dicom_dir, config, participant,
session=None, dryrun=False):
def __init__(self, dicom_dir, config, yes, participant, session=None):
self.dicomDir = dicom_dir
self.config = load_json(config)
self.yes = yes
self.participant = Participant(participant, session)
self.batch = Batch(
self.config["batch_options"], bids_dir, self.participant)
self.dryrun = dryrun


@property
Expand All @@ -36,46 +29,48 @@ def session(self):
def session(self, value):
self.participant.session = value

@property
def options(self):
if "dcm2bids_options" in self.config:
return self.config["dcm2bids_options"]
else:
return {}

@property
def force(self):
if "force" in self.options:
return self.options["force"]
else:
return False


def acquisitions(self):
for root, dirs, files in os.walk(self.dicomDir):
for f in sorted(files):
if f.startswith('.') == True:
continue
else:
dicomPath = os.path.join(root, f)
dcm = Dcmparser(dicomPath, self.force)
if dcm.isDicom():
yield dcm.search_from(self.config["descriptions"])
break
else:
continue
def run(self):
dcm2niix = Dcm2niix(self.dicomDir, self.participant)
dcm2niix.run()
parser = Sidecarparser(dcm2niix.sidecars, self.config["descriptions"])

for acq in parser.acquisitions:
state = self._move(acq)
if state != 0: return state
return 0


def _move(self, acquisition):
targetDir = os.path.join(
os.getcwd(), self.participant.directory, acquisition.dataType)
filename = "{}_{}".format(self.participant.prefix, acquisition.suffix)
targetBase = os.path.join(targetDir, filename)

targetNiigz = targetBase + ".nii.gz"
if os.path.isfile(targetNiigz) and not self.yes:
reply = self._ask(targetNiigz)
if reply == "q":
return 1
elif reply == "a":
self.yes = True

make_directory_tree(targetDir)
for f in glob.glob(acquisition.base + ".*"):
_, ext = splitext_(f)
os.rename(f, targetBase + ext)

def run(self):
for acquisition in self.acquisitions():
if acquisition is not None:
self.batch.add(acquisition)
else:
pass
self.batch.write()
if self.dryrun:
self.batch.show()
else:
self.batch.execute()
return 0


def _ask(self, target):
print("")
print("'{}' already exists".format(os.path.basename(target)))
print("This file will be erase and replace if you continue")
while True:
p = "Do you want to (q)uit, (c)ontinue or continue for (a)ll? "
reply = input(p)
if reply == "c" or reply == "q" or reply == "a":
break
return reply

43 changes: 43 additions & 0 deletions dcm2bids/dcm2niix.py
@@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-


import glob
import os
from subprocess import call
from .utils import clean


class Dcm2niix(object):
"""
"""

def __init__(self, dicom_dir, participant=None):
self.dicomDir = dicom_dir
self.participant = participant
self.options = "-b y -ba y -z y -f '%f_%p_%t_%s'"
self.sidecars = []


@property
def outputDir(self):
if self.participant is None:
return os.path.join(os.getcwd(), 'tmp_dcm2bids', "config_help")
else:
return os.path.join(
os.getcwd(), 'tmp_dcm2bids', self.participant.prefix)


def run(self):
clean(self.outputDir)
self.execute()
self.sidecars = glob.glob(os.path.join(self.outputDir, "*.json"))
self.sidecars.sort()
return 0


def execute(self):
commandStr = "dcm2niix {} -o {} {}"
command = commandStr.format(
self.options, self.outputDir, self.dicomDir)
call(command, shell=True)

0 comments on commit 2e6aa8a

Please sign in to comment.