Source code for xmipp3.protocols.protocol_particle_pick_pairs

# **************************************************************************
# *
# * Authors:     Jose Gutierrez Tabuenca (jose.gutierrez@cnb.csic.es)
# *              J.M. De la Rosa Trevin (jmdelarosa@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'
# *
# **************************************************************************

from pyworkflow.object import String
import pyworkflow.protocol.params as params
import pyworkflow.utils as pwutils
from pwem.protocols import ProtParticlePicking
from pwem.objects import CoordinatesTiltPair
from pwem.viewers import launchTiltPairPickerGUI

from xmipp3 import convert
from xmipp3.base import XmippProtocol


[docs]class XmippProtParticlePickingPairs(ProtParticlePicking, XmippProtocol): """ Picks particles in paired tilted micrographs. Using paired data improves particle localization and orientation determination, enhancing reconstruction accuracy. AI Generated ## Overview The Tilt Pairs Particle Picking protocol allows the user to pick corresponding particles in paired tilted and untilted micrographs. Tilt-pair data contain two related images of the same field: one acquired without tilt, or with lower tilt, and another acquired at a known tilt angle. The same particles appear in both micrographs, but their positions are related by the geometry of the tilt. Correctly identifying corresponding particles in both views is essential for workflows such as random conical tilt, tilted-pair validation, and geometry-aware reconstruction strategies. This protocol launches the Xmipp tilt-pair particle-picking graphical interface. The user can inspect the paired micrographs and define particle coordinates in a way that preserves the relationship between untilted and tilted views. The main output is a **CoordinatesTiltPair** object, containing linked coordinate sets for the untilted and tilted micrographs, together with the tilt-angle information read from the paired micrograph metadata. ## Inputs and General Workflow The input is a set of paired tilted micrographs, represented in Scipion as a **MicrographsTiltPair** object. The protocol first converts the paired micrograph information into Xmipp metadata format. This metadata describes the relationship between the untilted and tilted micrograph sets and is used by the graphical picker. The protocol then opens the tilt-pair picking interface. After the user has picked particles and saved the results, the protocol reads the coordinate files for both the untilted and tilted micrographs. It also reads the tilt-pair angle information and creates the final paired-coordinate output. ## Micrographs Tilt Pair The **Micrographs tilt pair** parameter defines the paired micrograph dataset to be used for picking. This input contains two linked micrograph sets: - the untilted micrographs; - the tilted micrographs. Each untilted micrograph should correspond to a tilted micrograph showing the same field of view under the tilt geometry. The quality of the output depends on this pairing being correct. If the micrograph pairs are mismatched, the picked coordinates will not represent the same physical particles in both views, and downstream tilted-pair analysis will be unreliable. ## Tilt-Pair Picking Interface The protocol launches a graphical interface designed specifically for tilt-pair picking. Unlike ordinary single-micrograph picking, the goal here is not only to select particle positions, but also to preserve their correspondence between the two views. The picker uses the paired micrograph metadata to help manage this relationship. The user can inspect the untilted and tilted images, pick particles, and save coordinate information for both members of each pair. This interactive step is important because tilted images may be harder to interpret than untilted images. Particles can appear distorted, shifted, or less contrasted because of the tilt geometry and increased effective ice thickness. ## Untilited and Tilted Coordinate Sets After picking, the protocol creates two coordinate sets: - one coordinate set for the untilted micrographs; - one coordinate set for the tilted micrographs. These coordinate sets are not independent outputs. They are combined into a single **CoordinatesTiltPair** object that stores their relationship. This paired organization is essential. It tells downstream protocols which untilted coordinate corresponds to which tilted coordinate. ## Tilt Angles The protocol also reads the angle information associated with the input micrograph pairs. These angles describe the tilt geometry of the paired acquisition. They are stored together with the coordinate pairs and are needed by downstream tilted-pair processing protocols. The angle information is not manually estimated by this protocol. It is read from the paired micrograph metadata generated from the input MicrographsTiltPair object. Users should therefore ensure that the tilt-pair micrograph input contains correct tilt information. ## Output CoordinatesTiltPair The main output is an **outputCoordinatesTiltPair** object. This output contains: - the untilted coordinate set; - the tilted coordinate set; - the angle information linking the paired micrographs; - the box-size information when available. This object is the standard Scipion representation of picked tilt-pair coordinates. It can be used by protocols that extract paired particles or perform tilted-pair analysis. If the protocol is run several times and new coordinate outputs are registered, the outputs may receive numbered suffixes to avoid overwriting previous results. ## Particle Size and Box Size The protocol can store a particle box size in the output coordinate sets when available. The box size represents the approximate particle size used during picking and can guide later extraction of untilted and tilted particle pairs. For tilted-pair data, the box size should be large enough to contain the particle in both views. Tilted projections may appear slightly elongated, distorted, or less well centered, so users should be careful not to choose a box that is too small. ## Interpretation of the Output The output should be interpreted as a set of paired particle positions. Each pair represents one physical particle observed in two acquisition geometries. The untilted coordinate indicates its position in the untilted micrograph, and the tilted coordinate indicates the corresponding position in the tilted micrograph. This correspondence is more important than either coordinate set alone. Downstream analysis depends on the assumption that each pair refers to the same particle. ## Practical Recommendations Use this protocol only with correctly paired tilted and untilted micrographs. Before picking, inspect the micrograph pairs to verify that they correspond to the same field of view and that the tilt geometry is sensible. Pick particles that can be reliably identified in both views. Avoid ambiguous particles, overlapping particles, strong contamination, carbon edges, and areas where the tilted view is too degraded to identify the same particle. Choose a particle size or extraction box large enough for the tilted view, not only for the untilted view. After picking, inspect the paired coordinates before extraction. Errors in pairing can seriously affect downstream tilted-pair workflows. If several picking sessions are needed, keep track of the different output coordinate-pair objects generated by the protocol. ## Final Perspective Tilt Pairs Particle Picking is the coordinate-generation step for tilted-pair cryo-EM workflows. For biological users, its main role is to define which particles correspond between untilted and tilted micrographs. This correspondence is the foundation for subsequent extraction and analysis of particle pairs. Good tilted-pair picking requires both accurate particle identification and careful preservation of the pairing relationship. When done correctly, it provides the geometrical information needed for downstream protocols that use tilt-pair data. """ _label = 'tilt pairs particle picking' def __init__(self, **args): ProtParticlePicking.__init__(self, **args) # The following attribute is only for testing self.importFolder = String(args.get('importFolder', None)) #--------------- DEFINE param functions --------------------------------- def _defineParams(self, form): form.addSection(label='Input') form.addParam('inputMicrographsTiltedPair', params.PointerParam, pointerClass='MicrographsTiltPair', label="Micrographs tilt pair", help='Select the MicrographsTiltPair ') #----------- INSERT steps functions ---------------------------------- def _insertAllSteps(self): """ The Particle Picking process is realized for a pair of set of micrographs """ self.micsFn = self._getPath('input_micrographs.xmd') # Convert input into xmipp Metadata format self._insertFunctionStep('convertInputStep') # Launch Particle Picking GUI if not self.importFolder.hasValue(): self._insertFunctionStep('launchParticlePickGUIStep', interactive=True) else: # This is only used for test purposes self._insertFunctionStep('_importFromFolderStep') #------------------- STEPS functions -----------------------------------
[docs] def convertInputStep(self): micTiltPairs = self.inputMicrographsTiltedPair.get() # Get the converted input micrographs in Xmipp format convert.writeSetOfMicrographsPairs(micTiltPairs.getUntilted(), micTiltPairs.getTilted(), self.micsFn)
[docs] def launchParticlePickGUIStep(self): process = launchTiltPairPickerGUI(self.micsFn, self._getExtraPath(), self) process.wait()
def _importFromFolderStep(self): """ This function will copy Xmipp .pos files for simulating a particle picking run...this is only for testing purposes. """ extraDir = self._getExtraPath() for f in pwutils.getFiles(self.importFolder.get()): pwutils.copyFile(f, extraDir) self.registerCoords(extraDir, readFromExtra=True) #--------------------------- INFO functions -------------------------------------------- def _citations(self): return [] #--------------------------- UTILS functions ------------------------------------------- def __str__(self): """ String representation of a Particle Picking Tilt run """ outputs = self.getOutputsSize() if outputs == 0: msg = "No particles picked yet." elif outputs == 1: picked = self.getCoords().getSize() mics = self.inputMicrographsTiltedPair.get().getTilted().getSize() msg = "Number of particles picked: %d " % picked msg += "(from %d micrographs)" % mics else: msg = 'Number of outputs: %d' % outputs return msg
[docs] def getInputMicrographs(self): return self.inputMicrographsTiltedPair.get().getTilted()
[docs] def getCoords(self): return self.getCoordsTiltPair()
def _summary(self): summary = [] if self.getInputMicrographs() is not None: summary.append("Number of input micrographs: %d" % self.getInputMicrographs().getSize()) if self.getOutputsSize() >= 1: for key, output in self.iterOutputAttributes(CoordinatesTiltPair): summary.append("*%s:*" % key) summary.append(" Particles pairs picked: %d" % output.getSize()) summary.append(" Particle size: %d \n" % output.getBoxSize()) else: summary.append("Output tilpairs not ready yet.") return summary def __getOutputSuffix(self): maxCounter = -1 for attrName, _ in self.iterOutputAttributes(CoordinatesTiltPair): suffix = attrName.replace('outputCoordinatesTiltPair', '') try: counter = int(suffix) except: counter = 1 # when there is not number assume 1 maxCounter = max(counter, maxCounter) return str(maxCounter+1) if maxCounter > 0 else '' # empty if not outputs def _getBoxSize(self): """ Redefine this function to set a specific box size to the output coordinates untilted and tilted. """ return None def _readCoordinates(self, coordsDir, suffix=''): micTiltPairs = self.inputMicrographsTiltedPair.get() uSuffix = 'Untilted' + suffix tSuffix = 'Tilted' + suffix uSet = micTiltPairs.getUntilted() tSet = micTiltPairs.getTilted() # Create Untilted and Tilted SetOfCoordinates uCoordSet = self._createSetOfCoordinates(uSet, suffix=uSuffix) convert.readSetOfCoordinates(coordsDir, uSet, uCoordSet) uCoordSet.write() tCoordSet = self._createSetOfCoordinates(tSet, suffix=tSuffix) convert.readSetOfCoordinates(coordsDir, tSet, tCoordSet) tCoordSet.write() boxSize = self._getBoxSize() if boxSize: uCoordSet.setBoxSize(boxSize) tCoordSet.setBoxSize(boxSize) return uCoordSet, tCoordSet def _readAngles(self, micsFn, suffix=''): # Read Angles from input micrographs anglesSet = self._createSetOfAngles(suffix=suffix) convert.readAnglesFromMicrographs(micsFn, anglesSet) anglesSet.write() return anglesSet
[docs] def registerCoords(self, coordsDir, store=True, readFromExtra=False): micTiltPairs = self.inputMicrographsTiltedPair.get() suffix = self.__getOutputSuffix() uCoordSet, tCoordSet = self._readCoordinates(coordsDir, suffix) if readFromExtra: micsFn = self._getExtraPath('input_micrographs.xmd') else: micsFn = self._getPath('input_micrographs.xmd') anglesSet = self._readAngles(micsFn, suffix) # Create CoordinatesTiltPair object outputset = self._createCoordinatesTiltPair(micTiltPairs, uCoordSet, tCoordSet, anglesSet, suffix) summary = self.getSummary(outputset) outputset.setObjComment(summary) outputName = 'outputCoordinatesTiltPair' + suffix outputs = {outputName: outputset} self._defineOutputs(**outputs) self._defineSourceRelation(self.inputMicrographsTiltedPair, outputset) if store: self._store()