-
Notifications
You must be signed in to change notification settings - Fork 26
Description
WIP
The ImageSource class of aspire.source.image contains a number of Relion-specific parameters and defaults. This causes some issues when attempting to subclass for other types of data sources.
This issue can start as a description of parts of this class that currently rely on or set these defaults.
One of the main issues is the assumption baked into the ImageSource code that the data source will contain metadata related to CTF.
- The
filter_indicesproperty of this class has a setter method which sets
self.set_metadata(
[
"_rlnVoltage",
"_rlnDefocusU",
"_rlnDefocusV",
"_rlnDefocusAngle",
"_rlnSphericalAberration",
"_rlnAmplitudeContrast",
],
filter_values, These correspond to similarly named properties of the operators.filters.CTFFilter class. If filter_indices is set to None, or if the source does not have CTFFilters in its unique_filters, these values in metadata are all assigned to np.nan. Since not all sources contain CTF information, this behavior should probably either check for this, or be moved to RelionSource.
The __init__ of RelionSource depends on this behavior of ImageSource.filter_indices. In this code:
filter_params, filter_indices = np.unique(
metadata[
[
"_rlnVoltage",
"_rlnDefocusU",
"_rlnDefocusV",
"_rlnDefocusAngle",
"_rlnSphericalAberration",
"_rlnAmplitudeContrast",
]
].values,
return_inverse=True,
axis=0,
) filter_indices is set to a partition of the indices of metadata according to unique CTF parameters. But hidden in this assignment is the code in the setter above, which populates Relion-specific metadata. The sequence of events is, more or less:
CTF_params = {
"_rlnVoltage",
"_rlnDefocusU",
"_rlnDefocusV",
"_rlnDefocusAngle",
"_rlnAmplitudeContrast",
}
if CTF_params.issubset(metadata.columns):
filter_params, filter_indices = np.unique(metadata[list(CTF_params)])
filters = []
for row in filter_params:
filters.append(
CTFFilter(
pixel_size=self.pixel_size,
voltage=row[0],
defocus_u=row[1],
defocus_v=row[2],
defocus_ang=row[3] * np.pi / 180, # degrees to radians
Cs=row[4],
alpha=row[5],
B=B,
)
)
filter_values = np.zeros((len(indices), len(attribute_list)))
for i, filt in enumerate(self.unique_filters):
filter_values[indices == i] = [
getattr(filt, attribute, np.nan) for attribute in attribute_list
]
self.set_metadata(list(CTF_params), filter_values)This could probably be turned into its own method, and the extra code removed from the setter of filter_indices.
- In
ImageSource.__init__(), the_rotationsproperty ofImageSourceis populated from the_rlnAngleRot,_rlnAngleTilt, and_rlnAnglePsifields, if they exist in the starfile. This should probably be moved intoRelionSource
if self.has_metadata(["_rlnAngleRot", "_rlnAngleTilt", "_rlnAnglePsi"]):
self._rotations = R.from_euler(
"ZYZ",
self.get_metadata(
["_rlnAngleRot", "_rlnAngleTilt", "_rlnAnglePsi"]
),
degrees=True,
) - Several other properties of
ImageSourceexplicitly return Relion STAR file parameters, which are expected to be loaded intoImageSource.metadata. One way around this could be to simply move these toRelionSource. However, some could be properties of a generalImageSource, so perhaps leaving the implementation to child classes would be better.
@property
def offsets(self):
return np.atleast_2d(
self.get_metadata(["_rlnOriginX", "_rlnOriginY"], default_value=0.0)
)
@offsets.setter
def offsets(self, values):
return self.set_metadata(["_rlnOriginX", "_rlnOriginY"], values)
@property
def amplitudes(self):
return np.atleast_1d(self.get_metadata("_rlnAmplitude", default_value=1.0))
@amplitudes.setter
def amplitudes(self, values):
return self.set_metadata("_rlnAmplitude", values)