Skip to content

Separate Relion-specific defaults from ImageSource #519

@chris-langfield

Description

@chris-langfield

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_indices property 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 _rotations property of ImageSource is populated from the _rlnAngleRot, _rlnAngleTilt, and _rlnAnglePsi fields, if they exist in the starfile. This should probably be moved into RelionSource
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 ImageSource explicitly return Relion STAR file parameters, which are expected to be loaded into ImageSource.metadata. One way around this could be to simply move these to RelionSource. However, some could be properties of a general ImageSource, 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)       

Metadata

Metadata

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions