dbdicom 0.2.0__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dbdicom might be problematic. Click here for more details.
- dbdicom/__init__.py +5 -3
- dbdicom/create.py +77 -70
- dbdicom/dro.py +174 -0
- dbdicom/ds/dataset.py +30 -3
- dbdicom/ds/types/mr_image.py +18 -7
- dbdicom/extensions/__init__.py +10 -0
- dbdicom/{wrappers → extensions}/dipy.py +191 -205
- dbdicom/extensions/elastix.py +503 -0
- dbdicom/extensions/matplotlib.py +107 -0
- dbdicom/extensions/numpy.py +271 -0
- dbdicom/{wrappers → extensions}/scipy.py +131 -32
- dbdicom/{wrappers → extensions}/skimage.py +1 -1
- dbdicom/extensions/sklearn.py +243 -0
- dbdicom/extensions/vreg.py +1390 -0
- dbdicom/external/dcm4che/bin/emf2sf +57 -57
- dbdicom/manager.py +91 -36
- dbdicom/pipelines.py +66 -0
- dbdicom/record.py +447 -80
- dbdicom/types/instance.py +46 -20
- dbdicom/types/series.py +2182 -399
- dbdicom/utils/image.py +152 -21
- dbdicom/utils/variables.py +8 -2
- dbdicom/utils/vreg.py +327 -135
- dbdicom-0.2.3.dist-info/METADATA +88 -0
- dbdicom-0.2.3.dist-info/RECORD +67 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/WHEEL +1 -1
- dbdicom/external/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-310.pyc +0 -0
- dbdicom/external/dcm4che/bin/__pycache__/__init__.cpython-37.pyc +0 -0
- dbdicom/external/dcm4che/lib/linux-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/linux-x86-64/libopencv_java.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparc/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-sparcv9/libclib_jiio_vis2.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86/libclib_jiio.so +0 -0
- dbdicom/external/dcm4che/lib/solaris-x86-64/libclib_jiio.so +0 -0
- dbdicom/wrappers/__init__.py +0 -7
- dbdicom/wrappers/elastix.py +0 -855
- dbdicom/wrappers/numpy.py +0 -119
- dbdicom/wrappers/sklearn.py +0 -151
- dbdicom/wrappers/vreg.py +0 -273
- dbdicom-0.2.0.dist-info/METADATA +0 -276
- dbdicom-0.2.0.dist-info/RECORD +0 -81
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/LICENSE +0 -0
- {dbdicom-0.2.0.dist-info → dbdicom-0.2.3.dist-info}/top_level.txt +0 -0
dbdicom/utils/image.py
CHANGED
|
@@ -1,8 +1,94 @@
|
|
|
1
|
+
import math
|
|
1
2
|
import numpy as np
|
|
2
3
|
from scipy.interpolate import interpn
|
|
3
4
|
from scipy.ndimage import affine_transform
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
def as_mosaic(array, rows=None):
|
|
8
|
+
"""Reformat a 3D array (x,y,z) into a 2D mosaic"""
|
|
9
|
+
|
|
10
|
+
nz = array.shape[2]
|
|
11
|
+
if rows is None:
|
|
12
|
+
rows = math.ceil(math.sqrt(nz))
|
|
13
|
+
cols = math.ceil(nz/rows)
|
|
14
|
+
mosaic = np.zeros((array.shape[0]*cols, array.shape[1]*rows))
|
|
15
|
+
for k in range(nz):
|
|
16
|
+
j = math.floor(k/cols)
|
|
17
|
+
i = k-j*cols
|
|
18
|
+
mosaic[
|
|
19
|
+
i*array.shape[0]:(i+1)*array.shape[0],
|
|
20
|
+
j*array.shape[1]:(j+1)*array.shape[1],
|
|
21
|
+
] = array[:,:,k]
|
|
22
|
+
return mosaic
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def ellipsoid(a, b, c, spacing=(1., 1., 1.), levelset=False):
|
|
28
|
+
"""
|
|
29
|
+
Generates ellipsoid with semimajor axes aligned with grid dimensions
|
|
30
|
+
on grid with specified `spacing`.
|
|
31
|
+
|
|
32
|
+
Parameters
|
|
33
|
+
----------
|
|
34
|
+
a : float
|
|
35
|
+
Length of semimajor axis aligned with x-axis.
|
|
36
|
+
b : float
|
|
37
|
+
Length of semimajor axis aligned with y-axis.
|
|
38
|
+
c : float
|
|
39
|
+
Length of semimajor axis aligned with z-axis.
|
|
40
|
+
spacing : tuple of floats, length 3
|
|
41
|
+
Spacing in (x, y, z) spatial dimensions.
|
|
42
|
+
levelset : bool
|
|
43
|
+
If True, returns the level set for this ellipsoid (signed level
|
|
44
|
+
set about zero, with positive denoting interior) as np.float64.
|
|
45
|
+
False returns a binarized version of said level set.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
ellip : (N, M, P) array
|
|
50
|
+
Ellipsoid centered in a correctly sized array for given `spacing`.
|
|
51
|
+
Boolean dtype unless `levelset=True`, in which case a float array is
|
|
52
|
+
returned with the level set above 0.0 representing the ellipsoid.
|
|
53
|
+
|
|
54
|
+
Note
|
|
55
|
+
----
|
|
56
|
+
This function is copy-pasted directly from skimage source code without modification - this to avoid bringing in skimage as an essential dependency.
|
|
57
|
+
|
|
58
|
+
"""
|
|
59
|
+
if (a <= 0) or (b <= 0) or (c <= 0):
|
|
60
|
+
raise ValueError('Parameters a, b, and c must all be > 0')
|
|
61
|
+
|
|
62
|
+
offset = np.r_[1, 1, 1] * np.r_[spacing]
|
|
63
|
+
|
|
64
|
+
# Calculate limits, and ensure output volume is odd & symmetric
|
|
65
|
+
low = np.ceil(- np.r_[a, b, c] - offset)
|
|
66
|
+
high = np.floor(np.r_[a, b, c] + offset + 1)
|
|
67
|
+
|
|
68
|
+
for dim in range(3):
|
|
69
|
+
if (high[dim] - low[dim]) % 2 == 0:
|
|
70
|
+
low[dim] -= 1
|
|
71
|
+
num = np.arange(low[dim], high[dim], spacing[dim])
|
|
72
|
+
if 0 not in num:
|
|
73
|
+
low[dim] -= np.max(num[num < 0])
|
|
74
|
+
|
|
75
|
+
# Generate (anisotropic) spatial grid
|
|
76
|
+
x, y, z = np.mgrid[low[0]:high[0]:spacing[0],
|
|
77
|
+
low[1]:high[1]:spacing[1],
|
|
78
|
+
low[2]:high[2]:spacing[2]]
|
|
79
|
+
|
|
80
|
+
if not levelset:
|
|
81
|
+
arr = ((x / float(a)) ** 2 +
|
|
82
|
+
(y / float(b)) ** 2 +
|
|
83
|
+
(z / float(c)) ** 2) <= 1
|
|
84
|
+
else:
|
|
85
|
+
arr = ((x / float(a)) ** 2 +
|
|
86
|
+
(y / float(b)) ** 2 +
|
|
87
|
+
(z / float(c)) ** 2) - 1
|
|
88
|
+
|
|
89
|
+
return arr
|
|
90
|
+
|
|
91
|
+
|
|
6
92
|
def multislice_affine_transform(array_source, affine_source, output_affine, slice_thickness=None, **kwargs):
|
|
7
93
|
"""Generalization of scipy's affine transform.
|
|
8
94
|
|
|
@@ -370,10 +456,10 @@ def affine_matrix( # single slice function
|
|
|
370
456
|
return affine
|
|
371
457
|
|
|
372
458
|
|
|
373
|
-
def slice_location(
|
|
374
|
-
image_orientation, # ImageOrientationPatient
|
|
375
|
-
image_position, # ImagePositionPatient
|
|
376
|
-
):
|
|
459
|
+
def slice_location(
|
|
460
|
+
image_orientation:list, # ImageOrientationPatient
|
|
461
|
+
image_position:list, # ImagePositionPatient
|
|
462
|
+
) -> float:
|
|
377
463
|
"""Calculate Slice Location"""
|
|
378
464
|
|
|
379
465
|
row_cosine = np.array(image_orientation[:3])
|
|
@@ -383,6 +469,27 @@ def slice_location(
|
|
|
383
469
|
return np.dot(np.array(image_position), slice_cosine)
|
|
384
470
|
|
|
385
471
|
|
|
472
|
+
def image_position_from_slice_location(slice_location:float, affine=np.eye(4))->list:
|
|
473
|
+
v = dismantle_affine_matrix(affine)
|
|
474
|
+
return list(affine[:3, 3] + slice_location * np.array(v['slice_cosine']))
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def image_position_patient(affine, number_of_slices):
|
|
478
|
+
slab = dismantle_affine_matrix(affine)
|
|
479
|
+
image_positions = []
|
|
480
|
+
image_locations = []
|
|
481
|
+
for s in range(number_of_slices):
|
|
482
|
+
pos = [
|
|
483
|
+
slab['ImagePositionPatient'][i]
|
|
484
|
+
+ s*slab['SpacingBetweenSlices']*slab['slice_cosine'][i]
|
|
485
|
+
for i in range(3)
|
|
486
|
+
]
|
|
487
|
+
loc = np.dot(np.array(pos), np.array(slab['slice_cosine']))
|
|
488
|
+
image_positions.append(pos)
|
|
489
|
+
image_locations.append(loc)
|
|
490
|
+
return image_positions, image_locations
|
|
491
|
+
|
|
492
|
+
|
|
386
493
|
def affine_matrix_multislice(
|
|
387
494
|
image_orientation, # ImageOrientationPatient (assume same for all slices)
|
|
388
495
|
image_positions, # ImagePositionPatient for all slices
|
|
@@ -442,22 +549,6 @@ def affine_to_RAH(affine):
|
|
|
442
549
|
return np.matmul(rot_180, affine)
|
|
443
550
|
|
|
444
551
|
|
|
445
|
-
def image_position_patient(affine, number_of_slices):
|
|
446
|
-
slab = dismantle_affine_matrix(affine)
|
|
447
|
-
image_positions = []
|
|
448
|
-
image_locations = []
|
|
449
|
-
for s in range(number_of_slices):
|
|
450
|
-
pos = [
|
|
451
|
-
slab['ImagePositionPatient'][i]
|
|
452
|
-
+ s*slab['SpacingBetweenSlices']*slab['slice_cosine'][i]
|
|
453
|
-
for i in range(3)
|
|
454
|
-
]
|
|
455
|
-
loc = np.dot(np.array(pos), np.array(slab['slice_cosine']))
|
|
456
|
-
image_positions.append(pos)
|
|
457
|
-
image_locations.append(loc)
|
|
458
|
-
return image_positions, image_locations
|
|
459
|
-
|
|
460
|
-
|
|
461
552
|
def clip(array, value_range = None):
|
|
462
553
|
|
|
463
554
|
array[np.isnan(array)] = 0
|
|
@@ -467,7 +558,47 @@ def clip(array, value_range = None):
|
|
|
467
558
|
return np.clip(array, value_range[0], value_range[1])
|
|
468
559
|
|
|
469
560
|
|
|
470
|
-
def scale_to_range(array, bits_allocated):
|
|
561
|
+
def scale_to_range(array, bits_allocated, signed=False):
|
|
562
|
+
|
|
563
|
+
range = 2.0**bits_allocated - 1
|
|
564
|
+
if signed:
|
|
565
|
+
minval = -2.0**(bits_allocated-1)
|
|
566
|
+
else:
|
|
567
|
+
minval = 0
|
|
568
|
+
maximum = np.amax(array)
|
|
569
|
+
minimum = np.amin(array)
|
|
570
|
+
if maximum == minimum:
|
|
571
|
+
slope = 1
|
|
572
|
+
else:
|
|
573
|
+
slope = range / (maximum - minimum)
|
|
574
|
+
intercept = -slope * minimum + minval
|
|
575
|
+
array *= slope
|
|
576
|
+
array += intercept
|
|
577
|
+
|
|
578
|
+
if bits_allocated == 8:
|
|
579
|
+
if signed:
|
|
580
|
+
return array.astype(np.int8), slope, intercept
|
|
581
|
+
else:
|
|
582
|
+
return array.astype(np.uint8), slope, intercept
|
|
583
|
+
if bits_allocated == 16:
|
|
584
|
+
if signed:
|
|
585
|
+
return array.astype(np.int16), slope, intercept
|
|
586
|
+
else:
|
|
587
|
+
return array.astype(np.uint16), slope, intercept
|
|
588
|
+
if bits_allocated == 32:
|
|
589
|
+
if signed:
|
|
590
|
+
return array.astype(np.int32), slope, intercept
|
|
591
|
+
else:
|
|
592
|
+
return array.astype(np.uint32), slope, intercept
|
|
593
|
+
if bits_allocated == 64:
|
|
594
|
+
if signed:
|
|
595
|
+
return array.astype(np.int64), slope, intercept
|
|
596
|
+
else:
|
|
597
|
+
return array.astype(np.uint64), slope, intercept
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def _scale_to_range(array, bits_allocated):
|
|
601
|
+
# Obsolete - generalized as above
|
|
471
602
|
|
|
472
603
|
range = 2.0**bits_allocated - 1
|
|
473
604
|
maximum = np.amax(array)
|
dbdicom/utils/variables.py
CHANGED
|
@@ -20,8 +20,11 @@ def str_to_seconds(dicom_tm):
|
|
|
20
20
|
return seconds_since_midnight
|
|
21
21
|
|
|
22
22
|
def seconds_to_str(seconds_since_midnight):
|
|
23
|
-
if not isinstance(seconds_since_midnight, float):
|
|
23
|
+
# if not isinstance(seconds_since_midnight, float):
|
|
24
|
+
# return None
|
|
25
|
+
if seconds_since_midnight is None:
|
|
24
26
|
return None
|
|
27
|
+
seconds_since_midnight = float(seconds_since_midnight)
|
|
25
28
|
hours = math.floor(seconds_since_midnight/3600)
|
|
26
29
|
minutes = math.floor((seconds_since_midnight-hours*3600)/60)
|
|
27
30
|
seconds = math.floor(seconds_since_midnight-hours*3600-minutes*60)
|
|
@@ -48,8 +51,11 @@ def time_to_seconds(tm):
|
|
|
48
51
|
return seconds_since_midnight
|
|
49
52
|
|
|
50
53
|
def seconds_to_time(seconds_since_midnight):
|
|
51
|
-
if not isinstance(seconds_since_midnight, float):
|
|
54
|
+
# if not isinstance(seconds_since_midnight, float):
|
|
55
|
+
# return None
|
|
56
|
+
if seconds_since_midnight is None:
|
|
52
57
|
return None
|
|
58
|
+
seconds_since_midnight = float(seconds_since_midnight)
|
|
53
59
|
hours = math.floor(seconds_since_midnight/3600)
|
|
54
60
|
minutes = math.floor((seconds_since_midnight-hours*3600)/60)
|
|
55
61
|
seconds = math.floor(seconds_since_midnight-hours*3600-minutes*60)
|