├── .gitignore ├── INSTALL ├── LICENSE ├── README.md ├── bin └── genfire ├── data └── vesicle.mrc ├── documentation ├── .DS_Store ├── GUI_parameters.html ├── html_helper.py ├── images │ ├── CalculateProjection_dialog_empty.png │ ├── CalculateProjection_dialog_ready.png │ ├── GENFIRE.png │ ├── ProjectionCalculator_blank.png │ ├── ProjectionCalculator_modelLoaded.png │ ├── gui.png │ ├── gui_finished.png │ ├── gui_ready.png │ ├── summarize_results.png │ └── volume_slicer.png ├── index.html ├── installation.html ├── main.css ├── make_html.txt ├── src_html │ ├── CalculateProjectionSeries_Dialog.html │ ├── GENFIRE_MainWindow.html │ ├── GENFIRE_qrc.html │ ├── GENFIRE_qrc_py3.html │ ├── GENFIRE_rc.html │ ├── ProjectionCalculator.html │ ├── ProjectionCalculator_MainWindow.html │ ├── VolumeSlicer.html │ ├── VolumeSlicer_MainWindow.html │ ├── __init__.html │ ├── fileio.html │ ├── html_helper.html │ ├── launch.html │ ├── main.css │ ├── main.html │ ├── projections.html │ ├── reconstruct.html │ ├── setup.html │ └── utility.html └── tutorial.html ├── genfire ├── __init__.py ├── fileio.py ├── gui │ ├── CalculateProjectionSeries_Dialog.py │ ├── GENFIRE.png │ ├── GENFIRE.pro │ ├── GENFIRE.pro.user │ ├── GENFIRE.pro.user.bd486de │ ├── GENFIRE.qrc │ ├── GENFIRE_MainWindow.cpp │ ├── GENFIRE_MainWindow.h │ ├── GENFIRE_MainWindow.py │ ├── GENFIRE_MainWindow.ui │ ├── GENFIRE_qrc.py │ ├── GENFIRE_qrc_py3.py │ ├── GF.icns │ ├── GF.png │ ├── ProjectionCalculator.py │ ├── ProjectionCalculator_MainWindow.py │ ├── ProjectionCalculator_MainWindow.ui │ ├── VolumeSlicer.py │ ├── VolumeSlicer_MainWindow.py │ ├── VolumeSlicer_MainWindow.ui │ ├── __init__.py │ ├── calculateprojectionseries_dialog.ui │ ├── launch.py │ └── utility.py ├── main.py ├── reconstruct.py └── utility.py ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | GENFIRE.egg-info/ 2 | .idea/ 3 | .DS_Store 4 | build/ 5 | dist/ 6 | UNKNOWN.egg-info/ 7 | PhaseRetrieval/ 8 | dist/ 9 | build/ 10 | *.tif 11 | *.tiff 12 | *.xml 13 | *.pyc 14 | *rec*.mrc 15 | *rec*.txt 16 | results* 17 | test* 18 | projections* 19 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | First install dependencies with pip: 2 | 3 | $ pip install -r requirements.txt 4 | 5 | You will also need to download sip and PyQt. More detail is given 6 | in the GENFIRE documentation. 7 | 8 | https://www.riverbankcomputing.com/software/sip/intro 9 | http://pyqt.sourceforge.net/Docs/PyQt4/installation.html 10 | 11 | Finally, install GENFIRE with 12 | 13 | $ python setup.py install 14 | 15 | You can verify that the install worked with 16 | 17 | $ python -c "import GENFIRE" 18 | 19 | If no errors occur, the installation was successful. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GENFIRE 2 | GENeralized Fourier Iterative REconstruction (GENFIRE) is a python package for 3D reconstruction from arbitrarily oriented projection images 3 | ## Documentation 4 | The full documentation for installing/using GENFIRE is the website in ./documentation/ and can be viewed by opening ./documentation/index.html in a web browser 5 | -------------------------------------------------------------------------------- /bin/genfire: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import genfire as gf 3 | gf.gui.launch.main() 4 | -------------------------------------------------------------------------------- /data/vesicle.mrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genfire-em/GENFIRE-Python/b7b46daed4adf4f3afccea95ca71fa88e7c39e0a/data/vesicle.mrc -------------------------------------------------------------------------------- /documentation/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/genfire-em/GENFIRE-Python/b7b46daed4adf4f3afccea95ca71fa88e7c39e0a/documentation/.DS_Store -------------------------------------------------------------------------------- /documentation/GUI_parameters.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 |
46 | "Select Projection File" - Choose filename containing projection images. Alternatively you enter a file by editing
47 | the text, but it must be a valid file type.
48 | "Use default support" - Automatically generate a cubic support defined by the oversampling ratio. For example
49 | if the projections are size 128x128 and the oversampling ratio is set to 3, the default support will be
50 | 384x384x384 with a central 128x128x128 region of 1's and the remaining region set to 0. Uncheck to enter a filename manually.
51 | "Select Support File" - Choose filename containing the 3D support. Alternatively you enter a file by editing
52 | the text, but it must be a valid file type. The array width should match that of the inputted projections.
53 | "Select Angle File" - Choose filename with the Euler angles.
54 | "Provide initial object" - Check to enable selection of an intial model.
55 | "Select Initial Object File" - Choose filename containing the initial model.
56 | "Results Filename" - Filename for the output volume. Must be a valid filetype. Error curves
57 | are saved as .txt files with the same base filename as the results.
58 | "Resolution Extension/Suppression" - Choose the style of constraint enforcement.
67 | "Number of Iterations" - Number of GENFIRE iterations to use
68 | "Oversampling Ratio" - Final oversampling ratio to use when gridding the projections. Larger oversampling ratios
69 | will produce more accurate griddings at the cost of larger array size. Ideally use at least 3.
70 | "Interpolation Cutoff Distance (pixels)" - Size of the interpolation kernel used for gridding. Small radii
71 | will result in an assembled grid with fewer values, but more accurate ones. Conversely, larger values for this
72 | parameter will be more accepting of datapoints, but at the cost of accuracy. The default 0.7 was found to be
73 | ideal based on simulations.
74 | "Summarize Results" - Display a summary of reconstruction results. Choose a volume file, and projections and central
75 | slices along the three principal directions will appear. Error curves will be plotted if the corresponding files exist.
76 | "Launch Reconstruction" - Begin the reconstruction, if the parameters are valid.
77 |
80 | "Model" - Filename of volume from which to calculate projections.
81 | "Clear Model" - Clear display and model from memory.
82 | "Calculate Projection Series from Model" - Use the current model to simulate a series of projections.
83 | "Phi" - set Phi Euler angle interactively.
84 | "Theta" - set Theta Euler angle interactively.
85 | "Psi" - set Psi Euler angle interactively.
86 |
89 | "X" - Set X value for the YZ plane.
90 | "Y" - Set Y value for the XZ plane.
91 | "Z" - Set Z value for the XY plane.
92 | "Lock Colormap" - If checked, locks the colormap for each image based on the current ones. If you want to lock
93 | a different colormap, uncheck the box, move the slicer, and recheck the box to lock the new setting.
94 |
")
36 | in_comment_flag = 0
37 | for line in fid_source:
38 | if line.lstrip().startswith("#"): # then this line is a single line comment and we don't format it
39 | fid.write(line.replace('\n',"
"))
40 | elif line.lstrip().startswith("\"\"\""): # ignore formatting on multi-line comments
41 | fid.write(line.replace('\n',"
"))
42 | in_comment_flag = (in_comment_flag + 1) % 2
43 | elif in_comment_flag:
44 | fid.write(line.replace('\n',"
"))
45 | else:
46 | inline_comment_start = line.find("#")
47 | if inline_comment_start == -1: # no inline comment
48 | fid.write(code_formatter(line))
49 | else:
50 | fid.write(code_formatter(line[:inline_comment_start].replace('\n',"
")))
51 | fid.write(line[inline_comment_start:].replace('\n',"
"))
52 | fid.write("
")
53 | fid.write(closing_str)
54 |
55 | def code_formatter(string):
56 | # A simple custom HTML code formatter that colors some keywords and tries to avoid instances of them within comments
57 | color_string = "\"orange\""
58 | keywords = ["from", "def","class", "self", "__main__", "for", "if",\
59 | "while","import","else","elif", "True","False", "del", \
60 | "with", "plt", "QtCore", "QtGui", "Qt", "ui"]
61 | short_keywords = keywords[:]
62 | new_keywords = []
63 | for i, v in enumerate(keywords):
64 | keywords[i] = v + " "
65 | new_keywords.append(v + ".")
66 | new_keywords.append(v + "\n")
67 | keywords += new_keywords
68 | format_string = ("", "")
69 | replacers={}
70 |
71 | for kw in keywords:
72 | replacers[kw] = format_string
73 |
74 | GENFIRE_format_string = ("", "")
75 | replacers['GENFIRE'] = GENFIRE_format_string
76 | replacers['genfire'] = GENFIRE_format_string
77 |
78 |
79 |
80 | # replacers={"from":("", "")}
81 |
82 |
83 |
84 | string = string.replace('\t', "    ")
85 | if replacers is not None:
86 | for key, value in replacers.iteritems():
87 | # print (key, value)
88 | string = string.replace(key,value[0] + key + value[1])
89 |
90 | # for key in short_keywords:
91 | # if string.endswith(key):
92 | # string.replace(key, ("" + key+ "") )
93 | string = string.replace('\n',"
28 | GENFIRE, for GENeralized Fourier Iterative REconstruction, is an open-sourced, GUI-enabled
29 | python package for 3D reconstruction from arbitrarily oriented projection images. GENFIRE
30 | was developed at the University of California, Los Angeles and has found
31 | successful applications in a number of imaging fields including Atomic Electron Tomography (AET),
32 | electron cryo-tomography, tomographic Scanning Transmission X-ray Microscopy (STXM), and
33 | phase-contrast X-ray ptychographic tomography.
34 | GENFIRE iterates between real and reciprocal space using the Fast Fourier Transform (FFT),
35 | enforcing constraints in both. This technique of alternating projections onto convex sets allows
36 | GENFIRE to recover unmeasured datapoints, producing reconstructions with superior contrast
37 | and mitigated missing-wedge artifacts when compared with traditional tomographic techniques such as
38 | filtered backprojection. This type of reconstruction algorithm was originally demonstrated
39 | in the form of Equally Sloped Tomography (EST). GENFIRE builds upon EST by introducing a 3D
40 | gridding routine, allowing it to tolerate arbitrary Euler rotations of the input projection images.
41 | In addition, GENFIRE introduces a novel technique for enforcing Fourier constraints deemed
42 | resolution extension/suppression and implements a modified version of R-free from X-ray crystallography
43 | for the purposes of prevention of overfitting data.
44 | GENFIRE is written in python using primarily NumPy and SciPy. It runs on python 3 and the GUI is programmed using PyQt5.
45 | The user interface was designed with Qt Creator. Embedded plots were made with matplotlib.
46 |
47 |
48 |
51 | GENFIRE can be downloaded for free here. There are precompiled binaries for Mac OS X, Windows, and Linux, 52 | and the code is also available as a python package. Installation instructions are 53 | here Once you have installed GENFIRE, check out the 54 | tutorial. 55 |
56 |
60 | The coordinate and Euler angle conventions used in GENFIRE are the standard defined by
61 | Heymann et. al (2005).
For 3D
62 | arrays the fastest changing index represents X, the second-fastest represents Y, and the slowest
63 | Z. GENFIRE is built on top of NumPy and uses row-major array storage (sometimes called "C-style".
64 | As such an array is indexed as [x, y, z].
65 | Euler rotations are composed of angle triplets in the form (phi, theta, psi) where:
66 |
75 | GENFIRE natively works with .mrc, 76 | .mat, and 77 | .npy files for volume data. 78 | Euler angle triplets and error curves are stored as plain .txt files. File formats can 79 | be mix-and-matched using the GUI without trouble, or users can convert filetypes from 80 | the command line using GENFIRE's fileio module. For example the following code reads in 81 | an .mrc file and saves it in .mat format. 82 |
115 | Author: Alan (AJ) Pryor, Jr.
116 | Jianwei (John) Miao Coherent Imaging Group
117 | University of California, Los Angeles
118 | Copyright 2015-2016. All rights reserved.
119 |
49 | To install GENFIRE as a python package, first 50 | download the source code. Then follow the procedure for 51 | your operating system. 52 |
53 | 54 | 55 |58 | Python is preinstalled on Mac OS X, but it is generally a bad idea to alter the system 59 | python in /usr/bin as some programs depend on it. I would highly recommend using the 60 | package manager homebrew to create a python environment for you. The following 61 | commands will install homebrew and python3 62 |
63 | 64 |71 |
72 | Once that's done, navigate to the top source code directory. That's the same 73 | folder as requirements.txt and setup.py
74 |80 | Install (most of the) dependencies using pip3, you may need to prepend the command with sudo depending on your system
81 |90 | You will also need sip and PyQt5: 91 |
92 |99 | You should now have all the dependencies necessary to run GENFIRE. To install it, make 100 | sure you are in the directory with setup.py and enter the follow. Again, you may need sudo 101 |
102 |108 | If everything worked, you can now launch the gui from the command line with 109 |
110 |116 | You can also use the code as any other python package. Here's an example of launching the gui from within python 117 |
118 |126 | 127 |
GENFIRE can make use of pyFFTW, which wraps the FFTW library. I have tested a number of 129 | FFT routines including those in NumPy, SciPy, and pyFFTW, and found this to be the fastest one by a factor of 2-3. 130 |
131 | 132 |133 | The following are details for installing FFTW from source, but recently pip has begun to support the package, so you should first try the easy way with 134 | "pip install pyfftw" 135 |
136 |137 | In order for GENFIRE to use pyFFTW you must install 138 | the FFTW libraries. Download the source code, decompress it, and navigate into the unpacked directory 139 | from your terminal. PyFFTW needs FFTW to be compiled for all precision types, so you have to compile it three times. 140 | Use the following commands 141 |
as well as
149 |and finally
157 |Now, install pyFFTW with pip and and test that it worked
165 | 166 |If you don't receive an error, then it was successful. If you get an error along the lines of 173 | "ImportError: libfftw3l.so cannot open shared object file" then you need to set your DYLD_LIBRARY_PATH 174 | environmental variable so that pyFFTW can find the libraries
175 |If the fftw .so files are somewhere other than /usr/local/lib, then you should replace that part appropriately. 181 | To make this change permanent add the above line to the end of your ~/.bash_profile
182 | 183 |185 | Navigate to the source code directory. That's the same folder as requirements.txt and setup.py
186 |192 | Install (most of the) dependencies using pip
193 |200 | You will also need sip and PyQt4, this command should install both.
201 |209 | You should now have all the dependencies necessary to run GENFIRE. To install it, make 210 | sure you are in the directory with setup.py and enter 211 |
212 |218 | If everything worked, you can now launch the gui from the command line with 219 |
220 |226 | You can also use the code as any other python package. Here's an example of launching the gui from within python 227 |
228 |236 |
GENFIRE can make use of pyFFTW, which wraps the FFTW library. I have tested a number of 238 | FFT routines including those in NumPy, SciPy, and pyFFTW, and found this to be the fastest one by a factor of 2-3. 239 |
240 |241 | The following are details for installing FFTW from source, but recently pip has begun to support the package, 242 | so you should first try the easy way with "pip install pyfftw" 243 | 244 |
245 |246 | In order for GENFIRE to use pyFFTW you must install 247 | the FFTW libraries. Download the source code, decompress it, and navigate into the unpacked directory 248 | from your terminal. PyFFTW needs FFTW to be compiled for all precision types, so you have to compile it three times. 249 | Use the following commands 250 |
as well as
258 |and finally
266 |Now, install pyFFTW with pip and and test that it worked
274 | 275 |If you don't receive an error, then it was successful. If you get an error along the lines of 282 | "ImportError: libfftw3l.so cannot open shared object file" then you need to set your LD_LIBRARY_PATH 283 | environmental variable so that pyFFTW can find the libraries
284 |If the fftw .so files are somewhere other than /usr/local/lib, then you should replace that part appropriately. 290 | To make this change permanent add the above line to the end of your ~/.bashrc
291 | 292 |
294 | For python on Windows 10 I would recommend using
295 | Anaconda from Continuum Analytics. It's a distribution of python that contains
296 | most of the packages used by GENFIRE all wrapped into a simple-to-use installer. Note you must use python 3.
297 | Once you have python setup, open up a cmd prompt and navigate to the source directory.
298 | That's the same folder as requirements.txt and setup.py
299 |
302 | Now install GENFIRE with
303 |309 | If everything worked you can now launch the gui. 310 |
311 |327 | 328 |
GENFIRE can make use of pyFFTW, which wraps the FFTW library. I have tested a number of 330 | FFT routines including those in NumPy, SciPy, and pyFFTW, and found this to be the fastest one by a factor of 2-3. 331 | On Windows, pyFFTW can be installed simply with 332 |
339 |
341 | If you have trouble installing PyQt4 or sip, consult their
342 | documentation
343 | If you have some problem with the "pip install -r requirements.txt" step, you can view
344 | the requirements.txt file to see the packages that are necessary, and try to install
345 | them one-by-one.
346 |
"""
* GENFIRE.gui.VolumeSlicer *
The volume slicer module
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
from GENFIRE.gui import VolumeSlicer_MainWindow
from PyQt4 import QtCore, QtGui
import matplotlib
matplotlib.use("Qt4Agg")
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
import numpy as np
from functools import partial
from GENFIRE.gui.utility import toString, toQString, toInt, toFloat
class VolumeSlicer(QtGui.QMainWindow):
def __init__(self, volume):
super(VolumeSlicer, self).__init__()
self.volume = volume
self.ui = VolumeSlicer_MainWindow.Ui_VolumeSlicer()
self.ui.setupUi(self) # build interface
self.ui.checkBox_lockcmap.toggled.connect(self.toggleLockCmap)
self.lockColormap = False
self.ui.checkBox_lockcmap.setChecked(False)
# figures for plotting
self.fig1 = plt.figure()
self.fig2 = plt.figure()
self.fig3 = plt.figure()
# canvases link the Qt widgets with the matplotlib figures
self.canvas1 = FigureCanvas(self.fig1)
self.canvas2 = FigureCanvas(self.fig2)
self.canvas3 = FigureCanvas(self.fig3)
self.slice1 = self.fig1.add_subplot(111)
self.slice2 = self.fig2.add_subplot(111)
self.slice3 = self.fig3.add_subplot(111)
self.slice1.hold(False)
self.slice2.hold(False)
self.slice3.hold(False)
self.navigationToolbar1 = NavigationToolbar(self.canvas1, self)
self.navigationToolbar2 = NavigationToolbar(self.canvas2, self)
self.navigationToolbar3 = NavigationToolbar(self.canvas3, self)
self.ui.vt_lyt_fig1.addWidget(self.navigationToolbar1)
self.ui.vt_lyt_fig1.addWidget(self.canvas1)
self.ui.vt_lyt_fig2.addWidget(self.navigationToolbar2)
self.ui.vt_lyt_fig2.addWidget(self.canvas2)
self.ui.vt_lyt_fig3.addWidget(self.navigationToolbar3)
self.ui.vt_lyt_fig3.addWidget(self.canvas3)
dimx, dimy, dimz = np.shape(volume)
ncx, ncy, ncz = dimx//2., dimy//2, dimz//2
ncx, ncy, ncz = int(ncx), int(ncy), int(ncz)
# use of partial allows binding functions to arguments to prevent having to avoid extra parameter forwarding
self.ui.scrlbr_fig1.valueChanged.connect(partial(self.setTextFromSlider,self.ui.lineEdit_scrlbr1))
self.ui.scrlbr_fig2.valueChanged.connect(partial(self.setTextFromSlider,self.ui.lineEdit_scrlbr2))
self.ui.scrlbr_fig3.valueChanged.connect(partial(self.setTextFromSlider,self.ui.lineEdit_scrlbr3))
self.ui.scrlbr_fig1.setValue(ncx)
self.ui.scrlbr_fig1.setMinimum(0)
self.ui.scrlbr_fig1.setMaximum(dimx-1)
self.ui.scrlbr_fig2.setValue(ncy)
self.ui.scrlbr_fig2.setMinimum(0)
self.ui.scrlbr_fig2.setMaximum(dimy-1)
self.ui.scrlbr_fig3.setValue(ncz)
self.ui.scrlbr_fig3.setMinimum(0)
self.ui.scrlbr_fig3.setMaximum(dimz-1)
self.ui.lineEdit_scrlbr1.textChanged.connect(partial(self.setSliderFromText,self.ui.scrlbr_fig1))
self.ui.lineEdit_scrlbr2.textChanged.connect(partial(self.setSliderFromText,self.ui.scrlbr_fig2))
self.ui.lineEdit_scrlbr3.textChanged.connect(partial(self.setSliderFromText,self.ui.scrlbr_fig3))
self.currentSliceX = ncx
self.currentSliceY = ncy
self.currentSliceZ = ncz
self.updateAll()
self.ui.scrlbr_fig1.valueChanged[int].connect(self.updateSliceX)
self.ui.scrlbr_fig2.valueChanged[int].connect(self.updateSliceY)
self.ui.scrlbr_fig3.valueChanged[int].connect(self.updateSliceZ)
self.clim1 = plt.getp(plt.getp(self.slice1,'images')[0],'clim')
self.clim2 = plt.getp(plt.getp(self.slice2,'images')[0],'clim')
self.clim3 = plt.getp(plt.getp(self.slice3,'images')[0],'clim')
self.lockColormap = True
self.ui.checkBox_lockcmap.setChecked(True)
def updateSliceX(self, nx):
self.slice1.imshow(np.squeeze(self.volume[nx, :, :]))
if self.lockColormap:
plt.setp(plt.getp(self.slice1,'images')[0],'clim',self.clim1)
self.canvas1.draw()
def updateSliceY(self, ny):
self.slice2.imshow(np.squeeze(self.volume[:, ny, :]))
if self.lockColormap:
plt.setp(plt.getp(self.slice2,'images')[0],'clim',self.clim2)
self.canvas2.draw()
def updateSliceZ(self, nz):
self.slice3.imshow(np.squeeze(self.volume[:, :, nz]))
if self.lockColormap:
plt.setp(plt.getp(self.slice3,'images')[0],'clim',self.clim3)
self.canvas3.draw()
def updateAll(self):
self.updateSliceX(self.currentSliceX)
self.updateSliceY(self.currentSliceY)
self.updateSliceZ(self.currentSliceZ)
def toggleLockCmap(self):
if self.ui.checkBox_lockcmap.isChecked():
self.clim1 = plt.getp(plt.getp(self.slice1,'images')[0],'clim')
self.clim2 = plt.getp(plt.getp(self.slice2,'images')[0],'clim')
self.clim3 = plt.getp(plt.getp(self.slice3,'images')[0],'clim')
self.lockColormap = True
else:
self.lockColormap = False
def setSliderFromText(self, slider, text):
slider.setValue(toInt(text))
def setTextFromSlider(self, lineedit, value):
lineedit.setText(toQString(value))
12 |
--------------------------------------------------------------------------------
/documentation/src_html/__init__.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | from . import utility
from . import VolumeSlicer
from . import launch
from . import ProjectionCalculator
from . import GENFIRE_MainWindow
from . import ProjectionCalculator_MainWindow
from . import CalculateProjectionSeries_Dialog
from . import VolumeSlicer_MainWindow
12 |
--------------------------------------------------------------------------------
/documentation/src_html/fileio.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | """
* GENFIRE.fileio *
The primary file input/output module for GENFIRE.
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
import numpy as np
def readVolume(filename, order="C"):
"""
* readVolume *
Load volume into a numpy array
:param filename: filename of volume
:param order: "C" for row-major order (C-style), "F" for column-major (Fortran style)
:return:
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
import os
base, ext = os.path.splitext(filename)
if (ext == ".mrc"):
return readMRC(filename, order=order)
elif (ext == ".mat"):
return readMAT_volume(filename)
elif (ext == ".npy"):
return np.load(filename)
def readMAT_volume(filename):
#wrapper around scipy's loadmat
import numpy as np
import scipy.io
data = scipy.io.loadmat(filename)
var_name = [key for key in data if not key.startswith("__")]
if len(var_name) > 1:
raise IOError("Only 1 variable allowed per .MAT file")
else:
return np.array(data[var_name[0]])
def readNPY(filename, dtype=float, order="C"):
import numpy as np
return np.load(filename)
def readMRC(filename, dtype=float, order="C"):
"""
* readMRC *
Read in a volume in .mrc file format. See http://bio3d.colorado.edu/imod/doc/mrc_format.txt
:param filename: Filename of .mrc
:return: NumPy array containing the .mrc data
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
import numpy as np
import struct
headerIntNumber = 56
sizeof_int = 4
headerCharNumber = 800
sizeof_char = 1
with open(filename,'rb') as fid:
int_header = struct.unpack('=' + 'i'*headerIntNumber, fid.read(headerIntNumber * sizeof_int))
char_header = struct.unpack('=' + 'c'*headerCharNumber, fid.read(headerCharNumber * sizeof_char))
dimx, dimy, dimz, data_flag= int_header[:4]
if (data_flag == 0):
datatype='u1'
elif (data_flag ==1):
datatype='i1'
elif (data_flag ==2):
datatype='f4'
elif (data_flag ==3):
datatype='c'
elif (data_flag ==4):
datatype='f4'
elif (data_flag ==6):
datatype='u2'
else:
raise ValueError("No supported datatype found!\n")
return np.fromfile(file=fid, dtype=datatype,count=dimx*dimy*dimz).reshape((dimx,dimy,dimz),order=order).astype(dtype)
def writeMRC(filename, arr, datatype='f4', order="C"):
"""
* writeMRC *
Write a volume to .mrc file format. See http://bio3d.colorado.edu/imod/doc/mrc_format.txt
This version is bare-bones and doesn't write out the full header -- just the critical bits and the
volume itself
:param filename: Filename of .mrc file to write
:param arr: NumPy volume of data to write
:param dtype: Type of data to write
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved
"""
import numpy as np
dimx, dimy, dimz = np.shape(arr)
if datatype != arr.dtype:
arr = arr.astype(datatype)
int_header = np.zeros(56,dtype='int32') #must be 4-byte ints
if (datatype == 'u1'):
data_flag = 0
elif (datatype =='i1'):
data_flag = 1
elif (datatype =='f4'):
data_flag = 2
elif (datatype =='c'):
data_flag = 3
elif (datatype =='f4'):
data_flag = 4
elif (datatype =='u2'):
data_flag = 6
else:
raise ValueError("No supported datatype found!\n")
int_header[:4] = (dimx,dimy,dimz,data_flag)
char_header = str(' '*800)
with open(filename,'wb') as fid:
fid.write(int_header.tobytes())
fid.write(char_header.encode('UTF-8'))
fid.write(arr.tobytes(order=order))
def loadProjections(filename):
"""
* loadProjections *
Wrapper function for loading in projections of arbitrary (supported) extension
:param filename: Filename of images to load
:return: NumPy array containing projections
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
import os
filename, file_extension = os.path.splitext(filename)
if file_extension == ".mat":
print ("Reading projections from MATLAB file.\n")
# return readMAT_projections(filename + file_extension)
return readMAT_volume(filename + file_extension)
elif file_extension == ".tif":
print ("Reading projections from .tif file.\n")
return readTIFF_projections(filename + file_extension)
elif file_extension == ".mrc":
print ("Reading projections from .mrc file.\n")
return readMRC(filename + file_extension)
elif file_extension == ".npy":
print ("Reading projections from .npy file.\n")
return readNPY(filename + file_extension)
else:
raise Exception('File format %s not supported.', file_extension)
# def readMAT_projections(filename):
# """
# * readMAT *
#
# Read projections from a .mat file
#
# :param filename: MATLAB file (.mat) containing projections
# :return: NumPy array containing projections
#
#
# Author: Alan (AJ) Pryor, Jr.
# Jianwei (John) Miao Coherent Imaging Group
# University of California, Los Angeles
# Copyright 2015-2016. All rights reserved.
# """
#
# import scipy.io
# import numpy as np
# import os
# try: #try to open the projections as a stack
# projections = scipy.io.loadmat(filename)
# key = None
# for k in projections.keys():
# if k[0] != "_":
# key = k
# break
#
# projections = np.array(projections[key])
# except: ## -- figure out where error is thrown
# #check if the projections are in individual files
# flag = True
# filename_base, file_extension = os.path.splitext(filename)
# projectionCount = 1
# while flag: #first count the number of projections so the array can be initialized
# projectionCount = projectionCount
# nextFile = filename_base + str(projectionCount) + file_extension
# if os.path.isfile(nextFile):
# projectionCount += 1
# else:
# flag = False
#
#
# ## open first projection to get dimensions
# pj = scipy.io.loadmat(filename_base + str(1) + file_extension)
# pj = pj[projections.keys()[0]]
# dims = np.shape(pj)
# #initialize projection array
# projections = np.zeros((dims[0], dims[1], projectionCount),dtype=int)
#
# #now actually load in the tiff images
# for projNum in range(projectionCount):
# nextFile = filename_base + str(projNum) + file_extension
# pj = scipy.io.loadmat(filename_base + str(projNum) + file_extension)
# pj = pj[pj.keys()[0]]
# projections[:, :, projNum] = np.array(pj)
#
# return projections
def readTIFF_projections(filename):
"""
* readTIFF *
Read (possibly multiple) TIFF images into a NumPy array
:param filename: Name of TIFF file or TIFF file basename to read. If the filename is a base then
# the images must begin with the string contained in filename followed by consecutive integers with
# no zero padding, i.e. foo1.tiff, foo2.tiff,..., foo275.tiff
:return: NumPy array containing projections
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
import functools
from PIL import Image
import os
import numpy as np
from multiprocessing import Pool
try:
projections = np.array(Image.open(filename))
except:
flag = True
filename_base, file_extension = os.path.splitext(filename)
projectionCount = 1
while flag: #first count the number of projections so the array can be initialized
projectionCount = projectionCount
nextFile = filename_base + str(projectionCount) + file_extension
if os.path.isfile(nextFile):
projectionCount += 1
else:
flag = False
## open first projection to get dimensions
dims = np.shape(Image.open(filename_base + str(1) + file_extension))
#initialize projection array
projections = np.zeros((dims[0], dims[1], projectionCount),dtype=int)
pool = Pool(4)
func = functools.partial(readInTiffProjection, filename_base)
pj = pool.map(func, range(projectionCount))
for j in range(projectionCount):
projections[:, :, j] = pj[j]
return projections
def readInTiffProjection(filename_base, fileNumber):
"""
* readInTiffProjection *
Reads and returns a single TIFF image as a NumPy array
:param filename_base: Base filename of TIFF
:param fileNumber: Image number
:return: Image in a 2D NumPy array
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
from PIL import Image
import numpy as np
nextFile = filename_base + str(fileNumber) + '.tif'
return np.array(Image.open(nextFile))
def loadAngles(filename):
"""
* loadAngles *
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
:param filename:
:return:
"""
import os
base,ext = os.path.splitext(filename)
ext = ext.lower()
if ext == ".txt":
return np.loadtxt(filename,dtype=float)
elif ext== ".npy":
return np.load(filename)
elif ext==".mat":
from GENFIRE.fileio import readVolume
return readVolume(filename)
else:
raise IOError("Unsupported file extension \"{}\" for Euler angles".format(ext))
def saveResults(reconstruction_outputs, filename):
"""
* saveResults *
Helper function to save results of GENFIRE reconstruction
:param reconstruction_outputs: dictionary containing reconstruction, reciprocal error (errK), and possible R_free
:param filename: Output filename
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved
"""
import os
fn, ext = os.path.splitext(filename)
writeVolume(filename, reconstruction_outputs['reconstruction'])
np.savetxt(fn+'_errK.txt',reconstruction_outputs['errK'])
if 'R_free_total' in reconstruction_outputs.keys():
np.savetxt(fn+'_Rfree_total.txt',reconstruction_outputs['R_free_total'])
np.savetxt(fn+'_Rfree_bybin.txt',reconstruction_outputs['R_free_bybin'])
def writeVolume(filename, data, order="C"):
"""
* writeVolume *
Wrapper volume to file
:param filename: output filename with valid extension
:param data: numpy volume to write
:param order: "C" for row-major order (C-style), "F" for column-major (Fortran style)
"""
import os
fn, ext = os.path.splitext(filename)
if ext == ".mrc":
writeMRC(filename, arr=data, order=order)
elif ext == ".npy":
import numpy as np
np.save(filename,data)
elif ext == ".mat":
import scipy.io
scipy.io.savemat(filename, {"data":data})
else:
raise IOError("Unsupported file extension \"{}\" for volume object".format(ext))
12 |
--------------------------------------------------------------------------------
/documentation/src_html/html_helper.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | """
bla
"""
def generate_html(filename=None):
#convert source code files into formatted html for documentation
import sys
import os
print(filename)
if not os.path.isfile(filename):
raise NameError("File Not Found")
# if filename is None and len(sys.argv)>1:
# filename=sys.argv[1]
html_filename = os.path.abspath(os.path.splitext(filename)[0]) + '.html'
header_str = """
""" + os.path.splitext(filename)[0] + """
GENFIRE Documentation">
\
\n"""
closing_str = """
"""
print("hs = {}".format(header_str))
with open(filename,'r') as fid_source:
with open(html_filename,'w') as fid:
print ("html_filename = " , html_filename)
fid.write(header_str)
fid.write("")
in_comment_flag = 0
for line in fid_source:
if line.lstrip().startswith("#"): # then this line is a single line comment and we don't format it
fid.write(line.replace('\n',"
"))
elif line.lstrip().startswith("\"\"\""): # ignore formatting on multi-line comments
fid.write(line.replace('\n',"
"))
in_comment_flag = (in_comment_flag + 1) % 2
elif in_comment_flag:
fid.write(line.replace('\n',"
"))
else:
inline_comment_start = line.find("#")
if inline_comment_start == -1: # no inline comment
fid.write(code_formatter(line))
else:
fid.write(code_formatter(line[:inline_comment_start].replace('\n',"
")))
fid.write(line[inline_comment_start:].replace('\n',"
"))
fid.write("
")
fid.write(closing_str)
def code_formatter(string):
# A simple custom HTML code formatter that colors some keywords and tries to avoid instances of them within comments
color_string = "\"orange\""
keywords = ["from", "def","class", "self", "__main__", "for", "if",\
"while","import","else","elif", "True","False", "del", \
"with", "plt", "QtCore", "QtGui", "Qt", "ui"]
short_keywords = keywords[:]
new_keywords = []
for i, v in enumerate(keywords):
keywords[i] = v + " "
new_keywords.append(v + ".")
new_keywords.append(v + "\n")
keywords += new_keywords
format_string = ("", "")
replacers={}
for kw in keywords:
replacers[kw] = format_string
GENFIRE_format_string = ("", "")
replacers['GENFIRE'] = GENFIRE_format_string
replacers['genfire'] = GENFIRE_format_string
# replacers={"from":("", "")}
string = string.replace('\t', "    ")
if replacers is not None:
for key, value in replacers.iteritems():
# print (key, value)
string = string.replace(key,value[0] + key + value[1])
# for key in short_keywords:
# if string.endswith(key):
# string.replace(key, ("" + key+ "") )
string = string.replace('\n',"
")
return string
if __name__=="__main__":
import sys
if len(sys.argv) > 1:
for j in range(1,len(sys.argv)):
generate_html(sys.argv[j])
else:
generate_html("html_helper.py")
12 |
--------------------------------------------------------------------------------
/documentation/src_html/main.css:
--------------------------------------------------------------------------------
1 | #bounding_box {
2 | width:85%;
3 | padding = 14px 40px;
4 | text-align:justify;
5 | overflow-x: scroll;
6 | }
7 | h1 {
8 | margin: 10px;
9 | padding: 14px 0px;
10 | font-size: 36px;
11 | font-weight: bold;
12 | background-color: black;
13 | color: #ffffff;
14 | text-align: center
15 | }
16 | .h1_left {
17 | margin: 10px;
18 | padding: 14px 0px;
19 | font-size: 36px;
20 | font-weight: bold;
21 | color: #ffffff;
22 | background-color: black;
23 |
24 | text-align: left;
25 | }
26 |
27 |
28 | body {
29 | margin: 0;
30 | padding: 0;
31 | line-height: 1.5em;
32 | font-family: Arial, Helvetica, sans-serif;
33 | font-size: 16px;
34 | color: #AAAAAA;
35 | background: #080808;
36 | background: black;
37 | overflow-x: scroll;
38 |
39 | }
40 |
41 | a:link, a:visited {
42 | color: #ff6600;
43 | color:orange;
44 | text-decoration: none;
45 | font-weight: normal;
46 | }
47 |
48 | a:active, a:hover{
49 | color: #FFCC00;
50 | color:orange;
51 | text-decoration: underline;
52 | }
53 |
54 | p {
55 | margin: 10px;
56 | margin-right:155px;
57 | padding: 0px;
58 | text-align:justify;
59 | }
60 |
61 | ul{
62 | margin: 10px;
63 | margin-right:155px;
64 | padding: 15px;
65 | text-align:justify;
66 | }
67 |
68 | .code_box{
69 | width: 85%;
70 | text-align: justify;
71 | }
72 | .code {
73 | height:150%;
74 | /* margin-left:500px; */
75 | margin-top:100px;
76 | background:#666666;
77 | color:orange;
78 | margin: 25px 25px 25px 25px;
79 | /* margin-right:155px; */
80 | /* margin:0 auto; */
81 | padding: 15;
82 | text-align:justify;
83 | width:1000px;
84 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
85 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
86 | }
87 |
88 |
89 | img {
90 | margin: 0px;
91 | padding: 30px;
92 | border: none;
93 | }
94 |
95 | h2 {
96 | position: relative;
97 | height: 25px;
98 | margin: 0px;
99 | padding: 10px 0 0 0;
100 | font-size: 20px;
101 | font-weight: bold;
102 | text-align: left;
103 | color: #000000;
104 | color:white
105 | }
106 |
107 |
108 | h3 {
109 | margin: 0 0 5px 0;
110 | padding: 2px 0 3px 0;
111 | font-size: 14px;
112 | font-weight: normal;
113 | color: #d7e13d;
114 | color: orange;
115 | border-bottom: 1px dotted #d7e13d;
116 | }
117 |
118 | .h2_left {
119 | margin-left:10px;
120 | font-size: 24px;
121 | font-weight: bold;
122 | background-color: black;
123 | color: #ffffff;
124 | text-align: left;
125 | text-decoration:underline;
126 | /* float: left */
127 | }
128 | .h2_center {
129 | margin-left:10px;
130 | font-size: 42px;
131 | padding-top: 10px;
132 | padding-bottom: 10px;
133 | font-weight: bold;
134 | background-color: black;
135 | color: #ffffff;
136 | text-align: center;
137 | text-decoration:underline;
138 | /* float: left */
139 | }
140 | .h3_left {
141 | margin-left:10px;
142 | font-size: 20px;
143 | font-weight: bold;
144 | background-color: black;
145 | color: #ffffff;
146 | text-align: left;
147 | text-decoration:/* underline */;
148 | /* float: left */
149 | }
150 | .h5_left {
151 | margin-left:10px;
152 | font-size: 20px;
153 | font-weight: bold;
154 | background-color: black;
155 | color: #ffffff;
156 | text-align: left;
157 | /* float: left */
158 | }
159 | h4 {
160 | margin: 0px;
161 | padding: 0px;
162 | font-size: 14px;
163 | font-weight: bold;
164 | }
165 |
166 | #Main_Header {
167 | background: #EEEEEE;
168 | height: 150px;
169 | width:100%;
170 | overflow:auto;
171 | }
172 |
173 | #welcome {
174 | float:left;
175 | height: 50%;
176 | width:40%;
177 | color:black;
178 | /*font-family: 'Brush Script MT', cursive;*/
179 | font-size:7vw;
180 | display:block;
181 | white-space:nowrap;
182 | text-align:center;
183 | vertical-align:middle;
184 |
185 | }
186 |
187 | #GENFIRE_logo{
188 | right:35%;
189 | height: 150px;
190 | overflow:auto;
191 | height: auto;
192 | }
193 |
194 |
195 | #Sidebar {
196 | position: fixed;
197 | width: 280px;
198 | height: 500px;
199 | background-color: #888888;
200 | font-size: 16px;
201 | overflow-y: scroll;
202 | top: 0;
203 | bottom: 0;
204 | right: 0;
205 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
206 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
207 | }
208 | #Sidebar .h2_sidebar{
209 | font-size: 16px;
210 | color: #FFFFFF;
211 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
212 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
213 | }
214 |
215 | #Sidebar .h1_sidebar{
216 | font-size: 20px;
217 | font-weight: bold;
218 | color: #222222;
219 | text-align: center;
220 | margin-bottom:15px;
221 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
222 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
223 | }
224 |
225 | #Sidebar .h2_sidebar{
226 | font-size: 16px;
227 | color: #FFFFFF;
228 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
229 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
230 | }
231 |
232 |
233 | #Sidebar_lowered {
234 | margin-top:150px;
235 | position: fixed;
236 | width: 280px;
237 | height: 500px;
238 | background-color: #888888;
239 | font-size: 16px;
240 | overflow-y: scroll;
241 | top: 0;
242 | bottom: 0;
243 | right: 0;
244 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
245 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
246 | }
247 | #Sidebar_lowered .h2_sidebar{
248 | font-size: 16px;
249 | color: #FFFFFF;
250 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
251 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
252 | }
253 |
254 | #Sidebar_lowered .h1_sidebar{
255 | font-size: 20px;
256 | font-weight: bold;
257 | color: #222222;
258 | text-align: center;
259 | margin-bottom:15px;
260 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
261 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
262 | }
263 |
264 | #Sidebar_lowered .h2_sidebar{
265 | font-size: 16px;
266 | color: #FFFFFF;
267 | box-shadow: inset 0 -2px 2px rgba(0,0,0,0.5), /*bottom internal shadow*/
268 | inset 0 2px 2px rgba(255,255,255,1); /*top internal highlight*/
269 | }
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 | .side_link{
288 | font-size: 16px;
289 | margin-left: 15px;
290 | color:white;
291 | }
292 |
--------------------------------------------------------------------------------
/documentation/src_html/main.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | """
* GENFIRE.main *
The primary control module for running GENFIRE reconstructions.
Author: Alan (AJ) Pryor, Jr.
Jianwei (John) Miao Coherent Imaging Group
University of California, Los Angeles
Copyright 2015-2016. All rights reserved.
"""
from __future__ import division
import numpy as np
import GENFIRE
import sys
import os
from GENFIRE.reconstruct import ReconstructionParameters
def main_InteractivelySetParameters():
#######################################################################################################################
############################################### User Parameters ######################################################
#######################################################################################################################
# GENFIRE's reconstruction parameters can be edited here by the user and run interactively, any inputs provided
# by either the command line or the GUI will override these momentarily
filename_projections = '../data/projections.mat' #filename of projections, which should be size NxNxN_projections where N_projections is the number of projections
filename_angles = '../data/angles.mat' #angles can be either a 1xN_projections array containing a single tilt series, or 3xN_projections array containing 3 Euler angles for each projections in the form [phi;theta;psi]
filename_support = '../data/support60.mat' #NxNxN binary array specifying a region of 1's in which the reconstruction can exist
filename_initialObject = None #initial object to use in reconstruction; set to None to provide no initial guess
filename_results = 'GENFIRE_rec.mrc' #filename to save results
resolutionExtensionSuppressionState = 2 # 1) Turn on resolution extension/suppression, 2) No resolution extension/suppression, 3) Just resolution extension
numIterations = 100 #number of iterations to run in iterative reconstruction
oversamplingRatio = 3 #input projections will be padded internally to match this oversampling ratio. If you prepad your projections, set this to 1
interpolationCutoffDistance = 0.7 #radius of spherical interpolation kernel (in pixels) within which to include measured datapoints
doYouWantToDisplayFigure = True
displayFigure = GENFIRE.reconstruct.DisplayFigure()
displayFigure.DisplayFigureON = doYouWantToDisplayFigure
calculateRFree = True
if filename_support is None:
useDefaultSupport = True
else:
useDefaultSupport = False
reconstruction_parameters = ReconstructionParameters()
reconstruction_parameters.projectionFilename = filename_projections
reconstruction_parameters.angleFilename = filename_angles
reconstruction_parameters.supportFilename = filename_support
reconstruction_parameters.interpolationCutoffDistance = interpolationCutoffDistance
reconstruction_parameters.numIterations = numIterations
reconstruction_parameters.oversamplingRatio = oversamplingRatio
reconstruction_parameters.displayFigure = displayFigure
reconstruction_parameters.calculateRfree = calculateRFree
reconstruction_parameters.resolutionExtensionSuppressionState = resolutionExtensionSuppressionState
reconstruction_parameters.useDefaultSupport = useDefaultSupport
if os.path.isfile(filename_results): # If a valid initial object was provided, use it
reconstruction_parameters.initialObjectFilename = filename_results
main(reconstruction_parameters)
def main(reconstruction_parameters):
import GENFIRE.fileio
filename_projections = reconstruction_parameters.projectionFilename
filename_angles = reconstruction_parameters.angleFilename
filename_support = reconstruction_parameters.supportFilename
filename_results = reconstruction_parameters.resultsFilename
numIterations = reconstruction_parameters.numIterations
oversamplingRatio = reconstruction_parameters.oversamplingRatio
interpolationCutoffDistance = reconstruction_parameters.interpolationCutoffDistance
displayFigure = reconstruction_parameters.displayFigure
resolutionExtensionSuppressionState = reconstruction_parameters.resolutionExtensionSuppressionState
calculateRFree = reconstruction_parameters.calculateRfree
useDefaultSupport = reconstruction_parameters.useDefaultSupport
use_positivity = reconstruction_parameters.constraint_positivity
use_support = reconstruction_parameters.constraint_support
gridding_method = reconstruction_parameters.griddingMethod
enforceResolutionCircle = reconstruction_parameters.enforceResolutionCircle
permitMultipleGridding = reconstruction_parameters.permitMultipleGridding
if reconstruction_parameters.isInitialObjectDefined:
filename_initialObject = reconstruction_parameters.initialObjectFilename
else:
filename_initialObject = None
### begin reconstruction ###
projections = GENFIRE.fileio.loadProjections(filename_projections) # load projections into a 3D numpy array
# get dimensions of array and determine the array size after padding
dims = np.shape(projections)
paddedDim = dims[0] * oversamplingRatio
padding = int((paddedDim-dims[0])/2)
# load the support, or generate one if none was provided
if useDefaultSupport or filename_support == "":
support = np.ones((dims[0],dims[0],dims[0]),dtype=float)
else:
support = (GENFIRE.fileio.readVolume(filename_support) != 0).astype(bool)
displayFigure.reconstructionDisplayWindowSize = np.shape(support) # this is used to show the central region of reconstruction
# now zero-pad to match the oversampling ratio
support = np.pad(support,((padding,padding),(padding,padding),(padding,padding)),'constant')
projections = np.pad(projections,((padding,padding),(padding,padding),(0,0)),'constant')
#load initial object, or initialize it to zeros if none was given
if filename_initialObject is not None and os.path.isfile(filename_initialObject):
initialObject = GENFIRE.fileio.readVolume(filename_initialObject)
initialObject = np.pad(initialObject,((padding,padding),(padding,padding),(padding,padding)),'constant')
else:
initialObject = np.zeros_like(support)
# load angles and check that the dimensions match the number of provided projections and that they
# are either 1 x num_projections or 3 x num_projections
angles = GENFIRE.fileio.loadAngles(filename_angles)
if np.shape(angles)[1] > 3:
raise ValueError("Error! Dimension of angles incorrect.")
if np.shape(angles)[1] == 1:
tmp = np.zeros([np.shape(angles)[1], 3])
tmp[1, :] = angles
angles = tmp
del tmp
# grid the projections
if gridding_method == "DFT":
measuredK = GENFIRE.reconstruct.fillInFourierGrid_DFT(projections, angles, interpolationCutoffDistance, enforceResolutionCircle)
else:
measuredK = GENFIRE.reconstruct.fillInFourierGrid(projections, angles, interpolationCutoffDistance, enforceResolutionCircle, permitMultipleGridding)
# the grid is assembled with the origin at the geometric center of the array, but for efficiency in the
# iterative algorithm the origin is shifted to array position [0,0,0] to avoid unnecessary fftshift calls
measuredK = np.fft.ifftshift(measuredK)
# create a map of the spatial frequency to be used to control resolution extension/suppression behavior
K_indices = GENFIRE.utility.generateKspaceIndices(support)
K_indices = np.fft.fftshift(K_indices)
resolutionIndicators = np.zeros_like(K_indices)
resolutionIndicators[measuredK != 0] = 1-K_indices[measuredK != 0]
# if calculating Rfree, setup some infrastructure
if calculateRFree:
R_freeInd_complexX = []
R_freeInd_complexY = []
R_freeInd_complexZ = []
R_freeVals_complex = []
shell_thickness_pixels = 1 # pixel thickness of an individual shell of Rfree points
numberOfBins = int(round(dims[0]/2/shell_thickness_pixels)) # number of frequency bins. Rfree will be tracked within each shell separately
percentValuesForRfree = 0.05 # percentage of measured points to withhold
spatialFrequencyForRfree = np.linspace(0,1,numberOfBins+1)
K_indicesSmall =(K_indices)[:, :, 0:(np.shape(measuredK)[-1]//2+1)]
for shellNum in range(0,numberOfBins):
# collect relevant points
measuredPointInd_complex = np.where((measuredK[:, :, 0:(np.shape(measuredK)[-1]//2+1)] != 0) & (K_indicesSmall>=(spatialFrequencyForRfree[shellNum])) & (K_indicesSmall<=(spatialFrequencyForRfree[shellNum+1])))
# randomly shuffle
shuffledPoints = np.random.permutation(np.shape(measuredPointInd_complex)[1])
measuredPointInd_complex = (measuredPointInd_complex[0][shuffledPoints], measuredPointInd_complex[1][shuffledPoints], measuredPointInd_complex[2][shuffledPoints])
# determine how many values to take
cutoffInd_complex = np.floor(np.shape(measuredPointInd_complex)[1] * percentValuesForRfree).astype(int)
# collect the Rfree values and coordinates
R_freeInd_complexX.append(measuredPointInd_complex[0][:cutoffInd_complex])
R_freeInd_complexY.append(measuredPointInd_complex[1][:cutoffInd_complex])
R_freeInd_complexZ.append(measuredPointInd_complex[2][:cutoffInd_complex])
R_freeVals_complex.append(measuredK[R_freeInd_complexX[shellNum], R_freeInd_complexY[shellNum], R_freeInd_complexZ[shellNum] ])
# delete the points from the measured data
measuredK[R_freeInd_complexX[shellNum], R_freeInd_complexY[shellNum], R_freeInd_complexZ[shellNum]] = 0
# create tuple of coordinates
R_freeInd_complex = [[R_freeInd_complexX], [R_freeInd_complexY], [R_freeInd_complexZ]]
del R_freeInd_complexX
del R_freeInd_complexY
del R_freeInd_complexZ
else:
R_freeInd_complex = []
R_freeVals_complex = []
if resolutionExtensionSuppressionState==1: # resolution extension/suppression
constraintEnforcementDelayIndicators = np.array(np.concatenate((np.arange(0.95, -.25, -0.15), np.arange(-0.15, .95, .1)), axis=0))
elif resolutionExtensionSuppressionState==2:# no resolution extension or suppression
constraintEnforcementDelayIndicators = np.array([-999, -999, -999, -999])
elif resolutionExtensionSuppressionState==3:# resolution extension only
constraintEnforcementDelayIndicators = np.concatenate((np.arange(0.95, -.15, -0.15),[-0.15, -0.15, -0.15]))
else:
print("Warning! Input resolutionExtensionSuppressionState does not match an available option. Deactivating dynamic constraint enforcement and continuing.\n")
constraintEnforcementDelayIndicators = np.array([-999, -999, -999, -999])
reconstructionOutputs = GENFIRE.reconstruct.reconstruct(numIterations, np.fft.fftshift(initialObject), np.fft.fftshift(support), (measuredK)[:, :, 0:(np.shape(measuredK)[-1] // 2 + 1)], (resolutionIndicators)[:, :, 0:(np.shape(measuredK)[-1] // 2 + 1)], constraintEnforcementDelayIndicators, R_freeInd_complex, R_freeVals_complex, displayFigure, use_positivity, use_support)
# reclaim original array size. ncBig is center of oversampled array, and n2 is the half-width of original array
ncBig = paddedDim//2
n2 = dims[0]//2
reconstructionOutputs['reconstruction'] = reconstructionOutputs['reconstruction'][ncBig-n2:ncBig+n2,ncBig-n2:ncBig+n2,ncBig-n2:ncBig+n2]
GENFIRE.fileio.saveResults(reconstructionOutputs, filename_results)
if __name__ == "__main__" and len(sys.argv) == 1:
print ("starting with user parameters")
main_InteractivelySetParameters()
elif __name__ == "__main__":
if len(sys.argv) > 1: # Parse inputs provided either from the GUI or from the command line
inputArgumentOptions = {"-p" : "filename_projections",
"-a" : "filename_angles",
"-s" : "filename_support",
"-o" : "filename_results",
"-i" : "filename_initialObject",
"-r" : "resolutionExtensionSuppressionState",
"-it": "numIterations",
"-or": "oversamplingRatio",
"-t" : "interpolationCutoffDistance",
"-d" : "displayFigure",
"-rf": "calculateRFree"
}
print (sys.argv[:])
if len(sys.argv)%2==0:
raise Exception("Number of input options and input arguments does not match!")
for argumentNum in range(1,len(sys.argv),2):
print (inputArgumentOptions[sys.argv[argumentNum]])
print (sys.argv[argumentNum+1])
print (inputArgumentOptions[sys.argv[argumentNum]] + "=" + sys.argv[argumentNum+1])
exec(inputArgumentOptions[sys.argv[argumentNum]] + "= '" + sys.argv[argumentNum+1] +"'")
print("Setting argument %s from option %s equal to GENFIRE parameter %s " % (sys.argv[argumentNum+1],sys.argv[argumentNum], inputArgumentOptions[sys.argv[argumentNum]] ))
numIterations = int(numIterations)
# displayFigure = bool(displayFigure)
doYouWantToDisplayFigure = bool(displayFigure)
displayFigure = GENFIRE.reconstruct.DisplayFigure()
displayFigure.DisplayFigureON = doYouWantToDisplayFigure
oversamplingRatio = float(oversamplingRatio)
resolutionExtensionSuppressionState = int(resolutionExtensionSuppressionState)
calculateRFree = bool(calculateRFree)
try:
main(filename_projections,
filename_angles,
filename_support,
filename_results,
numIterations,
oversamplingRatio,
interpolationCutoffDistance,
resolutionExtensionSuppressionState,
displayFigure,
calculateRFree,
filename_initialObject)
except (NameError, IOError):
main(filename_projections,
filename_angles,
filename_support,
filename_results,
numIterations,
oversamplingRatio,
interpolationCutoffDistance,
resolutionExtensionSuppressionState,
displayFigure,
calculateRFree)
12 |
--------------------------------------------------------------------------------
/documentation/src_html/projections.html:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/genfire-em/GENFIRE-Python/b7b46daed4adf4f3afccea95ca71fa88e7c39e0a/documentation/src_html/projections.html
--------------------------------------------------------------------------------
/documentation/src_html/setup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | from setuptools import find_packages,setup, Extension
from subprocess import call
setup(
    name                 = "GENFIRE",
    packages         = find_packages(),
    version             = "1.0",
    description         = "GENeralized Fourier Iterative REconstruction",
    author                 = "Alan (AJ) Pryor, Jr.",
author_email         = "apryor6@gmail.com",
install_requires    = ['Pillow','numpy','matplotlib','scipy', 'pyparsing','six'],
    scripts                = ['bin/genfire']
)
12 |
--------------------------------------------------------------------------------
/documentation/tutorial.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 45 | This tutorial will walk you through your first GENFIRE 3D reconstruction. You will 46 | simulate a reconstruction of a tomographically-acquired tilt series using a vesicle model 47 | with GENFIRE's GUI. 48 |
49 | 50 |51 | First thing's first -- open the GUI. It looks like this 52 |
53 |55 | This is the main window for running GENFIRE reconstructions. Here you can select the filenames 56 | containing the projection images, Euler angles, 3D support and set reconstruction parameters 57 | like the number of iterations to run and the oversampling ratio (the amount of zero padding to add 58 | to the projections prior to gridding). First we have to create a simulated dataset so that we have 59 | something to work with. For that we can use the projection calculator, which can be accessed from a drop-down menu at the top of the screen: 60 |
61 |62 | Projection Calculator -> Launch Projection Calculator 63 |
64 |65 | You should now have a blank instance of the projection calculator, like this: 66 |
67 |70 | Now we need to select a 3D model. Click Browse, find "vesicle.mrc" in the data 71 | directory of the GENFIRE source code, then click open. You will be prompted to select 72 | an oversampling ratio. The oversampling ratio controls the amount of zero padding applied 73 | to the model -- specifically the oversampling ratio is the total array size divided 74 | by the size of the object. The purpose of this zero-padding is to increase the accuracy 75 | of the projection calculation. The tradeoff is that larger oversampling ratios mean the 76 | array size is bigger, and, therefore, slower. I find that an oversampling ratio of 3 77 | is a good choice. Click OK, and GENFIRE will load the model, pad it, 78 | compute the 3D FFT, and construct a linear interpolator. Once finished projections 79 | may be calculated relatively quickly. 80 |
81 | 82 |83 | Once loaded the zero-degree projection of the model will appear in the display. 84 |
85 |
88 | At this point you can adjust the Euler angles to explore what different views of the
89 | model look like. Note that these are projection images, not surface renderings. If you
90 | are new to tomography, take a moment to explore how the projection images change as you
91 | adjust the angles, in particular theta. This can give you some really nice intuition as
92 | to how 3D information is encoded in the 2D projection series.
93 | Once you are ready, calculate a projection image dataset from this model by clicking "Calculate Projection
94 | Series from Model"
95 |
99 | From this dialog you can specify the Euler angles for each of the calculated projections. 100 | To accomplish this you have two options. 101 |
102 |103 | The first is to provide the Euler angles as a space-delimited .txt file where each 104 | row corresponds to one projection and provides the Euler angles as phi theta psi. 105 | If you are confused about this format you can view the outputted file with option 2 to see an example. 106 | Note there is no limitation on the angles for GENFINRE like there are in many single-axis tomography 107 | reconstruction techniques, so you can use whatever you'd like. 108 |
109 |110 | The second option is to specify a single-axis tilt series. Specify the tilt angle, theta, 111 | as start = 0, step = 2, stop = 180 to calculate 91 equally spaced projections with no missing wedge. 112 | Choose an output filename for the projection, make sure "Save Angles" is checked, 113 | then click "Calculate Projections" to perform the calculation. 114 |
115 |118 | The calculation runs in the background on a separate thread. Once it is finished you will hopefully see 119 | a success message like this 120 |
121 |124 | Note that the file created containing the Euler angles is the same name as the corresponding 125 | projections with "_euler_angles" appended, in case you want an example of how to format 126 | your own angle files. 127 |
128 |129 | For now, we will just use the default reconstruction parameters (more detail is given on them HERE). 130 | Verify that the filenames of your data are correct, then start the reconstruction 131 | by clicking the enormous green button. 132 |
133 |135 | Congratulations, you have completed your first GENFIRE reconstruction! You can now view 136 | the error curves and a simple visualization of the results by clicking "Summarize Results" 137 | and selecting the file with your results. 138 |
139 |
142 | What's all this, you ask?
143 | The left figure shows projection images of the reconstruction along the 3 principal
144 | axes and central slices. You'll be able to visualize the volume more closely in a moment.
145 | The top error curve plots the total reciprocal error vs iteration number. This is the R-factor
146 | between the FFT of the reconstruction and the entire constraint set. By default the reconstruction
147 | is performed using resolution extension/suppression, so for the early iterations only the lowest
148 | resolution constraints are enforced, but the error is still compared to all constraints so there
149 | are dips each time the constraint set is updated. This style of constraint enforcement is useful
150 | for noisy data -- here we have a noiseless simulation so you won't see much difference in the
151 | reconstruction if you turn it off.
152 | The middle and bottom curves summarize the results for R-free. GENFIRE implements a modified version
153 | of the concept of R-free from X-ray crystallography. First, the constraint set is divided up into
154 | bins (10 by default). In each spatial frequency bin, 5% of the values are withheld from the reconstruction.
155 | At each iteration, the R-factor is calculated between the voxels in reciprocal space and these withheld values.
156 | The purpose of this is a metric for prevention of overfitting to the data. Low values of R-free indicate
157 | that recovered values for missing datapoints match the (withheld) input data, and by extension
158 | suggests confidence in reconstructed values where there is no measured datapoint to compare.
159 | The middle curve shows the mean value of R-free across all resolutions at each iteration. For clean
160 | data it will generally mirror the reciprocal error curve. The bottom curve shows the value of R-free for
161 | each spatial frequency bin at the final iteration. It generally increases with spatial frequency. For this
162 | noiseless simulation the values are quite low, but for noisy data R-free will be higher. It is important
163 | to remember that high values of R-free are not necessarily bad, they simply mean there is difference between
164 | the recovered and measured reciprocal-space data. For noisy data this may be what you want, as resolution
165 | extension/suppression can act as a denoising technique. However, R-free will also be high if your data
166 | is not good. This illustrates the importance of considering multiple metrics when drawing conclusions about
167 | your results. Remember - "Garbage in, garbage out".
168 |
170 | To explore your reconstruction, open the volume slicer
171 | Volume Slicer -> Launch Volume Slicer
172 | and select your results.
173 |
177 | Here you can view individual layers of your reconstruction (or any volume) along the 3 principal directions. 178 | You can also use this module to view your calculated projections. 179 |
180 |181 | Hopefully this tutorial has been helpful. Happy reconstructing! 182 |
183 | 184 |