├── miptools ├── .DS_Store ├── data │ ├── test.dcm │ ├── test2.dcm │ └── test3.dcm └── __init__.py ├── .gitignore ├── setup.py └── README.md /miptools/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enochkan/miptools/HEAD/miptools/.DS_Store -------------------------------------------------------------------------------- /miptools/data/test.dcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enochkan/miptools/HEAD/miptools/data/test.dcm -------------------------------------------------------------------------------- /miptools/data/test2.dcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enochkan/miptools/HEAD/miptools/data/test2.dcm -------------------------------------------------------------------------------- /miptools/data/test3.dcm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enochkan/miptools/HEAD/miptools/data/test3.dcm -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled python modules. 2 | *.pyc 3 | 4 | # Setuptools distribution folder. 5 | /dist/ 6 | 7 | # Python egg metadata, regenerated from source files by setuptools. 8 | /*.egg-info 9 | /*.egg 10 | 11 | # build files 12 | /build/* -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup(name='miptools', 4 | version='0.0.4', 5 | description='miptools is a preprocessing utility that allows users to preprocess medical images at ease', 6 | url='https://github.com/chinokenochkan/miptools', 7 | author='Chi Nok Enoch Kan', 8 | author_email='chinokenoch.kan@marquette.edu', 9 | license='MIT', 10 | packages=['miptools'], 11 | install_requires=[ 12 | 'pydicom','numpy','matplotlib','scipy' 13 | ], 14 | classifiers=[ 15 | 'License :: OSI Approved :: MIT License', 16 | 'Programming Language :: Python :: 3.7', 17 | ], 18 | zip_safe=False) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miptools 2 | [![PyPI version](https://badge.fury.io/py/miptools.svg)](https://badge.fury.io/py/miptools) 3 | 4 | This is a medical image processing toolbox for quick preprocessing of CTs and MRIs 5 | 6 | Download the latest release ```pip install --upgrade miptools``` 7 | 8 | Sample usage: 9 | ~~~~{.python} 10 | # import library 11 | import miptools as mt 12 | 13 | # preprocessing usage 14 | mt.preprocess('./data/test', org='brain', windowing='bsb', resample=True, visualize=True) 15 | ~~~~ 16 | 17 | ## CT Preprocessing 18 | Currently supporting CT windowing preprocessing only. Available windows include: 19 | - Simple windowing (```window='simple'```) 20 | - Brain, Subdural, Bone windowing (```window='bs'```) 21 | - Sigmoid windowing (```window='sigmoid'```) 22 | 23 | Also supporting resampling of pixel spacing to ```[1, 1]``` 24 | 25 | ## Versions 26 | Python == 3.7.2 27 | 28 | ## Todo 29 | - [ ] MRI processing 30 | - [ ] gradient windowing for CT 31 | - [X] add saving function 32 | - [ ] add tests 33 | - [ ] add normalization functions 34 | 35 | ## Author 36 | Enoch Kan/ [@enochkan](https://github.com/enochkan) 37 | -------------------------------------------------------------------------------- /miptools/__init__.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import pydicom 3 | import numpy as np 4 | import scipy.ndimage 5 | from PIL import Image 6 | 7 | # { 8 | # helper functions 9 | # } 10 | 11 | def visualize_img(img): 12 | plt.imshow(img, cmap=plt.cm.bone) 13 | plt.axis('off') 14 | plt.show() 15 | 16 | def save_img(img, output, size=None, extension='png'): 17 | image_2d = img.astype('float32') 18 | image_2d_scaled = (np.maximum(image_2d, 0) / image_2d.max()) * 255.0 19 | image_2d_scaled = np.uint8(image_2d_scaled) 20 | #resize if necessary 21 | if size: 22 | image_2d_scaled = cv2.resize(img_2d_scaled, dsize=size, interpolation=cv2.INTER_CUBIC) 23 | # Write the PNG file 24 | if extension == 'png': 25 | im = Image.fromarray(image_2d_scaled) 26 | im.save(output+'.png') 27 | if extension == 'jpg': 28 | im = Image.fromarray(image_2d_scaled) 29 | im.save(output+'.jpg') 30 | 31 | def min_max(img, l, w): 32 | return (img - (l-w/2))/w 33 | 34 | def get_dicom_field(x): 35 | return int(x[0]) if type(x) == pydicom.multival.MultiValue else int(x) 36 | 37 | def get_meta(dcm): 38 | dicom_fields = [dcm[('0028','1050')].value, 39 | dcm[('0028','1051')].value,] 40 | return [get_dicom_field(x) for x in dicom_fields] 41 | 42 | # { 43 | # main functions 44 | # } 45 | 46 | def preprocess(path, org='brain', windowing=None, resample=False, visualize=False, save=False, save_filename='sample', extension='png'): 47 | if org == 'brain': 48 | if windowing is not None: 49 | img = brain_windowing(path, windowing, resample) 50 | # implement other preprocessing 51 | # implement other orgs 52 | 53 | if visualize: 54 | visualize_img(img) 55 | if save: 56 | save_img(img, output=save_filename, extension=extension) 57 | 58 | 59 | def brain_windowing(path, mode, resample): 60 | dcm = pydicom.dcmread(path) 61 | if mode == 'bsb': 62 | return bsb_window(dcm) 63 | elif mode == 'simple': 64 | center, width = get_meta(dcm) 65 | return window_image(dcm, center, width, resample) 66 | elif mode == 'sigmoid_brain': 67 | return window_image(dcm, 40, 80, resample, True) 68 | 69 | def window_image(dcm, window_center, window_width, resample, sigmoid=False): 70 | img = dcm.pixel_array * dcm.RescaleSlope + dcm.RescaleIntercept 71 | if sigmoid: 72 | U = 1.0 73 | eps = (1.0 / 255.0) 74 | ue = np.log((U / eps) - 1.0) 75 | W = (2 / window_width) * ue 76 | b = ((-2 * window_center) / window_width) * ue 77 | z = W * img + b 78 | img = U / (1 + np.power(np.e, -1.0 * z)) 79 | img = (img - np.min(img)) / (np.max(img) - np.min(img)) 80 | else: 81 | img_min = window_center - window_width // 2 82 | img_max = window_center + window_width // 2 83 | img = np.clip(img, img_min, img_max) 84 | 85 | 86 | # resampling 87 | # Determine current pixel spacing 88 | if resample: 89 | new_spacing = [1, 1] 90 | spacing = np.array(dcm.PixelSpacing, dtype=np.float32) 91 | 92 | resize_factor = spacing / new_spacing 93 | new_real_shape = img.shape * resize_factor 94 | new_shape = np.round(new_real_shape) 95 | real_resize_factor = new_shape / img.shape 96 | new_spacing = spacing / real_resize_factor 97 | 98 | img = scipy.ndimage.interpolation.zoom(img, real_resize_factor, mode='nearest') 99 | 100 | return img 101 | 102 | def bsb_window(dcm, resample=False): 103 | brain_img = window_image(dcm, 40, 80, resample) 104 | subdural_img = window_image(dcm, 80, 200, resample) 105 | bone_img = window_image(dcm, 600, 2000, resample) 106 | brain_img = min_max(brain_img, 40, 80) 107 | subdural_img = min_max(brain_img, 80, 200) 108 | bone_img = min_max(brain_img, 600, 2000) 109 | 110 | img = np.array([brain_img, subdural_img, bone_img]).transpose(1,2,0) 111 | return img 112 | 113 | --------------------------------------------------------------------------------