Source code for xmipp3.protocols.protocol_apply_transformation_matrix

# **************************************************************************
# *
# * Authors:     Mohsen Kazemi (mkazemi@cnb.csic.es)
# *
# * Unidad de  Bioinformatica of Centro Nacional de Biotecnologia , CSIC
# *
# * This program 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 2 of the License, or
# * (at your option) any later version.
# *
# * This program 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 this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************

import numpy as np

from pyworkflow import VERSION_1_1
import pyworkflow.protocol.params as params

from pwem.protocols import ProtProcessParticles


from xmipp3.convert import writeSetOfParticles

        
[docs]class XmippProtApplyTransformationMatrix(ProtProcessParticles): """ Apply transformation matrix of an aligned volume on a set of particles to modify their angular assignment. Note: These particles are practically related to the aligned volume (but before alignment). AI Generated: What this protocol is for Apply transformation matrix is a metadata-propagation tool that updates the angular assignment (projection alignment transforms) of a particle set using the rigid transformation stored in a volume. It is meant for situations where you have aligned a volume to another reference volume (for example with an “align volume” protocol), and you now want the particles that generated the original (pre-alignment) volume to be expressed in the new reference frame. Biologically, this comes up all the time when comparing reconstructions or consolidating multiple processing branches. If you align one map to another, the particle orientations associated with the first map become “out of frame” relative to the aligned map unless you apply the same transformation to the particle metadata. This protocol does exactly that: it takes the transformation matrix stored in the aligned volume and composes it onto every particle’s projection transform so that the particle angles/shifts are updated consistently. A useful way to think about it is: “I have particles aligned to a volume; I moved/rotated that volume to match another reference; now I need to move/rotate the particle orientations in the same way.” Inputs and what they must contain You provide two inputs. The first is an input particle set that must already have projection alignment (i.e., assigned angles/transform matrices consistent with a 3D reference). In Scipion terms, these are particles with 3D/projection alignment metadata, not merely 2D alignment. The second is an input volume that must contain a stored transform. In practice, this is typically the output aligned volume produced by a volume-to-volume alignment protocol (the common example is the output of an “align volume” step). The crucial requirement is that the volume’s transform represents the mapping you want to apply to the particle poses. This protocol assumes that the input particles are indeed the ones associated with the pre-aligned version of that volume. If the particle set is unrelated (different refinement, different reference, different convention), applying the matrix will produce particle orientations that are mathematically consistent but biologically meaningless. What the protocol does (conceptually) For each particle, the protocol takes the particle’s current transform matrix (its projection orientation and shift) and left-multiplies it by the volume’s transform matrix. The effect is that every particle’s pose is rotated/translated according to the same rigid-body transform that was applied to the volume. No image pixels are changed; only the particle metadata transforms are updated. This means the protocol is fast and deterministic: it is not a refinement and it does not depend on correlation or optimization. It simply re-expresses particle orientations in a new coordinate frame. Outputs and how to use them The output is a new SetOfParticles (outputParticles) that is identical to the input particle set in terms of particle membership and images, but with updated projection transforms. You would use this output whenever you want downstream steps (for example, further refinement, comparison, classification in a common frame, or export) to interpret the particles according to the aligned volume’s coordinate system. A typical biological workflow is: align volume A to volume B, then apply A’s alignment transform to the particles that produced A, so that those particles are now consistent with the aligned A (and therefore comparable to B). This is especially helpful when you want to merge particle sets, compare angular distributions across processing branches, or build consistent “state-to-state” comparisons where each state was reconstructed independently but later brought into a common frame. What this protocol does not do (important for interpretation) This protocol does not improve alignment quality and does not validate anything. If the volume-to-volume alignment transform is wrong or ambiguous (for instance due to symmetry, masking issues, or heterogeneity), then the particle updates will also be wrong. In biological practice, it is therefore wise to first verify that the aligned volume truly matches the intended reference (visual overlay, landmark comparison, fitting of a known model, etc.) before propagating the transform to particles and using them for further quantitative analysis. Also, because this protocol updates only metadata, it is not the right tool if your goal is to create a physically transformed particle stack where pixels have been rotated/shifted. For that, you would use protocols that apply transforms to images (like 2D apply alignment or similar image transformation steps). Here, the objective is coordinate-frame consistency for projection parameters. Practical recommendations This protocol is most valuable when you are building multi-branch workflows and need all results to live in a shared frame. It is also helpful when preparing data for downstream steps that assume one consistent reference orientation, such as some comparative analyses or downstream integration with external modeling pipelines. As a sanity check after running, it is good practice to examine whether downstream steps that depend on orientation—such as reprojections, angular distributions, or quick refinements—behave as expected. If something looks “rotated wrong” globally, that often indicates that the volume transform being propagated was not the one you intended (or that symmetry introduced an alternative but equivalent solution). """ _label = 'apply transformation matrix' _lastUpdateVersion = VERSION_1_1 #--------------------------- DEFINE param functions ------------------------------------ def _defineParams(self, form): form.addSection(label='Input') form.addParam('inputParticles', params.PointerParam, pointerClass='SetOfParticles', pointerCondition='hasAlignmentProj', label="Input particles", help="Aligned particles that their " "angular assignment needs to be modified.") form.addParam('inputVolume', params.PointerParam, pointerClass='Volume', pointerCondition='hasTransform', label='Input volume', help="Volume that we want to use its transformation matrix " "to modify angular assignment of input particles. " "(This is normally the output volume of protocol_" "align_volume)") form.addParallelSection(threads=1, mpi=2) #--------------------------- INSERT steps functions ------------------------------------ def _insertAllSteps(self): fnOutputParts = self._getExtraPath('output_particles.xmd') inputSet = self.inputParticles.get() inputVol = self.inputVolume.get() self._insertFunctionStep('createOutputStep', fnOutputParts, inputSet, inputVol) #--------------------------- STEPS functions --------------------------------------------
[docs] def createOutputStep(self, fnOutputParts, inputSet, inputVol): volTransformMatrix = np.matrix(inputVol.getTransform().getMatrix()) outputSet = self._createSetOfParticles() for part in inputSet.iterItems(): partTransformMat = part.getTransform().getMatrix() partTransformMatrix = np.matrix(partTransformMat) newTransformMatrix = volTransformMatrix * partTransformMatrix part.getTransform().setMatrix(newTransformMatrix) outputSet.append(part) outputSet.copyInfo(inputSet) self._defineOutputs(outputParticles=outputSet) writeSetOfParticles(outputSet, fnOutputParts)
#--------------------------- INFO functions -------------------------------------------- def _summary(self): summary = [] if not hasattr(self, 'outputParticles'): summary.append("Output particles not ready yet.") else: summary.append("Applied alignment to %s particles." % self.inputParticles.get().getSize()) return summary def _methods(self): if not hasattr(self, 'outputParticles'): return ["Output particles not ready yet."] else: return ["We applied alignment to %s particles from %s and produced %s." %(self.inputParticles.get().getSize(), self.getObjectTag('inputParticles'), self.getObjectTag('outputParticles'))]