-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from dmpelt/astra-plugin
Add ASTRA plugin and conda build files
- Loading branch information
Showing
4 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
python setup.py install |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package: | ||
name: mrfbp | ||
version: '1.1' | ||
|
||
source: | ||
git_url: https://github.com/dmpelt/pymrfbp.git | ||
git_tag: 1.1 | ||
|
||
build: | ||
number: 0 | ||
|
||
test: | ||
imports: | ||
- mrfbp | ||
|
||
requirements: | ||
build: | ||
- python | ||
|
||
run: | ||
- python | ||
- astra-toolbox | ||
- scipy | ||
- six | ||
- numpy | ||
|
||
about: | ||
home: http://dmpelt.github.io/pymrfbp/ | ||
license: GPLv3 | ||
summary: 'Python implementation of the MR-FBP tomographic reconstruction method, published in Pelt, D. M., & Batenburg, K. J. (2014). Improving Filtered Backprojection Reconstruction by Data-Dependent Filtering. Image Processing, IEEE Transactions on, 23(11), pp. 4750-4762.' | ||
|
||
# See | ||
# http://docs.continuum.io/conda/build.html for | ||
# more information about meta.yaml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#----------------------------------------------------------------------- | ||
#Copyright 2015 Daniel M. Pelt | ||
# | ||
#Contact: D.M.Pelt@cwi.nl | ||
#Website: http://www.dmpelt.com | ||
# | ||
# | ||
#This file is part of the PyMR-FBP, a Python implementation of the | ||
#MR-FBP tomographic reconstruction method. | ||
# | ||
#PyMR-FBP is free software: you can redistribute it and/or modify | ||
#it under the terms of the GNU General Public License as published by | ||
#the Free Software Foundation, either version 3 of the License, or | ||
#(at your option) any later version. | ||
# | ||
#PyMR-FBP is distributed in the hope that it will be useful, | ||
#but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
#GNU General Public License for more details. | ||
# | ||
#You should have received a copy of the GNU General Public License | ||
#along with PyMR-FBP. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
#----------------------------------------------------------------------- | ||
import astra | ||
import numpy as np | ||
from six.moves import range | ||
from scipy.signal import fftconvolve | ||
import scipy.ndimage.filters as snf | ||
import scipy.io as sio | ||
import numpy.linalg as na | ||
|
||
import os, errno | ||
|
||
try: | ||
from pywt import wavedec2 | ||
except: | ||
astra.log.warn("No pywavelets installed, wavelet functions will not work.") | ||
|
||
def mkdir_p(path): | ||
try: | ||
os.makedirs(path) | ||
except OSError as exc: # Python >2.5 | ||
if exc.errno == errno.EEXIST and os.path.isdir(path): | ||
pass | ||
else: raise | ||
|
||
|
||
class plugin(astra.plugin.ReconstructionAlgorithm2D): | ||
"""Reconstructs using the MR-FBP method [1]. | ||
Options: | ||
'save_filter' (optional): file to save filter to (overwrites existing) | ||
'use_saved_filter' (optional): file to load filter from | ||
'reg_grad' (optional): amount of l2 gradient minimization | ||
'reg_path' (optional): folder to save range of 'reg_grad' values to | ||
'reg_range' (optional): range of 'reg_grad' values to try (of form (min, max, number of trials)) | ||
'reg_wav' (optional): amount of l2 wavelet minimization | ||
'wav_bas' (optional): wavelet to use (see pywt.wavelist()) | ||
'nlinear' (optional): number of linear steps in exponential binning | ||
[1] Pelt, D. M., & Batenburg, K. J. (2014). Improving filtered backprojection | ||
reconstruction by data-dependent filtering. Image Processing, IEEE | ||
Transactions on, 23(11), 4750-4762. | ||
""" | ||
|
||
astra_name="MR-FBP" | ||
|
||
def customFBP(self, f, s): | ||
sf = np.zeros_like(s) | ||
padded = np.zeros(s.shape[1]*2) | ||
l = int(s.shape[1]/2.) | ||
r = l+s.shape[1] | ||
bl = f.shape[0]/len(s) | ||
for i in range(sf.shape[0]): | ||
padded[l:r] = s[i] | ||
padded[:l] = padded[l] | ||
padded[r:] = padded[r-1] | ||
sf[i] = fftconvolve(padded,f,'same')[l:r] | ||
return (self.W.T*sf).reshape(self.v.shape) | ||
|
||
def initialize(self, cfg, nlinear=2, reg_wav=None, wav_bas='haar', reg_grad=None, save_filter=None, use_saved_filter=None, reg_path=None, reg_range=(1,100,10)): | ||
self.W = astra.OpTomo(cfg['ProjectorId']) | ||
self.reg_gr = reg_grad | ||
self.reg_wav = reg_wav | ||
self.save_filter=save_filter | ||
self.use_saved=use_saved_filter | ||
self.reg_path = reg_path | ||
self.reg_range = reg_range | ||
self.wav_bas = wav_bas | ||
|
||
if not self.use_saved: | ||
self.bck = (self.W.T*np.ones_like(self.s)<self.s.shape[0]-0.5).reshape(self.v.shape) | ||
|
||
if self.reg_path: | ||
self.reg_gr=1. | ||
|
||
fs = self.s.shape[1] | ||
if fs%2==0: | ||
fs += 1 | ||
mf = int(fs/2) | ||
|
||
w=1 | ||
c=mf | ||
|
||
bas = np.zeros(fs,dtype=np.float32) | ||
self.basis = [] | ||
count=0 | ||
while c<fs: | ||
bas[:]=0 | ||
l = c | ||
r = c+w | ||
if r>fs: r=fs | ||
bas[l:r]=1 | ||
if l!=0: | ||
l = fs-c-w | ||
r = l+w | ||
if l<0: l=0 | ||
bas[l:r]=1 | ||
self.basis.append(bas.copy()) | ||
c += w | ||
count += 1 | ||
if count>nlinear: | ||
w=2*w | ||
self.nf = len(self.basis) | ||
|
||
def run(self, iterations): | ||
if self.use_saved: | ||
flt = sio.loadmat(self.use_saved)['mrfbp_flt'].flatten() | ||
self.v[:] = self.customFBP(flt, self.s) | ||
return | ||
nrows = self.s.size | ||
ncols = self.nf | ||
if self.reg_gr: | ||
nrows += 2*self.v.size | ||
if self.reg_wav: | ||
q = wavedec2(self.v,self.wav_bas) | ||
l = [q[0].flatten()] | ||
for z in range(1,len(q)): | ||
l.extend([y.flatten() for y in q[z]]) | ||
l = np.hstack(l) | ||
nrows += l.size | ||
A = np.zeros((nrows,ncols),dtype=np.float32) | ||
astra.log.info("Generating MR-FBP matrix") | ||
for i, bas in enumerate(self.basis): | ||
astra.log.info('{:.2f} % done'.format(100*float(i)/self.nf)) | ||
img = self.customFBP(bas, self.s) | ||
if self.reg_wav: | ||
q = wavedec2(img,self.wav_bas) | ||
l = [q[0].flatten()] | ||
for z in range(1,len(q)): | ||
l.extend([y.flatten() for y in q[z]]) | ||
l = np.hstack(l) | ||
A[nrows-l.size:,i] = self.reg_wav*l | ||
if self.reg_gr!=None: | ||
dx = np.zeros_like(self.v) | ||
dx[:-1,:] = img[:-1,:] - img[1:,:] | ||
dy = np.zeros_like(self.v) | ||
dx[:,:-1] = img[:,:-1] - img[:,1:] | ||
dx[self.bck]=0 | ||
dy[self.bck]=0 | ||
img[self.bck]=0 | ||
A[0:self.s.size,i] = self.W*img | ||
A[self.s.size+0*self.v.size:self.s.size+1*self.v.size,i] = self.reg_gr*dx.flatten() | ||
A[self.s.size+1*self.v.size:self.s.size+2*self.v.size,i] = self.reg_gr*dy.flatten() | ||
else: | ||
img[self.bck]=0 | ||
astra.extrautils.clipCircle(img) | ||
A[:self.s.size,i] = self.W*img | ||
b = np.zeros(nrows,dtype=np.float32) | ||
b[:self.s.size] = self.s.flatten() | ||
if self.reg_path: | ||
import tifffile | ||
mkdir_p(self.reg_path) | ||
l, r, s = self.reg_range | ||
astra.log.info("Generating regularized images") | ||
for i, reg in enumerate(np.linspace(l,r,s)): | ||
astra.log.info('{:.2f} % done'.format(100*float(i)/s)) | ||
A[self.s.size:nrows-self.v.size,:] *= reg | ||
out = na.lstsq(A,b) | ||
flt = np.zeros_like(self.basis[0]) | ||
for i, bas in enumerate(self.basis): | ||
flt += out[0][i]*bas | ||
rc = self.customFBP(flt, self.s) | ||
tifffile.imsave(self.reg_path + os.sep + str(reg) + '.tiff', rc) | ||
A[self.s.size:nrows-self.v.size,:] /= reg | ||
return | ||
else: | ||
out = na.lstsq(A,b) | ||
bl = self.basis[0].shape[0] | ||
flt = np.zeros_like(self.basis[0]) | ||
for i, bas in enumerate(self.basis): | ||
flt += out[0][i]*bas | ||
if self.save_filter: | ||
sio.savemat(self.save_filter, {'mrfbp_flt':flt}, do_compression=True, appendmat=False) | ||
astra.log.info("Done!") | ||
rc = self.customFBP(flt, self.s) | ||
self.v[:] = rc |