From 1cdeb8e2a6873410acfb6d576138e0ae11d7b420 Mon Sep 17 00:00:00 2001 From: SungHo Lee Date: Sat, 9 May 2020 17:55:36 -0400 Subject: [PATCH] release --- README.md | 94 +++-- brkraw/lib/utils.py | 9 +- examples/BrkRaw_PythonAPI.ipynb | 648 ++++++++++++++++++++++++++------ 3 files changed, 607 insertions(+), 144 deletions(-) diff --git a/README.md b/README.md index 42f6fa6..b5d2561 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,36 @@ -# BrkRaw: Comprehensive tool to handle Bruker PV dataset +# BrkRaw: Comprehensive tool to access Bruker PV dataset ## Version: 0.3 ### Description -The **BrkRaw** module designed to cover the needs from various type of user as much as possible, +The 'BrkRaw' python module is designed to be a more comprehensive tool for the preclinical MRI community for accessing and +utilizing raw data. And since the converter is front-line tools for medical imaging, the functionality is developed to cover +the requirements from the various user, including MRI system operator, maintainer, MR sequence developer, imaging researcher, +and data scientist. In addition to these, we had put extra efforts to preserve the metadata as well as provide tools to help +organize the data structure into a shareable format that suggested from the open science community for pursuing +reproducible science [BIDS](https://bids.neuroimaging.io). Therefore, the module designed not only can be used for the NifTi converter, +but also provides command-line tools and python API for previewing, organizing and archiving data, and parsing metadata, +accessing the data as users convenient object type ([nibabel](https://nipy.org/nibabel/) or +[SimpleITK](https://simpleitk.readthedocs.io/en/master/gettingStarted.html#python-binary-files)) +without the conversion step. The module is compatible with the ZIP file format, so no need to uncompress the file to access data. including MRI system operator, maintainer, MR sequence developer, imaging researcher, and data scientist. This module provides easy-to-access of the Bruker's PVdataset from PV 5 to PV 6.0.1 (it has not been tested for above versions) - -- preserving the subject position and orientation to converted the NifTi1 file. -- correction of animal orientation based on the species and position. (Anterior of subject is Anterior) -- providing the GUI tool for preview the dataset and NifTi1 format conversion. -- the command-line tool for converting to NifTi1 format, previewing metadata of the dataset, checking backup status. -- providing fMRI and DTI study friendly features: slice-order update on the header, Diffusion parameter file generation. -- BIDS(v1.2.2) support: parameter file generation with custom syntax, automatic generation of the folder structure. -- Object-oriented robust dataset parser. -- compressed data readability (compatible with .zip and .PVdatasets format). -- providing robust and easy-to-use python API for developers, including JCAMP-DX parser. -- the python API also providing data handler object through either nibabel and simpleITK to make convenient to the researcher can implement their own code. +The major features of this module as follows. + +- Reliable converting function with + - preserving the subject position and orientation to converted the NifTi1 file. + - correction of animal orientation based on the species and position. (Anterior of subject is Anterior) + - providing fMRI and DTI study friendly features: slice-order update on the header, Diffusion parameter file generation. + - BIDS(v1.2.2) support: parameter file generation with custom syntax, automatic generation of the folder structure. +- Capability of quick image validation by + - providing the GUI tool for preview the dataset without conversion. + - the command-line tool function for previewing metadata of the dataset for each scan. +- Data management tool 'brk-backup' + - Data management tool 'brk-backup' for archiving and performing inspection the backup status. +- Robust and easy-to-use python API for developers, including JCAMP-DX parser. + - Object-oriented robust dataset parser. + - compressed data readability (compatible with .zip and .PVdatasets format). + - the python API also providing data handler object through either nibabel and simpleITK to make convenient to the researcher can implement their own code. ![example_alignment](imgs/brkraw_alignment.png) @@ -55,16 +69,21 @@ $ pip install git+https://github.com/dvm-shlee/bruker #### Known issues - In most case, the issue will related to the pyenv build, please refer the above links to solve the issue. - If you experience any other issue, please use 'issue' tab in Github to report. +- If the dataset contains MR Spectroscopy, some method not work properly (such as summary to print out meta data) +- The legacy module 'pyBruker' had issues at orientation in the data has oblique FOV. This module resolved most of the issue, but if you experiencing +that any modality image is incorrectly positioned compared with any other modality, please report use. (except the image required custom reconstruction) -### Usage -#### Linux/Unix +## Usage +### Command-line tool (brkraw) +#### Quick access of metadata - Printing out dataset information ```angular2html $ brkraw summary ``` ![brkraw summary](imgs/brkraw_print_summary.png) -**Fig2.** Example of printed out dataset information +**Example of printed out dataset information** +#### Legacy converting to NifTi1 format - Convert a whole session, (adding option '-b' or '--bids' will generate JSON file that contains MR parameters based-on BIDS standard) ```angular2html $ brkraw tonii @@ -75,37 +94,54 @@ $ brkraw tonii $ brkraw tonii -s -r ``` -- Build BIDS dataset with multiple Bruker raw datasets. +- Build BIDS dataset with multiple Bruker raw datasets. +- Required to copy the datasets into the parent folder. - All dataset under parent folder will be converted into ./Data folder with BIDS ```angular2html $ brkraw tonii_all ``` -- Create BIDS file table with excel format to rename the file accordingly for BIDS standard +#### Automatic BIDS organizer with template files +![brkraw bids](imgs/brkraw_bids_conv.png) +**The usage of the command-line tool 'brkraw' for BIDS data organization.** + +- Upgraded feature to reduce burden on renaming according to BIDS standard. +- Create BIDS file table with excel format to rename the file accordingly for BIDS standard. +- If you need to crop data, you can also specify its range on excel file for each scan. +- This will return also the BIDS_META_REF.json which allows you to input the template of BIDS json parser syntax +- To learn more detail, please check our example Jupyter Notebook. ```angular2html $ brkraw bids_list .xlsx ``` - Build BIDS dataset according to the excel file generated with 'bids_list' command above. ```angular2html -$ brkraw bids_converter +$ brkraw bids_converter + +$ brkraw bids_converter -r ``` ![brkraw summary](imgs/brkraw_bids.png) -**Fig3.** Example of automatically generated BIDS dataset +**Example of automatically generated BIDS dataset** - Run GUI with input and output path ```angular2html $ brkraw gui -i -o ``` ![brkraw GUI](imgs/brkraw_gui.png) -**Fig4.** brkraw gui interface. +**brkraw gui interface.** -- Run GUI without path +- Run GUI without path, make sure you select correct button based on the dataset type (file or folder) +- In case of loading folder, you need to enter to the folder instead of just selecting it. ```angular2html $ brkraw gui ``` +### Data management tool +![brk-backup](imgs/brk_backup.png) +**brk-backup script utilizing the Python API to immediately access both raw data and archived data +to parse the metadata for data management.** + - Print out archived dataset and condition ```angular2html $ brk-backup archived @@ -136,6 +172,8 @@ $ brk-backup clean -l - If this command is not working, please check the version of your Anaconda and Python. #### Python API +- To learn more detail, please check the Jupyter notebook in 'examples' folder + - import module ```angular2html >>> import brkraw @@ -150,18 +188,14 @@ $ brk-backup clean -l ### Contributing - Please contact shlee@unc.edu if you interest to contribute for following items. -1. improve compatibility with other python versions. -2. integration of reconstruction tool with Python API (such as BART tool). -3. develop online analysis tools for fMRI study. -4. Documentation +1. integration of reconstruction tool with Python API (such as BART tool). +2. develop online analysis tools for fMRI or DTI study. +3. Documentation or develop tutorials for various use. - Also if you experience any bug or have any suggestion to improve this tool, please let us know. ### Credits: - SungHo Lee (shlee@unc.edu) -- Woomi Ban (banwoomi@unc.edu) - -### Citation -- +- Woomi Ban (banwoomi@unc.edu) ### License: GNU General Public License v3.0 diff --git a/brkraw/lib/utils.py b/brkraw/lib/utils.py index 7657e51..f33c23b 100644 --- a/brkraw/lib/utils.py +++ b/brkraw/lib/utils.py @@ -328,10 +328,15 @@ def meta_get_value(value, acqp, method, visu_pars): return parser elif isinstance(value, list): parser = [] - for vi in value: + max_index = len(value) - 1 + for i, vi in enumerate(value): val = meta_get_value(vi, acqp, method, visu_pars) if val is not None: - parser.append(val) + if val == vi: + if i == max_index: + parser.append(val) + else: + parser.append(val) if len(parser) > 0: return parser[0] else: diff --git a/examples/BrkRaw_PythonAPI.ipynb b/examples/BrkRaw_PythonAPI.ipynb index ee87a2d..484392f 100644 --- a/examples/BrkRaw_PythonAPI.ipynb +++ b/examples/BrkRaw_PythonAPI.ipynb @@ -27,7 +27,7 @@ "metadata": {}, "outputs": [], "source": [ - "path = './20190724_114946_BRKRAW_1_1.zip'\n", + "path = '20190724_114946_BRKRAW_1_1.zip'\n", "pvdset = brkraw.load(path)" ] }, @@ -64,23 +64,23 @@ "\n", "[ScanID]\tSequence::Protocol::[Parameters]\n", "[001]\tBruker:FLASH::0_Localizer_GOP::\n", - "\t[ TR: 100 ms, TE: 2.500 ms, pixelBW: 292.97 Hz, FlipAngle: 30 degree]\n", + "\t[ TR: 100 ms, TE: 2.5 ms, pixelBW: 292.96875 Hz, FlipAngle: 30 degree]\n", " [01] dim: 2D, matrix_size: 256 x 256 x 3, fov_size: 50 x 50 (unit:mm)\n", " spatial_resol: 0.195 x 0.195 x 1.000 (unit:mm), temporal_resol: 12800.000 (unit:msec)\n", "[002]\tBruker:RARE::T2_TurboRARE_3D_20180423::\n", - "\t[ TR: 1800 ms, TE: 34.000 ms, pixelBW: 685.31 Hz, FlipAngle: 90 degree]\n", + "\t[ TR: 1800 ms, TE: 34 ms, pixelBW: 685.30701754386 Hz, FlipAngle: 90 degree]\n", " [01] dim: 3D, matrix_size: 144 x 144 x 64, fov_size: 28.8 x 28.8 x 12.8 (unit:mm)\n", " spatial_resol: 0.200 x 0.200 x 0.200 (unit:mm), temporal_resol: 1134000.000 (unit:msec)\n", "[003]\tBruker:EPI::2_BOLD_EPI_isotropic_wholebrain::\n", - "\t[ TR: 2000 ms, TE: 14.000 ms, pixelBW: 3472.22 Hz, FlipAngle: 70 degree]\n", + "\t[ TR: 2000 ms, TE: 14 ms, pixelBW: 3472.22222222222 Hz, FlipAngle: 70 degree]\n", " [01] dim: 2D, matrix_size: 72 x 72 x 32, fov_size: 28.8 x 28.8 (unit:mm)\n", " spatial_resol: 0.400 x 0.400 x 0.400 (unit:mm), temporal_resol: 2000.000 (unit:msec)\n", "[004]\tBruker:FieldMap::B0Map-ADJ_B0MAP::\n", - "\t[ TR: 20 ms, TE: 0.000 ms, pixelBW: 1860.12 Hz, FlipAngle: 30 degree]\n", + "\t[ TR: 20 ms, TE: 0 ms, pixelBW: 1860.11904761905 Hz, FlipAngle: 30 degree]\n", " [01] dim: 3D, matrix_size: 64 x 64 x 64, fov_size: 58 x 58 x 58 (unit:mm)\n", " spatial_resol: 0.906 x 0.906 x 0.906 (unit:mm), temporal_resol: 81920.000 (unit:msec)\n", "[005]\tBruker:EPI::2_BOLD_EPI_isotropic_wholebrain::\n", - "\t[ TR: 2000 ms, TE: 14.000 ms, pixelBW: 3472.22 Hz, FlipAngle: 70 degree]\n", + "\t[ TR: 2000 ms, TE: 14 ms, pixelBW: 3472.22222222222 Hz, FlipAngle: 70 degree]\n", " [01] dim: 2D, matrix_size: 72 x 72 x 32, fov_size: 28.8 x 28.8 (unit:mm)\n", " spatial_resol: 0.400 x 0.400 x 0.400 (unit:mm), temporal_resol: 2000.000 (unit:msec)\n", "\n", @@ -92,6 +92,63 @@ "pvdset.summary()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check session start time" + ] + }, + { + "cell_type": "code", + "execution_count": 90, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'date': datetime.date(2019, 7, 24), 'start_time': datetime.time(11, 49, 46)}" + ] + }, + "execution_count": 90, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pvdset.get_scan_time()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Check scan start time" + ] + }, + { + "cell_type": "code", + "execution_count": 91, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'date': datetime.date(2019, 7, 24),\n", + " 'start_time': datetime.time(11, 49, 46),\n", + " 'scan_time': datetime.time(13, 10, 49)}" + ] + }, + "execution_count": 91, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "visu_pars = pvdset.get_visu_pars(5, 1)\n", + "pvdset.get_scan_time(visu_pars)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -101,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -118,7 +175,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -148,14 +205,14 @@ "NumberShots:\t\t\t\t1\n", "ParallelReductionFactorInPlane:\t\t1\n", "ParallelAcquisitionTechnique:\t\tNone\n", - "PartialFourier:\t\t\t\t1, 1.4\n", + "PartialFourier:\t\t\t\t1,1.4\n", "PartialFourierDirection:\t\tNone\n", "PhaseEncodingDirection:\t\t\tj\n", "EffectiveEchoSpacing:\t\t\t4.000000000000003e-06\n", "TotalReadoutTime:\t\t\t0.00028800000000000017\n", "EchoTime:\t\t\t\t14\n", "InversionTime:\t\t\t\tNone\n", - "SliceTiming:\t\t\t\t0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5, 1.625, 1.75, 1.875, 0.0625, 0.1875, 0.3125, 0.4375, 0.5625, 0.6875, 0.8125, 0.9375, 1.0625, 1.1875, 1.3125, 1.4375, 1.5625, 1.6875, 1.8125, 1.9375\n", + "SliceTiming:\t\t\t\t0.0,0.125,0.25,0.375,0.5,0.625,0.75,0.875,1.0,1.125,1.25,1.375,1.5,1.625,1.75,1.875,0.0625,0.1875,0.3125,0.4375,0.5625,0.6875,0.8125,0.9375,1.0625,1.1875,1.3125,1.4375,1.5625,1.6875,1.8125,1.9375\n", "SliceEncodingDirection:\t\t\tk\n", "DwellTime:\t\t\t\t0.00028800000000000017\n", "FlipAngle:\t\t\t\t70\n", @@ -175,12 +232,22 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "- the BIDS recommended MR parameters are defined at reference module, by changing METADATA_FILED_INFO, the metadata can be modified easily." + "# BIDS parser syntax\n", + "\n", + "#### Matadata Field Mapping for Bruker PvDataset\n", + " - BIDS Metadata will be automatically created according to below reference.\n", + " - If list is entered as value, each parameter will be tested and the first available value will be returned.\n", + " - If dict is entered as value, below condition will be tested.\n", + " - If key - where pair: parse value from given key and return index of 'where' from these values\n", + " - If key - idx pair: parse value from given key and return value of given 'idx'\n", + " - If 'Equation' in key: each key assigned as local variable and test in Equation will be executed to return the value\n", + " - Else, new key - value dictionary will be return (for the cases with sub-keys)\n", + " - If string is entered as value, The value of given parameter will be parsed from parameter files" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -259,9 +326,193 @@ } ], "source": [ + "# the default metadata reference.\n", "pp.pprint(brkraw.lib.reference.METADATA_FILED_INFO)" ] }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ThisIsFlipAngle:\t\t\t70\n", + "ThisIsJustTest:\t\t\t\tIm just string value\n", + "ThisIsEquation:\t\t\t\t7\n", + "Hierarchy:\t\t\t\t{'SecondLevel': 'ImHere', 'TryEquation': [0, 1, 2]}\n", + "SequenceName:\t\t\t\t2_BOLD_EPI_isotropic_wholebrain\n", + "DefaultKeyIfNone:\t\t\tI am default value if nothing exists\n" + ] + } + ], + "source": [ + "metadata_ref = dict(ThisIsFlipAngle='VisuAcqFlipAngle', # any parameter key among method, acqp, visupars can be used here\n", + " ThisIsJustTest='Im just string value',\n", + " \n", + " # If any operation required to set value, below example can be used.\n", + " ThisIsEquation=dict(a=3, \n", + " b=4, \n", + " Equation='a+b'),\n", + " \n", + " # Any standard and numpy method can be utilized to evaluate the value if needed.\n", + " Hierarchy=dict(SecondLevel='I am in the second level!',\n", + " TryEquation=dict(abc='3',\n", + " Equation='list(range(abc))')),\n", + " \n", + " # Some parameter does not exists depends on the version of Paravision, this case\n", + " # You can use multiple keys to parse the first available value.\n", + " SequenceName = ['VisuAcquisitionProtocol', 'ACQ_protocol_name'], \n", + " \n", + " # If no keys exists in parameter files, then you can set default string.\n", + " DefaultKeyIfNone = ['ImNotExistingKey', 'ImNeither', 'I am default value if nothing exists']\n", + " )\n", + "\n", + "pvdset.print_bids(scan_id, reco_id, metadata=metadata_ref)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "visu_pars = pvdset.get_visu_pars(scan_id, reco_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 1.4]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Some key contains multiple values\n", + "visu_pars.parameters['VisuAcqPartialFourier']" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "IHaveTwoValue:\t\t\t\t1,1.4\n", + "IWillTakeOnlyFirstValue:\t\t1\n", + "ImCuriousTheIndexOfSpecificValue:\t1\n" + ] + } + ], + "source": [ + "# To parse only one value, you can use index\n", + "metadata_ref = dict(IHaveTwoValue='VisuAcqPartialFourier',\n", + " IWillTakeOnlyFirstValue=dict(key='VisuAcqPartialFourier',\n", + " idx=0),\n", + " ImCuriousTheIndexOfSpecificValue=dict(key='VisuAcqPartialFourier',\n", + " where=1.4))\n", + "pvdset.print_bids(scan_id, reco_id, metadata=metadata_ref)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- To use your own reference file for bids_converter function in command-line tool, please modify the json file generated by bids_list function. The Python API can be used for debuging purpose, and in case you want to implement your python dictionaty object instead of using file generated, dump your dictionary into JSON file as below" + ] + }, + { + "cell_type": "code", + "execution_count": 76, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "with open('your_filename.json', 'w') as f:\n", + " json.dump(metadata_ref, f, indent=4)" + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{\n", + "\n", + " \"IHaveTwoValue\": \"VisuAcqPartialFourier\",\n", + "\n", + " \"IWillTakeOnlyFirstValue\": {\n", + "\n", + " \"key\": \"VisuAcqPartialFourier\",\n", + "\n", + " \"idx\": 0\n", + "\n", + " },\n", + "\n", + " \"ImCuriousTheIndexOfSpecificValue\": {\n", + "\n", + " \"key\": \"VisuAcqPartialFourier\",\n", + "\n", + " \"where\": 1.4\n", + "\n", + " }\n", + "\n", + "}\n" + ] + } + ], + "source": [ + "# The dumbed file will look like this.\n", + "with open('your_filename.json', 'r') as f:\n", + " for l in f.readlines():\n", + " print(l)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Access Diffusion Direction for DTI image" + ] + }, + { + "cell_type": "code", + "execution_count": 86, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(None, None, None)" + ] + }, + "execution_count": 86, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pvdset.get_bdata(scan_id) # bval, bvec, bmat will returned (no example DTI file provided here)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -271,7 +522,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -292,7 +543,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -350,6 +601,95 @@ "print(niiobj1.header)" ] }, + { + "cell_type": "markdown", + "metadata": { + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "### Access SimpleITK image object (the header is not preserved well compared to the nifti)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0.4, 0.4, 0.4)\n", + "(72, 72, 32)\n", + "(-0.999943970464059, 7.49879891330929e-33, -0.010585647480084857, 6.481839651694157e-19, 1.0, -6.122890913777527e-17, -0.010585647480084857, 6.123233995736766e-17, 0.999943970464059)\n", + "(14.3814929469549, -14.96818, 1.8244364794273555)\n" + ] + } + ], + "source": [ + "sitkobj = pvdset.get_sitkimg(scan_id, reco_id)\n", + "print(sitkobj.GetSpacing())\n", + "print(sitkobj.GetSize())\n", + "print(sitkobj.GetDirection())\n", + "print(sitkobj.GetOrigin())" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[ 0.4 -0. 0.004 -14.381]\n", + " [ -0. -0.4 0. 14.968]\n", + " [ -0.004 0. 0.4 1.824]\n", + " [ 0. 0. 0. 1. ]]\n" + ] + } + ], + "source": [ + "# Construct affine matrix from SimpleITK image object.\n", + "from nibabel.affines import from_matvec\n", + "resol = np.diag(sitkobj.GetSpacing())\n", + "direction = np.asarray(sitkobj.GetDirection())[:9].reshape([3, 3])\n", + "direction = resol.dot(direction)\n", + "origin = np.asarray(sitkobj.GetOrigin())[:3]\n", + "affine_from_sitk = from_matvec(direction, origin)\n", + "affine_from_sitk = np.diag([-1, -1, 1, 1]).dot(affine_from_sitk)\n", + "print(affine_from_sitk)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# export nifti data\n", + "pvdset.save_as(scan_id, reco_id, 'your_filename', \n", + " dir='./', ext='nii.gz', \n", + " crop=[10, 100]) # you can crop the data thorugh this option\n", + "\n", + "\n", + "# export diffution parameters !! This will only function when image has diffusion direction in their header, \n", + "# the example data does not have it.\n", + "pvdset.save_bdata(scan_id, 'your_filename', \n", + " dir='./')\n", + "\n", + "# export BIDS json file with your metadata_ref\n", + "pvdset.save_json(scan_id, reco_id, 'your_filename', \n", + " dir='./', metadata=metadata_ref)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -357,12 +697,12 @@ "## Low-level API\n", "\n", "### load JCAMP-DX parameter objects\n", - "- Using below, each parameter can be accessed as dictionary object." + "- Using the methods below, each parameter can be accessed as dictionary object. and the value is converted to python compatible type (some value may not converted due to the complex structure)" ] }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -373,7 +713,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 23, "metadata": { "scrolled": true }, @@ -420,7 +760,7 @@ " '$Bis,1,20190111,2048,PICS,5#\\\\ $Prd,1PT10324,S0030,,,Bruker,0#\\\\ $Nam,Generic Surface Receive Array 2x2#\\\\ $PComp,1.0,MRI,,400#\\\\ $PType,1.0,MRI#\\\\ $PACoi,1.0,4#\\\\ $PMRI,1.0,Other,,,#\\\\ $PChan,1.0,1H,,,,,,,,#\\\\ $PCoil,1.0,1,,1,1H#\\\\ $PCoil,1.0,2,,1,1H#\\\\ $PCoil,1.0,3,,1,1H#\\\\ $PCoil,1.0,4,,1,1H#\\\\ $PCConn,1.0,1,R/P/A/N,,,,,,,#\\\\ $PCConn,1.0,2,R/P/A/N,,,,,,,#\\\\ $PCConn,1.0,3,R/P/A/N,,,,,,,#\\\\ $PCConn,1.0,4,R/P/A/N,,,,,,,#\\\\ $EndBis,DB,DD'])])" ] }, - "execution_count": 37, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -429,9 +769,40 @@ "pvdset._subject.parameters" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- All parameter object has attibutes of headers and parameters" + ] + }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('TITLE', 'Parameter List, ParaVision 6.0.1'),\n", + " ('JCAMPDX', 4.24),\n", + " ('DATATYPE', 'Parameter Values'),\n", + " ('ORIGIN', 'Bruker BioSpin MRI GmbH'),\n", + " ('OWNER', 'nmrsu')])" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "acqp.headers" + ] + }, + { + "cell_type": "code", + "execution_count": 25, "metadata": { "scrolled": true }, @@ -1761,7 +2132,7 @@ " ('ACQ_MPI_frequency_select', ['Yes', 'Yes', 'Yes'])])" ] }, - "execution_count": 22, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -1772,7 +2143,31 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('TITLE', 'Parameter List, ParaVision 6.0.1'),\n", + " ('JCAMPDX', 4.24),\n", + " ('DATATYPE', 'Parameter Values'),\n", + " ('ORIGIN', 'Bruker BioSpin MRI GmbH'),\n", + " ('OWNER', 'nmrsu')])" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "method.headers" + ] + }, + { + "cell_type": "code", + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -4164,7 +4559,7 @@ " ['BRUKER_PARIMPORT_MAPSHIM', None, 'Shim Volume']])])" ] }, - "execution_count": 23, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -4175,7 +4570,31 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "OrderedDict([('TITLE', 'Parameter List, ParaVision 6.0.1'),\n", + " ('JCAMPDX', 4.24),\n", + " ('DATATYPE', 'Parameter Values'),\n", + " ('ORIGIN', 'Bruker BioSpin MRI GmbH'),\n", + " ('OWNER', 'nmrsu')])" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "visu_pars.headers" + ] + }, + { + "cell_type": "code", + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -4508,7 +4927,7 @@ " ('VisuRespSynchUsed', 'No')])" ] }, - "execution_count": 24, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -4521,12 +4940,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Useful functions to check parameters for data reconstruction and conversion." + "### Useful method to get parameters for data reconstruction and conversion.\n", + "- All function listed above is private methods" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -4535,7 +4955,7 @@ "(2, 'spatial_only')" ] }, - "execution_count": 26, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -4546,7 +4966,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -4555,7 +4975,7 @@ "['read_enc', 'phase_enc']" ] }, - "execution_count": 28, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -4566,7 +4986,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -4580,7 +5000,7 @@ " 'dependent_vals': [[['VisuCoreOrientation', 0], ['VisuCorePosition', 0]]]}" ] }, - "execution_count": 29, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -4591,7 +5011,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -4603,7 +5023,7 @@ " 'unit_slice_distances': 'mm'}" ] }, - "execution_count": 30, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -4614,7 +5034,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -4626,7 +5046,7 @@ " 'unit': 'mm'}" ] }, - "execution_count": 31, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -4637,7 +5057,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -4646,7 +5066,7 @@ "{'temporal_resol': 2000.0, 'num_frames': 1, 'unit': 'msec'}" ] }, - "execution_count": 33, + "execution_count": 35, "metadata": {}, "output_type": "execute_result" } @@ -4657,7 +5077,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -4675,7 +5095,7 @@ " [-0.011, 1. , 0. ]]])}" ] }, - "execution_count": 35, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -4686,7 +5106,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -4695,7 +5115,7 @@ "'normal'" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -4713,25 +5133,25 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ - "dataobj = pvdset.get_dataobj(scan_id, reco_id) # dataobj will 1D array\n", - "matrix_size = pvdset.get_matrixsize(scan_id, reco_id) # need to use matrix_size to reshape dataobj\n", - "affine = pvdset.get_affine(scan_id, reco_id) # to get orientation matrix" + "dataobj = pvdset.get_dataobj(scan_id, reco_id, slp_correct=False) \n", + "matrix_size = pvdset.get_matrixsize(scan_id, reco_id) \n", + "affine = pvdset.get_affine(scan_id, reco_id) " ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 46, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "(165888,) [72, 72, 32]\n" + "(72, 72, 32) [72, 72, 32]\n" ] } ], @@ -4741,7 +5161,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 47, "metadata": {}, "outputs": [ { @@ -4761,16 +5181,7 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "reshaped_dataobj = dataobj.reshape(*matrix_size)" - ] - }, - { - "cell_type": "code", - "execution_count": 44, + "execution_count": 50, "metadata": {}, "outputs": [ { @@ -4779,115 +5190,128 @@ "dtype('int16')" ] }, - "execution_count": 44, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "reshaped_dataobj.dtype" + "dataobj.dtype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "- The dataobj is rescaled to be stored with int16 dtype, the original value need to be correct with following steps." + "- If slp_correct option is False, the dataobj returned the rescaled int16 data, \n", + "- If slp_correct option is True, below step will performed internally" ] }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ + "from brkraw.lib.utils import is_all_element_same\n", "data_slope = visu_pars.parameters['VisuCoreDataSlope']\n", "data_offset = visu_pars.parameters['VisuCoreDataOffs']" ] }, { "cell_type": "code", - "execution_count": 48, - "metadata": {}, - "outputs": [], - "source": [ - "from brkraw.lib.utils import is_all_element_same" - ] - }, - { - "cell_type": "code", - "execution_count": 45, + "execution_count": 74, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 45, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "True True\n" + ] } ], "source": [ - "is_all_element_same(data_slope) # check if all elements are same." + "print(is_all_element_same(data_slope), # check if all elements are same.\n", + " is_all_element_same(data_offset)\n", + " )" ] }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 75, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "float64\n" + ] } ], - "source": [ - "is_all_element_same(data_offset)" - ] - }, - { - "cell_type": "code", - "execution_count": 50, - "metadata": {}, - "outputs": [], "source": [ "# correcting intensity to original value\n", - "orj_dataobj = reshaped_dataobj * data_slope[0] + data_offset[0]" + "orj_dataobj = reshaped_dataobj * data_slope[0] + data_offset[0]\n", + "print(orj_dataobj.dtype)\n", + "\n", + "# build nibabel object\n", + "import nibabel as nib\n", + "niiobj2 = nib.Nifti1Image(orj_dataobj, affine)" ] }, { - "cell_type": "code", - "execution_count": 52, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "import nibabel as nib\n", - "niiobj2 = nib.Nifti1Image(orj_dataobj, affine)" + "### Access binary FID file obj" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 71, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GO_32BIT_SGN_INT\n", + "i\n" + ] + } + ], "source": [ - "### Access binary FID file obj" + "from brkraw.lib.reference import WORDTYPE\n", + "print(acqp.parameters['GO_raw_data_format'])\n", + "\n", + "dt_code = WORDTYPE[acqp.parameters['GO_raw_data_format'][2:]]\n", + "print(dt_code)\n", + "\n", + "fid_binary = pvdset.get_fid(scan_id)\n", + "\n", + "# convert to numpy array\n", + "fid = np.frombuffer(fid_binary, dt_code)" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 72, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(940032,)" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "fid_binary = pvdset.get_fid(scan_id)" + "fid.shape" ] } ], @@ -4907,9 +5331,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.6" + "version": "3.7.5" } }, "nbformat": 4, "nbformat_minor": 4 -} +} \ No newline at end of file