diff --git a/README.rst b/README.rst
index d72ba06..b19998d 100644
--- a/README.rst
+++ b/README.rst
@@ -1,215 +1,486 @@
-med2image
-=========
+med2image 2.6.6
+==================
Quick Overview
--------------
-- Convert DICOM or NIfTI to jpg or png
+- Convert ``DICOM`` or ``NIfTI`` inputs to ``jpg`` or ``png`` outputs.
Overview
--------
-``med2image`` is a simple Python3 utility that converts medical image
-formatted files to more visual friendly ones, such as png and jpg.
+``med2image`` is a simple Python3 utility that converts medical image formatted files (such as ``DICOM`` and ``NifTI``) to more web friendly ones, such as ``png`` and ``jpg``.
-Currently, NIfTI and DICOM input formats are understood, while any
-graphical output type that is supported by matplotlib can be generated.
+Currently, ``NIfTI`` and ``DICOM`` input formats are understood, while any graphical output type that is supported by matplotlib can be generated.
+
+At present ``med2image`` does not convert ``DICOM`` to ``NifTI``, but this is planned for a future release.
Dependencies
------------
-Make sure that the following dependencies are installed on your host
-system (or even better, a python3 virtual env):
+Make sure that the following dependencies are installed on your host system (or even better, a ``python3`` virtual env):
+
+- ``pfmisc`` : (a general miscellaneous module for color support, etc)
+- ``nibabel`` : (to read NIfTI files)
+- ``pydicom`` : (to read DICOM files)
+- ``matplotlib`` : (to save data in various image formats)
+- ``pillow`` : (to save data in ``jpg`` format)
-- ``nibabel`` (to read NIfTI files)
-- ``pydicom`` (to read DICOM files)
-- ``matplotlib`` (to save data in various image formats)
-- ``pillow`` (to save data in jpg format)
+Assumptions
+-----------
+
+This document assumes UNIX conventions and a ``bash`` shell. The script should work fine under Windows, but we have not actively tested on that platform -- our dev envs are Linux Ubuntu and macOS.
Installation
~~~~~~~~~~~~
-The best method of installing this script and all of its dependencies is
-by fetching it from PyPI
+Python module
+~~~~~~~~~~~~~
+
+One method of installing this script and all of its dependencies is by fetching it from `PyPI `_.
.. code:: bash
pip3 install med2image
-Should you get an error about `python3-tk` not installed, simply do (for example on Ubuntu):
+Should you get an error about ``python3-tk`` not installed, simply do (for example on Ubuntu):
.. code:: bash
sudo apt-get update
sudo apt-get install -y python3-tk
+Docker container
+~~~~~~~~~~~~~~~~
-Command line arguments
-----------------------
+We also offer a docker container of ``med2image`` as a ChRIS-conformant platform plugin here https://github.com/FNNDSC/pl-med2img (see also the closely related https://github.com/FNNDSC/pl-dcm2img that performs conversions down a directory tree recursively) -- please that reference for information on running the dockerized container. The containerized version exposes a similar CLI and functionality as this module.
-::
+How to Use
+----------
- -i|--inputFile
- Input file to convert. Typically a DICOM file or a nifti volume.
+``med2image`` needs at a minimum (some of) the following required command line arguments:
- [-d|--outputDir ]
- The directory to contain the converted output image files.
+- ``-i | --inputFile `` : Input file to convert. Typically a ``DICOM`` file or a ``NifTI`` volume.
- -o|--outputFileStem
- The output file stem to store conversion. If this is specified
- with an extension, this extension will be used to specify the
- output file type.
+- ``--inputFileSubStr `` : A short hand trick to specify the ``inputFile``. By only specifying a sub string that identifies the file, the first file in the ``inputDir`` that contains the sub string is tagged as the ``inputFile``. This saves a user from needing to specify long and cumbersome file names, esp in the case of many DICOM filenames.
- SPECIAL CASES:
- For DICOM data, the can be set to the value of
- an internal DICOM tag. The tag is specified by preceding the tag
- name with a percent character '%', so
+- ``-d | --outputDir :`` The directory to contain the converted output image files.
- -o %ProtocolName
+**Example:**
- will use the DICOM 'ProtocolName' to name the output file. Note
- that special characters (like spaces) in the DICOM value are
- replaced by underscores '_'.
+.. code:: bash
- Multiple tags can be specified, for example
+ # Convert a NifTI file 'vol.nii' to JPEG and store
+ # the results in a dirctory called 'out'.
+ # The 'out' dir will contain a set of JPEG
+ # images of form 'output-sliceXXX.jpg'.
- -o %PatientName%PatientID%ProtocolName
+ med2image -i vol.nii -d out
- and the output filename will have each DICOM tag string as
- specified in order, connected with dashes.
+.. code:: bash
- A special %inputFile is available to specify the input file that
- was read (without extension).
+ # Convert a DICOM file 'file.dcm' to JPEG and store
+ # the results in a dirctory called 'out'.
+ # The 'out' dir will contain a set of JPEG
+ # images of form 'output-sliceXXX.jpg'.
- [-t|--outputFileType ]
- The output file type. If different to extension,
- will override extension in favour of .
+ # NOTE! If the directory containing 'file.dcm' contains
+ # multiple DICOM files, *ALL* of these will be converted
+ # to JPEG. See later for only converting a *single*
+ # DICOM file.
- [-s|--sliceToConvert ]
- In the case of volume files, the slice (z) index to convert. Ignored
- for 2D input data. If a '-1' is sent, then convert *all* the slices.
- If an 'm' is specified, only convert the middle slice in an input
- volume.
-
- [-f|--frameToConvert ]
- In the case of 4D volume files, the volume (V) containing the
- slice (z) index to convert. Ignored for 3D input data. If a '-1' is
- sent, then convert *all* the frames. If an 'm' is specified, only
- convert the middle frame in the 4D input stack.
+ med2image -i file.dcm -d out
- [--showSlices]
- If specified, render/show image slices as they are created.
+``NIfTI`` details
+-----------------
- [--reslice]
- For 3D data only. Assuming [i,j,k] coordinates, the default is to save
- along the 'k' direction. By passing a --reslice image data in the 'i' and
- 'j' directions are also saved. Furthermore, the is subdivided into
- 'slice' (k), 'row' (i), and 'col' (j) subdirectories.
+**NOTE:** ``NifTI`` is typically a *volume* format. One ``NIfTI`` (``.nii``) volume contains multiple *slices*. Converting a ``NifTI`` volume results in multiple ``.jpg`` or ``.png`` results.
- [-x|--man]
- Show full help.
+- ``NIfTI`` input data can be in 2 forms:
- [-y|--synopsis]
- Show brief help.
+ - 3D : The ``.nii`` volume contains multiple 2D slices
+
+ - 4D : The ``.nii`` file contains multiple 3D volumes that each contain multiple 2D slices
+
+- ``med2image`` understands both types of inputs.
+
+Pull ``NIfTI``
+~~~~~~~~~~~~~~
+
+The input should be a ``NIfTI`` volume with extension ``.nii``. We provide a sample volume here https://github.com/FNNDSC/SAG-anon-nii.git
+
+- Clone this repository (``SAG-anon-nii``) to your local computer.
+
+.. code:: bash
-NIfTI conversion
-----------------
+ git clone https://github.com/FNNDSC/SAG-anon-nii.git
-Both 3D and 4D NIfTI input data are understood. In the case of 4D NIfTI,
-a specific frame can be specified in conjunction with a specific slice
-index. In most cases, only a slice is required since most NIfTI data is
-3D. Furthermore, all slices can be converted, or just the middle one.
+Convert ``NIfTI``
+~~~~~~~~~~~~~~~~~
-Examples
-~~~~~~~~
+**NOTE:**
-All slices in a volume
-~~~~~~~~~~~~~~~~~~~~~~
+- If ``--outputDir | -d`` is not provided, outputs are created in the *current* directory.
-To convert all slices in an input NIfTI volume called vol.nii, to save
-the results in a directory called out and to use as output the file stem
-name image, do
+- if ``--sliceToConvert`` is not provided, *all* the slices of the ``.nii`` volume are converted.
-``med2image -i vol.nii -d out -o image.jpg -s -1``
+Both 3D and 4D ``NIfTI`` input data are understood. In the case of 4D ``NIfTI``, a specific frame (``--frameToConvert``) can be additionally provided in conjunction with a specific slice index. Conversion options include:
+
+- *all* slices (default)
+- *middle* slice only, with the CLI ``--sliceToConvert m``
+- *someSpecificSlice*, with the CLI ``--sliceToConvert ``
+
+CASE 1: All slices in a volume
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Now, let's convert all slices in the input ``NIfTI`` volume ``SAG-anon.nii``, and save the results to a nested subdir ``nifti-results/all-slices``. We'll use as output file name stem ``sample`` and convert to ``jpg``.
+
+Assuming you have cloned the ``SAG-anon-nii`` repo and assuming that you have ``med2image`` on your UNIX shell path,
+
+.. code:: bash
+
+ med2image -i SAG-anon-nii/SAG-anon.nii \
+ -d nifti-results/all-slices \
+ -o sample.jpg -s -1
or equivalently and more verbosely,
-::
+.. code:: bash
- med2image --inputFile vol.nii --outputDir out \
- --outputFileStem image --outputFileType jpg \
+ med2image --inputFile SAG-anon-nii/SAG-anon.nii \
+ --outputDir nifti-results/all-slices \
+ --outputFileStem sample --outputFileType jpg \
--sliceToConvert -1
-This will create the following files in out
+resulting in
::
- image-slice000.jpg
- image-slice001.jpg
- image-slice002.jpg
- image-slice003.jpg
- image-slice004.jpg
- image-slice005.jpg
- image-slice006.jpg
- image-slice007.jpg
+ nifti-results/all-slices/sample-slice000.jpg
+ nifti-results/all-slices/sample-slice001.jpg
+ nifti-results/all-slices/sample-slice002.jpg
+ nifti-results/all-slices/sample-slice003.jpg
...
- image-slice049.jpg
- image-slice050.jpg
- image-slice051.jpg
- image-slice052.jpg
- image-slice053.jpg
+ nifti-results/all-slices/sample-slice188.jpg
+ nifti-results/all-slices/sample-slice189.jpg
+ nifti-results/all-slices/sample-slice190.jpg
+ nifti-results/all-slices/sample-slice191.jpg
+
+Note that even if the nested output directory structure does not exist, ``med2image`` will create it for you.
-Convert only a single slice
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Case 2: Convert only a single slice
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often times, you might only want to convert the "middle" slice in a volume (for example to generate a representative thumbnail of the volume). To do this, simply specify an ``m`` to ``--sliceToConvert`` (or ``-s m``):
+
+.. code:: bash
-Mostly, you'll probably only want to convert the "middle" slice in a
-volume (for example to generate a representative thumbnail of the
-volume). To do this, simply specify a m to --sliceToConvert
+ med2image -i SAG-anon-nii/SAG-anon.nii \
+ -d nifti-results/middle-slice \
+ -o sample --outputFileType jpg \
+ --sliceToConvert m
-``med2image -i input.nii -o input.jpg -s m``
+resulting in
-or, again, slightly more verbosely and with an outputDirectory specifier
+::
-``med2image -i input.nii -d out -o vol --outputFileType jpg --sliceToConvert m``
+ nifti-results/middle-slice/sample-slice096.jpg
Alternatively a specific slice index can be converted. Use
-``med2image -i input.nii -d out -o vol --outputFileType jpg --sliceToConvert 20``
+.. code:: bash
+
+ med2image -i SAG-anon-nii/SAG-anon.nii \
+ -d nifti-results/specific-slice \
+ -o sample \
+ --outputFileType jpg \
+ --sliceToConvert 20
to convert only the 20th slice of the volume.
-DICOM conversion
-----------------
+resulting in
+
+::
+
+ nifti-results/specific-slice/sample-slice020.jpg
+
+``DICOM``
+---------
+
+**NOTE:** One ``DICOM`` (``.dcm``) file typically corresponds to one ``.png`` or ``.jpg`` file (slice).
+
+Pull DICOM
+~~~~~~~~~~
+
+The input should be a ``DICOM`` file usually with extension ``.dcm``
+
+We provide a sample directory of ``.dcm`` images here ``FNNDSC/SAG-anon``. (https://github.com/FNNDSC/SAG-anon.git)
+
+- Clone this repository (``SAG-anon``) to your local computer.
+
+.. code:: bash
+
+ git clone https://github.com/FNNDSC/SAG-anon.git
-Convert a single DICOM file
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Convert ``DICOM``
+~~~~~~~~~~~~~~~~~
-To convert a single DICOM file called slice.dcm to slice.jpg, do:
+**NOTE:**
-``med2image -i slice.dcm -o slice.jpg``
+- If ``--outputDir | -d`` is not provided, any output(s) are created in the current directory.
+- if ``--sliceToConvert`` argument is not specified and if mutiple ``dcm`` files are contained in the input directory with the ``DICOM`` input, then all the ``.dcm`` files are converted.
+- alternatively, specifying a ``--convertOnlySingleDICOM`` will only convert the DICOM file specified with the ``--inputFile`` flag.
-which will create a single file, slice.jpg in the current directory.
Convert all DICOMS in a directory/series
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To convert all the ``DICOM`` files in a directory, simply specify either ``--sliceToConvert -1`` (or just leave out the argument/value pair completely):
+
+.. code:: bash
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/all-slices \
+ -o sample \
+ --outputFileType jpg \
+ --sliceToConvert -1
+
+ # OR equivalently
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/all-slices \
+ -o sample \
+ --outputFileType jpg
+
+
+resulting in
+
+::
+
+ dicom-results/all-slices/sample-slice000.jpg
+ dicom-results/all-slices/sample-slice001.jpg
+ dicom-results/all-slices/sample-slice002.jpg
+ dicom-results/all-slices/sample-slice003.jpg
+ ...
+ dicom-results/all-slices/sample-slice188.jpg
+ dicom-results/all-slices/sample-slice189.jpg
+ dicom-results/all-slices/sample-slice190.jpg
+ dicom-results/all-slices/sample-slice191.jpg
+
+Convert a single ``DICOM`` file
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Mostly, you'll probably only want to convert the "middle" slice in a DICOM directory (for example to generate a representative thumbnail of the directory). To do this, simply specify a `m` to --sliceToConvert (or `-s m`)
+
+.. code:: bash
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/middle-slice \
+ -o sample --outputFileType jpg \
+ --sliceToConvert m
+
+resulting in
+
+::
+
+ dicom-results/middle-slice/sample.jpg
+
+Note that even though the first slice in the ``SAG-anon`` directory was supplied to the script, ``med2image`` nonetheless found and converted the middle slice in the directory.
+
+Alternatively a specific slice index can be converted. Use
+
+.. code:: bash
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/specific-slice \
+ -o sample --outputFileType jpg \
+ --sliceToConvert 20
+
+resulting in
+
+::
+
+ dicom-results/specific-slice/sample.jpg
+
+Again, even though the first slice was supplied to the script, ``med2image`` selected and converted the 20th slice in the directory.
+
+Special Cases
+^^^^^^^^^^^^^
+
+For ``DICOM`` data, the ```` can optionally be set to the value of an internal ``DICOM`` tag. The tag is specified by preceding the tag name with a percent character ``%``, so
+
+- ``-o %PatientID``
+
+will use the ``DICOM`` ``PatientID`` to name the output file. Note that special characters (like spaces) in the ``DICOM`` value are replaced by underscores '_'.
+
+.. code:: bash
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/tags \
+ -o %PatientID.jpg -s m
+
+This will create the following file in the ``tags`` sub-directory within ``dicom-results`` directory.
+
+.. code:: bash
+
+ dicom-results/tags/1449c1d.jpg
+
+Multiple tags can be specified, for example
+
+- ``-o %PatientName%PatientID%ProtocolName``
+
+and the output filename will have each ``DICOM`` tag string as specified in order, connected with dashes.
+
+.. code:: bash
+
+ med2image -i SAG-anon/0001-1.3.12.2.1107.5.2.19.45152.2013030808110258929186035.dcm \
+ -d dicom-results/tags \
+ -o %PatientName%PatientID%ProtocolName.jpg \
+ -s m
-To convert all the DICOMS in a directory, simply specifiy a '-1' to the
-sliceIndex:
+This will create the following file in the ``tags`` sub-directory within ``dicom-results`` directory.
-``med2image -i inputDir/slice.dcm -d outputDir -o slice.jpg -s -1``
+.. code:: bash
+
+ dicom-results/tags/anonymized-1449c1d-SAG_MPRAGE_220_FOV.jpg
-Note that this assumes all the DICOM files in the directory inputDir
-belong to the same series.
Multiple Direction Reslicing
----------------------------
-By default, only the slice (or slices) in the acquisition direction are
-converted. However, by passing a -r to the script, all dimensions are
-converted. Since the script does not know the anatomical orientation of
-the image, the directions are simply labeled x, y, and z.
+By default, only the slice (or slices) in the acquisition direction are converted. However, by passing a `--reslice` to the script, all dimensions are converted. Since the script does not know the anatomical orientation of the image, the directions are simply labeled ``x``, ``y``, and ``z``.
+
+The ``z`` direction is the original acquistion (slice) direction, while ``x`` and ``y`` correspond to planes normal to the row and column directions. Converted images are stored in subdirectories labeled ``x``, ``y``, and ``z``.
+
+No interpolation in the ``x`` and ``y`` directions is performed. This often results in ugly images!
+
+**NOTE:** In case of ``DICOM`` images, the `--reslice` option will work only if all slices in the directory are converted, i.e. converting with ``--sliceToConvert -1``
+
+Special Operations
+------------------
+
+``med2image`` also supports some very basic image processing through a ``--func `` CLI, which applies some canned transformation on the image. Currently supported is
+
+::
+
+ --func invertIntensities
+
+which simply inverts the contrast intensity of the source image. Additional functions are planned for future releases.
+
+Command Line Arguments
+----------------------
+
+::
+
+ [-i|--inputFile ]
+ Input file to convert. Typically a DICOM file or a nifti volume.
+
+ [--inputFileSubStr ]
+ As a convenience, the input file can be determined via a substring
+ search of all the files in the using this flag. The first
+ filename hit that contains the will be assigned the
+ .
+
+ This flag is useful is input names are long and cumbersome, but
+ a short substring search would identify the file. For example, an
+ input file of
+
+ 0043-1.3.12.2.1107.5.2.19.45152.2013030808110149471485951.dcm
+
+ can be specified using ``--inputFileSubStr 0043-``
+
+ [-I|--inputDir ]
+ If specified, a directory containing the . In this case
+ should be specified as relative to .
+
+ [-d|--outputDir ]
+ The directory to contain the converted output image files.
+
+ -o|--outputFileStem
+ The output file stem to store conversion. If this is specified
+ with an extension, this extension will be used to specify the
+ output file type.
+
+ SPECIAL CASES:
+ For DICOM data, the can be set to the value of
+ an internal DICOM tag. The tag is specified by preceding the tag
+ name with a percent character '%', so
+
+ -o %ProtocolName
+
+ will use the DICOM 'ProtocolName' to name the output file. Note
+ that special characters (like spaces) in the DICOM value are
+ replaced by underscores '_'.
+
+ Multiple tags can be specified, for example
+
+ -o %PatientName%PatientID%ProtocolName
+
+ and the output filename will have each DICOM tag string as
+ specified in order, connected with dashes.
+
+ [--convertOnlySingleDICOM]
+ If specified, will only convert the single DICOM specified by the
+ '--inputFile' flag. This is useful for the case when an input
+ directory has many DICOMS but you specifially only want to convert
+ the named file. By default the script assumes that multiple DICOMS
+ should be converted en mass otherwise.
+
+ [--preserveDICOMinputName]
+ If specified, use the input DICOM name as the stem of the output
+ filename, with the specified type ('jpg' or 'png') as the extension.
+ In the case where [--reslice] is additionally specified, only the
+ slice or 'z' direction will preserve original DICOM names.
+
+ [-t|--outputFileType ]
+ The output file type. If different to extension,
+ will override extension in favour of .
+
+ [-s|--sliceToConvert ]
+ In the case of volume files, the slice (z) index to convert. Ignored
+ for 2D input data. If a '-1' is sent, then convert *all* the slices.
+ If an 'm' is specified, only convert the middle slice in an input
+ volume.
+
+ [-f|--frameToConvert ]
+ In the case of 4D volume files, the volume (V) containing the
+ slice (z) index to convert. Ignored for 3D input data. If a '-1' is
+ sent, then convert *all* the frames. If an 'm' is specified, only
+ convert the middle frame in the 4D input stack.
+
+ [--showSlices]
+ If specified, render/show image slices as they are created.
+
+ [--rot <3DbinVector>]
+ A per dimension binary rotation vector. Useful to rotate individual
+ dimensions by an angle specified with [--rotAngle ]. Default
+ is '110', i.e. rotate 'x' and 'y' but not 'z'. Note that for a
+ non-reslice selection, only the 'z' (or third) element of the vector
+ is used.
+
+ [--rotAngle ]
+ Default 90 -- the rotation angle to apply to a given dimension of the
+ <3DbinVector>.
-The z direction is the original acquistion (slice) direction, while x
-and y correspond to planes normal to the row and column directions.
+ [--func ]
+ Apply the specified transformation function before saving. Currently
+ support functions:
+
+ * invertIntensities
+ Inverts the contrast intensity of the source image.
+
+ [--reslice]
+ For 3D data only. Assuming [x,y,z] coordinates, the default is to save
+ along the 'z' direction. By passing a --reslice image data in the 'x'
+ and 'y' directions are also saved. Furthermore, the is
+ subdivided into 'slice' (z), 'row' (x), and 'col' (y) subdirectories.
+
+ [-x|--man]
+ Show full help.
+
+ [-y|--synopsis]
+ Show brief help.
-Converted images are stored in subdirectories labeled x, y, and z.
+ [--verbosity ]
+ Control how chatty med2image is. Set to '0' for blissful silence, '1'
+ for sane progress and '3' for full information.
diff --git a/bin/med2image b/bin/med2image
index 0711cc2..7a50504 100755
--- a/bin/med2image
+++ b/bin/med2image
@@ -21,19 +21,17 @@ from pfmisc._colors import Colors
import pudb
-str_version = "2.0.1"
-#import pdb; pdb.set_trace()
-
+str_version = "2.6.6"
str_desc = Colors.CYAN + """
- _ _____ _
- | |/ __ \(_)
- _ __ ___ ___ __| |`' / /' _ _ __ ___ __ _ __ _ ___
+ _ _____ _
+ | |/ __ \(_)
+ _ __ ___ ___ __| |`' / /' _ _ __ ___ __ _ __ _ ___
| '_ ` _ \ / _ \/ _` | / / | | '_ ` _ \ / _` |/ _` |/ _ \\
| | | | | | __/ (_| |./ /___| | | | | | | (_| | (_| | __/
|_| |_| |_|\___|\__,_|\_____/|_|_| |_| |_|\__,_|\__, |\___|
- __/ |
- |___/
+ __/ |
+ |___/
med(ical image)2image
@@ -55,26 +53,33 @@ def synopsis(ab_shortOnly=False):
shortSynopsis = '''
NAME
- med2image.py - convert medical images to jpg/png/etc.
+ med2image.py - convert medical images to jpg/png/etc.
SYNOPSIS
%s \\
- -i|--input \\
+ [-i|--input ] \\
+ [--inputFileSubStr ] \\
+ [-I|--inputDir ] \\
[-d|--outputDir ] \\
-o|--output \\
[-t|--outputFileType ] \\
[-s|--sliceToConvert ] \\
+ [--convertOnlySingleDICOM] \\
+ [--preserveDICOMinputName] \\
[-f|--frameToConvert ] \\
[--showSlices] \\
[--func ] \\
[--reslice] \\
+ [--rotAngle ] \\
+ [--rot <3vec>] \\
[-x|--man] \\
- [-y|--synopsis]
+ [-y|--synopsis] \\
+ [--verbosity ]
BRIEF EXAMPLE
- med2image.py -i slice.dcm -o slice.jpg
+ med2image.py -i slice.dcm -o slice.jpg
''' % scriptName
@@ -88,9 +93,27 @@ def synopsis(ab_shortOnly=False):
ARGS
- -i|--inputFile
+ [-i|--inputFile ]
Input file to convert. Typically a DICOM file or a nifti volume.
+ [--inputFileSubStr ]
+ As a convenience, the input file can be determined via a substring
+ search of all the files in the using this flag. The first
+ filename hit that contains the will be assigned the
+ .
+
+ This flag is useful if input names are long and cumbersome, but
+ a short substring search would identify the file. For example, an
+ input file of
+
+ 0043-1.3.12.2.1107.5.2.19.45152.2013030808110149471485951.dcm
+
+ can be specified using ``--inputFileSubStr 0043-``
+
+ [-I|--inputDir ]
+ If specified, a directory containing the . In this case
+ should be specified as relative to .
+
[-d|--outputDir ]
The directory to contain the converted output image files.
@@ -102,23 +125,33 @@ def synopsis(ab_shortOnly=False):
SPECIAL CASES:
For DICOM data, the can be set to the value of
an internal DICOM tag. The tag is specified by preceding the tag
- name with a percent character '%%', so
+ name with a percent character '%%', so
-o %%ProtocolName
will use the DICOM 'ProtocolName' to name the output file. Note
- that special characters (like spaces) in the DICOM value are
+ that special characters (like spaces) in the DICOM value are
replaced by underscores '_'.
Multiple tags can be specified, for example
-o %%PatientName%%PatientID%%ProtocolName
- and the output filename will have each DICOM tag string as
+ and the output filename will have each DICOM tag string as
specified in order, connected with dashes.
- A special %%inputFile is available to specify the input file that
- was read (without extension).
+ [--convertOnlySingleDICOM]
+ If specified, will only convert the single DICOM specified by the
+ '--inputFile' flag. This is useful for the case when an input
+ directory has many DICOMS but you specifially only want to convert
+ the named file. By default the script assumes that multiple DICOMS
+ should be converted en mass otherwise.
+
+ [--preserveDICOMinputName]
+ If specified, use the input DICOM name as the stem of the output
+ filename, with the specified type ('jpg' or 'png') as the extension.
+ In the case where [--reslice] is additionally specified, only the
+ slice or 'z' direction will preserve original DICOM names.
[-t|--outputFileType ]
The output file type. If different to extension,
@@ -138,19 +171,30 @@ def synopsis(ab_shortOnly=False):
[--showSlices]
If specified, render/show image slices as they are created.
-
+
+ [--rot <3DbinVector>]
+ A per dimension binary rotation vector. Useful to rotate individual
+ dimensions by an angle specified with [--rotAngle ]. Default
+ is '110', i.e. rotate 'x' and 'y' but not 'z'. Note that for a
+ non-reslice selection, only the 'z' (or third) element of the vector
+ is used.
+
+ [--rotAngle ]
+ Default 90 -- the rotation angle to apply to a given dimension of the
+ <3DbinVector>.
+
[--func ]
- Apply the specified transformation function before saving. Currently
+ Apply the specified transformation function before saving. Currently
support functions:
* invertIntensities
Inverts the contrast intensity of the source image.
[--reslice]
- For 3D data only. Assuming [i,j,k] coordinates, the default is to save
- along the 'k' direction. By passing a --reslice image data in the 'i' and
- 'j' directions are also saved. Furthermore, the is subdivided into
- 'slice' (k), 'row' (i), and 'col' (j) subdirectories.
+ For 3D data only. Assuming [x,y,z] coordinates, the default is to save
+ along the 'z' direction. By passing a --reslice image data in the 'x'
+ and 'y' directions are also saved. Furthermore, the is
+ subdivided into 'slice' (z), 'row' (x), and 'col' (y) subdirectories.
[-x|--man]
Show full help.
@@ -158,6 +202,10 @@ def synopsis(ab_shortOnly=False):
[-y|--synopsis]
Show brief help.
+ [--verbosity ]
+ Control how chatty med2image is. Set to '0' for blissful silence, '1'
+ for sane progress and '3' for full information.
+
EXAMPLES
NIfTI
@@ -165,22 +213,22 @@ def synopsis(ab_shortOnly=False):
o Convert each slice in a NIfTI volume 'vol.nii' to a jpg called
'image-sliceXXX.jpg' and store results in a directory called 'out':
- med2image -i vol.nii -d out -o image.jpg -s -1
+ med2image -i vol.nii -d out -o image.jpg -s -1
o Convert only the middle slice in an input volume and store in current
directory:
- med2image -i vol.nii -o image.jpg -s m
+ med2image -i vol.nii -o image.jpg -s m
o Convert a specific slice, i.e. slice 20
- med2image -i vol.nii -o image.jpg -s 20
+ med2image -i vol.nii -o image.jpg -s 20
DICOM
o Simply convert a DICOM file called 'slice.dcm' to a jpg called 'slice.jpg':
- med2image -i slice.dcm -o slice.jpg
+ med2image -i slice.dcm -o slice.jpg
o Convert all DICOMs in a directory. Note that is assumes all DICOM files
in the directory containing the passed file belong to the same series.
@@ -206,7 +254,12 @@ parser = ArgumentParser(description = str_desc, formatter_class = RawTextHelpFo
parser.add_argument("-i", "--inputFile",
help = "input file",
- dest = 'inputFile')
+ dest = 'inputFile',
+ default = '')
+parser.add_argument("--inputFileSubStr",
+ help = "input file substring",
+ dest = 'inputFileSubStr',
+ default = '')
parser.add_argument("-I", "--inputDir",
help = "input directory",
dest = 'inputDir',
@@ -223,6 +276,16 @@ parser.add_argument("-t", "--outputFileType",
help = "output image type",
dest = 'outputFileType',
default = '')
+parser.add_argument("--convertOnlySingleDICOM",
+ help = "if specified, only convert the specific input DICOM",
+ dest = 'convertOnlySingleDICOM',
+ action = 'store_true',
+ default = False)
+parser.add_argument("--preserveDICOMinputName",
+ help = "if specified, save output files with the basename of their input DICOM",
+ dest = 'preserveDICOMinputName',
+ action = 'store_true',
+ default = False)
parser.add_argument("-s", "--sliceToConvert",
help="slice to convert (for 3D data)",
dest='sliceToConvert',
@@ -250,6 +313,18 @@ parser.add_argument('--func',
help = "apply transformation function before saving",
dest = 'func',
default = "")
+parser.add_argument('--verbosity',
+ help = "verbosity level for app",
+ dest = 'verbosity',
+ default = "1")
+parser.add_argument('--rot',
+ help = "3D slice/dimenstion rotation vector",
+ dest = 'rot',
+ default = "110")
+parser.add_argument('--rotAngle',
+ help = "3D slice/dimenstion rotation angle",
+ dest = 'rotAngle',
+ default = "90")
parser.add_argument("-x", "--man",
help = "man",
dest = 'man',
@@ -270,6 +345,7 @@ parser.add_argument('-v', '--version',
# parse passed arguments
args = parser.parse_args()
+# Do some minor CLI checks
if args.b_version:
print("Version: %s" % str_version)
sys.exit(1)
@@ -283,15 +359,16 @@ if args.man or args.synopsis:
print(str_help)
sys.exit(1)
+# Create the object
imgConverter = med2image.object_factoryCreate(args).C_convert
-if args.func:
- imgConverter.func = args.func
-
-# pudb.set_trace()
-# And now run it!
-imgConverter.tic()
-imgConverter.run()
-if args.printElapsedTime: print("Elapsed time = %f seconds" % imgConverter.toc())
-sys.exit(0)
+# and if it's valid...
+if imgConverter:
+ # run it!
+ imgConverter.tic()
+ imgConverter.run()
+ if args.printElapsedTime: print("Elapsed time = %f seconds" % imgConverter.toc())
+ sys.exit(0)
+else:
+ sys.exit(1)
diff --git a/med2image/med2image.py b/med2image/med2image.py
index 4b1b02b..9c3474d 100755
--- a/med2image/med2image.py
+++ b/med2image/med2image.py
@@ -2,27 +2,25 @@
# System imports
import os
+from os import listdir
+from os import walk
+from os.path import isfile, join
+from pathlib import Path
+
import sys
import glob
import numpy as np
import re
import time
-# import pudb
-
+import pudb
+from scipy import ndimage
# System dependency imports
-import nibabel as nib
-import pydicom as dicom
-import pylab
-import matplotlib.cm as cm
-
-# # Project specific imports
-# from . import error
-# from . import message as msg
-# from . import systemMisc as misc
+import nibabel as nib
+import pydicom as dicom
+import pylab
+import matplotlib.cm as cm
import pfmisc
-
-# pfurl local dependencies
from pfmisc._colors import Colors
from pfmisc.message import Message
@@ -34,8 +32,10 @@ def report( callingClass,
):
'''
Error handling.
+
Based on the , error information is extracted from
_dictErr and sent to log object.
+
If is False, error is considered non-fatal and
processing can continue, otherwise processing terminates.
'''
@@ -96,41 +96,45 @@ class med2image(object):
_dictErr = {
'inputFileFail' : {
- 'action' : 'trying to read input file, ',
- 'error' : 'could not access/read file -- does it exist? Do you have permission?',
- 'exitCode' : 10},
+ 'action' : 'trying to read input file, ',
+ 'error' : 'could not access/read file -- does it exist? Do you have permission?',
+ 'exitCode' : 10},
'emailFail' : {
- 'action' : 'attempting to send notification email, ',
- 'error' : 'sending failed. Perhaps host is not email configured?',
- 'exitCode' : 20},
+ 'action' : 'attempting to send notification email, ',
+ 'error' : 'sending failed. Perhaps host is not email configured?',
+ 'exitCode' : 20},
'dcmInsertionFail': {
- 'action' : 'attempting insert DICOM into volume structure, ',
- 'error' : 'a dimension mismatch occurred. This DICOM file is of different image size to the rest.',
- 'exitCode' : 20},
+ 'action' : 'attempting insert DICOM into volume structure, ',
+ 'error' : 'a dimension mismatch occurred. This DICOM file is of different image size to the rest.',
+ 'exitCode' : 30},
'ProtocolNameTag': {
- 'action' : 'attempting to parse DICOM header, ',
- 'error' : 'the DICOM file does not seem to contain a ProtocolName tag.',
- 'exitCode' : 30},
- 'PatientNameTag': {
- 'action': 'attempting to parse DICOM header, ',
- 'error': 'the DICOM file does not seem to contain a PatientName tag.',
- 'exitCode': 30},
+ 'action' : 'attempting to parse DICOM header, ',
+ 'error' : 'the DICOM file does not seem to contain a ProtocolName tag.',
+ 'exitCode' : 40},
+ 'PatientNameTag': {
+ 'action': 'attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a PatientName tag.',
+ 'exitCode': 41},
'PatientAgeTag': {
- 'action': 'attempting to parse DICOM header, ',
- 'error': 'the DICOM file does not seem to contain a PatientAge tag.',
- 'exitCode': 30},
+ 'action': ' attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a PatientAge tag.',
+ 'exitCode': 42},
'PatientNameSex': {
- 'action': 'attempting to parse DICOM header, ',
- 'error': 'the DICOM file does not seem to contain a PatientSex tag.',
- 'exitCode': 30},
+ 'action': 'attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a PatientSex tag.',
+ 'exitCode': 43},
'PatientIDTag': {
- 'action': 'attempting to parse DICOM header, ',
- 'error': 'the DICOM file does not seem to contain a PatientID tag.',
- 'exitCode': 30},
+ 'action': 'attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a PatientID tag.',
+ 'exitCode': 44},
'SeriesDescriptionTag': {
- 'action': 'attempting to parse DICOM header, ',
- 'error': 'the DICOM file does not seem to contain a SeriesDescription tag.',
- 'exitCode': 30}
+ 'action': 'attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a SeriesDescription tag.',
+ 'exitCode': 45},
+ 'PatientSexTag': {
+ 'action': 'attempting to parse DICOM header, ',
+ 'error': 'the DICOM file does not seem to contain a PatientSex tag.',
+ 'exitCode': 46}
}
@staticmethod
@@ -147,11 +151,12 @@ def mkdir(newdir, mode=0x775):
raise OSError("a file with the same name as the desired " \
"dir, '%s', already exists." % newdir)
else:
- head, tail = os.path.split(newdir)
- if head and not os.path.isdir(head):
- os.mkdir(head)
- if tail:
- os.mkdir(newdir)
+ Path(newdir).mkdir(parents = True, exist_ok = True)
+ # head, tail = os.path.split(newdir)
+ # if head and not os.path.isdir(head):
+ # os.mkdirs(head, exist_ok=True)
+ # if tail:
+ # os.mkdirs(newdir, exist_ok=True)
def log(self, *args):
'''
@@ -186,6 +191,7 @@ def description(self, *args):
@staticmethod
def urlify(astr, astr_join = '_'):
# Remove all non-word characters (everything except numbers and letters)
+ # pudb.set_trace()
astr = re.sub(r"[^\w\s]", '', astr)
# Replace all runs of whitespace with an underscore
@@ -198,27 +204,29 @@ def __init__(self, **kwargs):
#
# Object desc block
#
- self.str_desc = ''
- # self._log = msg.Message()
- # self._log._b_syslog = True
- self.__name__ = "med2image"
+ self.str_desc = ''
+ # self._log = msg.Message()
+ # self._log._b_syslog = True
+ self.__name__ = "med2image"
# Directory and filenames
- self.str_workingDir = ''
- self.str_inputFile = ''
- self.str_outputFileStem = ''
- self.str_outputFileType = ''
- self.str_outputDir = ''
- self.str_inputDir = ''
+ self.str_workingDir = ''
+ self.str_inputFile = ''
+ self.lstr_inputFile = []
+ self.str_inputFileSubStr = ''
+ self.str_outputFileStem = ''
+ self.str_outputFileType = ''
+ self.str_outputDir = ''
+ self.str_inputDir = ''
self._b_convertAllSlices = False
- self.str_sliceToConvert = ''
- self.str_frameToConvert = ''
+ self.str_sliceToConvert = ''
+ self.str_frameToConvert = ''
self._sliceToConvert = -1
self._frameToConvert = -1
- self.str_stdout = ""
- self.str_stderr = ""
+ self.str_stdout = ""
+ self.str_stderr = ""
self._exitCode = 0
# The actual data volume and slice
@@ -226,6 +234,8 @@ def __init__(self, **kwargs):
self._b_4D = False
self._b_3D = False
self._b_DICOM = False
+ self.convertOnlySingleDICOM = False
+ self.preserveDICOMinputName = False
self._Vnp_4DVol = None
self._Vnp_3DVol = None
self._Mnp_2Dslice = None
@@ -234,34 +244,39 @@ def __init__(self, **kwargs):
self.verbosity = 1
- # A logger
- # self._log = msg.Message()
- # self._log.syslog(True)
-
- self.dp = pfmisc.debug(
- verbosity = self.verbosity,
- within = self.__name__
- )
-
-
# Flags
self._b_showSlices = False
self._b_convertMiddleSlice = False
self._b_convertMiddleFrame = False
self._b_reslice = False
- self.func = None #transformation function
+ self.func = None # transformation function
+ self.rot = '110'
+ self.rotAngle = 90
for key, value in kwargs.items():
- if key == "inputFile": self.str_inputFile = value
- if key == "inputDir": self.str_inputDir = value
- if key == "outputDir": self.str_outputDir = value
- if key == "outputFileStem": self.str_outputFileStem = value
- if key == "outputFileType": self.str_outputFileType = value
- if key == "sliceToConvert": self.str_sliceToConvert = value
- if key == "frameToConvert": self.str_frameToConvert = value
- if key == "showSlices": self._b_showSlices = value
- if key == 'reslice': self._b_reslice = value
- if key == "func": self.func = value
+ if key == "inputFile": self.str_inputFile = value
+ if key == "inputFileSubStr": self.str_inputFileSubStr = value
+ if key == "inputDir": self.str_inputDir = value
+ if key == "outputDir": self.str_outputDir = value
+ if key == "outputFileStem": self.str_outputFileStem = value
+ if key == "outputFileType": self.str_outputFileType = value
+ if key == "sliceToConvert": self.str_sliceToConvert = value
+ if key == "frameToConvert": self.str_frameToConvert = value
+ if key == "convertOnlySingleDICOM": self.convertOnlySingleDICOM = value
+ if key == "preserveDICOMinputName": self.preserveDICOMinputName = value
+ if key == "showSlices": self._b_showSlices = value
+ if key == 'reslice': self._b_reslice = value
+ if key == "func": self.func = value
+ if key == "verbosity": self.verbosity = int(value)
+ if key == "rot": self.rot = value
+ if key == "rotAngle": self.rotAngle = int(value)
+
+ # A logger
+ self.dp = pfmisc.debug(
+ verbosity = self.verbosity,
+ within = self.__name__
+ )
+ self.LOG = self.dp.qprint
if self.str_frameToConvert.lower() == 'm':
self._b_convertMiddleFrame = True
@@ -286,7 +301,7 @@ def __init__(self, **kwargs):
self.str_outputFileType = str_fileExtension
if not len(self.str_outputFileType) and not len(str_fileExtension):
- self.str_outputFileType = '.png'
+ self.str_outputFileType = 'png'
def tic(self):
"""
@@ -368,17 +383,19 @@ def get_output_file_name(self, **kwargs):
frame, index,
self.str_outputFileType)
else:
- str_outputFile = '%s/%s/%s-slice%03d.%s' % (
+ if self.preserveDICOMinputName and (str_subDir == 'z' or str_subDir == ''):
+ str_filePart = os.path.splitext(self.lstr_inputFile[index])[0]
+ else:
+ str_filePart = '%s-slice%03d' % (self.str_outputFileStem, index)
+ str_outputFile = '%s/%s/%s.%s' % (
self.str_outputDir,
str_subDir,
- self.str_outputFileStem,
- index,
+ str_filePart,
self.str_outputFileType)
return str_outputFile
def dim_save(self, **kwargs):
dims = self._Vnp_3DVol.shape
- self.dp.qprint('Image volume logical (i, j, k) size: %s' % str(dims))
str_dim = 'z'
b_makeSubDir = False
b_rot90 = False
@@ -401,7 +418,7 @@ def dim_save(self, **kwargs):
dim_ix = {'x':0, 'y':1, 'z':2}
if indexStart == 0 and indexStop == -1:
indexStop = dims[dim_ix[str_dim]]
-
+ self.LOG('Saving along "%s" dimension with %i degree rotation...' % (str_dim, self.rotAngle*b_rot90))
for i in range(indexStart, indexStop):
if str_dim == 'x':
self._Mnp_2Dslice = self._Vnp_3DVol[i, :, :]
@@ -414,26 +431,32 @@ def dim_save(self, **kwargs):
if str_outputFile.endswith('dcm'):
self._dcm = self._dcmList[i]
self.slice_save(str_outputFile)
+ self.LOG('%d images saved along "%s" dimension' % ((i+1), str_dim),
+ end = '')
+ if self.func:
+ self.LOG(" with '%s' function applied." % self.func,
+ syslog = False)
+ else:
+ self.LOG(".", syslog = False)
- def process_slice(self, b_rot90=None):
+ def process_slice(self, b_rot90 = False):
'''
Processes a single slice.
'''
if b_rot90:
- self._Mnp_2Dslice = np.rot90(self._Mnp_2Dslice)
+ self._Mnp_2Dslice = ndimage.rotate(self._Mnp_2Dslice, self.rotAngle)
if self.func == 'invertIntensities':
self.invert_slice_intensities()
def slice_save(self, astr_outputFile):
'''
- Saves a single slice.
-
ARGS
o astr_output
- The output filename to save the slice to.
+ The output filename.
'''
- self.dp.qprint('Outputfile = %s' % astr_outputFile)
+ self.LOG('Input file = %s' % self.str_inputFile, level = 3)
+ self.LOG('Outputfile = %s' % astr_outputFile, level = 3)
fformat = astr_outputFile.split('.')[-1]
if fformat == 'dcm':
if self._dcm:
@@ -468,30 +491,32 @@ def __init__(self, **kwargs):
self.l_dcmFileNames = sorted(glob.glob('%s/*.dcm' % self.str_inputDir))
self.slices = len(self.l_dcmFileNames)
-
- if self._b_convertMiddleSlice:
- self._sliceToConvert = int(self.slices/2)
- self._dcm = dicom.read_file(self.l_dcmFileNames[self._sliceToConvert],force=True)
- self.str_inputFile = self.l_dcmFileNames[self._sliceToConvert]
-
- # if not self.str_outputFileStem.startswith('%'):
- # self.str_outputFileStem, ext = os.path.splitext(self.l_dcmFileNames[self._sliceToConvert])
- if not self._b_convertMiddleSlice and self._sliceToConvert != -1:
- self._dcm = dicom.read_file(self.l_dcmFileNames[self._sliceToConvert],force=True)
- self.str_inputFile = self.l_dcmFileNames[self._sliceToConvert]
+ if self._sliceToConvert != -1 or self.convertOnlySingleDICOM:
+ if self._b_convertMiddleSlice:
+ self._sliceToConvert = int(self.slices/2)
+ self._dcm = dicom.read_file(self.l_dcmFileNames[self._sliceToConvert],force=True)
+ self.str_inputFile = self.l_dcmFileNames[self._sliceToConvert]
+ if not self._b_convertMiddleSlice and self._sliceToConvert != -1:
+ self._dcm = dicom.read_file(self.l_dcmFileNames[self._sliceToConvert],force=True)
+ self.str_inputFile = self.l_dcmFileNames[self._sliceToConvert]
+ else:
+ self._dcm = dicom.read_file(self.str_inputFile,force=True)
+ if self.convertOnlySingleDICOM:
+ self._sliceToConvert = 1
+ self._dcm = dicom.read_file(self.str_inputFile,force=True)
+ self.lstr_inputFile.append(os.path.basename(self.str_inputFile))
else:
- self._dcm = dicom.read_file(self.str_inputFile,force=True)
- if self._sliceToConvert == -1:
- self._b_3D = True
- self._dcm = dicom.read_file(self.str_inputFile,force=True)
- image = self._dcm.pixel_array
- shape2D = image.shape
+ self._b_3D = True
+ self._dcm = dicom.read_file(self.str_inputFile,force=True)
+ image = self._dcm.pixel_array
+ shape2D = image.shape
#print(shape2D)
- self._Vnp_3DVol = np.empty( (shape2D[0], shape2D[1], self.slices) )
- i = 0
+ self._Vnp_3DVol = np.empty( (shape2D[0], shape2D[1], self.slices) )
+ i = 0
for img in self.l_dcmFileNames:
- self._dcm = dicom.read_file(img,force=True)
- image = self._dcm.pixel_array
+ self._dcm = dicom.read_file(img,force=True)
+ self.lstr_inputFile.append(os.path.basename(img))
+ image = self._dcm.pixel_array
self._dcmList.append(self._dcm)
#print('%s: %s' % (img, image.shape))
try:
@@ -499,20 +524,21 @@ def __init__(self, **kwargs):
except Exception as e:
self.warn(
'dcmInsertionFail',
- '\nFor input DICOM file %s%s' % (img, str(e)),
+ '\nFor input DICOM file %s, %s' % (img, str(e)),
True)
i += 1
if self.str_outputFileStem.startswith('%'):
- str_spec = self.str_outputFileStem
+ str_spec = self.str_outputFileStem
self.str_outputFileStem = ''
for key in str_spec.split('%')[1:]:
- str_fileComponent = ''
+ str_fileComponent = ''
if key == 'inputFile':
- str_fileName, str_ext = os.path.splitext(self.str_inputFile)
- str_fileComponent = str_fileName
+ str_fileName, str_ext = os.path.splitext(self.str_inputFile)
+ str_fileComponent = str_fileName
else:
- str_fileComponent = eval('self._dcm.%s' % key)
- str_fileComponent = med2image.urlify(str_fileComponent)
+ # pudb.set_trace()
+ str_fileComponent = eval('str(self._dcm.%s)' % key)
+ str_fileComponent = med2image.urlify(str_fileComponent)
if not len(self.str_outputFileStem):
self.str_outputFileStem = str_fileComponent
else:
@@ -543,17 +569,17 @@ def warn(self, str_tag, str_extraMsg = '', b_exit = False):
str_action = med2image._dictErr[str_tag]['action']
str_error = med2image._dictErr[str_tag]['error']
exitCode = med2image._dictErr[str_tag]['exitCode']
- self.dp.qprint(
+ self.LOG(
'Some error seems to have occured!', comms = 'error'
)
- self.dp.qprint(
+ self.LOG(
'While %s' % str_action, comms = 'error'
)
- self.dp.qprint(
+ self.LOG(
'%s' % str_error, comms = 'error'
)
if len(str_extraMsg):
- self.dp.qprint(str_extraMsg, comms = 'error')
+ self.LOG(str_extraMsg, comms = 'error')
if b_exit:
sys.exit(exitCode)
@@ -561,57 +587,75 @@ def run(self):
'''
Runs the DICOM conversion based on internal state.
'''
- self.dp.qprint('Converting DICOM image.')
+ self.LOG('DICOM conversion (ref: %s).' % self.lstr_inputFile[0])
+ if self._b_convertMiddleSlice:
+ self.LOG('Converting middle slice in DICOM series')
try:
- self.dp.qprint('PatientName: %s' % self._dcm.PatientName)
+ self.LOG('\tPatientName: %s' % self._dcm.PatientName)
except AttributeError:
- self.dp.qprint('PatientName: %s' % 'PatientName not found in DCM header.')
+ self.LOG('\tPatientName: %s' % 'PatientName not found in DCM header.')
self.warn( 'PatientNameTag')
try:
- self.dp.qprint('PatientAge: %s' % self._dcm.PatientAge)
+ self.LOG('\tPatientAge: %s' % self._dcm.PatientAge)
except AttributeError:
- self.dp.qprint('PatientAge: %s' % 'PatientAge not found in DCM header.')
+ self.LOG('\tPatientAge: %s' % 'PatientAge not found in DCM header.')
self.warn( 'PatientAgeTag')
try:
- self.dp.qprint('PatientSex: %s' % self._dcm.PatientSex)
+ self.LOG('\tPatientSex: %s' % self._dcm.PatientSex)
except AttributeError:
- self.dp.qprint('PatientSex: %s' % 'PatientSex not found in DCM header.')
+ self.LOG('\tPatientSex: %s' % 'PatientSex not found in DCM header.')
self.warn( 'PatientSexTag')
try:
- self.dp.qprint('PatientID: %s' % self._dcm.PatientID)
+ self.LOG('\tPatientID: %s' % self._dcm.PatientID)
except AttributeError:
- self.dp.qprint('PatientID: %s' % 'PatientID not found in DCM header.')
+ self.LOG('\tPatientID: %s' % 'PatientID not found in DCM header.')
self.warn( 'PatientIDTag')
try:
- self.dp.qprint('SeriesDescription: %s' % self._dcm.SeriesDescription)
+ self.LOG('\tSeriesDescription: %s' % self._dcm.SeriesDescription)
except AttributeError:
- self.dp.qprint('SeriesDescription: %s' % 'SeriesDescription not found in DCM header.')
+ self.LOG('\tSeriesDescription: %s' % 'SeriesDescription not found in DCM header.')
self.warn( 'SeriesDescriptionTag')
try:
- self.dp.qprint('ProtocolName: %s' % self._dcm.ProtocolName)
+ self.LOG('\tProtocolName: %s' % self._dcm.ProtocolName)
except AttributeError:
- self.dp.qprint('ProtocolName: %s' % 'ProtocolName not found in DCM header.')
+ self.LOG('\tProtocolName: %s' % 'ProtocolName not found in DCM header.')
self.warn( 'ProtocolNameTag')
- if self._b_convertMiddleSlice:
- self.dp.qprint('Converting middle slice in DICOM series: %d' % self._sliceToConvert)
-
- l_rot90 = [ True, True, False ]
+ l_rot90 = [ bool(int(self.rot[0])), bool(int(self.rot[1])), bool(int(self.rot[2])) ]
med2image.mkdir(self.str_outputDir)
if not self._b_3D:
- str_outputFile = '%s/%s.%s' % (self.str_outputDir,
+ if self.preserveDICOMinputName:
+ str_outputFile = '%s/%s.%s' % (self.str_outputDir,
+ os.path.splitext(self.lstr_inputFile[0])[0],
+ self.str_outputFileType)
+ else:
+ str_outputFile = '%s/%s.%s' % (self.str_outputDir,
self.str_outputFileStem,
self.str_outputFileType)
self.process_slice()
self.slice_save(str_outputFile)
if self._b_3D:
+ dims = self._Vnp_3DVol.shape
+ self.LOG('Image volume logical (i, j, k) size: %s' % str(dims))
rotCount = 0
if self._b_reslice:
for dim in ['x', 'y', 'z']:
- self.dim_save(dimension = dim, makeSubDir = True, rot90 = l_rot90[rotCount], indexStart = 0, indexStop = -1)
+ self.dim_save(
+ dimension = dim,
+ makeSubDir = True,
+ rot90 = l_rot90[rotCount],
+ indexStart = 0,
+ indexStop = -1
+ )
rotCount += 1
else:
- self.dim_save(dimension = 'z', makeSubDir = False, rot90 = False, indexStart = 0, indexStop = -1)
+ self.dim_save(
+ dimension = 'z',
+ makeSubDir = False,
+ rot90 = l_rot90[2],
+ indexStart = 0,
+ indexStop = -1
+ )
class med2image_nii(med2image):
@@ -641,7 +685,7 @@ def run(self):
Runs the NIfTI conversion based on internal state.
'''
- self.dp.qprint('About to perform NifTI to %s conversion...\n' %
+ self.LOG('About to perform NifTI to %s conversion...\n' %
self.str_outputFileType)
frames = 1
@@ -652,10 +696,10 @@ def run(self):
sliceEnd = 0
if self._b_4D:
- self.dp.qprint('4D volume detected.\n')
+ self.LOG('4D volume detected.\n')
frames = self._Vnp_4DVol.shape[3]
if self._b_3D:
- self.dp.qprint('3D volume detected.\n')
+ self.LOG('3D volume detected.\n')
if self._b_convertMiddleFrame:
self._frameToConvert = int(frames/2)
@@ -689,13 +733,49 @@ def run(self):
class object_factoryCreate:
"""
A class that examines input file string for extension information and
- returns the relevant convert object.
+ creates a relevant convert object (or None).
+
+ Returns true or false denoting converter object creation.
"""
def __init__(self, args):
"""
Parse relevant CLI args.
"""
+
+ def inputFile_defineFromSubStr(astr_dir):
+ """
+ This nested function simply determines the first
+ file in the that has a substring in the
+ filename that conforms to the .
+
+ That file becomes the . The
+ is a useful shortcut for quickly identifying and using
+ a file without needing to provide its full name.
+ """
+ l_filesInDir : list = []
+ l_fileHit : list = []
+ # First, get a list of all the files in the directory
+ try:
+ (_, _, l_filesInDir) = next(os.walk(astr_dir))
+ except:
+ return ''
+ l_fileHit = [
+ s for s in l_filesInDir if args.inputFileSubStr in s
+ ]
+ if len(l_fileHit):
+ return l_fileHit[0]
+ else:
+ return ''
+
+ if len(args.inputFileSubStr):
+ args.inputFile = inputFile_defineFromSubStr(args.inputDir)
+ if not len(args.inputFile):
+ print( 'Input dir "%s" has no files with substring "%s"' %
+ (args.inputDir, args.inputFileSubStr))
+ # print('Exiting to system with code 1.')
+ # sys.exit(1)
+
str_outputFileStem, str_outputFileExtension = os.path.splitext(args.outputFileStem)
if len(str_outputFileExtension):
str_outputFileExtension = str_outputFileExtension.split('.')[1]
@@ -713,28 +793,38 @@ def __init__(self, args):
b_niftiExt = (str_inputFileExtension == '.nii' or
str_inputFileExtension == '.gz')
b_dicomExt = str_inputFileExtension == '.dcm'
+
+ self.C_convert = None
if b_niftiExt:
self.C_convert = med2image_nii(
- inputFile = args.inputFile,
- inputDir = args.inputDir,
- outputDir = args.outputDir,
- outputFileStem = args.outputFileStem,
- outputFileType = args.outputFileType,
- sliceToConvert = args.sliceToConvert,
- frameToConvert = args.frameToConvert,
- showSlices = args.showSlices,
- reslice = args.reslice
+ inputFile = args.inputFile,
+ inputDir = args.inputDir,
+ outputDir = args.outputDir,
+ outputFileStem = args.outputFileStem,
+ outputFileType = args.outputFileType,
+ sliceToConvert = args.sliceToConvert,
+ frameToConvert = args.frameToConvert,
+ showSlices = args.showSlices,
+ func = args.func,
+ reslice = args.reslice,
+ verbosity = args.verbosity
)
print('sliceToConvert:', args.sliceToConvert)
if b_dicomExt:
self.C_convert = med2image_dcm(
- inputFile = args.inputFile,
- inputDir = args.inputDir,
- outputDir = args.outputDir,
- outputFileStem = args.outputFileStem,
- outputFileType = args.outputFileType,
- sliceToConvert = args.sliceToConvert,
- reslice = args.reslice
+ inputFile = args.inputFile,
+ inputDir = args.inputDir,
+ outputDir = args.outputDir,
+ outputFileStem = args.outputFileStem,
+ outputFileType = args.outputFileType,
+ sliceToConvert = args.sliceToConvert,
+ convertOnlySingleDICOM = args.convertOnlySingleDICOM,
+ preserveDICOMinputName = args.preserveDICOMinputName,
+ reslice = args.reslice,
+ rot = args.rot,
+ rotAngle = args.rotAngle,
+ func = args.func,
+ verbosity = args.verbosity
)
diff --git a/setup.py b/setup.py
index 61c6221..b2e6f34 100644
--- a/setup.py
+++ b/setup.py
@@ -12,14 +12,14 @@ def readme():
setup(
name = 'med2image',
- version = '2.0.1',
+ version = '2.6.6',
description = '(Python) utility to convert medical images to jpg and png',
long_description = readme(),
author = 'FNNDSC',
author_email = 'dev@babymri.org',
url = 'https://github.com/FNNDSC/med2image',
packages = ['med2image'],
- install_requires = ['nibabel', 'dicom', 'pydicom', 'numpy', 'matplotlib', 'pillow'],
+ install_requires = ['pfmisc', 'nibabel', 'pydicom', 'numpy', 'matplotlib', 'pillow'],
#test_suite = 'nose.collector',
#tests_require = ['nose'],
scripts = ['bin/med2image'],