Skip to content

Commit 8811228

Browse files
chris-langfieldgarrettwrongjandenj-c-c
authored
estimate_ctf integration with CoordinateSource (#623)
* Fix `k` bug in BFSReddyChetterji * restrict corr to a disc * Change `self.L` and `self.n` to `self.src.L` and `self.src.n` for estimators (#550) * changed self.L and self.n to self.src.L and self.src.n for estimators * extraneous comma * Add `aspire.utils.coor_trans.crop_2d` and move `coor_trans` objects to second-level API access (#565) * utils.coor_trans methods accessible from second level * crop_2d method * crop2d test * clarify flooring behavior for centering and add apdding * tests finished * isort shenanigans * fix even to odd n->n+1 padding bug * slightly better comment * better behavior for dtype * remove mistaken characters * fixes after merge * last ensure removed * rect tests * padding tests and fill value test * better comments * even better comments * np.diag * crop_pad_2d * clarifying comment when padding * Logical rather than hardcoded downsample tests (#567) * downsample tests * cleanup and deleting test downsample from preprocess_pipeline * remove hardcoded ds / whiten test * flake8 * 3d volume downsample test * remove redundant test from tests/test_preprocess.py * remove unused import * remove numbered tests for uniformity * typo * skip one of the ds tests * use gaussian_3d to generate test volume * isort * checkCenterPoint * max_resolution -> L_ds * parametrize ds tests * Bessel zero finding cleanup (#596) * docstring for besselj_zeros * rest of docstrings * remove pdb import * second order differences comment * comments and docstring for num_besselj_zeros * formatting * tiny fixes; * docstring fixes * NumPy * NumPy again * sanity check * parentheses inside sentence * `Basis` size fixes (#598) * fix size checking bug * fb2d * fb3d and pswf2d * dirac * fpswf * polar * format * sz -> size * test init with int * docstring * typo * the same typo * Authors page grammar fix. * cmap=gray * Gallery verb tense. Change cmap to gray. * Update gallery/experiments/README.rst header language. * Encode size in 2D FB test * Add test for 2D FB Gaussian expansion * Add eval–expand test for 2D FB * Add adjoint test for 2D FB * Add test for isotropic and modulated in 2D FB * Add test for indices * Add test for specific FB 2D basis element * Make 2D FB tests FFB friendly Cast `Images` to arrays when needed. * Move tests not specific to FB into a mixin * Add mixin to FFB 2D tests * Remove hardcoding from complex conversion tests * First attempt at testElement for FFB2D * Remove hardcoded (F)FB2D tests * Parameterize tests * Pass dtype to gaussian_2d * Expand on comment regarding factor of 1/2 * Fix rtoc bug * Add shape checks To make sure we don't get the same inadvertent broadcasting bug again. * Loop over different indices in 2D (F)FB tests * Fix #604 (#607) * Bump version: 0.9.0 → 0.9.1 * Fourier-Bessel Mixin class (#599) Fourier Bessel Mixin Class: fb.py * New downsample method (#606) * dropped in new ds method * adjust tests temporarily * some imports * change to centered_fft2 and allow gridpoint checks * format * remove debug show() * comment * simplify centered_fft2 and centered_ifft2 * dict of dicts * adjusted tests * Zenodo file with authors and affiliations (#615) * add zenodo file with just creators * name * add zenodo file to manifest * json errors * chris orcid * updated affiliations * update tox.ini to include json.tool check * josh orcid * author ordering * review changes * Remove offending sk import * Explicitly use svd_solver='full' for repro unit test * Fix PIL deprecation warning * sensible defaults * optional saving mrc * populate_unique_filters and read_ctf_file * body of populate_unique_filters * loop var * tests * work around ImageSource.set_metadata * update test * final test * rationalize sample CTF values * readme space * correct metadata test * write_all_star * remove write_one_star * method name * reading from Relion micrograph_ctf.star * format * import_ctf() instead of __init__ option * added tests and clarified import_ctf method * condense ctf args coordinate source * .values() instead of items() * add save image flags to increase test converage * docstrings * rest of docstrings * rename fun to populate_ctf * first pass refactor * refactor 2 * docstring * refactor 3 Co-authored-by: Garrett Wright <[email protected]> Co-authored-by: Garrett Wright <[email protected]> Co-authored-by: Joakim Andén <[email protected]> Co-authored-by: Josh Carmichael <[email protected]> Co-authored-by: Josh Carmichael <[email protected]>
1 parent 63e0bd7 commit 8811228

File tree

5 files changed

+488
-122
lines changed

5 files changed

+488
-122
lines changed

src/aspire/ctf/ctf_estimator.py

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -671,22 +671,19 @@ def gd(
671671
# Note, This doesn't actually use anything from the class.
672672
# It is used in a solver loop of some sort, so it may not be correct
673673
# to just use what is avail in the obj.
674-
def write_star(self, df1, df2, ang, cs, voltage, pixel_size, amp, name, output_dir):
674+
def write_star(self, name, params_dict, output_dir):
675675
"""
676-
Writes CTF parameters to starfile.
676+
Writes CTF parameters to starfile for a single micrograph.
677677
"""
678-
679-
if not os.path.isdir(output_dir):
680-
os.mkdir(output_dir)
681678
data_block = {}
682679
data_block["_rlnMicrographName"] = name
683-
data_block["_rlnDefocusU"] = df1
684-
data_block["_rlnDefocusV"] = df2
685-
data_block["_rlnDefocusAngle"] = ang
686-
data_block["_rlnSphericalAbberation"] = cs
687-
data_block["_rlnAmplitudeContrast"] = amp
688-
data_block["_rlnVoltage"] = voltage
689-
data_block["_rlnDetectorPixelSize"] = pixel_size
680+
data_block["_rlnDefocusU"] = params_dict["defocus_u"]
681+
data_block["_rlnDefocusV"] = params_dict["defocus_v"]
682+
data_block["_rlnDefocusAngle"] = params_dict["defocus_ang"]
683+
data_block["_rlnSphericalAberration"] = params_dict["cs"]
684+
data_block["_rlnAmplitudeContrast"] = params_dict["amplitude_contrast"]
685+
data_block["_rlnVoltage"] = params_dict["voltage"]
686+
data_block["_rlnDetectorPixelSize"] = params_dict["pixel_size"]
690687
df = DataFrame([data_block])
691688
blocks = OrderedDict()
692689
blocks["root"] = df
@@ -696,16 +693,18 @@ def write_star(self, df1, df2, ang, cs, voltage, pixel_size, amp, name, output_d
696693

697694
def estimate_ctf(
698695
data_folder,
699-
pixel_size,
700-
cs,
701-
amplitude_contrast,
702-
voltage,
703-
num_tapers,
704-
psd_size,
705-
g_min,
706-
g_max,
707-
output_dir,
696+
pixel_size=1.0,
697+
cs=2.0,
698+
amplitude_contrast=0.07,
699+
voltage=300,
700+
num_tapers=2,
701+
psd_size=512,
702+
g_min=30,
703+
g_max=5,
704+
output_dir="results",
708705
dtype=np.float32,
706+
save_ctf_images=False,
707+
save_noise_images=False,
709708
):
710709
"""
711710
Given paramaters estimates CTF from experimental data
@@ -736,7 +735,7 @@ def estimate_ctf(
736735
# closer to original code.
737736
ffbbasis = FFBBasis2D((psd_size, psd_size), 2, dtype=dtype)
738737

739-
results = []
738+
results = {}
740739
for name in file_names:
741740
with mrcfile.open(
742741
os.path.join(data_folder, name), mode="r", permissive=True
@@ -816,50 +815,55 @@ def estimate_ctf(
816815
cc_array[a, 3] = p
817816
ml = np.argmax(cc_array[:, 3], -1)
818817

819-
result = (
820-
cc_array[ml, 0],
821-
cc_array[ml, 1],
822-
cc_array[ml, 2], # Radians
823-
cs,
824-
voltage,
825-
pixel_size,
826-
amp,
827-
name,
828-
)
829-
830-
ctf_object.write_star(*result, output_dir)
831-
results.append(result)
832-
833-
ctf_object.set_df1(cc_array[ml, 0])
834-
ctf_object.set_df2(cc_array[ml, 1])
835-
ctf_object.set_angle(cc_array[ml, 2]) # Radians
836-
ctf_object.generate_ctf()
837-
838-
with mrcfile.new(
839-
output_dir + "/" + os.path.splitext(name)[0] + "_noise.mrc", overwrite=True
840-
) as mrc:
841-
mrc.set_data(background_2d[0].astype(np.float32))
842-
mrc.voxel_size = pixel_size
843-
mrc.close()
844-
845-
df = (cc_array[ml, 0] + cc_array[ml, 1]) * np.ones(theta.shape, theta.dtype) + (
846-
cc_array[ml, 0] - cc_array[ml, 1]
847-
) * np.cos(2 * theta - 2 * cc_array[ml, 2] * np.ones(theta.shape, theta.dtype))
848-
ctf_im = -np.sin(
849-
np.pi * lmbd * r_ctf**2 / 2 * (df - lmbd**2 * r_ctf**2 * cs * 1e6)
850-
+ amplitude_contrast
851-
)
852-
ctf_signal = np.zeros(ctf_im.shape, ctf_im.dtype)
853-
ctf_signal[: ctf_im.shape[0] // 2, :] = ctf_im[: ctf_im.shape[0] // 2, :]
854-
ctf_signal[ctf_im.shape[0] // 2 + 1 :, :] = signal[
855-
:, :, ctf_im.shape[0] // 2 + 1
856-
]
818+
result = {
819+
"defocus_u": cc_array[ml, 0],
820+
"defocus_v": cc_array[ml, 1],
821+
"defocus_ang": cc_array[ml, 2], # Radians
822+
"cs": cs,
823+
"voltage": voltage,
824+
"pixel_size": pixel_size,
825+
"amplitude_contrast": amp,
826+
}
827+
results[name] = result
828+
829+
# we write each micrograph's ctf parameters to an individual starfile
830+
if not os.path.isdir(output_dir):
831+
os.mkdir(output_dir)
832+
ctf_object.write_star(name, result, output_dir)
833+
834+
if save_noise_images:
835+
with mrcfile.new(
836+
os.path.join(output_dir, os.path.splitext(name)[0] + "_noise.mrc"),
837+
overwrite=True,
838+
) as mrc:
839+
mrc.set_data(background_2d[0].astype(np.float32))
840+
mrc.voxel_size = pixel_size
841+
842+
if save_ctf_images:
843+
ctf_object.set_df1(cc_array[ml, 0])
844+
ctf_object.set_df2(cc_array[ml, 1])
845+
ctf_object.set_angle(cc_array[ml, 2]) # Radians
846+
ctf_object.generate_ctf()
847+
df = (cc_array[ml, 0] + cc_array[ml, 1]) * np.ones(
848+
theta.shape, theta.dtype
849+
) + (cc_array[ml, 0] - cc_array[ml, 1]) * np.cos(
850+
2 * theta - 2 * cc_array[ml, 2] * np.ones(theta.shape, theta.dtype)
851+
)
852+
ctf_im = -np.sin(
853+
np.pi * lmbd * r_ctf**2 / 2 * (df - lmbd**2 * r_ctf**2 * cs * 1e6)
854+
+ amplitude_contrast
855+
)
856+
ctf_signal = np.zeros(ctf_im.shape, ctf_im.dtype)
857+
ctf_signal[: ctf_im.shape[0] // 2, :] = ctf_im[: ctf_im.shape[0] // 2, :]
858+
ctf_signal[ctf_im.shape[0] // 2 + 1 :, :] = signal[
859+
:, :, ctf_im.shape[0] // 2 + 1
860+
]
857861

858-
with mrcfile.new(
859-
output_dir + "/" + os.path.splitext(name)[0] + ".ctf", overwrite=True
860-
) as mrc:
861-
mrc.set_data(np.float32(ctf_signal))
862-
mrc.voxel_size = pixel_size
863-
mrc.close()
862+
with mrcfile.new(
863+
os.path.join(output_dir, os.path.splitext(name)[0] + ".ctf"),
864+
overwrite=True,
865+
) as mrc:
866+
mrc.set_data(np.float32(ctf_signal))
867+
mrc.voxel_size = pixel_size
864868

865869
return results

0 commit comments

Comments
 (0)