diff --git a/Applications/SlicerApp/Testing/Python/CMakeLists.txt b/Applications/SlicerApp/Testing/Python/CMakeLists.txt index cb76e0ba18926fa81b97d3f93c8b9f3b0d060eb5..4e55076334527f230cf45828e15b80f020b8fd03 100644 --- a/Applications/SlicerApp/Testing/Python/CMakeLists.txt +++ b/Applications/SlicerApp/Testing/Python/CMakeLists.txt @@ -289,6 +289,16 @@ slicer_add_python_unittest( TESTNAME_PREFIX nomainwindow_ ) +# Test multi-shell DWI I/O +slicer_add_python_test( + SCRIPT DWMRIMultishellIOTests.py + SLICER_ARGS --no-main-window --disable-modules --testing + SCRIPT_ARGS + ${Slicer_SOURCE_DIR}/ + ${Slicer_BINARY_DIR}/Testing/Temporary/ + TESTNAME_PREFIX nomainwindow_ + ) + # DCMTKPrivateDictionary test slicer_add_python_test( SCRIPT DCMTKPrivateDictTest.py diff --git a/Applications/SlicerApp/Testing/Python/DWMRIMultishellIOTests.py b/Applications/SlicerApp/Testing/Python/DWMRIMultishellIOTests.py new file mode 100644 index 0000000000000000000000000000000000000000..53aa0fa5810ebbf182e838e5bdcf3041ee0d236a --- /dev/null +++ b/Applications/SlicerApp/Testing/Python/DWMRIMultishellIOTests.py @@ -0,0 +1,183 @@ +import sys, os, re, nose +from nose.tools import assert_equal +from collections import namedtuple + +import numpy as np +import numpy.testing +from vtk.util import numpy_support +import slicer + +#=============================================================================== + +mrmlcore_testdata_path = "Libs/MRML/Core/Testing/TestData/" + + +multishell_dwi_451 = os.path.join(mrmlcore_testdata_path, "multishell-DWI-451dir.nhdr") + +#================================================================================ +NRRD = namedtuple('NRRD', ['header', 'bvalue', 'gradients']) + +def parse_nhdr(path): + dwmri_bval_key = "DWMRI_b-value" + dwmri_grad_keybase = "DWMRI_gradient_" + dwmri_grad_key_n = "DWMRI_gradient_{:04d}" + + kvdict = {} + grad_count = 0 + + with open(path, "rU") as f: + magic = f.readline().strip() + assert(magic == "NRRD0005") + + while True: + line = f.readline() + # NRRD data section is separated by a newline + if (line == "\n") or (line == "") or (line is None): + break + + # careful about precedence -- ":=" must match first + key, val = [x.strip() for x in re.split(":=|=|:", line)] + assert(not kvdict.has_key(key)) + kvdict[key] = val + + if key.startswith(dwmri_grad_keybase): + _gn = int(key[ len(dwmri_grad_keybase):None ]) + # monotonic keys + assert( _gn == grad_count ) # offset + grad_count += 1 + + bvalue = float(kvdict[dwmri_bval_key]) + grads = np.zeros((grad_count, 3)) + + # parse gradients + for i in range(0, grad_count): + grad_str = kvdict[dwmri_grad_key_n.format(i)] + grads[i] = np.fromstring(grad_str, count=3, dtype=np.float64, sep=" ") + + return NRRD(header=kvdict, bvalue=bvalue, gradients=grads) + + +#================================================================================ +def normalize(vec): + norm = np.linalg.norm(vec) + if norm == 0.0: + return vec + else: + return vec * 1/norm + + +def test_nrrd_dwi_load(first_file, second_file=None): + """ + - load a DWI NRRD file into Slicer + - validate b values and gradient vectors against original header + - check the values in the vtkMRMLDiffusionWeightedVolumeNode + - check the values in the vtkMRMLDWVNode attribute dictionary + """ + if second_file is None: + second_file = first_file + + # load NRRD into Slicer + storagenode = slicer.vtkMRMLNRRDStorageNode() + storagenode.SetFileName(first_file) + dw_node = slicer.vtkMRMLDiffusionWeightedVolumeNode() + storagenode.ReadData(dw_node) + + slicer_grads = numpy_support.vtk_to_numpy(dw_node.GetDiffusionGradients()) + slicer_numgrads = slicer_grads.shape[0] + + # load NRRD with pure-python parser + parsed_nrrd = parse_nhdr(second_file) + + ################################## + # 1) check the number of gradients + + assert( len(parsed_nrrd.gradients) == slicer_numgrads ) + + ################################## + # 2) check the node b values and gradients are correct + + # Note: vtkDataArray.GetMaxNorm gives max for scalar array. + # max b value from the node + nose.tools.assert_equal(parsed_nrrd.bvalue, dw_node.GetBValues().GetMaxNorm()) + + max_parsed_grad_norm = np.max(np.apply_along_axis(np.linalg.norm, 1, parsed_nrrd.gradients)) + + for i in range(0, slicer_numgrads): + g_parsed_raw = parsed_nrrd.gradients[i] + g_parsed_normed = normalize(g_parsed_raw) + + bval_parsed = parsed_nrrd.bvalue * pow(np.linalg.norm(g_parsed_raw) / max_parsed_grad_norm, 2) + np.testing.assert_almost_equal(bval_parsed, dw_node.GetBValue(i), decimal=7, + err_msg="MRMLNode b value does not match NRRD header") + + g_from_node = slicer_grads[i, :] + + # gradients stored in the vtkMRMLDiffusionWeightedVolumeNode must be *normalized*. + np.testing.assert_allclose(np.linalg.norm(g_parsed_normed - g_from_node), 0.0, atol=1e-15) + + # b value from the node attribute dictionary + np.testing.assert_equal(parsed_nrrd.bvalue, float(dw_node.GetAttribute("DWMRI_b-value"))) + + # 3) check gradients in the node attribute dictionary + # gradients must match the value on-disk. + for i in range(0, slicer_numgrads): + grad_key = "DWMRI_gradient_{:04d}".format(i) + parsed_gradient = np.fromstring(parsed_nrrd.header[grad_key], count=3, sep=' ', dtype=np.float64) + attr_gradient = np.fromstring(dw_node.GetAttribute(grad_key), count=3, sep=' ', dtype=np.float64) + + np.testing.assert_array_almost_equal(parsed_gradient, attr_gradient, decimal=12, + err_msg="NHDR gradient does not match gradient in node attribute dictionary") + + return (parsed_nrrd, dw_node) + +def test_nrrd_dwi_roundtrip(test_nrrd_path): + """DWI NRRD round-trip test + - loads and saves a NRRD file via Slicer's I/O, twice + - checks the node values against the original file each time + """ + + import tempfile + + # load and re-save NRRD once + storagenode1 = slicer.vtkMRMLNRRDStorageNode() + storagenode1.SetFileName(test_nrrd_path) + dw_node1 = slicer.vtkMRMLDiffusionWeightedVolumeNode() + storagenode1.ReadData(dw_node1) + __f_tmp_nrrd1 = tempfile.NamedTemporaryFile(suffix=".nhdr", dir=tmp_dir, delete=False) + tmp_nrrd1 = __f_tmp_nrrd1.name + storagenode1.SetFileName(tmp_nrrd1) + storagenode1.WriteData(dw_node1) + + parsed_nrrd2, dw_node2 = test_nrrd_dwi_load(test_nrrd_path, tmp_nrrd1) + + # re-save NRRD again + storagenode2 = slicer.vtkMRMLNRRDStorageNode() + __f_tmp_nrrd2 = tempfile.NamedTemporaryFile(suffix=".nhdr", dir=tmp_dir, delete=False) + tmp_nrrd2 = __f_tmp_nrrd2.name + storagenode2.SetFileName(tmp_nrrd2) + storagenode2.WriteData(dw_node2) + + # test twice-saved file against original NRRD + parsed_nrrd3, dw_node3 = test_nrrd_dwi_load(test_nrrd_path, tmp_nrrd2) + + +def run_tests(data_dir, tmp_dir): + # construct path to test data + testnrrd_path = os.path.join(data_dir, multishell_dwi_451) + + test_nrrd_dwi_load(testnrrd_path) + test_nrrd_dwi_roundtrip(testnrrd_path) + + +if __name__ == '__main__': + # TODO make sure data paths exist + data_dir = sys.argv[1] + tmp_dir = sys.argv[2] + + try: + run_tests(data_dir, tmp_dir) + exit(slicer.util.EXIT_SUCCESS) + except: + raise + + exit(slicer.util.EXIT_SUCCESS) diff --git a/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.nhdr b/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.nhdr new file mode 100644 index 0000000000000000000000000000000000000000..3199e786eaff0b714c898c88532bb5b882f4ef64 --- /dev/null +++ b/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.nhdr @@ -0,0 +1,467 @@ +NRRD0005 +# Complete NRRD file format specification at: +# http://teem.sourceforge.net/nrrd/format.html +type: short +dimension: 4 +space: right-anterior-superior +sizes: 451 7 7 2 +space directions: none (-2.4904873929542752,0.001526874873563903,0.2178766031241865) (2.6429097682729909e-08,-2.4999386104169656,0.017519821531186731) (0.21788201640672655,0.017453167118519793,2.4904271887081069) +kinds: list domain domain domain +endian: little +encoding: gzip +space origin: (9.1900975709163664,98.098053174005756,9.8750159998483902) +measurement frame: (-1,0,0) (0,-1,0) (0,0,1) +data file: multishell-DWI-451dir.raw.gz +modality:=DWMRI +DWMRI_b-value:=10000 +DWMRI_gradient_0000:=0 0 0 +DWMRI_gradient_0001:=-0.0435405933728607 -0.08253993587221661 -0.127637672255725 +DWMRI_gradient_0002:=0.019808779758852785 -0.08236379602390693 -0.13350586883320079 +DWMRI_gradient_0003:=0.05080403555446169 -0.028030237301649163 -0.14708247833139243 +DWMRI_gradient_0004:=-0.07127480876410455 -0.1156972833518431 -0.08083340846799643 +DWMRI_gradient_0005:=-0.03550577181866828 -0.14896142375685542 -0.039367931921825736 +DWMRI_gradient_0006:=-0.1354225383313697 -0.08157129416774117 0.0026191112230762223 +DWMRI_gradient_0007:=-0.12138675083097808 -0.0820846816605407 -0.05939159387738341 +DWMRI_gradient_0008:=-0.07635146390615032 -0.028018593683141906 -0.13559281703437362 +DWMRI_gradient_0009:=-0.12456935077162568 -0.027843181760964994 -0.09331255939942835 +DWMRI_gradient_0010:=-0.09959284031365688 -0.1147493362544074 0.043747635951462506 +DWMRI_gradient_0011:=-0.04914768787611955 -0.14849992593393874 0.023071116963782706 +DWMRI_gradient_0012:=-0.03996209622465154 -0.08027147869389462 0.1302287227824093 +DWMRI_gradient_0013:=-0.09466969047450009 -0.08072730123066364 0.09757434194185656 +DWMRI_gradient_0014:=-0.1526104111205339 -0.027156004686520706 0.03118675185070729 +DWMRI_gradient_0015:=-0.12773979422051843 -0.026395244965366403 0.08936350306264973 +DWMRI_gradient_0016:=0.009535439920981431 -0.11414700003182758 0.1089932907713021 +DWMRI_gradient_0017:=0.005411147615978945 -0.1481464581367289 0.05498496237091392 +DWMRI_gradient_0018:=0.11064111433671167 -0.08099254652004932 0.07873214648692521 +DWMRI_gradient_0019:=0.0628949878120545 -0.08037436692862567 0.1207649838026506 +DWMRI_gradient_0020:=0.01809211645521744 0.02580870271120508 -0.15494058813121173 +DWMRI_gradient_0021:=0.04548288844650951 -0.025988992432039046 0.14918404336536828 +DWMRI_gradient_0022:=0.10594735047745904 -0.11469463852801046 0.02490579217061331 +DWMRI_gradient_0023:=0.052785443271180864 -0.14849781525152084 0.012731673421055852 +DWMRI_gradient_0024:=0.027996174799976632 -0.14878577027266218 -0.04559614484635787 +DWMRI_gradient_0025:=0.05593561942904512 -0.11577331320945371 -0.09202035694076104 +DWMRI_gradient_0026:=0.10601294465588054 -0.027857716213258322 -0.11395263435603882 +DWMRI_gradient_0027:=0.10889171205826474 -0.08198051468445483 -0.08013607128504707 +DWMRI_gradient_0028:=0.13368893953315997 -0.08156135835876928 -0.021794768479656165 +DWMRI_gradient_0029:=0.14149412627559807 -0.02685656948095772 0.06525439884888913 +DWMRI_gradient_0030:=0.15573156628933804 -0.027119798587823 0.003492198381495499 +DWMRI_gradient_0031:=-0.06164219464482649 -0.11638798730716488 -0.18070438581444667 +DWMRI_gradient_0032:=0.028078368266032265 -0.11670667624389976 -0.18865618399150996 +DWMRI_gradient_0033:=0.07207638550787794 -0.04004145530409182 -0.20785012704318945 +DWMRI_gradient_0034:=-0.10082282331018944 -0.16352816890853175 -0.11442593707636764 +DWMRI_gradient_0035:=-0.0506145355675154 -0.21065383808855018 -0.05534553360902229 +DWMRI_gradient_0036:=-0.19172071484165362 -0.11501840251434411 0.0037327451391003107 +DWMRI_gradient_0037:=-0.1716967527588114 -0.11593202609554756 -0.0841426718144883 +DWMRI_gradient_0038:=-0.10827801413964402 -0.03981492511354331 -0.1915480153768183 +DWMRI_gradient_0039:=-0.17587159737155433 -0.03938737788796415 -0.132354880921364 +DWMRI_gradient_0040:=-0.14106492090108647 -0.1622429583849325 0.06146470001793797 +DWMRI_gradient_0041:=-0.0699082131329457 -0.20994023985539517 0.03221701768689335 +DWMRI_gradient_0042:=-0.05672703605187412 -0.11373621261502757 0.1839731396790804 +DWMRI_gradient_0043:=-0.13381370700355508 -0.11435720681878117 0.13789967320190813 +DWMRI_gradient_0044:=-0.21588102020550604 -0.038091814519146394 0.04409532383698598 +DWMRI_gradient_0045:=-0.18037458026540057 -0.03775485962721007 0.12664746413264685 +DWMRI_gradient_0046:=0.013485148388621667 -0.16142823470139028 0.15413978920690635 +DWMRI_gradient_0047:=0.0072310941397070545 -0.20951155027051543 0.07779858856950933 +DWMRI_gradient_0048:=0.15645938102556564 -0.1141586697467961 0.11175087925094195 +DWMRI_gradient_0049:=0.0887530817140321 -0.11393362555728564 0.17071033435692867 +DWMRI_gradient_0050:=0.025337947765010523 0.03675848421076293 -0.21910454364116802 +DWMRI_gradient_0051:=0.06394762191979594 -0.036768840187391454 0.2110894427599275 +DWMRI_gradient_0052:=0.14970501161354594 -0.16225000808946774 0.035543539150956246 +DWMRI_gradient_0053:=0.07469633599075422 -0.2099830288798696 0.018099275527270243 +DWMRI_gradient_0054:=0.03927081229507744 -0.21043519377332578 -0.06461293466394662 +DWMRI_gradient_0055:=0.07942178819154533 -0.1635281831443905 -0.13019489882699625 +DWMRI_gradient_0056:=0.1500592224032128 -0.03935485648531481 -0.16103857874864394 +DWMRI_gradient_0057:=0.1537212066728381 -0.11583687666979026 -0.11380512916847906 +DWMRI_gradient_0058:=0.1890642624419567 -0.11530511771980552 -0.030974720297696223 +DWMRI_gradient_0059:=0.2002163334930603 -0.03776032298108295 0.0921280446638253 +DWMRI_gradient_0060:=0.22029399780670564 -0.03807782601815774 0.0045423010347048105 +DWMRI_gradient_0061:=-0.07535451657102352 -0.14269716333112586 -0.22126730900808086 +DWMRI_gradient_0062:=0.03436248554566541 -0.14284361960041161 -0.23111669250111952 +DWMRI_gradient_0063:=0.08805718583376586 -0.04882626598861433 -0.2546800608581812 +DWMRI_gradient_0064:=-0.12338394786809032 -0.2004432810414723 -0.1399960646585964 +DWMRI_gradient_0065:=-0.06180214624863405 -0.2579995963755464 -0.06794636572472271 +DWMRI_gradient_0066:=-0.23474236588963474 -0.1409893136136397 0.004247437977391619 +DWMRI_gradient_0067:=-0.2101110854993156 -0.14223799098260892 -0.10306158982671847 +DWMRI_gradient_0068:=-0.13232607485403577 -0.04894564844555471 -0.23472140728542093 +DWMRI_gradient_0069:=-0.21539933074016962 -0.0481477733897098 -0.1621262651774239 +DWMRI_gradient_0070:=-0.17255596441170093 -0.1988477943195271 0.07539230376031128 +DWMRI_gradient_0071:=-0.08548766373663456 -0.2572107663955452 0.03917252385471977 +DWMRI_gradient_0072:=-0.06947366706395741 -0.1393302485540371 0.22530090334625114 +DWMRI_gradient_0073:=-0.16400270419860832 -0.14004051677607113 0.16879506006608994 +DWMRI_gradient_0074:=-0.2644435536743765 -0.046796998772346936 0.053662402592954406 +DWMRI_gradient_0075:=-0.2209379327291914 -0.04604989519832049 0.15513169456837372 +DWMRI_gradient_0076:=0.017021872767417352 -0.19783494497138357 0.18860433722939596 +DWMRI_gradient_0077:=0.009131596117518879 -0.25669555892503426 0.09499478726009616 +DWMRI_gradient_0078:=0.19175766435225572 -0.13987625208935528 0.1366149255743419 +DWMRI_gradient_0079:=0.10874281252543104 -0.13968693687788247 0.20895589490563213 +DWMRI_gradient_0080:=0.03078200473441319 0.045332673141582604 -0.26832335447450184 +DWMRI_gradient_0081:=0.07854003089989477 -0.0450849183653179 0.2584546755396705 +DWMRI_gradient_0082:=0.18318703257612026 -0.19882913156983048 0.043697747782039026 +DWMRI_gradient_0083:=0.09130163476193216 -0.2572408137064588 0.02216260091951948 +DWMRI_gradient_0084:=0.048214371926680474 -0.2576949174435143 -0.07917518228318952 +DWMRI_gradient_0085:=0.09720007301616003 -0.20044328271857065 -0.15929419903039946 +DWMRI_gradient_0086:=0.18370984624303588 -0.048155973553261344 -0.19731117614209767 +DWMRI_gradient_0087:=0.1883155613019827 -0.14187426724158334 -0.13931599797676922 +DWMRI_gradient_0088:=0.2315556429872094 -0.14123581183858117 -0.03787393934035816 +DWMRI_gradient_0089:=0.2452171413366311 -0.04616895910749692 0.11285825816350858 +DWMRI_gradient_0090:=0.26978559612678416 -0.04676420434565768 0.005370934789536939 +DWMRI_gradient_0091:=-0.08702919249905602 -0.16484918269524795 -0.25544209769204496 +DWMRI_gradient_0092:=0.039625126432284066 -0.16489202422230043 -0.2669091013036816 +DWMRI_gradient_0093:=0.10186285485420807 -0.05625971735920944 -0.2940387757749879 +DWMRI_gradient_0094:=-0.1426045962162091 -0.23138210319663938 -0.16163616586009727 +DWMRI_gradient_0095:=-0.07127502867422089 -0.2979149116020049 -0.07852753904395476 +DWMRI_gradient_0096:=-0.27092702924056533 -0.1630139455450491 0.004999704027122781 +DWMRI_gradient_0097:=-0.24274120239060498 -0.16399530103560625 -0.1190892461805238 +DWMRI_gradient_0098:=-0.15295757190697923 -0.05635690708652583 -0.27097579020200896 +DWMRI_gradient_0099:=-0.24872621100755893 -0.055815451645712555 -0.18713606272188582 +DWMRI_gradient_0100:=-0.19944464706545803 -0.22955507331683445 0.08675424904336879 +DWMRI_gradient_0101:=-0.09884642477189538 -0.2969223070283711 0.04545906460107797 +DWMRI_gradient_0102:=-0.08029704128935039 -0.16095101934548425 0.2600906633992599 +DWMRI_gradient_0103:=-0.18932757611875436 -0.16170255161097546 0.19495474443864735 +DWMRI_gradient_0104:=-0.3053200579575673 -0.054105531478854635 0.06206651535103009 +DWMRI_gradient_0105:=-0.25520771846625817 -0.053078101982127264 0.17902998123460556 +DWMRI_gradient_0106:=0.01950918560759315 -0.22840368713964385 0.2178328396582696 +DWMRI_gradient_0107:=0.010613930480301419 -0.2963780727231232 0.10976056279159473 +DWMRI_gradient_0108:=0.22148639622943986 -0.16146454935315982 0.15771168074406472 +DWMRI_gradient_0109:=0.1256551041521829 -0.16103881447735585 0.24140690480570978 +DWMRI_gradient_0110:=0.035816442716266654 0.05227373081495207 -0.3098138766315665 +DWMRI_gradient_0111:=0.09075929336556052 -0.05203910822898737 0.29842030852165047 +DWMRI_gradient_0112:=0.21161847219019125 -0.22953844317377475 0.05029636158699937 +DWMRI_gradient_0113:=0.10546226328662543 -0.29702598098412125 0.02555927030500116 +DWMRI_gradient_0114:=0.055802539292673654 -0.2975283972897679 -0.09144904630619646 +DWMRI_gradient_0115:=0.11237105319924057 -0.2313821066502574 -0.18394310473791495 +DWMRI_gradient_0116:=0.21222204844954162 -0.055603113697575215 -0.2277500703724505 +DWMRI_gradient_0117:=0.21739462315544272 -0.16381806907937743 -0.1609447629525377 +DWMRI_gradient_0118:=0.26726424165992707 -0.16320367228868424 -0.04398164545663403 +DWMRI_gradient_0119:=0.2831373922942583 -0.05324142984762755 0.13037854842046928 +DWMRI_gradient_0120:=0.31150056452887814 -0.05409036278555934 0.006452101018890364 +DWMRI_gradient_0121:=-0.10661730950027032 -0.2017028711773885 -0.3129675854648912 +DWMRI_gradient_0122:=0.04856482018161502 -0.2020144510893584 -0.3268510796521793 +DWMRI_gradient_0123:=0.12472869386518388 -0.06911626995752577 -0.3600912420926761 +DWMRI_gradient_0124:=-0.17464631519842963 -0.2833356492451853 -0.19803934692990355 +DWMRI_gradient_0125:=-0.08741127326951775 -0.36479641968891496 -0.09634754779211196 +DWMRI_gradient_0126:=-0.33191278726221735 -0.19949363934355094 0.006016476651149365 +DWMRI_gradient_0127:=-0.2972437649792216 -0.20090735193856776 -0.1458848470404393 +DWMRI_gradient_0128:=-0.18732264085780567 -0.0689666245681091 -0.33189432263258395 +DWMRI_gradient_0129:=-0.30454943647819244 -0.06829466834932084 -0.22931525787412024 +DWMRI_gradient_0130:=-0.24428977177657205 -0.2811020092184974 0.10632109995987046 +DWMRI_gradient_0131:=-0.12109215977607642 -0.36368641885082686 0.05539752758227258 +DWMRI_gradient_0132:=-0.09827467994530596 -0.1971376986015449 0.3185574101315754 +DWMRI_gradient_0133:=-0.2319195178331428 -0.19798696785062492 0.2387771058693875 +DWMRI_gradient_0134:=-0.37393180996141284 -0.06616954319365938 0.07613541574409922 +DWMRI_gradient_0135:=-0.312583666933829 -0.06517418633077454 0.21918892554215696 +DWMRI_gradient_0136:=0.023714218549932328 -0.27970184621873023 0.26684175373880775 +DWMRI_gradient_0137:=0.012825322605173942 -0.36302283240425737 0.1343500752931172 +DWMRI_gradient_0138:=0.27121826354934536 -0.1977865919205903 0.193186764690851 +DWMRI_gradient_0139:=0.1539839780236933 -0.19739227521210348 0.2955084308045889 +DWMRI_gradient_0140:=0.04392218393741768 0.06394561885287031 -0.3794493484850035 +DWMRI_gradient_0141:=0.11107237586072549 -0.06375970242111817 0.3655101040269977 +DWMRI_gradient_0142:=0.25919354249537785 -0.28113691951248404 0.061487791420170725 +DWMRI_gradient_0143:=0.12931529779908077 -0.36370623872832186 0.03154891667291467 +DWMRI_gradient_0144:=0.06838930642552103 -0.36439417197039453 -0.11198124253038663 +DWMRI_gradient_0145:=0.13747024634142554 -0.2833425677534917 -0.22543054173666957 +DWMRI_gradient_0146:=0.25987262272626965 -0.06846227960066191 -0.2788891261904151 +DWMRI_gradient_0147:=0.26620635910067997 -0.20060874206637058 -0.19720627641865465 +DWMRI_gradient_0148:=0.3273766489886954 -0.19982677457672363 -0.05379403626052265 +DWMRI_gradient_0149:=0.3468199975713337 -0.0652836906490088 0.15954290174704477 +DWMRI_gradient_0150:=0.3815291817605719 -0.06615980878186926 0.007640133837676735 +DWMRI_gradient_0151:=-0.12319461666641202 -0.2329319746509491 -0.3613388670211172 +DWMRI_gradient_0152:=0.05610502411743374 -0.2332178050137576 -0.37744095816938905 +DWMRI_gradient_0153:=0.1440078512746057 -0.07961746098527506 -0.4158398656790959 +DWMRI_gradient_0154:=-0.2015019972088169 -0.3272225910327197 -0.2287407183891199 +DWMRI_gradient_0155:=-0.100746873597869 -0.4212779137986792 -0.11124289647508712 +DWMRI_gradient_0156:=-0.3832133960476074 -0.23043497091002552 0.006871133955507228 +DWMRI_gradient_0157:=-0.34324342585435064 -0.23193436645142718 -0.16849449687459597 +DWMRI_gradient_0158:=-0.21626450711695822 -0.07979369887356572 -0.3832265958829229 +DWMRI_gradient_0159:=-0.35164306128568185 -0.07890389375032804 -0.2648043206051414 +DWMRI_gradient_0160:=-0.28201831853963616 -0.32464034131408237 0.12277748983438137 +DWMRI_gradient_0161:=-0.13976355429000226 -0.41995940879928534 0.06403310912067042 +DWMRI_gradient_0162:=-0.11336034889214539 -0.2276450214943984 0.36786841492352296 +DWMRI_gradient_0163:=-0.26775510103027467 -0.22864031788049993 0.2757368421561591 +DWMRI_gradient_0164:=-0.43177004087322635 -0.07659134664975159 0.08779745231605111 +DWMRI_gradient_0165:=-0.36095708226934065 -0.0752547955415867 0.2530744865016578 +DWMRI_gradient_0166:=0.0275827222732003 -0.3230982777868819 0.30797190055852464 +DWMRI_gradient_0167:=0.014779249931607244 -0.41922779516328484 0.15501491781267127 +DWMRI_gradient_0168:=0.3131497852540416 -0.22839458538444177 0.22309890311898878 +DWMRI_gradient_0169:=0.17766641884128737 -0.22792600031061885 0.3412980767730684 +DWMRI_gradient_0170:=0.05070594444514501 0.07379531846907662 -0.4381588220958842 +DWMRI_gradient_0171:=0.1283602266849844 -0.0735870180369772 0.42202914377123796 +DWMRI_gradient_0172:=0.29922093226125707 -0.32468224396027323 0.07105117426267547 +DWMRI_gradient_0173:=0.14927221539110186 -0.4199951981369137 0.036357041472682095 +DWMRI_gradient_0174:=0.07886237164040218 -0.420792189401262 -0.12928516886566213 +DWMRI_gradient_0175:=0.15872141072907534 -0.32722259073921706 -0.26025542279953806 +DWMRI_gradient_0176:=0.3000737623262345 -0.07893029625425067 -0.32206480824063033 +DWMRI_gradient_0177:=0.3074020775359278 -0.23165069192128027 -0.2276881965967203 +DWMRI_gradient_0178:=0.3780250758214732 -0.23071566171951066 -0.062187785761972855 +DWMRI_gradient_0179:=0.40049348087649705 -0.07531433730240077 0.1842083537264436 +DWMRI_gradient_0180:=0.44052421378429657 -0.07654178533049413 0.008931258513069753 +DWMRI_gradient_0181:=-0.13775578629717292 -0.26039103596920354 -0.4040047669626343 +DWMRI_gradient_0182:=0.06264040809987523 -0.2607707460700034 -0.4219890961472521 +DWMRI_gradient_0183:=0.1609696781012842 -0.08909122840344762 -0.4649209779667225 +DWMRI_gradient_0184:=-0.22530004147890145 -0.365764286291542 -0.25584444137648177 +DWMRI_gradient_0185:=-0.11274645758712232 -0.47100974303925697 -0.12424998295230573 +DWMRI_gradient_0186:=-0.4284966316406468 -0.25754526416246826 0.007814958739762633 +DWMRI_gradient_0187:=-0.383704340863291 -0.25933066440717156 -0.18846375062807752 +DWMRI_gradient_0188:=-0.24177562048205267 -0.08903691300704049 -0.4285055162570197 +DWMRI_gradient_0189:=-0.3931759320502539 -0.088154837997076 -0.2960429208647342 +DWMRI_gradient_0190:=-0.3153405258815403 -0.3629314383158261 0.13726297322570008 +DWMRI_gradient_0191:=-0.1561394173900406 -0.46959316401855067 0.07143348405436457 +DWMRI_gradient_0192:=-0.12680780450524642 -0.25454192476301923 0.4112519778122613 +DWMRI_gradient_0193:=-0.2993782059325791 -0.2556770951707159 0.3082238031328128 +DWMRI_gradient_0194:=-0.4827847504214065 -0.08548291069049567 0.09803854721700524 +DWMRI_gradient_0195:=-0.4035818704367852 -0.08406211701479535 0.2829403370637291 +DWMRI_gradient_0196:=0.03070096400814271 -0.3611889005218447 0.3443835491843237 +DWMRI_gradient_0197:=0.01664150367030084 -0.4686648084069295 0.1734253664158948 +DWMRI_gradient_0198:=0.35014460374350204 -0.25539953161045026 0.24933879706629786 +DWMRI_gradient_0199:=0.19870433165346452 -0.25490224302435366 0.38149893195565215 +DWMRI_gradient_0200:=0.056723148457959505 0.08244215817286303 -0.4898834299961745 +DWMRI_gradient_0201:=0.14345839285847356 -0.08251543151075878 0.4718165883911429 +DWMRI_gradient_0202:=0.33463838642439314 -0.3629440063337457 0.07930194573709043 +DWMRI_gradient_0203:=0.16680701471653372 -0.46960575499601337 0.04056913501644258 +DWMRI_gradient_0204:=0.08823549920132281 -0.47045404639564853 -0.14452503904394634 +DWMRI_gradient_0205:=0.17745152042469944 -0.365764288582441 -0.2910797881968846 +DWMRI_gradient_0206:=0.3354320001424081 -0.08818289080075435 -0.3601515667749978 +DWMRI_gradient_0207:=0.3436588586788565 -0.25897791543161763 -0.2546154515543776 +DWMRI_gradient_0208:=0.4225985429102583 -0.2580117596262961 -0.06957300787423887 +DWMRI_gradient_0209:=0.44775707283790095 -0.08421647690524289 0.205964047205582 +DWMRI_gradient_0210:=0.4925394380878641 -0.08547043843090789 0.00998529331163515 +DWMRI_gradient_0211:=-0.15084788471209937 -0.2852944036484605 -0.44255173870303033 +DWMRI_gradient_0212:=0.06851557293260434 -0.28558782333084026 -0.4623258958085633 +DWMRI_gradient_0213:=0.17632819248088213 -0.09749437480594739 -0.5093164418335061 +DWMRI_gradient_0214:=-0.24678657353304453 -0.40071833927949646 -0.28021638459094844 +DWMRI_gradient_0215:=-0.12356673119564145 -0.5159444392635841 -0.1361345695954213 +DWMRI_gradient_0216:=-0.4693102928146301 -0.28227162810171846 0.008402352728375442 +DWMRI_gradient_0217:=-0.4204038104035923 -0.284056367476596 -0.2063313810317332 +DWMRI_gradient_0218:=-0.2647259778261163 -0.09768709828024345 -0.46944372399459494 +DWMRI_gradient_0219:=-0.430660779251521 -0.0966672110845936 -0.3243250931086277 +DWMRI_gradient_0220:=-0.34541636388120683 -0.3976015046415838 0.15033496457629228 +DWMRI_gradient_0221:=-0.17107491151285778 -0.5143858346293447 0.0783620462204833 +DWMRI_gradient_0222:=-0.13895193819693596 -0.2787975175100607 0.4505156219332057 +DWMRI_gradient_0223:=-0.32804445724175807 -0.28002517764209534 0.33759851371680255 +DWMRI_gradient_0224:=-0.5288249079994926 -0.09375554901280139 0.1074901679827383 +DWMRI_gradient_0225:=-0.4421318599977444 -0.09209323882224675 0.3099004295616326 +DWMRI_gradient_0226:=0.033519704769112715 -0.3957486218387528 0.37717301097671363 +DWMRI_gradient_0227:=0.018190404568125366 -0.5134326568508469 0.18988427677153588 +DWMRI_gradient_0228:=0.3835730451831964 -0.27980352889489823 0.273096552030911 +DWMRI_gradient_0229:=0.21764410377990437 -0.27913623093556605 0.41798807539599664 +DWMRI_gradient_0230:=0.06208473045998043 0.09042897567664963 -0.5366266011912793 +DWMRI_gradient_0231:=0.15725817703797926 -0.09035408385714651 0.5168230139896521 +DWMRI_gradient_0232:=0.36650179199803484 -0.3976668927092877 0.0868187848883514 +DWMRI_gradient_0233:=0.1827689894533363 -0.5144126906468925 0.04444212753628538 +DWMRI_gradient_0234:=0.09657291924484927 -0.5153605122720848 -0.15835793685036126 +DWMRI_gradient_0235:=0.1943795961710207 -0.4007183717645608 -0.3188124552571167 +DWMRI_gradient_0236:=0.36746759550927977 -0.09656616231514072 -0.39451560812048364 +DWMRI_gradient_0237:=0.3764722055357021 -0.2837662347815696 -0.27882866596047123 +DWMRI_gradient_0238:=0.46300439809393995 -0.2825349571298158 -0.07616395374218934 +DWMRI_gradient_0239:=0.49053681643404395 -0.09226780733497751 0.2255222959192047 +DWMRI_gradient_0240:=0.5395618539023956 -0.09357644007361858 0.01079250577387872 +DWMRI_gradient_0241:=-0.174145541090509 -0.3293800494262352 -0.5110597892427358 +DWMRI_gradient_0242:=0.07911797164963137 -0.32977567366859867 -0.5338429977446879 +DWMRI_gradient_0243:=0.2036302842209438 -0.11269353308405082 -0.5880772649675352 +DWMRI_gradient_0244:=-0.2849635114736244 -0.46268314466105037 -0.32360485070182105 +DWMRI_gradient_0245:=-0.14265662907487553 -0.5957245409284968 -0.1573574025557423 +DWMRI_gradient_0246:=-0.5419801143623331 -0.325829261770931 0.009635477786144207 +DWMRI_gradient_0247:=-0.4854195105880537 -0.32800472833870303 -0.23828720391677105 +DWMRI_gradient_0248:=-0.3057404907671426 -0.1127513883851191 -0.5420423150873552 +DWMRI_gradient_0249:=-0.49727911967507377 -0.11147642745935948 -0.37454836520621276 +DWMRI_gradient_0250:=-0.39878081616788325 -0.4591655353203526 0.17361125094018479 +DWMRI_gradient_0251:=-0.19759251473014022 -0.5939575446589992 0.0903970568538438 +DWMRI_gradient_0252:=-0.1605077205815083 -0.3219471673355463 0.5201800531555502 +DWMRI_gradient_0253:=-0.3786900554395371 -0.3234320906482386 0.3898532022090811 +DWMRI_gradient_0254:=-0.6106650948436265 -0.10825440286612308 0.12397225827695735 +DWMRI_gradient_0255:=-0.510528773151259 -0.10642922250222007 0.35781725364326417 +DWMRI_gradient_0256:=0.0387831384814108 -0.456936543411862 0.4355512117844654 +DWMRI_gradient_0257:=0.02105064578025935 -0.5928772980072334 0.21921079910086771 +DWMRI_gradient_0258:=0.4429566466038691 -0.322988840582325 0.31538486442964453 +DWMRI_gradient_0259:=0.25142944872073403 -0.32233642582694755 0.4825789629932716 +DWMRI_gradient_0260:=0.0715703476783268 0.10434177191727766 -0.6196696495625158 +DWMRI_gradient_0261:=0.18162749269070422 -0.10431943111524422 0.596765368309225 +DWMRI_gradient_0262:=0.42320909132351703 -0.45915884593361134 0.1003355064002869 +DWMRI_gradient_0263:=0.21101016422890867 -0.59402275447428 0.051104524079997334 +DWMRI_gradient_0264:=0.11153588757236818 -0.5950794632210522 -0.18286654384701795 +DWMRI_gradient_0265:=0.22444245175795352 -0.4626831663980482 -0.36817097659011566 +DWMRI_gradient_0266:=0.42425677081710195 -0.11160118913595186 -0.4555780521731169 +DWMRI_gradient_0267:=0.43470281223830987 -0.32763169425654104 -0.322010764685372 +DWMRI_gradient_0268:=0.5345898697442978 -0.32627217141121123 -0.08809163383632272 +DWMRI_gradient_0269:=0.5664443837750432 -0.10651428021327813 0.26037561871827325 +DWMRI_gradient_0270:=0.6230264836619488 -0.10809143682721667 0.012419201384154836 +DWMRI_gradient_0271:=-0.19481992666006587 -0.36821618909131876 -0.5713685475706217 +DWMRI_gradient_0272:=0.08853308990141766 -0.368664550733228 -0.5968654152410775 +DWMRI_gradient_0273:=0.22763846860036965 -0.1259367266223498 -0.6575109515336266 +DWMRI_gradient_0274:=-0.318600351249333 -0.5172666205824635 -0.36184118380959396 +DWMRI_gradient_0275:=-0.1594711761505868 -0.6660638342691484 -0.17586328650846364 +DWMRI_gradient_0276:=-0.6059304334021373 -0.3643255265418356 0.010733288072223053 +DWMRI_gradient_0277:=-0.5427143819565083 -0.3666554920566887 -0.2665048454493118 +DWMRI_gradient_0278:=-0.341835675497033 -0.1261102594941734 -0.6060070596070052 +DWMRI_gradient_0279:=-0.5559409608713415 -0.12474743406229913 -0.4187692784888246 +DWMRI_gradient_0280:=-0.4458748009395038 -0.5133502621457553 0.1940802749579955 +DWMRI_gradient_0281:=-0.22096030502105934 -0.6640539671908363 0.10103887939224726 +DWMRI_gradient_0282:=-0.17946283657340378 -0.3599918619508966 0.5815487361381532 +DWMRI_gradient_0283:=-0.423469645259782 -0.3615882113821413 0.4358066174563522 +DWMRI_gradient_0284:=-0.6827275431602937 -0.1210695784072898 0.13865511847861683 +DWMRI_gradient_0285:=-0.5708345081804402 -0.11895770857053611 0.3999962633922612 +DWMRI_gradient_0286:=0.04340874890411187 -0.5108965538480681 0.48692953768535835 +DWMRI_gradient_0287:=0.023432854834614446 -0.6628682674674555 0.24506436743174817 +DWMRI_gradient_0288:=0.4952092353446183 -0.3612509164931913 0.3525132216640345 +DWMRI_gradient_0289:=0.2810444172364893 -0.3604556550801526 0.5395236224176556 +DWMRI_gradient_0290:=0.0800585921421424 0.11675028981136384 -0.6927914365369348 +DWMRI_gradient_0291:=0.20306749051709172 -0.11660127458169998 0.6672089027783897 +DWMRI_gradient_0292:=0.47318872861927436 -0.5133392066135684 0.11213950662793158 +DWMRI_gradient_0293:=0.23600178291259297 -0.6641086475724707 0.05712132322758379 +DWMRI_gradient_0294:=0.12467657909857668 -0.6653186603948177 -0.20446714562601645 +DWMRI_gradient_0295:=0.2509287625505557 -0.5172666235159562 -0.41166731617530405 +DWMRI_gradient_0296:=0.4743676473260879 -0.12466214302077612 -0.5093472962071252 +DWMRI_gradient_0297:=0.4860063190673166 -0.36628979534362405 -0.3600411456201813 +DWMRI_gradient_0298:=0.5976957857352455 -0.3647653309729926 -0.09851895208648796 +DWMRI_gradient_0299:=0.6332866792668793 -0.11910952887716257 0.29113722688711224 +DWMRI_gradient_0300:=0.6965591399032316 -0.12086987838420858 0.013993570037194886 +DWMRI_gradient_0301:=-0.21335953791464166 -0.4033870110704929 -0.625904673886108 +DWMRI_gradient_0302:=0.09697110792722347 -0.40386274747963663 -0.6538283561742918 +DWMRI_gradient_0303:=0.24934578004029823 -0.13791406628416172 -0.7202821851254365 +DWMRI_gradient_0304:=-0.3490511959461475 -0.5666106554312642 -0.3963781801163221 +DWMRI_gradient_0305:=-0.17464487945159252 -0.7296320097065424 -0.1927078887978932 +DWMRI_gradient_0306:=-0.6637733050368033 -0.3990801685094859 0.011834379365626138 +DWMRI_gradient_0307:=-0.5944588905482845 -0.4017452552053027 -0.29192363946782274 +DWMRI_gradient_0308:=-0.3744736165832915 -0.1380021547356803 -0.663871184555671 +DWMRI_gradient_0309:=-0.609010824704722 -0.13656596270825985 -0.4587543865011548 +DWMRI_gradient_0310:=-0.4884326426558975 -0.5623428625627044 0.2126125406107785 +DWMRI_gradient_0311:=-0.24204517126705571 -0.7274396653969123 0.11066030085599915 +DWMRI_gradient_0312:=-0.1965325285142462 -0.39434206088886864 0.6370787535004815 +DWMRI_gradient_0313:=-0.46394249707279117 -0.39611613403940726 0.4773357326615597 +DWMRI_gradient_0314:=-0.7479191522856983 -0.1326017745765285 0.15176872186128632 +DWMRI_gradient_0315:=-0.6253173749709214 -0.1302677931599773 0.43818776401984993 +DWMRI_gradient_0316:=0.04758720109910647 -0.5596728763647636 0.5333870673958602 +DWMRI_gradient_0317:=0.025717199153192517 -0.7261381571393711 0.26844373027873675 +DWMRI_gradient_0318:=0.5425053076898178 -0.3957139460404167 0.3861327465386855 +DWMRI_gradient_0319:=0.3079509737631084 -0.39485686235887635 0.590977402206266 +DWMRI_gradient_0320:=0.08773670408660034 0.1278213769000712 -0.7589229211688104 +DWMRI_gradient_0321:=0.2223182484104924 -0.127691358478348 0.7309374441808648 +DWMRI_gradient_0322:=0.5183132173537264 -0.562390374972419 0.12275385438682232 +DWMRI_gradient_0323:=0.2584690850181157 -0.7275131004919079 0.06259756077164043 +DWMRI_gradient_0324:=0.13659490409548386 -0.7288155835636982 -0.2239859621535725 +DWMRI_gradient_0325:=0.27487491394753283 -0.566626233712685 -0.4509750845256581 +DWMRI_gradient_0326:=0.5196063491483992 -0.13668297216884315 -0.5579668815940365 +DWMRI_gradient_0327:=0.5323887315897045 -0.40124044665171893 -0.3944215718733512 +DWMRI_gradient_0328:=0.6547001104599284 -0.3996605668143983 -0.10788526595120394 +DWMRI_gradient_0329:=0.6937493632488455 -0.13047029682619823 0.31888768641939674 +DWMRI_gradient_0330:=0.7630397895765301 -0.13242635919608622 0.0152830895137172 +DWMRI_gradient_0331:=-0.23042124995645655 -0.43568253069459695 -0.6760819341167124 +DWMRI_gradient_0332:=0.10473541215530176 -0.43618459207210153 -0.706238979880389 +DWMRI_gradient_0333:=0.26932301959722854 -0.1489968519479155 -0.7779878217861645 +DWMRI_gradient_0334:=-0.37705053939353295 -0.6119890965027557 -0.42813810637191096 +DWMRI_gradient_0335:=-0.18862578340796693 -0.7880762719000488 -0.20822128883663626 +DWMRI_gradient_0336:=-0.7169395367430371 -0.4310880007901389 0.012682186419522545 +DWMRI_gradient_0337:=-0.6421104194178042 -0.4338879852755916 -0.31533383442237645 +DWMRI_gradient_0338:=-0.40448262671897534 -0.14909578527079412 -0.7170524758481365 +DWMRI_gradient_0339:=-0.6578023534616918 -0.14752317706849238 -0.49551284305335297 +DWMRI_gradient_0340:=-0.5276241567568416 -0.6073504899768932 0.22964784596071983 +DWMRI_gradient_0341:=-0.26134359768680815 -0.7857568527398291 0.11952277583562546 +DWMRI_gradient_0342:=-0.21232549944679951 -0.42592424973722237 0.6881180257727524 +DWMRI_gradient_0343:=-0.501067298762128 -0.4278956186463297 0.5155937385940973 +DWMRI_gradient_0344:=-0.8078512867659556 -0.143124106549223 0.1639871647965633 +DWMRI_gradient_0345:=-0.6754585877197897 -0.1407504126107696 0.4732282950632014 +DWMRI_gradient_0346:=0.05143083213843501 -0.6044897155711622 0.5761484660199858 +DWMRI_gradient_0347:=0.027813875173994737 -0.7843258393262019 0.289929935908649 +DWMRI_gradient_0348:=0.5859641049953632 -0.4274329620835501 0.4170696970584226 +DWMRI_gradient_0349:=0.3326212894283419 -0.4264342464199101 0.6383705138258264 +DWMRI_gradient_0350:=0.09481138274742848 0.13813589676690216 -0.8197129237415154 +DWMRI_gradient_0351:=0.2401735713751109 -0.137946544798163 0.7894855337586062 +DWMRI_gradient_0352:=0.559890308815755 -0.607384230319764 0.13269227737892353 +DWMRI_gradient_0353:=0.2792452129826264 -0.7857814511800263 0.06759899134335043 +DWMRI_gradient_0354:=0.14750951590155145 -0.787186783522694 -0.24202874248449013 +DWMRI_gradient_0355:=0.2969374783460515 -0.6120034710017194 -0.48711383377838385 +DWMRI_gradient_0356:=0.5612678273059114 -0.14754000713733068 -0.6026693736603485 +DWMRI_gradient_0357:=0.5749981341098892 -0.4334074220529689 -0.42606942380612584 +DWMRI_gradient_0358:=0.7071941429087747 -0.43159891999296573 -0.11661397051540695 +DWMRI_gradient_0359:=0.7493234554971193 -0.14092065527873557 0.3444644097444717 +DWMRI_gradient_0360:=0.8241743325920375 -0.1430609284293623 0.016439035140095804 +DWMRI_gradient_0361:=-0.24638048482381333 -0.46576669809463894 -0.7227433987840477 +DWMRI_gradient_0362:=0.11201398230750398 -0.46632393107164577 -0.7549800261724933 +DWMRI_gradient_0363:=0.28786621911176796 -0.15923340448620998 -0.8317317738501663 +DWMRI_gradient_0364:=-0.4030550030098881 -0.6542472700001349 -0.4577195157386088 +DWMRI_gradient_0365:=-0.20164297898318015 -0.8425066127912046 -0.22253695709206947 +DWMRI_gradient_0366:=-0.7664550668007076 -0.4608291697559966 0.013530991073782334 +DWMRI_gradient_0367:=-0.6864261990884374 -0.46383990222000465 -0.3371521899016154 +DWMRI_gradient_0368:=-0.43240251882237984 -0.15937603759890454 -0.7665685365296774 +DWMRI_gradient_0369:=-0.7031704180171602 -0.1578016393675544 -0.5297640858725717 +DWMRI_gradient_0370:=-0.5640614893743962 -0.6492843713975831 0.24548813027415545 +DWMRI_gradient_0371:=-0.27942978280096686 -0.8399976519603708 0.12776119139545458 +DWMRI_gradient_0372:=-0.22701046901229546 -0.4553816205373317 0.7355907880591819 +DWMRI_gradient_0373:=-0.5356827674458104 -0.45742739116207887 0.551184302496152 +DWMRI_gradient_0374:=-0.863622624310415 -0.15300950252966355 0.17533977231858527 +DWMRI_gradient_0375:=-0.7221169467669905 -0.1504661782981715 0.5058725385135858 +DWMRI_gradient_0376:=0.05492592392698272 -0.6462446993391064 0.6159146956347056 +DWMRI_gradient_0377:=0.029657744099444713 -0.8384854617181761 0.30993955788847893 +DWMRI_gradient_0378:=0.6264456353088292 -0.4569266550732165 0.44585185737869987 +DWMRI_gradient_0379:=0.355546798852838 -0.4559556840267565 0.6824154664653411 +DWMRI_gradient_0380:=0.10135998676218283 0.14759035285530314 -0.8763236968549545 +DWMRI_gradient_0381:=0.2567920660073086 -0.14745305287972776 0.8439878037472244 +DWMRI_gradient_0382:=0.5985738946413125 -0.6493078448074816 0.14180478189155396 +DWMRI_gradient_0383:=0.2984960407903775 -0.8400469340408746 0.07225816966207313 +DWMRI_gradient_0384:=0.157684476665634 -0.841549711499336 -0.2587076916830777 +DWMRI_gradient_0385:=0.3174128633357809 -0.6542607285644119 -0.5207609375608574 +DWMRI_gradient_0386:=0.5999440652020176 -0.15780119745261115 -0.6443336725279195 +DWMRI_gradient_0387:=0.6147218909630658 -0.4633722327390399 -0.4554153615537651 +DWMRI_gradient_0388:=0.7559864820712768 -0.46146761974923606 -0.12462766823778343 +DWMRI_gradient_0389:=0.8010944933345742 -0.15065037282948354 0.36817395176366924 +DWMRI_gradient_0390:=0.881074488697678 -0.1529683352750741 0.017561737135448122 +DWMRI_gradient_0391:=-0.2612742408846108 -0.49400341655060814 -0.7666136184109954 +DWMRI_gradient_0392:=0.118799087879937 -0.4945806363752914 -0.8007976119396271 +DWMRI_gradient_0393:=0.3053877770411869 -0.1688521002374165 -0.8821719324065792 +DWMRI_gradient_0394:=-0.42749724955423973 -0.693930821205759 -0.4854957893236185 +DWMRI_gradient_0395:=-0.21396335172952047 -0.8935827197189607 -0.23607128496078747 +DWMRI_gradient_0396:=-0.8129298222290111 -0.4888119321303101 0.014423716041429191 +DWMRI_gradient_0397:=-0.7280826446297302 -0.49197748213102516 -0.3575665869620606 +DWMRI_gradient_0398:=-0.4586242905519082 -0.16904987340420907 -0.8130719186298847 +DWMRI_gradient_0399:=-0.7458629688851396 -0.16729124244360052 -0.5618737547654493 +DWMRI_gradient_0400:=-0.5982652930552783 -0.688669911675262 0.2604082109407669 +DWMRI_gradient_0401:=-0.29637779328377106 -0.8909600114225336 0.13546404002872361 +DWMRI_gradient_0402:=-0.24074077806137884 -0.48301208490674774 0.7802199997815619 +DWMRI_gradient_0403:=-0.5681802080793281 -0.4851802780242171 0.5846121735047748 +DWMRI_gradient_0404:=-0.9160157797103989 -0.16233368919904773 0.1859110247383916 +DWMRI_gradient_0405:=-0.765913054863417 -0.15955824105286906 0.5365802871412679 +DWMRI_gradient_0406:=0.058284205356476215 -0.6854608368483011 0.6532583233299774 +DWMRI_gradient_0407:=0.03149683055643594 -0.8893362879474291 0.3287688565717387 +DWMRI_gradient_0408:=0.6644366395836395 -0.48465603045797057 0.4728980107446277 +DWMRI_gradient_0409:=0.3771464116291017 -0.48355372699875576 0.7238345264959442 +DWMRI_gradient_0410:=0.10742596779142062 0.15661570479323267 -0.9294790083175076 +DWMRI_gradient_0411:=0.27237350497383334 -0.15647611190421923 0.8951692268159746 +DWMRI_gradient_0412:=0.6348866031101201 -0.6887097934781525 0.15032586605529755 +DWMRI_gradient_0413:=0.3165475437386691 -0.891024263391925 0.07663849585289974 +DWMRI_gradient_0414:=0.16726318062989168 -0.8925892564166048 -0.2744221058550618 +DWMRI_gradient_0415:=0.33666291133761017 -0.693943487055211 -0.5523590909454107 +DWMRI_gradient_0416:=0.6363676616225468 -0.16729329530370782 -0.6834099772714621 +DWMRI_gradient_0417:=0.6520111942644069 -0.4914667160048629 -0.48305477335391184 +DWMRI_gradient_0418:=0.8018426295494818 -0.4894352910756585 -0.1322933775121033 +DWMRI_gradient_0419:=0.8496735246324469 -0.15980410270611914 0.3905350109064095 +DWMRI_gradient_0420:=0.9345368318406979 -0.1621564737852442 0.018607320854685704 +DWMRI_gradient_0421:=-0.27535635467838543 -0.5207860514247633 -0.8080598780905005 +DWMRI_gradient_0422:=0.1251986308383404 -0.5213851264351731 -0.8440869936021898 +DWMRI_gradient_0423:=0.32187259592379036 -0.177988218465993 -0.9299022669509855 +DWMRI_gradient_0424:=-0.45066365463316155 -0.7314541317749341 -0.5117393120545456 +DWMRI_gradient_0425:=-0.22549987882876316 -0.9419191293164335 -0.24887378020993775 +DWMRI_gradient_0426:=-0.8569154629569048 -0.5152350598113161 0.015123573869378392 +DWMRI_gradient_0427:=-0.7674614149833409 -0.5185952158729682 -0.3769111017009679 +DWMRI_gradient_0428:=-0.48343759755790006 -0.17822116606246616 -0.8570445176505013 +DWMRI_gradient_0429:=-0.7861782669887688 -0.17634236277511753 -0.5923065959496184 +DWMRI_gradient_0430:=-0.6305958099950751 -0.7259523946086852 0.2744850545632146 +DWMRI_gradient_0431:=-0.31244138011860934 -0.9391430059009637 0.14279635309377092 +DWMRI_gradient_0432:=-0.2537636703202753 -0.5091444742237354 0.8224207584886328 +DWMRI_gradient_0433:=-0.5989294113271988 -0.5114147119283124 0.6162293020220118 +DWMRI_gradient_0434:=-0.9655647235493465 -0.17110262736157963 0.19598126325503154 +DWMRI_gradient_0435:=-0.807356352746493 -0.16825538773056212 0.5655668344055529 +DWMRI_gradient_0436:=0.06138623917451572 -0.7225516484497474 0.6885861202221272 +DWMRI_gradient_0437:=0.03322792813563007 -0.9374595923624337 0.346504570647304 +DWMRI_gradient_0438:=0.7003968239876176 -0.5108609113336778 0.4984630560225926 +DWMRI_gradient_0439:=0.39759199296221553 -0.5097837039921421 0.7629162354258723 +DWMRI_gradient_0440:=0.11325844819092336 0.16512544011027339 -0.9797479844030157 +DWMRI_gradient_0441:=0.28711516519172425 -0.16491863622127612 0.9435924572313211 +DWMRI_gradient_0442:=0.6692036610275418 -0.7259866237857513 0.15846097990199576 +DWMRI_gradient_0443:=0.3337268480455391 -0.9392036056415041 0.08076495554129723 +DWMRI_gradient_0444:=0.1763260545684712 -0.940871255858654 -0.28925836613532024 +DWMRI_gradient_0445:=0.35488598859803666 -0.7314990022892535 -0.5822071321674274 +DWMRI_gradient_0446:=0.6707680233305371 -0.17643314620857228 -0.7203760155614486 +DWMRI_gradient_0447:=0.6872437043525724 -0.5180612835109211 -0.5092235239605204 +DWMRI_gradient_0448:=0.8452117317543049 -0.5159263534207851 -0.13941709490166473 +DWMRI_gradient_0449:=0.8956628142670163 -0.16844656131756755 0.4115991728849497 +DWMRI_gradient_0450:=0.9850878565136942 -0.17093448079953444 0.01957851432178666 diff --git a/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.raw.gz b/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.raw.gz new file mode 100644 index 0000000000000000000000000000000000000000..39c23a1e473edf1ddf47497fec13ed8e538bfbdf Binary files /dev/null and b/Libs/MRML/Core/Testing/TestData/multishell-DWI-451dir.raw.gz differ diff --git a/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.cxx b/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.cxx index 2f621258528f51f465ddc2f5dc2ddfdbd36f463e..196b38e128fe454d1fae4110c80eb485c66b41c2 100644 --- a/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.cxx +++ b/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.cxx @@ -20,21 +20,21 @@ Version: $Revision: 1.14 $ #include #include -#include #include #include #include +#include + //------------------------------------------------------------------------------ vtkMRMLNodeNewMacro(vtkMRMLDiffusionWeightedVolumeNode); //---------------------------------------------------------------------------- -vtkMRMLDiffusionWeightedVolumeNode::vtkMRMLDiffusionWeightedVolumeNode() +vtkMRMLDiffusionWeightedVolumeNode::vtkMRMLDiffusionWeightedVolumeNode() : + DiffusionGradients(vtkDoubleArray::New()), + BValues(vtkDoubleArray::New()) { - this->DiffusionGradients = vtkDoubleArray::New(); this->DiffusionGradients->SetNumberOfComponents(3); - this->BValues = vtkDoubleArray::New(); - this->SetNumberOfGradientsInternal(7); //6 gradients + 1 baseline for(int i=0; i<3; i++) @@ -44,8 +44,6 @@ vtkMRMLDiffusionWeightedVolumeNode::vtkMRMLDiffusionWeightedVolumeNode() this->MeasurementFrameMatrix[i][j] = (i == j) ? 1.0 : 0.0; } } - - this->ExtractComponents = NULL; } //---------------------------------------------------------------------------- @@ -53,12 +51,6 @@ vtkMRMLDiffusionWeightedVolumeNode::~vtkMRMLDiffusionWeightedVolumeNode() { this->DiffusionGradients->Delete(); this->BValues->Delete(); - - if (this->ExtractComponents) - { - this->ExtractComponents->Delete(); - this->ExtractComponents = NULL; - } } //---------------------------------------------------------------------------- @@ -275,24 +267,52 @@ int vtkMRMLDiffusionWeightedVolumeNode::GetNumberOfGradients() return this->DiffusionGradients->GetNumberOfTuples(); } +//------------------------------------------------------------------------------ + +inline bool valid_grad_length(vnl_double_3 grad) { + // returns true if grad length is: within GRAD_EPS of 0.0 or 1.0 + return (grad.two_norm() < 1e-6) || (fabs(1.0 - grad.two_norm()) < 1e-6); +} + //---------------------------------------------------------------------------- -void vtkMRMLDiffusionWeightedVolumeNode::SetDiffusionGradient(int num,const double grad[3]) +void vtkMRMLDiffusionWeightedVolumeNode::SetDiffusionGradient(int num, const double grad[3]) { - if (num < 0 || num >= this->DiffusionGradients->GetNumberOfTuples()) + if ((num < 0) || + (num >= this->DiffusionGradients->GetNumberOfTuples())) { vtkErrorMacro(<< "Gradient number is out of range. " "Allocate first the number of gradients with SetNumberOfGradients"); return; } - this->DiffusionGradients->SetComponent(num,0,grad[0]); - this->DiffusionGradients->SetComponent(num,1,grad[1]); - this->DiffusionGradients->SetComponent(num,2,grad[2]); + + vnl_double_3 tmp_grad(grad[0], grad[1], grad[2]); + if (!valid_grad_length(tmp_grad)) + { + vtkErrorMacro(<< "vtkMRMLDiffusionWeightedVolumeNode only accepts gradient vectors with length 0.0 or 1.0!" + << " Got vector with length: " << tmp_grad.two_norm()); + return; + } + + this->DiffusionGradients->SetTuple3(num, grad[0], grad[1], grad[2]); this->Modified(); } //---------------------------------------------------------------------------- void vtkMRMLDiffusionWeightedVolumeNode::SetDiffusionGradients(vtkDoubleArray *grad) { + // gradients must all be length 0 (baseline) or 1. + vnl_double_3 tmp_grad; + for (int i = 0; i < grad->GetNumberOfTuples(); i++) + { + tmp_grad.copy_in(grad->GetTuple3(i)); + if (!valid_grad_length(tmp_grad)) + { + vtkErrorMacro(<< "vtkMRMLDiffusionWeightedVolumeNode only accepts gradient vectors with length 0.0 or 1.0!" + << " Got vector with length: " << tmp_grad.two_norm()); + return; + } + } + this->DiffusionGradients->DeepCopy(grad); this->Modified(); } diff --git a/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.h b/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.h index e287400fb949b0f5f13bdf5318d7721af368a05a..1a9e113cf1f825f3862e024ca161fdb23bb9bccc 100644 --- a/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.h +++ b/Libs/MRML/Core/vtkMRMLDiffusionWeightedVolumeNode.h @@ -113,8 +113,6 @@ protected: vtkDoubleArray *DiffusionGradients; vtkDoubleArray *BValues; - vtkImageExtractComponents *ExtractComponents; - }; #endif diff --git a/Libs/MRML/Core/vtkMRMLNRRDStorageNode.cxx b/Libs/MRML/Core/vtkMRMLNRRDStorageNode.cxx index ea66a57a8e7c4457d765a26d2f112b28c14962ea..00c8460a54edd2eac1a960dab864c9693b65ae0a 100644 --- a/Libs/MRML/Core/vtkMRMLNRRDStorageNode.cxx +++ b/Libs/MRML/Core/vtkMRMLNRRDStorageNode.cxx @@ -32,6 +32,9 @@ Version: $Revision: 1.6 $ #include #include +// vnl includes +#include + //---------------------------------------------------------------------------- vtkMRMLNodeNewMacro(vtkMRMLNRRDStorageNode); @@ -401,104 +404,164 @@ int vtkMRMLNRRDStorageNode::WriteDataInternal(vtkMRMLNode *refNode) } //---------------------------------------------------------------------------- -int vtkMRMLNRRDStorageNode::ParseDiffusionInformation(vtkTeemNRRDReader *reader,vtkDoubleArray *grad,vtkDoubleArray *bvalues) +// internal + +const std::string dwmri_grad_tag("DWMRI_gradient_"); +const std::string dwmri_bvalue_tag("DWMRI_b-value"); + +bool parse_gradient_key(std::string key, size_t &grad_number, size_t &gradkey_pad_width, std::string err) +{ + std::stringstream err_stream; + + // note: Slicer no longer supports NRRDs with DWMRI_NEX_ keys. This was removed + // from Dicom2Nrrd in the following commit: + // Slicer3/4 SVN: r26101, git-svn: 63a18f7d6900a + // and never un-commented in Dicom2Nrrd (or DWIConvert). + // If such a key is found, we print an error and fail. + if (key.find("DWMRI_NEX_") != std::string::npos) + { + err_stream << "DWMRI_NEX_ NRRD tag is no longer supported (since SVN r6101)." + << " Please adjust header manually to encode repeat excitations" + << " as unique DWMRI_gradient_###N keys, and re-load."; + err = err_stream.str(); + return false; + } + + if (key.find(dwmri_grad_tag) == std::string::npos) + { + return false; + } + // below here key is: DWMRI_gradient_#### + + // padding is the extra zeros to give a specific digit count + // 0001 + // ^^^ <- zeros here are padding to 4 digits + // we enforce the constraint that the padding of the grad keys must be consistent + if (gradkey_pad_width == 0) { + gradkey_pad_width = key.size() - dwmri_grad_tag.size(); + } + else if (gradkey_pad_width != key.size() - dwmri_grad_tag.size()) + { + err_stream << "DWMRI NRRD gradient key-numbers must have consistent padding (####N)" + << " Found tag: '" << key << "' but previous key had padding: " << gradkey_pad_width; + err = err_stream.str(); + return false; + } + + // slices key string from `dwmri_grad_tag.size()` to `key.size()`. + // e.g.: "DWMRI_gradient_000001" -> "000001" + std::string cur_grad_num_str = key.substr(dwmri_grad_tag.size(), key.size()); + size_t cur_grad_num = atol(cur_grad_num_str.c_str()); + + // enforce monotonic order + if (cur_grad_num != grad_number) + { + err_stream << "DWMRI NRRD gradient key-numbers must be consecutive." + << " Found tag: '" << key << "' but previous key was: " << grad_number; + err = err_stream.str(); + return false; + } + + grad_number += 1; + return true; +} + +//---------------------------------------------------------------------------- +int vtkMRMLNRRDStorageNode::ParseDiffusionInformation( + vtkTeemNRRDReader* reader, + vtkDoubleArray* gradients_array, + vtkDoubleArray* bvalues_array) { - std::string keys(reader->GetHeaderKeys()); - std::string key,value,num; - std::string tag,tagnex; - const char *tmp; - vtkNew factor; - grad->SetNumberOfComponents(3); - double g[3]; - int rep; - - // search for modality tag - key = "modality"; - tmp = reader->GetHeaderValue(key.c_str()); - if (tmp == NULL) + // Validate modality tag + std::string modality(reader->GetHeaderValue("modality")); + if (modality != "DWMRI") { + vtkErrorMacro(<< "NRRD header missing 'modality: DWMRI' tag!") return 0; } - if (strcmp(tmp,"DWMRI") != 0) + + std::map nrrd_keys = reader->GetHeaderKeysMap(); + + /* + Step 1: get DWMRI_b-value + */ + std::string ref_bvalue_str(reader->GetHeaderValue(dwmri_bvalue_tag.c_str())); + if (ref_bvalue_str.empty()) { + vtkErrorMacro(<< "Missing 'DWMRI_b-value' tag!") return 0; } - // search for tag DWMRI_gradient_ - tag = "DWMRI_gradient_"; - tagnex = "DWMRI_NEX_"; - unsigned int pos = 0; - int gbeginpos =0; - int gendpos = 0; - pos = (unsigned int)keys.find(tag,pos); - while ( pos < keys.size() ) - { - num = keys.substr(pos+tag.size(),4); - // Insert gradient - key = tag+num; - tmp = reader->GetHeaderValue(key.c_str()); - if (tmp == NULL) - { - continue; - } - else - { - value = tmp; - } - gbeginpos = -1; - gendpos = 0; - for (int i=0 ;i<3; i++) + double ref_bvalue = atof(ref_bvalue_str.c_str()); + + + /* + Step 2: loop over all keys + - for all DWMRI_gradient_ keys, validate + - consecutive + - consistent padding + - save each gradient to tmp_grads + - record maximum gradient length + */ + vtkNew tmp_grads; + tmp_grads->SetNumberOfComponents(3); + size_t grad_idx = 0; + size_t gradkey_pad_width = 0; + double max_grad_norm = 0; + std::string err; + + std::map::iterator nrrd_keys_iter = nrrd_keys.begin(); + for (; nrrd_keys_iter != nrrd_keys.end(); nrrd_keys_iter++) + { + std::string key = nrrd_keys_iter->first; + + if (!parse_gradient_key(key, grad_idx, gradkey_pad_width, err)) { - do + if (err.empty()) { - gbeginpos++; - gendpos=(int)value.find(" ",gbeginpos); + continue; + } + else + { + vtkErrorMacro(<< err); + return 0; } - while(gendpos==gbeginpos); - g[i] = atof(value.substr(gbeginpos,gendpos).c_str()); - gbeginpos = gendpos; - } - grad->InsertNextTuple3(g[0],g[1],g[2]); - factor->InsertNextValue(sqrt(g[0]*g[0]+g[1]*g[1]+g[2]*g[2])); - // find repetitions of this gradient - key = tagnex+num; - tmp = reader->GetHeaderValue(key.c_str()); - if (tmp == NULL) - { - value = ""; - } - else - { - value = tmp; - } - if (value.size()>0) { - rep = atoi(value.c_str()); - for (int i=0;iInsertNextTuple3(g[0],g[1],g[2]); - factor->InsertNextValue(sqrt( g[0]*g[0]+g[1]*g[1]+g[2]*g[2] )); } - } - pos = (unsigned int)keys.find(tag,pos+1); - } + // parse the gradient vector into double[3] + vnl_double_3 cur_grad(0,0,0); + std::stringstream grad_value_stream(nrrd_keys_iter->second); + grad_value_stream >> cur_grad[0] >> cur_grad[1] >> cur_grad[2]; - grad->Modified(); - factor->Modified(); - double range[2]; - // search for tag DWMRI_b-value - key = "DWMRI_b-value"; - tmp = reader->GetHeaderValue(key.c_str()); - if (tmp == NULL) - { - return 0; + max_grad_norm = std::max(cur_grad.two_norm(), max_grad_norm); + tmp_grads->InsertNextTuple(cur_grad.data_block()); } - double bval = atof(tmp); - factor->GetRange(range); - bvalues->SetNumberOfTuples(grad->GetNumberOfTuples()); - for (int i=0; iGetNumberOfTuples();i++) + + assert(grad_idx == tmp_grads->GetNumberOfTuples()); + + /* + Step 3: loop over gradients + - calculate each b-value based on NA-MIC DWI gradient length-encoding + - then normalize each gradient to unit-length + */ + bvalues_array->SetNumberOfTuples(tmp_grads->GetNumberOfTuples()); + // calculate the b-values + for (int i=0; i < tmp_grads->GetNumberOfTuples(); i++) { + vnl_double_3 cur_grad(0,0,0); + cur_grad.copy_in(tmp_grads->GetTuple3(i)); + // note: this is norm^2, per the NA-MIC NRRD DWI convention // http://wiki.na-mic.org/Wiki/index.php/NAMIC_Wiki:DTI:Nrrd_format - bvalues->SetValue(i, bval * (pow(factor->GetValue(i)/range[1], 2))); + double cur_bval = ref_bvalue * pow(cur_grad.two_norm() / max_grad_norm, 2); + bvalues_array->SetValue(i, cur_bval); + + // normalize gradient vector to unit-length + // must be done *after* bvalue extraction + cur_grad.normalize(); + tmp_grads->InsertTuple(i, cur_grad.data_block()); } + + // Step 4: copy tmp_grads to output + gradients_array->DeepCopy(tmp_grads); return 1; } diff --git a/Libs/vtkTeem/CMakeLists.txt b/Libs/vtkTeem/CMakeLists.txt index 89b5650386d2d5f7514a81565bb8454bfb4e8408..83e4ea32c5374b05b52b5e385fda258e7d37a621 100644 --- a/Libs/vtkTeem/CMakeLists.txt +++ b/Libs/vtkTeem/CMakeLists.txt @@ -87,6 +87,8 @@ set(srcs ${vtkTeem_SRCS}) add_library(${lib_name} ${srcs}) set(libs + itkvnl + ITKCommon ${Teem_LIBRARIES} ${VTK_LIBRARIES} ) diff --git a/Libs/vtkTeem/vtkTeemNRRDReader.cxx b/Libs/vtkTeem/vtkTeemNRRDReader.cxx index 8fcaf526c194ea6b17b6cc45c467932b5a4a92cf..6ed57223a5f9ae338db0fa525372d504e183ea87 100644 --- a/Libs/vtkTeem/vtkTeemNRRDReader.cxx +++ b/Libs/vtkTeem/vtkTeemNRRDReader.cxx @@ -105,7 +105,13 @@ const char* vtkTeemNRRDReader::GetHeaderKeys() } //---------------------------------------------------------------------------- -std::vector vtkTeemNRRDReader::GetHeaderKeysVector() +const std::map vtkTeemNRRDReader::GetHeaderKeysMap() +{ + return this->HeaderKeyValue; +} + +//---------------------------------------------------------------------------- +const std::vector vtkTeemNRRDReader::GetHeaderKeysVector() { std::vector keys; for (std::map::iterator i = HeaderKeyValue.begin(); i != HeaderKeyValue.end(); i++) diff --git a/Libs/vtkTeem/vtkTeemNRRDReader.h b/Libs/vtkTeem/vtkTeemNRRDReader.h index 3908c92436e077020196fce15a933aa216a307da..b870920071839351026b7d907147d6e1d8e0aa7f 100644 --- a/Libs/vtkTeem/vtkTeemNRRDReader.h +++ b/Libs/vtkTeem/vtkTeemNRRDReader.h @@ -75,7 +75,12 @@ public: /// /// Get a list of keys in the header. Preferred method to use as it /// supports spaces in key names. - std::vector GetHeaderKeysVector(); + const std::vector GetHeaderKeysVector(); + + /// + /// Get a map of keys in the header. Preferred method to use as it + /// supports spaces in key names. + const std::map GetHeaderKeysMap(); /// /// Get a value given a key in the header diff --git a/Libs/vtkTeem/vtkTeemNRRDWriter.cxx b/Libs/vtkTeem/vtkTeemNRRDWriter.cxx index 2b41c7cfd9945cda9bc4927188f36bef529f169d..286afe05d95581e72b921764269c78e01a7bcaa8 100644 --- a/Libs/vtkTeem/vtkTeemNRRDWriter.cxx +++ b/Libs/vtkTeem/vtkTeemNRRDWriter.cxx @@ -9,6 +9,12 @@ #include "vtkInformation.h" #include +#include +#include + +#include "itkNumberToString.h" + + class AttributeMapType: public std::map {}; class AxisInfoMapType : public std::map {}; @@ -23,7 +29,7 @@ vtkTeemNRRDWriter::vtkTeemNRRDWriter() this->IJKToRASMatrix = vtkMatrix4x4::New(); this->MeasurementFrameMatrix = vtkMatrix4x4::New(); this->UseCompression = 1; - this->DiffusionWeigthedData = 0; + this->DiffusionWeightedData = 0; this->FileType = VTK_BINARY; this->WriteErrorOff(); this->Attributes = new AttributeMapType; @@ -75,7 +81,7 @@ void vtkTeemNRRDWriter::vtkImageDataInfoToNrrdInfo(vtkImageData *in, int &kind, { vtkDataArray *array; - this->DiffusionWeigthedData = 0; + this->DiffusionWeightedData = 0; if ((array = static_cast (in->GetPointData()->GetScalars()))) { numComp = array->GetNumberOfComponents(); @@ -102,7 +108,7 @@ void vtkTeemNRRDWriter::vtkImageDataInfoToNrrdInfo(vtkImageData *in, int &kind, if (numGrad == numBValues && numGrad == numComp && numGrad>6) { kind = nrrdKindList; - this->DiffusionWeigthedData = 1; + this->DiffusionWeightedData = 1; } else { @@ -279,6 +285,9 @@ void* vtkTeemNRRDWriter::MakeNRRD() AttributeMapType::iterator ait; for (ait = this->Attributes->begin(); ait != this->Attributes->end(); ++ait) { + // Don't set `space` as k-v. it is handled above, and needs to be a nrrd *field*. + if (ait->first == "space") { continue; } + nrrdKeyValueAdd(nrrd, (*ait).first.c_str(), (*ait).second.c_str()); } @@ -296,8 +305,12 @@ void* vtkTeemNRRDWriter::MakeNRRD() } } + // use double-conversion library (via ITK) for better + // float64 string representation. + itk::NumberToString DoubleConvert; + // 2. Take care about diffusion data - if (this->DiffusionWeigthedData) + if (this->DiffusionWeightedData) { unsigned int numGrad = this->DiffusionGradients->GetNumberOfTuples(); unsigned int numBValues = this->BValues->GetNumberOfTuples(); @@ -305,30 +318,38 @@ void* vtkTeemNRRDWriter::MakeNRRD() if (kind[0] == nrrdKindList && numGrad == size[0] && numBValues == size[0]) { // This is diffusion Data - double *grad; - double bVal,factor; + vnl_double_3 grad; + double bVal, factor; double maxbVal = this->BValues->GetRange()[1]; - char value[1024]; - char key[1024]; - strcpy(key,"modality"); - strcpy(value,"DWMRI"); - nrrdKeyValueAdd(nrrd, key, value); - - strcpy(key,"DWMRI_b-value"); - //sprintf(value,"%f",maxbVal,1024); - sprintf(value,"%f",maxbVal); - nrrdKeyValueAdd(nrrd,key, value); - for (unsigned int ig =0; ig< numGrad; ig++) + + std::string modality_key("modality"); + std::string modality_value("DWMRI"); + nrrdKeyValueAdd(nrrd, modality_key.c_str(), modality_value.c_str()); + + std::string bval_key("DWMRI_b-value"); + std::stringstream bval_value; + bval_value << DoubleConvert(maxbVal); + nrrdKeyValueAdd(nrrd, bval_key.c_str(), bval_value.str().c_str()); + + for (unsigned int ig =0; ig < numGrad; ig++) { - grad=this->DiffusionGradients->GetTuple3(ig); + // key + std::stringstream key_stream; + key_stream << "DWMRI_gradient_" << setfill('0') << setw(4) << ig; + + // gradient value + grad.copy_in(this->DiffusionGradients->GetTuple3(ig)); + bVal = this->BValues->GetValue(ig); - // for multiple b-values, scale factor is `sqrt(b/b_max)` - // per NA-MIC DWI convention. so we take `norm^2 * b_max` - // to get back the original b-values. + // per NA-MIC DWI convention factor = sqrt(bVal/maxbVal); - sprintf(key,"%s%04d","DWMRI_gradient_",ig); - sprintf(value,"%f %f %f",grad[0]*factor, grad[1]*factor, grad[2]*factor); - nrrdKeyValueAdd(nrrd,key, value); + std::stringstream value_stream; + value_stream << std::setprecision(17) << + DoubleConvert(grad[0] * factor) << " " << + DoubleConvert(grad[1] * factor) << " " << + DoubleConvert(grad[2] * factor); + + nrrdKeyValueAdd(nrrd, key_stream.str().c_str(), value_stream.str().c_str()); } } } diff --git a/Libs/vtkTeem/vtkTeemNRRDWriter.h b/Libs/vtkTeem/vtkTeemNRRDWriter.h index b414af1c3328af784e2a3ae3bfbd37de7e005484..5e69546f955e8e53c5b27538bb48aa423ad65a0f 100644 --- a/Libs/vtkTeem/vtkTeemNRRDWriter.h +++ b/Libs/vtkTeem/vtkTeemNRRDWriter.h @@ -117,7 +117,7 @@ private: void operator=(const vtkTeemNRRDWriter&); /// Not implemented. void vtkImageDataInfoToNrrdInfo(vtkImageData *in, int &nrrdKind, size_t &numComp, int &vtkType, void **buffer); int VTKToNrrdPixelType( const int vtkPixelType ); - int DiffusionWeigthedData; + int DiffusionWeightedData; }; #endif