Skip to content

Commit

Permalink
Branchless distance-driven BProj and Proj working
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigovimieiro committed Jan 22, 2020
0 parents commit 3f9e6c0
Show file tree
Hide file tree
Showing 16 changed files with 2,573 additions and 0 deletions.
674 changes: 674 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
pyDBT
======

This repository is a python extension of the [DBT toolbox](https://github.com/LAVI-USP/DBT-Reconstruction) from LAVI-USP.



## How to install?

1. Download the toolbox or clone the directory:

* ```git clone https://github.com/LAVI-USP/pyDBT.git```

2. Go to parent directory:

* ```cd pyDBT```

3. Install the package:

* ```python3 setup.py install```

4. run the example:

* ```cd pydbt && python3 example.py```

5. The toolbox was tested on **Linux** (Ubuntu) x64, and **MacoS** Catalina machines, with python **3.7.5**.

6. You can also run the [MATLAB version](https://github.com/LAVI-USP/DBT-Reconstruction) of the toolbox.

** Please report issues [here](https://github.com/LAVI-USP/pyDBT/issues). **

## Contribute?

We are pleased with any contributions. Fell free to make any [pull requests](https://github.com/LAVI-USP/pyDBT/pulls) or send us an e-mail.


## Toolbox manual:

You can find the [manual](https://github.com/LAVI-USP/DBT-Reconstruction/wiki/Toolbox-Manual) from the MATLAB version, which is pretty much the same. I will create a specific one for the python version in the future.

## Contact:

If you have any question or suggestion, please send us an e-mail:

- Rodrigo - rodrigo dot vimieiro at gmail dot com
- Marcelo - mvieira at sc dot usp dot br

## License:

The toolbox is licensed under the **GNU General Public License v3.0**. Please check the [licence file](https://github.com/LAVI-USP/pyDBT/blob/master/LICENSE).

## Reference:

If you use the toolbox, we will be very grateful if you refer to this [paper](https://doi.org/10.1007/978-981-13-2517-5_53):

> Vimieiro R.B., Borges L.R., Vieira M.A.C. (2019) Open-Source Reconstruction Toolbox for Digital Breast Tomosynthesis. In: Costa-Felix R., Machado J., Alvarenga A. (eds) XXVI Brazilian Congress on Biomedical Engineering. IFMBE Proceedings, vol 70/2. Springer, Singapore.
## Citations:

You can find [here](https://scholar.google.com.br/scholar?oi=bibs&hl=pt-BR&cites=3156269064066227282) the papers that have used the toolbox.

## Acknowledgments:

This work was supported by the São Paulo Research Foundation ([FAPESP](http://www.fapesp.br/) grant 2016/25750-0) and by the National Council for Scientific and Technological Development ([CNPq](http://www.cnpq.br/)). Nobody does anything alone, so we would like to thank the contribution of our lab members and the [Barretos Love Hospital](https://www.hcancerbarretos.com.br) for providing the images of DBT.

---

Laboratory of Computer Vision ([Lavi](http://iris.sel.eesc.usp.br/lavi/))
Department of Electrical and Computer Engineering
São Carlos School of Engineering, University of São Paulo
São Carlos - Brazil
51 changes: 51 additions & 0 deletions pydbt/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Jan 14 08:37:11 2020
@author: Rodrigo
"""

#%%
import matplotlib.pyplot as plt

from parameterSettings import geometry_settings
from functions.manageDicom import readDicom
from functions.dataPreProcess import dataPreProcess
from functions.projectionDDb import projectionDDb
from functions.backprojectionDDb import backprojectionDDb
from functions.initialConfig import initialConfig

#%% Call function for initial configurations

libFiles = initialConfig()

#%% Create a DBT geometry

geo = geometry_settings()
geo.GE()

#%% Get DICOM data

dcmPath = "/home/rodrigo/Downloads/imgs/"
proj = readDicom(dcmPath,geo)

proj = dataPreProcess(proj, geo)

#%%

print("Starting reconstruction...")

vol = backprojectionDDb(proj, geo, libFiles)
plt.figure()
plt.title('Reconstructed slice')
plt.imshow(vol[:,:,50] , cmap=plt.get_cmap('gist_gray'))

print("Starting projection...")

proj = projectionDDb(vol, geo, libFiles)
plt.figure()
plt.title('Projected volume')
plt.imshow(proj[:,:,4] , cmap=plt.get_cmap('gist_gray'))


Empty file added pydbt/functions/__init__.py
Empty file.
68 changes: 68 additions & 0 deletions pydbt/functions/backprojectionDDb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 17 18:46:40 2020
@author: Rodrigo
"""

import numpy as np
import numpy.ctypeslib as ctl

from functions.utilities import findAndLoadLibray
from functions.utilities import geoAsNp

def backprojectionDDb(proj, geo, libFiles):


# Check if the input is in proper size
if not (proj.shape[0] == geo.nv and proj.shape[1] == geo.nu and proj.shape[2] == geo.nProj):
raise ValueError('First argument needs to have the same number of rows, cols and slices as in the configuration file.')


# Find and library
lib = findAndLoadLibray(libFiles, 'backprojectionDDb')


backprojectionDDb_lib = lib.backprojectionDDb_lib

backprojectionDDb_lib.argtypes = [ctl.ndpointer(np.float64, flags='aligned, c_contiguous'),
ctl.ndpointer(np.float64, flags='aligned, c_contiguous'),
ctl.ndpointer(np.float32, flags='aligned, c_contiguous')]


# Transform geo class in numpy array
geoNp = geoAsNp(geo)


vol_transp = np.empty([geo.nz, geo.nx, geo.ny], dtype=np.float64)

proj_transp = np.transpose(proj, (2, 1, 0)).copy()

backprojectionDDb_lib (proj_transp, vol_transp, geoNp)

vol = np.transpose(vol_transp, (2, 1, 0)).copy()


return vol





















45 changes: 45 additions & 0 deletions pydbt/functions/dataPreProcess.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 22 10:34:24 2019
@author: rodrigo
"""

import numpy as np

def dataPreProcess(proj, geo):

proj = cropProj(proj)

proj = transfIntensity(proj)

# Modifies parameters based on segmentation
geo.nu = proj.shape[1] # Number of pixels (columns)

return proj



def cropProj(proj):

Gap = 20;

maxValue = np.max(proj)

vertProj = np.sum(maxValue-proj[:,:,proj.shape[2]//2 - 1], axis=0) # Horizontal Profile

Ind = np.argmax(np.diff(vertProj[10:])) - Gap # Smooth the signal and takes its max positive derivative

proj_crop = proj[:,Ind:,:]

return proj_crop


def transfIntensity(proj):

# Transform intensity image in attenuation coefficients
proj = -np.log(proj/np.max(proj));

return proj

52 changes: 52 additions & 0 deletions pydbt/functions/initialConfig.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 22 13:35:00 2020
@author: rodrigo
"""

import pathlib

def initialConfig():

workDir = pathlib.Path().absolute()

outDir = workDir / 'output'

if not(outDir.exists() and outDir.is_dir()):
pathlib.Path(outDir).mkdir()

libFiles = findLibraries(workDir)

return libFiles


def findLibraries(workDir):

buildDir = workDir / '../build'

if not(buildDir.exists() and buildDir.is_dir()):
raise ValueError('Cannot find the build folder. Make sure to run the setup.py or follow the instructions on the package Github.')

libDir = []

# Find libray directory
for f in buildDir.iterdir():
if f.is_dir():
if 'lib.' in str(f):
libDir = f

# Test if libDir is empty
if not libDir:
raise ValueError('Cannot find the lib folder. Make sure to run the setup.py or follow the instructions on the package Github.')

libFiles = []

# Find all .so files
for libFile in pathlib.Path(libDir).glob('*.so'):
libFiles.append((str(libDir),str(libFile).split('/')[-1]))


return libFiles

34 changes: 34 additions & 0 deletions pydbt/functions/manageDicom.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 8 08:39:59 2019
@author: rodrigo
"""

import pydicom
import numpy as np
from os import walk

def readDicom(path, geo):

dcmFiles = []
for (_, _, filenames) in walk(path):
for file in filenames:
if file.endswith(".dcm"):
dcmFiles.append(file)
break

# Test if list is empty
if not dcmFiles:
raise ValueError('No DICOM files found in the specified path.')

proj = np.zeros([geo.nv,geo.nu,geo.nProj],dtype='uint16')


for f in dcmFiles:
nProj = int(f.split('.')[0])
proj_dcmH = pydicom.dcmread(path + f)
proj[:,:,nProj] = proj_dcmH.pixel_array

return proj
53 changes: 53 additions & 0 deletions pydbt/functions/projectionDDb.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Jan 17 13:36:50 2020
@author: Rodrigo
"""

import numpy as np
import numpy.ctypeslib as ctl

from functions.utilities import findAndLoadLibray
from functions.utilities import geoAsNp

def projectionDDb(vol, geo, libFiles):


# Check if the input is in proper size
if not (vol.shape[0] == geo.ny and vol.shape[1] == geo.nx and vol.shape[2] == geo.nz):
raise ValueError('First argument needs to have the same number of rows, cols and slices as in the configuration file.')


# Find and library
lib = findAndLoadLibray(libFiles, 'projectionDDb')


projectionDDb_lib = lib.projectionDDb_lib

projectionDDb_lib.argtypes = [ctl.ndpointer(np.float64, flags='aligned, c_contiguous'),
ctl.ndpointer(np.float64, flags='aligned, c_contiguous'),
ctl.ndpointer(np.float32, flags='aligned, c_contiguous')]


# Transform geo class in numpy array
geoNp = geoAsNp(geo)


proj_transp = np.empty([geo.nProj, geo.nu, geo.nv], dtype=np.float64)

vol_transp = np.transpose(vol, (2, 1, 0)).copy()

projectionDDb_lib (vol_transp, proj_transp, geoNp)

proj = np.transpose(proj_transp, (2, 1, 0)).copy()


return proj






Loading

0 comments on commit 3f9e6c0

Please sign in to comment.