Skip to content

Commit

Permalink
Merge pull request #1 from dmpelt/astra-plugin
Browse files Browse the repository at this point in the history
Add ASTRA plugin and conda build files
  • Loading branch information
dmpelt committed Jul 31, 2015
2 parents 5c231a7 + 8723e2c commit 9b13906
Show file tree
Hide file tree
Showing 4 changed files with 235 additions and 0 deletions.
1 change: 1 addition & 0 deletions conda/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python setup.py install
34 changes: 34 additions & 0 deletions conda/meta.yaml
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
1 change: 1 addition & 0 deletions mrfbp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
#-----------------------------------------------------------------------

from Reconstructor import *
from astra_plugin import plugin
199 changes: 199 additions & 0 deletions mrfbp/astra_plugin.py
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

0 comments on commit 9b13906

Please sign in to comment.