Source code for xmipp3.convert.convert

# **************************************************************************
# *
# * Authors:     J.M. De la Rosa Trevin (jmdelarosa@cnb.csic.es)
# *              Laura del Cano (ldelcano@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'
# *
# **************************************************************************
"""
This module contains converter functions that will serve to:
1. Write from base classes to Xmipp specific files
2. Read from Xmipp files to base classes
"""

import os
from typing import Union
from os.path import join, exists
from collections import OrderedDict
try:
    from itertools import izip
except ImportError:
    izip = zip
import numpy as np

from pyworkflow.utils import replaceBaseExt
from pyworkflow.utils.path import cleanPath
from pyworkflow.object import ObjectWrap, String, Float, Integer, Object
from pwem.constants import (NO_INDEX, ALIGN_NONE, ALIGN_PROJ, ALIGN_2D,
                            ALIGN_3D)
from pwem.objects import (Angles, Coordinate, Micrograph, Volume, Particle,
                          MovieParticle, CTFModel, Acquisition, SetOfParticles,
                          Class3D, SetOfVolumes, Movie, SetOfMovies, Transform)
from pwem.emlib.image import ImageHandler
import pwem.emlib.metadata as md

from xmipp3.base import getLabelPythonType, iterMdRows

from pwem import emlib

[docs]def prefixAttribute(attribute): return '_xmipp_%s' % attribute
if not getattr(emlib, "GHOST_ACTIVATED", False): """ Some of MDL may not exist when Ghost is activated """ # This dictionary will be used to map # between CTFModel properties and Xmipp labels ACQUISITION_DICT = OrderedDict([ ("_amplitudeContrast", emlib.MDL_CTF_Q0), ("_sphericalAberration", emlib.MDL_CTF_CS), ("_voltage", emlib.MDL_CTF_VOLTAGE) ]) COOR_DICT = OrderedDict([ ("_x", emlib.MDL_XCOOR), ("_y", emlib.MDL_YCOOR) ]) COOR_EXTRA_LABELS = [ # Additional autopicking-related metadata md.RLN_PARTICLE_AUTOPICK_FOM, md.RLN_PARTICLE_CLASS, md.RLN_ORIENT_PSI, emlib.MDL_GOOD_REGION_SCORE ] CTF_DICT = OrderedDict([ ("_defocusU", emlib.MDL_CTF_DEFOCUSU), ("_defocusV", emlib.MDL_CTF_DEFOCUSV), ("_defocusAngle", emlib.MDL_CTF_DEFOCUS_ANGLE), ("_resolution", emlib.MDL_CTF_CRIT_MAXFREQ), ("_fitQuality", emlib.MDL_CTF_CRIT_FITTINGSCORE) ]) # TODO: remove next dictionary when all # cTFmodel has resolution and fitQuality CTF_DICT_NORESOLUTION = OrderedDict([ ("_defocusU", emlib.MDL_CTF_DEFOCUSU), ("_defocusV", emlib.MDL_CTF_DEFOCUSV), ("_defocusAngle", emlib.MDL_CTF_DEFOCUS_ANGLE) ]) CTF_PSD_DICT = OrderedDict([ ("_psdFile", emlib.MDL_PSD), (prefixAttribute("enhanced_psd"), emlib.MDL_PSD_ENHANCED), (prefixAttribute("ctfmodel_quadrant"), emlib.MDL_IMAGE1), (prefixAttribute("ctfmodel_halfplane"), emlib.MDL_IMAGE1) ]) CTF_EXTRA_LABELS = [ emlib.MDL_CTF_CA, emlib.MDL_CTF_ENERGY_LOSS, emlib.MDL_CTF_LENS_STABILITY, emlib.MDL_CTF_CONVERGENCE_CONE, emlib.MDL_CTF_LONGITUDINAL_DISPLACEMENT, emlib.MDL_CTF_TRANSVERSAL_DISPLACEMENT, emlib.MDL_CTF_K, emlib.MDL_CTF_BG_GAUSSIAN_K, emlib.MDL_CTF_BG_GAUSSIAN_SIGMAU, emlib.MDL_CTF_BG_GAUSSIAN_SIGMAV, emlib.MDL_CTF_BG_GAUSSIAN_CU, emlib.MDL_CTF_BG_GAUSSIAN_CV, emlib.MDL_CTF_BG_SQRT_K, emlib.MDL_CTF_BG_SQRT_U, emlib.MDL_CTF_BG_SQRT_V, emlib.MDL_CTF_BG_SQRT_ANGLE, emlib.MDL_CTF_BG_BASELINE, emlib.MDL_CTF_BG_GAUSSIAN2_K, emlib.MDL_CTF_BG_GAUSSIAN2_SIGMAU, emlib.MDL_CTF_BG_GAUSSIAN2_SIGMAV, emlib.MDL_CTF_BG_GAUSSIAN2_CU, emlib.MDL_CTF_BG_GAUSSIAN2_CV, emlib.MDL_CTF_BG_GAUSSIAN2_ANGLE, emlib.MDL_CTF_CRIT_FITTINGCORR13, emlib.MDL_CTF_CRIT_ICENESS, emlib.MDL_CTF_VPP_RADIUS, emlib.MDL_CTF_DOWNSAMPLE_PERFORMED, emlib.MDL_CTF_CRIT_PSDVARIANCE, emlib.MDL_CTF_CRIT_PSDPCA1VARIANCE, emlib.MDL_CTF_CRIT_PSDPCARUNSTEST, emlib.MDL_CTF_CRIT_FIRSTZEROAVG, emlib.MDL_CTF_CRIT_DAMPING, emlib.MDL_CTF_CRIT_FIRSTZERORATIO, emlib.MDL_CTF_CRIT_PSDCORRELATION90, emlib.MDL_CTF_CRIT_PSDRADIALINTEGRAL, emlib.MDL_CTF_CRIT_NORMALITY, # In xmipp the ctf also contains acquisition information emlib.MDL_CTF_Q0, emlib.MDL_CTF_CS, emlib.MDL_CTF_VOLTAGE, emlib.MDL_CTF_SAMPLING_RATE ] # TODO: remove next dictionary when all # cTFmodel has resolution and fitquality CTF_EXTRA_LABELS_PLUS_RESOLUTION = [ emlib.MDL_CTF_CA, emlib.MDL_CTF_ENERGY_LOSS, emlib.MDL_CTF_LENS_STABILITY, emlib.MDL_CTF_CONVERGENCE_CONE, emlib.MDL_CTF_LONGITUDINAL_DISPLACEMENT, emlib.MDL_CTF_TRANSVERSAL_DISPLACEMENT, emlib.MDL_CTF_K, emlib.MDL_CTF_BG_GAUSSIAN_K, emlib.MDL_CTF_BG_GAUSSIAN_SIGMAU, emlib.MDL_CTF_BG_GAUSSIAN_SIGMAV, emlib.MDL_CTF_BG_GAUSSIAN_CU, emlib.MDL_CTF_BG_GAUSSIAN_CV, emlib.MDL_CTF_BG_SQRT_K, emlib.MDL_CTF_BG_SQRT_U, emlib.MDL_CTF_BG_SQRT_V, emlib.MDL_CTF_BG_SQRT_ANGLE, emlib.MDL_CTF_BG_BASELINE, emlib.MDL_CTF_BG_GAUSSIAN2_K, emlib.MDL_CTF_BG_GAUSSIAN2_SIGMAU, emlib.MDL_CTF_BG_GAUSSIAN2_SIGMAV, emlib.MDL_CTF_BG_GAUSSIAN2_CU, emlib.MDL_CTF_BG_GAUSSIAN2_CV, emlib.MDL_CTF_BG_GAUSSIAN2_ANGLE, emlib.MDL_CTF_CRIT_MAXFREQ, # ### emlib.MDL_CTF_CRIT_FITTINGSCORE, # ### emlib.MDL_CTF_CRIT_FITTINGCORR13, emlib.MDL_CTF_CRIT_ICENESS, emlib.MDL_CTF_DOWNSAMPLE_PERFORMED, emlib.MDL_CTF_CRIT_PSDVARIANCE, emlib.MDL_CTF_CRIT_PSDPCA1VARIANCE, emlib.MDL_CTF_CRIT_PSDPCARUNSTEST, emlib.MDL_CTF_CRIT_FIRSTZEROAVG, emlib.MDL_CTF_CRIT_DAMPING, emlib.MDL_CTF_CRIT_FIRSTZERORATIO, emlib.MDL_CTF_CRIT_PSDCORRELATION90, emlib.MDL_CTF_CRIT_PSDRADIALINTEGRAL, emlib.MDL_CTF_CRIT_NORMALITY, # In xmipp the ctf also contains acquisition information emlib.MDL_CTF_Q0, emlib.MDL_CTF_CS, emlib.MDL_CTF_VOLTAGE, emlib.MDL_CTF_SAMPLING_RATE, emlib.MDL_CTF_VPP_RADIUS, ] # Some extra labels to take into account the zscore IMAGE_EXTRA_LABELS = [ emlib.MDL_ZSCORE, emlib.MDL_ZSCORE_HISTOGRAM, emlib.MDL_ZSCORE_RESMEAN, emlib.MDL_ZSCORE_RESVAR, emlib.MDL_ZSCORE_RESCOV, emlib.MDL_ZSCORE_SHAPE1, emlib.MDL_ZSCORE_SHAPE2, emlib.MDL_ZSCORE_SNR1, emlib.MDL_ZSCORE_SNR2, emlib.MDL_CUMULATIVE_SSNR, emlib.MDL_PARTICLE_ID, emlib.MDL_FRAME_ID, emlib.MDL_SCORE_BY_VAR, emlib.MDL_SCORE_BY_GINI, emlib.MDL_ZSCORE_DEEPLEARNING1 ] ANGLES_DICT = OrderedDict([ ("_angleY", emlib.MDL_ANGLE_Y), ("_angleY2", emlib.MDL_ANGLE_Y2), ("_angleTilt", emlib.MDL_ANGLE_TILT) ]) ALIGNMENT_DICT = OrderedDict([ (prefixAttribute("shiftX"), emlib.MDL_SHIFT_X), (prefixAttribute("shiftY"), emlib.MDL_SHIFT_Y), (prefixAttribute("shiftZ"), emlib.MDL_SHIFT_Z), (prefixAttribute("flip"), emlib.MDL_FLIP), (prefixAttribute("anglePsi"), emlib.MDL_ANGLE_PSI), (prefixAttribute("angleRot"), emlib.MDL_ANGLE_ROT), (prefixAttribute("angleTilt"), emlib.MDL_ANGLE_TILT), ])
[docs]def objectToRow(obj, row, attrDict, extraLabels=[]): """ This function will convert an EMObject into a Row. Params: obj: the EMObject instance (input) row: the Row instance (output) -see emlib.metadata.utils.Row()- attrDict: dictionary with the map between obj attributes(keys) and row MDLabels in Xmipp (values). extraLabels: a list with extra labels that could be included as _xmipp_labelName """ if obj.isEnabled(): enabled = 1 else: enabled = -1 row.setValue(emlib.MDL_ENABLED, enabled) for attr, label in attrDict.items(): if hasattr(obj, attr): valueType = getLabelPythonType(label) value = getattr(obj, attr).get() try: row.setValue(label, valueType(value)) except Exception as e: print(e) print("Problems found converting metadata: ") print("Label id = %s" % label) print("Attribute = %s" % attr) print("Value = %s" % value) print("Value type = %s" % valueType) raise e row.setValue(label, valueType(getattr(obj, attr).get())) attrLabels = attrDict.values() for label in extraLabels: attrName = prefixAttribute(emlib.label2Str(label)) if label not in attrLabels and hasattr(obj, attrName): value = obj.getAttributeValue(attrName) row.setValue(label, value)
[docs]def rowToObject(row, obj, attrDict, extraLabels=[]): """ This function will convert from a Row to an EMObject. Params: row: the Row instance (input) -see emlib.metadata.utils.Row()- obj: the EMObject instance (output) attrDict: dictionary with the map between obj attributes(keys) and row MDLabels in Xmipp (values). extraLabels: a list with extra labels that could be included as _xmipp_labelName """ obj.setEnabled(row.getValue(emlib.MDL_ENABLED, 1) > 0) for attr, label in attrDict.items(): value = row.getValue(label) if not hasattr(obj, attr): setattr(obj, attr, ObjectWrap(value)) else: getattr(obj, attr).set(value) attrLabels = attrDict.values() for label in extraLabels: if label not in attrLabels and row.hasLabel(label): labelStr = emlib.label2Str(label) setattr(obj, prefixAttribute(labelStr), row.getValueAsObject(label))
[docs]def setXmippAttributes(obj, objRow, *labels, **kwargs): """ Set the labels attribute(s) to obj from a objRow (those not basic ones). More than one label can be set and default can also be a list of same number of entries. The new attribute will be named _xmipp_LabelName and the datatype will be set correctly. If obj doesn't contain a label and a default value is in kwargs, it's set in its corresponding Scipion datatype. """ defaults = kwargs.get('default', None) for idx, label in enumerate(labels): if objRow.containsLabel(label): value = objRow.getValueAsObject(label) else: if isinstance(defaults, list): value = defaults[idx] else: value = defaults value = getScipionObj(value) if value is not None: setXmippAttribute(obj, label, value)
[docs]def getScipionObj(value): if isinstance(value, Object): return value elif isinstance(value, int): return Integer(value) elif isinstance(value, float): return Float(value) elif isinstance(value, str): return String(value) else: return None
[docs]def setXmippAttribute(obj, label, value): """ Sets an attribute of an object prefixing it with xmipp""" setattr(obj, prefixAttribute(emlib.label2Str(label)), value)
[docs]def getXmippAttribute(obj, label, default=None): """ Sets an attribute of an object prefixing it with xmipp""" return getattr(obj, prefixAttribute(emlib.label2Str(label)), default)
[docs]def rowFromMd(mdIn, objId): row = md.Row() row.readFromMd(mdIn, objId) return row
def _containsAll(row, labels): """ Check if the labels (values) in labelsDict are present in the row. """ values = labels.values() if isinstance(labels, dict) else labels return all(row.containsLabel(l) for l in values) def _containsAny(row, labels): """ Check if the labels (values) in labelsDict are present in the row. """ values = labels.values() if isinstance(labels, dict) else labels return any(row.containsLabel(l) for l in values) def _rowToObjectFunc(obj): """ From a given object, return the function rowTo{OBJECT_CLASS_NAME}. """ return globals()['rowTo' + obj.getClassName()] def _readSetFunc(obj): """ From a given object, return the function read{OBJECT_CLASS_NAME}. """ return globals()['read' + obj.getClassName()]
[docs]def locationToXmipp(index, filename): """ Convert an index and filename location to a string with @ as expected in Xmipp. """ return ImageHandler.locationToXmipp((index, filename))
[docs]def fixVolumeFileName(image): """ This method will add :mrc to .mrc volumes because for mrc format is not possible to distinguish between 3D volumes and 2D stacks. """ return ImageHandler.fixXmippVolumeFileName(image)
[docs]def getMovieFileName(movie): """ Add the :mrcs or :ems extensions to movie files to be recognized by Xmipp as proper stack files. """ fn = movie.getFileName() if fn.endswith('.mrc'): fn += ':mrcs' elif fn.endswith('.em'): fn += ':ems' return fn
[docs]def getImageLocation(image): return ImageHandler.locationToXmipp(image)
[docs]def xmippToLocation(xmippFilename): """ Return a location (index, filename) given a Xmipp filename with the index@filename structure. """ if '@' in xmippFilename: return emlib.FileName(xmippFilename).decompose() else: return NO_INDEX, str(xmippFilename)
[docs]def setObjId(obj, mdRow, label=emlib.MDL_ITEM_ID): if mdRow.containsLabel(label): obj.setObjId(mdRow.getValue(label)) else: obj.setObjId(None)
[docs]def setRowId(mdRow, obj, label=emlib.MDL_ITEM_ID): mdRow.setValue(label, int(obj.getObjId()))
[docs]def micrographToCTFParam(mic, ctfparam): """ This function is used to convert a Micrograph object to the .ctfparam metadata needed by some Xmipp programs. If the micrograph already comes from xmipp, the ctfparam will be returned, if not, the new file. """ ctf = mic.getCTF() if hasattr(ctf, '_xmippMd'): return ctf._xmippMd.get() mdCtf = emlib.MetaData() mdCtf.setColumnFormat(False) row = md.Row() ctfModelToRow(ctf, row) acquisitionToRow(mic.getAcquisition(), row) row.writeToMd(mdCtf, mdCtf.addObject()) mdCtf.write(ctfparam) return ctfparam
[docs]def imageToRow(img, imgRow, imgLabel, **kwargs): # Provide a hook to be used if something is needed to be # done for special cases before converting image to row preprocessImageRow = kwargs.get('preprocessImageRow', None) if preprocessImageRow: preprocessImageRow(img, imgRow) setRowId(imgRow, img) # Set the id in the metadata as MDL_ITEM_ID index, filename = img.getLocation() fn = locationToXmipp(index, filename) imgRow.setValue(imgLabel, fn) if kwargs.get('writeCtf', True) and img.hasCTF(): ctfModelToRow(img.getCTF(), imgRow) # alignment is mandatory at this point, it shoud be check # and detected defaults if not passed at readSetOf.. level alignType = kwargs.get('alignType') if alignType != ALIGN_NONE: alignmentToRow(img.getTransform(), imgRow, alignType) if kwargs.get('writeAcquisition', True) and img.hasAcquisition(): acquisitionToRow(img.getAcquisition(), imgRow) # Write all extra labels to the row objectToRow(img, imgRow, {}, extraLabels=IMAGE_EXTRA_LABELS) # Provide a hook to be used if something is needed to be # done for special cases before converting image to row postprocessImageRow = kwargs.get('postprocessImageRow', None) if postprocessImageRow: postprocessImageRow(img, imgRow)
[docs]def rowToImage(imgRow, imgLabel, imgClass, **kwargs): """ Create an Image from a row of a metadata. """ img = imgClass() # Provide a hook to be used if something is needed to be # done for special cases before converting image to row preprocessImageRow = kwargs.get('preprocessImageRow', None) if preprocessImageRow: preprocessImageRow(img, imgRow) # Decompose Xmipp filename index, filename = xmippToLocation(imgRow.getValue(imgLabel)) img.setLocation(index, filename) if imgRow.containsLabel(emlib.MDL_REF): img.setClassId(imgRow.getValue(emlib.MDL_REF)) elif imgRow.containsLabel(emlib.MDL_REF3D): img.setClassId(imgRow.getValue(emlib.MDL_REF3D)) if kwargs.get('readCtf', True): img.setCTF(rowToCtfModel(imgRow)) # alignment is mandatory at this point, it shoud be check # and detected defaults if not passed at readSetOf.. level alignType = kwargs.get('alignType') if alignType != ALIGN_NONE: img.setTransform(rowToAlignment(imgRow, alignType)) if kwargs.get('readAcquisition', True): img.setAcquisition(rowToAcquisition(imgRow)) if kwargs.get('magnification', None): img.getAcquisition().setMagnification(kwargs.get("magnification")) setObjId(img, imgRow) # Read some extra labels rowToObject(imgRow, img, {}, extraLabels=IMAGE_EXTRA_LABELS + kwargs.get('extraLabels', [])) # Provide a hook to be used if something is needed to be # done for special cases before converting image to row postprocessImageRow = kwargs.get('postprocessImageRow', None) if postprocessImageRow: postprocessImageRow(img, imgRow) return img
[docs]def micrographToRow(mic, micRow, **kwargs): """ Set labels values from Micrograph mic to md row. """ imageToRow(mic, micRow, imgLabel=emlib.MDL_MICROGRAPH, **kwargs)
[docs]def rowToMicrograph(micRow, **kwargs): """ Create a Micrograph object from a row of Xmipp metadata. """ return rowToImage(micRow, emlib.MDL_MICROGRAPH, Micrograph, **kwargs)
[docs]def volumeToRow(vol, volRow, **kwargs): """ Set labels values from Micrograph mic to md row. """ imageToRow(vol, volRow, imgLabel=emlib.MDL_IMAGE, writeAcquisition=False, **kwargs)
[docs]def rowToVolume(volRow, **kwargs): """ Create a Volume object from a row of Xmipp metadata. """ return rowToImage(volRow, emlib.MDL_IMAGE, Volume, **kwargs)
[docs]def coordinateToRow(coord, coordRow, copyId=True): """ Set labels values from Coordinate coord to md row. """ if copyId: setRowId(coordRow, coord) objectToRow(coord, coordRow, COOR_DICT, extraLabels=COOR_EXTRA_LABELS) if coord.getMicId(): coordRow.setValue(emlib.MDL_MICROGRAPH, str(coord.getMicId()))
[docs]def rowToCoordinate(coordRow): """ Create a Coordinate from a row of a metadata. """ # Check that all required labels are present in the row if _containsAll(coordRow, COOR_DICT): coord = Coordinate() rowToObject(coordRow, coord, COOR_DICT, extraLabels=COOR_EXTRA_LABELS) # Setup the micId if is integer value try: coord.setMicId(int(coordRow.getValue(emlib.MDL_MICROGRAPH_ID))) except Exception: pass else: coord = None return coord
def _rowToParticle(partRow, particleClass, **kwargs): """ Create a Particle from a row of a metadata. """ # Since postprocessImage is intended to be after the object is # setup, we need to intercept it here and call it at the end postprocessImageRow = kwargs.get('postprocessImageRow', None) if postprocessImageRow: del kwargs['postprocessImageRow'] img = rowToImage(partRow, emlib.MDL_IMAGE, particleClass, **kwargs) img.setCoordinate(rowToCoordinate(partRow)) # copy micId if available # if not copy micrograph name if available try: if partRow.hasLabel(emlib.MDL_MICROGRAPH_ID): img.setMicId(partRow.getValue(emlib.MDL_MICROGRAPH_ID)) # elif partRow.hasLabel(emlib.MDL_MICROGRAPH): # micName = partRow.getValue(emlib.MDL_MICROGRAPH) # img._micrograph = micName # print("setting micname as %s" % micName) # img.printAll() # print("getAttributes1 %s" % img._micrograph) # print("getAttributes2 %s" % getattr(img, "_micrograph", 'kk') # else: # print("WARNING: No micname") except Exception as e: print("Warning:", e.message) if postprocessImageRow: postprocessImageRow(img, partRow) return img
[docs]def rowToParticle(partRow, **kwargs): return _rowToParticle(partRow, Particle, **kwargs)
[docs]def rowToMovieParticle(partRow, **kwargs): return _rowToParticle(partRow, MovieParticle, **kwargs)
[docs]def particleToRow(part, partRow, **kwargs): """ Set labels values from Particle to md row. """ imageToRow(part, partRow, emlib.MDL_IMAGE, **kwargs) coord = part.getCoordinate() if coord is not None: coordinateToRow(coord, partRow, copyId=False) if part.hasMicId(): partRow.setValue(emlib.MDL_MICROGRAPH_ID, int(part.getMicId())) partRow.setValue(emlib.MDL_MICROGRAPH, str(part.getMicId()))
[docs]def rowToClass(classRow, classItem): """ Method base to create a class2D, class3D or classVol from a row of a metadata """ setObjId(classItem, classRow, label=emlib.MDL_REF) if classRow.containsLabel(emlib.MDL_IMAGE): index, filename = xmippToLocation(classRow.getValue(emlib.MDL_IMAGE)) img = classItem.REP_TYPE() # class id should be set previously from MDL_REF # classItem.setObjId(classRow.getObjId()) # img.copyObjId(classItem) img.setLocation(index, filename) img.setSamplingRate(classItem.getSamplingRate()) classItem.setRepresentative(img) return classItem
[docs]def class2DToRow(class2D, classRow): """ Set labels values from Class2D to md row. """ if class2D.hasRepresentative(): index, filename = class2D.getRepresentative().getLocation() fn = locationToXmipp(index, filename) classRow.setValue(emlib.MDL_IMAGE, fn) n = int(len(class2D)) classRow.setValue(emlib.MDL_CLASS_COUNT, n) classRow.setValue(emlib.MDL_REF, int(class2D.getObjId())) classRow.setValue(emlib.MDL_ITEM_ID, int(class2D.getObjId()))
[docs]def ctfModelToRow(ctfModel, ctfRow): """ Set labels values from ctfModel to md row. """ # TODO: compatibility check remove eventually if ctfModel.hasResolution(): objectToRow(ctfModel, ctfRow, CTF_DICT, extraLabels=CTF_EXTRA_LABELS) if ctfModel.hasPhaseShift(): ctfRow.setValue(emlib.MDL_CTF_PHASE_SHIFT, ctfModel.getPhaseShift()) else: objectToRow(ctfModel, ctfRow, CTF_DICT_NORESOLUTION, extraLabels=CTF_EXTRA_LABELS)
[docs]def defocusGroupSetToRow(defocusGroup, defocusGroupRow): """ Set labels values from ctfModel to md row. """ objectToRow(defocusGroup, defocusGroupRow, CTF_DICT)
[docs]def setPsdFiles(ctfModel, ctfRow): """ Set the PSD files of CTF estimation related to this ctfModel. The values will be read from the ctfRow if present. """ for attr, label in CTF_PSD_DICT.items(): if ctfRow.containsLabel(label): setattr(ctfModel, attr, String(ctfRow.getValue(label)))
[docs]def rowToCtfModel(ctfRow): """ Create a CTFModel from a row of a metadata. """ # Check if the row has CTF values, this could be called from a xmipp # particles metadata if _containsAll(ctfRow, CTF_DICT_NORESOLUTION): # for compatibility reason ignore resolution and fitQuality # Instantiate Scipion CTF Model ctfModel = CTFModel() # Case for metadata coming with Xmipp resolution label # Populate Scipion CTF from metadata row (using mapping dictionary # plus extra labels if ctfRow.hasLabel(md.MDL_CTF_PHASE_SHIFT): ctfModel.setPhaseShift(ctfRow.getValue(md.MDL_CTF_PHASE_SHIFT, 0)) if ctfRow.containsLabel(emlib.label2Str(emlib.MDL_CTF_CRIT_MAXFREQ)): rowToObject(ctfRow, ctfModel, CTF_DICT, extraLabels=CTF_EXTRA_LABELS) else: rowToObject(ctfRow, ctfModel, CTF_DICT_NORESOLUTION, extraLabels=CTF_EXTRA_LABELS_PLUS_RESOLUTION) # Standarize defocus values ctfModel.standardize() # Set psd file names setPsdFiles(ctfModel, ctfRow) # ctfModel.setPhaseShift(0.0) # for consistency with ctfModel else: ctfModel = None return ctfModel
[docs]def acquisitionToRow(acquisition, ctfRow): """ Set labels values from acquisition to md row. """ objectToRow(acquisition, ctfRow, ACQUISITION_DICT)
[docs]def rowToAcquisition(acquisitionRow): """ Create an acquisition from a row of a metadata. """ if _containsAll(acquisitionRow, ACQUISITION_DICT): acquisition = Acquisition() rowToObject(acquisitionRow, acquisition, ACQUISITION_DICT) else: acquisition = None return acquisition
[docs]def readSetOfMicrographs(filename, micSet, **kwargs): readSetOfImages(filename, micSet, rowToMicrograph, **kwargs)
[docs]def writeSetOfMicrographs(micSet, filename, blockName='Micrographs', **kwargs): writeSetOfImages(micSet, filename, micrographToRow, blockName, **kwargs)
[docs]def readSetOfVolumes(filename, volSet, **kwargs): readSetOfImages(filename, volSet, rowToVolume, **kwargs)
[docs]def writeSetOfVolumes(volSet, filename, blockName='Volumes', **kwargs): writeSetOfImages(volSet, filename, volumeToRow, blockName, **kwargs)
[docs]def mdToCTFModel(mdIn, mic): ctfRow = rowFromMd(mdIn, mdIn.firstObject()) ctfObj = rowToCtfModel(ctfRow) setXmippAttributes(ctfObj, ctfRow, emlib.MDL_CTF_CRIT_NONASTIGMATICVALIDITY, emlib.MDL_CTF_CRIT_FIRSTMINIMUM_FIRSTZERO_DIFF_RATIO) ctfObj.setMicrograph(mic) return ctfObj
[docs]def readCTFModel(filename, mic): """ Read from Xmipp .ctfparam and create a CTFModel object. """ mdCtf = emlib.MetaData(filename) return mdToCTFModel(mdCtf, mic)
[docs]def openMd(fn, state='Manual'): # We are going to write metadata directy to file to do it faster f = open(fn, 'w') ismanual = state == 'Manual' block = 'data_particles' if ismanual else 'data_particles_auto' s = """# XMIPP_STAR_1 * # data_header loop_ _pickingMicrographState %s %s loop_ _itemId _enabled _xcoor _ycoor _cost _micrographId """ % (state, block) f.write(s) return f
[docs]def writeSetOfCoordinates(posDir, coordSet, ismanual=True, scale=1): state = 'Manual' if ismanual else 'Supervised' writeSetOfCoordinatesWithState(posDir, coordSet, state, scale)
[docs]def writeSetOfCoordinatesWithState(posDir, coordSet, state, scale=1): """ Write a pos file on metadata format for each micrograph on the coordSet. Params: posDir: the directory where the .pos files will be written. coordSet: the SetOfCoordinates that will be read. """ boxSize = coordSet.getBoxSize() or 100 # Create a dictionary with the pos filenames for each micrograph posDict = {} for mic in coordSet.iterMicrographs(): micIndex, micFileName = mic.getLocation() micName = os.path.basename(micFileName) if micIndex != NO_INDEX: micName = '%06d_at_%s' % (micIndex, micName) posFn = join(posDir, replaceBaseExt(micName, "pos")) posDict[mic.getObjId()] = posFn f = None lastMicId = None try: for coord in coordSet.iterItems(orderBy='_micId'): micId = coord.getMicId() if micId != lastMicId: # we need to close previous opened file if f: f.close() f = openMd(posDict[micId], state) lastMicId = micId if scale != 1: x = coord.getX() * scale y = coord.getY() * scale else: x = coord.getX() y = coord.getY() f.write(" %06d 1 %d %d %d %06d\n" % (coord.getObjId(), x, y, 1, micId)) except (FileNotFoundError, PermissionError) as error: print(f"Error occurred: {error}") except Exception as e: print(f"Unexpected error occurred: {e}") finally: if f: f.close() # Write config.xmd metadata configFn = join(posDir, 'config.xmd') writeCoordsConfig(configFn, int(boxSize), state) return posDict.values()
[docs]def writeCoordsConfig(configFn, boxSize, state): """ Write the config.xmd file needed for Xmipp picker. Params: configFn: The filename were to store the configuration. boxSize: the box size in pixels for extraction. state: picker state """ # Write config.xmd metadata print("writeCoordsConfig: state=", state) mdCoord = emlib.MetaData() # Write properties block objId = mdCoord.addObject() mdCoord.setValue(emlib.MDL_PICKING_PARTICLE_SIZE, int(boxSize), objId) mdCoord.setValue(emlib.MDL_PICKING_STATE, state, objId) mdCoord.write('properties@%s' % configFn)
[docs]def writeMicCoordinates(mic, coordList, outputFn, isManual=True, getPosFunc=None): """ Write the pos file as expected by Xmipp with the coordinates of a given micrograph. Params: mic: input micrograph. coordList: list of (x, y) pairs of the mic coordinates. outputFn: output filename for the pos file . isManual: if the coordinates are 'Manual' or 'Supervised' getPosFunc: a function to get the positions from the coordinate, it can be useful for scaling the coordinates if needed. """ if getPosFunc is None: getPosFunc = lambda coord: coord.getPosition() state = 'Manual' if isManual else 'Supervised' f = openMd(outputFn, state) for coord in coordList: x, y = getPosFunc(coord) f.write(" %06d 1 %d %d %d %06d \n" % (coord.getObjId(), x, y, 1, mic.getObjId())) f.close()
[docs]def readSetOfCoordinates(outputDir, micSet, coordSet, readDiscarded=False, scale=1): """ Read from Xmipp .pos files. Params: outputDir: the directory where the .pos files are. It is also expected a file named: config.xmd in this directory where the box size can be read. micSet: the SetOfMicrographs to associate the .pos, which name should be the same of the micrographs. coordSet: the SetOfCoordinates that will be populated. readDiscarded: read only the coordinates with the MDL_ENABLE set at -1 scale: Factor to scale ONLY x,y coordinates, you are supposed to use an appropiate boxsize (you created the set) """ # Read the boxSize from the config.xmd metadata configfile = join(outputDir, 'config.xmd') if exists(configfile): mdCoords = emlib.MetaData('properties@' + join(outputDir, 'config.xmd')) boxSize = mdCoords.getValue(emlib.MDL_PICKING_PARTICLE_SIZE, mdCoords.firstObject()) coordSet.setBoxSize(int(boxSize)) #Only coordinates x,y are scaled, you are supposed to use an appropiate boxsize for mic in micSet: posFile = join(outputDir, replaceBaseExt(mic.getFileName(), 'pos')) readCoordinates(mic, posFile, coordSet, outputDir, readDiscarded, scale=scale) coordSet._xmippMd = String(outputDir)
[docs]def readCoordinates(mic, fileName, coordsSet, outputDir, readDiscarded=False, scale=1): posMd = readPosCoordinates(fileName, readDiscarded) # TODO: CHECK IF THIS LABEL IS STILL NECESSARY posMd.addLabel(md.MDL_ITEM_ID) for objId in posMd: # When do an union of two metadatas of coordinates and one of # them doesn't has MDL_ENABLED, the default vaule to is 0, # and its not allowed value. Maybe we need to solve this in xmipp # code. if posMd.getValue(md.MDL_ENABLED, objId) == 0: posMd.setValue(md.MDL_ENABLED, 1, objId) coord = rowToCoordinate(rowFromMd(posMd, objId)) coord.setMicrograph(mic) coord.setX(int(coord.getX()*scale)) coord.setY(int(coord.getY()*scale)) coordsSet.append(coord) posMd.setValue(md.MDL_ITEM_ID, int(coord.getObjId()), objId)
[docs]def readPosCoordinates(posFile, readDiscarded=False): """ Read the coordinates in .pos file and return corresponding metadata. There are two possible blocks with particles: particles: with manual/supervised particles particles_auto: with automatically picked particles. If posFile doesn't exist, the metadata will be empty readDiscarded: read only the coordinates in the particles_auto DB with the MDL_ENABLE set at -1 and a positive cost """ mData = md.MetaData() if exists(posFile): blocks = md.getBlocksInMetaDataFile(posFile) blocksToRead = ['particles_auto'] if readDiscarded \ else ['particles','particles_auto'] for b in blocksToRead: if b in blocks: mdAux = md.MetaData('%(b)s@%(posFile)s' % locals()) mData.unionAll(mdAux) if readDiscarded: for objId in mData: if mData.getValue(md.MDL_COST, objId)>0: enabled=mData.getValue(md.MDL_ENABLED, objId) mData.setValue(md.MDL_ENABLED, -1*enabled, objId) mData.removeDisabled() return mData
[docs]def readSetOfImages(filename, imgSet, rowToFunc, **kwargs): """read from Xmipp image metadata. filename: The metadata filename where the image are. imgSet: the SetOfParticles that will be populated. rowToFunc: this function will be used to convert the row to Object """ imgMd = emlib.MetaData(filename) # By default remove disabled items from metadata # be careful if you need to preserve the original number of items if kwargs.get('removeDisabled', True): imgMd.removeDisabled() # If the type of alignment is not sent through the kwargs # try to deduced from the metadata labels if 'alignType' not in kwargs: imgRow = rowFromMd(imgMd, imgMd.firstObject()) if _containsAny(imgRow, ALIGNMENT_DICT): if imgRow.containsLabel(emlib.MDL_ANGLE_TILT): kwargs['alignType'] = ALIGN_PROJ else: kwargs['alignType'] = ALIGN_2D else: kwargs['alignType'] = ALIGN_NONE if imgMd.size() > 0: for objId in imgMd: imgRow = rowFromMd(imgMd, objId) img = rowToFunc(imgRow, **kwargs) imgSet.append(img) imgSet.setHasCTF(img.hasCTF()) imgSet.setAlignment(kwargs['alignType'])
[docs]def setOfImagesToMd(imgSet, mdIn, imgToFunc, **kwargs): """ This function will fill Xmipp metadata from a SetOfMicrographs Params: imgSet: the set of images to be converted to metadata mdIn: metadata to be filled rowFunc: this function can be used to setup the row before adding to metadata. """ if 'alignType' not in kwargs: kwargs['alignType'] = imgSet.getAlignment() if 'where' in kwargs: where = kwargs['where'] for img in imgSet.iterItems(where=where): objId = mdIn.addObject() imgRow = md.Row() imgToFunc(img, imgRow, **kwargs) imgRow.writeToMd(mdIn, objId) else: for img in imgSet: objId = mdIn.addObject() imgRow = md.Row() imgToFunc(img, imgRow, **kwargs) imgRow.writeToMd(mdIn, objId)
[docs]def readAnglesFromMicrographs(micFile, anglesSet): """ Read the angles from a micrographs Metadata. """ micMd = emlib.MetaData(micFile) # micMd.removeDisabled() for objId in micMd: # TODO I have added the import from pyworkflow.em import Angles # because next line gave an error. I do not know if this # is the right Angle class angles = Angles() row = rowFromMd(micMd, objId) rowToObject(row, angles, ANGLES_DICT) angles.setObjId(micMd.getValue(emlib.MDL_ITEM_ID, objId)) anglesSet.append(angles)
[docs]def writeSetOfImages(imgSet, filename, imgToFunc, blockName='Images', **kwargs): """ This function will write a SetOfImages as a Xmipp metadata. Params: imgSet: the set of images to be written (particles, micrographs or volumes) filename: the filename where to write the metadata. rowFunc: this function can be used to setup the row before adding to metadata. """ mdSet = emlib.MetaData() setOfImagesToMd(imgSet, mdSet, imgToFunc, **kwargs) mdSet.write('%s@%s' % (blockName, filename))
[docs]def readSetOfParticles(filename, partSet, **kwargs): readSetOfImages(filename, partSet, rowToParticle, **kwargs)
[docs]def readSetOfMovieParticles(filename, partSet, **kwargs): readSetOfImages(filename, partSet, rowToMovieParticle, **kwargs)
[docs]def setOfParticlesToMd(imgSet, mdParts, **kwargs): setOfImagesToMd(imgSet, mdParts, particleToRow, **kwargs)
[docs]def setOfMicrographsToMd(imgSet, mdMics, **kwargs): setOfImagesToMd(imgSet, mdMics, micrographToRow, **kwargs)
[docs]def writeSetOfParticles(imgSet, filename, blockName='Particles', **kwargs): writeSetOfImages(imgSet, filename, particleToRow, blockName, **kwargs)
[docs]def writeCTFModel(ctfModel, ctfFile): """ Given a CTFModel object write as Xmipp ctfparam """ mdCtf = emlib.MetaData() objId = mdCtf.addObject() ctfRow = md.Row() ctfModelToRow(ctfModel, ctfRow) ctfRow.writeToMd(mdCtf, objId) mdCtf.setColumnFormat(False) mdCtf.write(ctfFile)
[docs]def writeSetOfCTFs(ctfSet, mdCTF): """ Write a ctfSet on metadata format. Params: ctfSet: the SetOfCTF that will be read. mdCTF: The file where metadata should be written. """ mdCtf = emlib.MetaData() for ctfModel in ctfSet: objId = mdCtf.addObject() ctfRow = md.Row() ctfRow.setValue(emlib.MDL_MICROGRAPH, ctfModel.getMicFile()) if ctfModel.getPsdFile(): ctfRow.setValue(emlib.MDL_PSD, ctfModel.getPsdFile()) ctfModelToRow(ctfModel, ctfRow) ctfRow.writeToMd(mdCtf, objId) mdCtf.write(mdCTF) ctfSet._xmippMd = String(mdCTF)
[docs]def writeSetOfDefocusGroups(defocusGroupSet, fnDefocusGroup): # also metadata """ Write a defocuGroupSet on metadata format. Params: defocusGroupSet: the SetOfDefocus that will be read. fnDefocusGroup: The file where defocusGroup should be written. """ mdDefGr = emlib.MetaData() for defocusGroup in defocusGroupSet: objId = mdDefGr.addObject() defocusGroupRow = md.Row() defocusGroupSetToRow(defocusGroup, defocusGroupRow) defocusGroupRow.setValue(emlib.MDL_CTF_GROUP, defocusGroup.getObjId()) defocusGroupRow.setValue(emlib.MDL_MIN, defocusGroup.getDefocusMin()) defocusGroupRow.setValue(emlib.MDL_MAX, defocusGroup.getDefocusMax()) defocusGroupRow.setValue(emlib.MDL_AVG, defocusGroup.getDefocusAvg()) defocusGroupRow.writeToMd(mdDefGr, objId) mdDefGr.write(fnDefocusGroup) defocusGroupSet._xmippMd = String(fnDefocusGroup)
[docs]def writeSetOfClasses2D(classes2DSet, filename, classesBlock='classes', writeParticles=True): """ This function will write a SetOfClasses2D as Xmipp metadata. Params: classes2DSet: the SetOfClasses2D instance. filename: the filename where to write the metadata. """ classFn = '%s@%s' % (classesBlock, filename) classMd = emlib.MetaData() classMd.write(classFn) # Empty write to ensure the classes is the first # block classRow = md.Row() for class2D in classes2DSet: class2DToRow(class2D, classRow) classRow.writeToMd(classMd, classMd.addObject()) if writeParticles: ref = class2D.getObjId() imagesFn = 'class%06d_images@%s' % (ref, filename) imagesMd = emlib.MetaData() imgRow = md.Row() if class2D.getSize() > 0: for img in class2D: particleToRow(img, imgRow) imgRow.writeToMd(imagesMd, imagesMd.addObject()) imagesMd.write(imagesFn, emlib.MD_APPEND) # Empty write to ensure the classes is the first block classMd.write(classFn, emlib.MD_APPEND)
[docs]def writeSetOfMicrographsPairs(uSet, tSet, filename): """ This function will write a MicrographsTiltPair as Xmipp metadata. Params: uSet: the untilted set of micrographs to be written tSet: the tilted set of micrographs to be written filename: the filename where to write the metadata. """ mdMics = emlib.MetaData() for micU, micT in izip(uSet, tSet): objId = mdMics.addObject() pairRow = md.Row() pairRow.setValue(emlib.MDL_ITEM_ID, int(micU.getObjId())) pairRow.setValue(emlib.MDL_MICROGRAPH, micU.getFileName()) pairRow.setValue(emlib.MDL_MICROGRAPH_TILTED, micT.getFileName()) pairRow.writeToMd(mdMics, objId) mdMics.write(filename)
def __readSetOfClasses(classBaseSet, readSetFunc, classesSet, filename, classesBlock='classes', **kwargs): """ Read a set of classes from an Xmipp metadata with the given convention of a block for classes and another block for each set of images assigned to each class. Params: classesSet: the SetOfClasses object that will be populated. filename: the file path where to read the Xmipp metadata. classesBlock (by default 'classes'): the block name of the classes group in the metadata. """ blocks = emlib.getBlocksInMetaDataFile(filename) classesMd = emlib.MetaData('%s@%s' % (classesBlock, filename)) # Provide a hook to be used if something is needed to be # done for special cases before converting row to class preprocessClass = kwargs.get('preprocessClass', None) postprocessClass = kwargs.get('postprocessClass', None) for objId in classesMd: classItem = classesSet.ITEM_TYPE() classRow = rowFromMd(classesMd, objId) # class id should be set in rowToClass function using MDL_REF classItem = rowToClass(classRow, classItem) classBaseSet.copyInfo(classItem, classesSet.getImages()) if preprocessClass: preprocessClass(classItem, classRow) classesSet.append(classItem) ref = classItem.getObjId() b = 'class%06d_images' % ref if b in blocks: readSetFunc('%s@%s' % (b, filename), classItem, **kwargs) if postprocessClass: postprocessClass(classItem, classRow) # Update with new properties of classItem such as _size classesSet.update(classItem)
[docs]def readSetOfClasses(classesSet, filename, classesBlock='classes', **kwargs): __readSetOfClasses(SetOfParticles, readSetOfParticles, classesSet, filename, classesBlock, **kwargs)
[docs]def readSetOfClasses2D(classes2DSet, filename, classesBlock='classes', **kwargs): """ Just a wrapper to readSetOfClasses. """ readSetOfClasses(classes2DSet, filename, classesBlock, **kwargs)
[docs]def readSetOfClasses3D(classes3DSet, filename, classesBlock='classes', **kwargs): """ Just a wrapper to readSetOfClasses. """ readSetOfClasses(classes3DSet, filename, classesBlock, **kwargs)
[docs]def writeSetOfClassesVol(classesVolSet, filename, classesBlock='classes'): """ This function will write a SetOfClassesVol as Xmipp metadata. Params: classesVolSet: the SetOfClassesVol instance. filename: the filename where to write the metadata. """ classFn = '%s@%s' % (classesBlock, filename) classMd = emlib.MetaData() classMd.write(classFn) # Empty write to ensure the classes is the first # block # FIXME: review implementation of this function since there are syntax # errors classRow = md.Row() for classVol in classesVolSet: # FIXME Where does this method come from classVolToRow(classVol, classRow) classRow.writeToMd(classMd, classMd.addObject()) ref = Class3D.getObjId() imagesFn = 'class%06d_images@%s' % (ref, filename) imagesMd = emlib.MetaData() imgRow = md.Row() for vol in classVol: volumeToRow(vol, imgRow) imgRow.writeToMd(imagesMd, imagesMd.addObject()) imagesMd.write(imagesFn, emlib.MD_APPEND) # Empty write to ensure the classes is the first block classMd.write(classFn, emlib.MD_APPEND)
[docs]def readSetOfClassesVol(classesVolSet, filename, classesBlock='classes', **kwargs): """read from Xmipp image metadata. fnImages: The metadata filename where the particles properties are. imgSet: the SetOfParticles that will be populated. hasCtf: is True if the ctf information exists. """ __readSetOfClasses(SetOfVolumes, readSetOfVolumes, classesVolSet, filename, classesBlock, **kwargs)
[docs]def writeSetOfMovies(moviesSet, filename, moviesBlock='movies'): """ This function will write a SetOfMovies as Xmipp metadata. Params: moviesSet: the SetOfMovies instance. filename: the filename where to write the metadata. """ for movie in moviesSet: ref = movie.getObjId() micrographsFn = 'movie%06d_micrographs@%s' % (ref, filename) micrographsMd = emlib.MetaData() micRow = md.Row() for mic in movie: micrographToRow(mic, micRow) micRow.writeToMd(micrographsMd, micrographsMd.addObject()) micrographsMd.write(micrographsFn, emlib.MD_APPEND)
[docs]def geometryFromMatrix(matrix, inverseTransform): from pwem.convert.transformations import (translation_from_matrix, euler_from_matrix) if inverseTransform: matrix = np.linalg.inv(matrix) shifts = -translation_from_matrix(matrix) else: shifts = translation_from_matrix(matrix) angles = -np.rad2deg(euler_from_matrix(matrix, axes='szyz')) return shifts, angles
[docs]def matrixFromGeometry(shifts, angles, inverseTransform): """ Create the transformation matrix from a given 2D shifts in X and Y...and the 3 euler angles. """ from pwem.convert import euler_matrix radAngles = -np.deg2rad(angles) M = euler_matrix(radAngles[0], radAngles[1], radAngles[2], 'szyz') if inverseTransform: M[:3, 3] = -shifts[:3] M = np.linalg.inv(M) else: M[:3, 3] = shifts[:3] return M
[docs]def rowToAlignment(alignmentRow, alignType): """ is2D == True-> matrix is 2D (2D images alignment) otherwise matrix is 3D (3D volume alignment or projection) invTransform == True -> for xmipp implies projection """ is2D = alignType == ALIGN_2D inverseTransform = alignType == ALIGN_PROJ if _containsAny(alignmentRow, ALIGNMENT_DICT): alignment = Transform() angles = np.zeros(3) shifts = np.zeros(3) flip = alignmentRow.getValue(emlib.MDL_FLIP) shifts[0] = alignmentRow.getValue(emlib.MDL_SHIFT_X, 0.) shifts[1] = alignmentRow.getValue(emlib.MDL_SHIFT_Y, 0.) if not is2D: angles[0] = alignmentRow.getValue(emlib.MDL_ANGLE_ROT, 0.) angles[1] = alignmentRow.getValue(emlib.MDL_ANGLE_TILT, 0.) shifts[2] = alignmentRow.getValue(emlib.MDL_SHIFT_Z, 0.) angles[2] = alignmentRow.getValue(emlib.MDL_ANGLE_PSI, 0.) if flip: angles[1] = angles[1]+180 # tilt + 180 angles[2] = - angles[2] # - psi, COSS: this is mirroring X shifts[0] = -shifts[0] # -x else: psi = alignmentRow.getValue(emlib.MDL_ANGLE_PSI, 0.) rot = alignmentRow.getValue(emlib.MDL_ANGLE_ROT, 0.) if not np.isclose(rot, 0., atol=1e-6 ) and not np.isclose(psi, 0., atol=1e-6): print("HORROR rot and psi are different from zero in 2D case") angles[0] = \ alignmentRow.getValue(emlib.MDL_ANGLE_PSI, 0.)\ + alignmentRow.getValue(emlib.MDL_ANGLE_ROT, 0.) matrix = matrixFromGeometry(shifts, angles, inverseTransform) if flip: if alignType == ALIGN_2D: matrix[0, :2] *= -1. # invert only the first two columns # keep x matrix[2, 2] = -1. # set 3D rot elif alignType == ALIGN_3D: matrix[0, :3] *= -1. # now, invert first line excluding x matrix[3, 3] *= -1. elif alignType == ALIGN_PROJ: pass alignment.setMatrix(matrix) # FIXME: now are also storing the alignment parameters since # the conversions to the Transform matrix have not been extensively # tested. # After this, we should only keep the matrix # for paramName, label in ALIGNMENT_DICT.iter(): # if alignmentRow.hasLabel(label): # setattr(alignment, paramName, # alignmentRow.getValueAsObject(label)) else: alignment = None return alignment
[docs]def alignmentToRow(alignment, alignmentRow, alignType): """ is2D == True-> matrix is 2D (2D images alignment) otherwise matrix is 3D (3D volume alignment or projection) invTransform == True -> for xmipp implies projection -> for xmipp implies alignment """ if alignment is None: return is2D = alignType == ALIGN_2D inverseTransform = alignType == ALIGN_PROJ # only flip is meaninfull if 2D case # in that case the 2x2 determinant is negative flip = False matrix = alignment.getMatrix() if alignType == ALIGN_2D: # get 2x2 matrix and check if negative flip = bool(np.linalg.det(matrix[0:2, 0:2]) < 0) if flip: matrix[0, :2] *= -1. # invert only the first two columns keep x matrix[2, 2] = 1. # set 3D rot else: pass elif alignType == ALIGN_3D: flip = bool(np.linalg.det(matrix[0:3, 0:3]) < 0) if flip: matrix[0, :4] *= -1. # now, invert first line including x matrix[3, 3] = 1. # set 3D rot else: pass else: flip = bool(np.linalg.det(matrix[0:3, 0:3]) < 0) if flip: raise Exception("the det of the transformation matrix is " "negative. This is not a valid transformation " "matrix for Scipion.") shifts, angles = geometryFromMatrix(matrix, inverseTransform) alignmentRow.setValue(emlib.MDL_SHIFT_X, shifts[0]) alignmentRow.setValue(emlib.MDL_SHIFT_Y, shifts[1]) if is2D: angle = angles[0] + angles[2] alignmentRow.setValue(emlib.MDL_ANGLE_PSI, angle) else: # if alignType == ALIGN_3D: alignmentRow.setValue(emlib.MDL_SHIFT_Z, shifts[2]) alignmentRow.setValue(emlib.MDL_ANGLE_ROT, angles[0]) alignmentRow.setValue(emlib.MDL_ANGLE_TILT, angles[1]) alignmentRow.setValue(emlib.MDL_ANGLE_PSI, angles[2]) alignmentRow.setValue(emlib.MDL_FLIP, flip)
[docs]def fillClasses(clsSet, updateClassCallback=None): """ Give an empty SetOfClasses (either 2D or 3D). Iterate over the input images and append to the corresponding class. It is important that each image has the classId properly set. """ clsDict = {} # Dictionary to store the (classId, classSet) pairs inputImages = clsSet.getImages() for img in inputImages: ref = img.getClassId() if ref is None: raise Exception('Particle classId is None!!!') # Register a new class set if the ref was not found. if ref not in clsDict: classItem = clsSet.ITEM_TYPE(objId=ref) rep = clsSet.REP_TYPE() classItem.setRepresentative(rep) clsDict[ref] = classItem classItem.copyInfo(inputImages) classItem.setAcquisition(inputImages.getAcquisition()) if updateClassCallback is not None: updateClassCallback(classItem) clsSet.append(classItem) else: # Try to get the class set given its ref number classItem = clsDict[ref] classItem.append(img) for classItem in clsDict.values(): clsSet.update(classItem)
# FIXME: USE THIS FUNCTION AS THE WAY TO CREATE CLASSES, REMOVE THE NEXT ONE
[docs]def createClassesFromImages2(inputImages, inputMd, classesFn, ClassType, classLabel, alignType, getClassFn=None, preprocessImageRow=None): """ From an intermediate X.xmd file produced by xmipp, create the set of classes in which those images are classified. Params: inputImages: the SetOfImages that were initially classified by relion. inputMd: the filename metadata. classesFn: filename where to write the classes. ClassType: the output type of the classes set ( usually SetOfClass2D or 3D ) classLabel: label that is the class reference. classFnTemplate: the template to get the classes averages filenames iter: the iteration number, just used in Class template """ mdIter = iterMdRows(inputMd) clsDict = {} # Dictionary to store the (classId, classSet) pairs clsSet = ClassType(filename=classesFn) clsSet.setImages(inputImages) hasCtf = inputImages.hasCTF() sampling = inputImages.getSamplingRate() for img, row in izip(inputImages, mdIter): ref = row.getValue(emlib.MDL_REF) if ref is None: raise Exception('MDL_REF not found in metadata: %s' % inputMd) if ref not in clsDict: # Register a new class set if the ref was not # found. classItem = clsSet.ITEM_TYPE(objId=ref) if getClassFn is None: refFn = '' else: refFn = getClassFn(ref) refLocation = xmippToLocation(refFn) rep = clsSet.REP_TYPE() rep.setLocation(refLocation) rep.setSamplingRate(sampling) classItem.setRepresentative(rep) clsDict[ref] = classItem classItem.copyInfo(inputImages) classItem.setAlignment(alignType) classItem.setAcquisition(inputImages.getAcquisition()) clsSet.append(classItem) else: # Try to get the class set given its ref number classItem = clsDict[ref] img.setTransform(rowToAlignment(row, alignType)) classItem.append(img) for classItem in clsDict.values(): clsSet.update(classItem) clsSet.write() return clsSet
# FIXME: remove this function and use previous one
[docs]def createClassesFromImages(inputImages, inputMd, classesFn, ClassType, classLabel, classFnTemplate, alignType, iter, preprocessImageRow=None): """ From an intermediate X.xmd file produced by xmipp, create the set of classes in which those images are classified. Params: inputImages: the SetOfImages that were initially classified by relion. inputMd: the filename metadata. classesFn: filename where to write the classes. ClassType: the output type of the classes set (usually SetOfClass2D or 3D) classLabel: label that is the class reference. classFnTemplate: the template to get the classes averages filenames iter: the iteration number, just used in Class template """ # We assume here that the volumes (classes3d) are in the same folder than # imgsFn # rootDir here is defined to be used expanding locals() if "@" in inputMd: inputFn = inputMd.split('@')[1] tmpDir = os.path.dirname(inputFn) else: tmpDir = os.path.dirname(inputMd) def getClassFn(ref): args = {'rootDir': tmpDir, 'iter': iter, 'ref': ref} return classFnTemplate % args createClassesFromImages2(inputImages, inputMd, classesFn, ClassType, classLabel, alignType, getClassFn=getClassFn, preprocessImageRow=preprocessImageRow)
[docs]def createItemMatrix(item, row, align): item.setTransform(rowToAlignment(row, alignType=align))
[docs]def readShiftsMovieAlignment(xmdFn): shiftsMd = md.MetaData(xmdFn) shiftsMd.removeDisabled() return (shiftsMd.getColumnValues(md.MDL_SHIFT_X), shiftsMd.getColumnValues(md.MDL_SHIFT_Y))
[docs]def writeShiftsMovieAlignment(movie, xmdFn, s0, sN): movieAlignment = movie.getAlignment() shiftListX, shiftListY = movieAlignment.getShifts() # Generating metadata for global shifts a0, aN = movieAlignment.getRange() globalShiftsMD = emlib.MetaData() alFrame = a0 if s0 < a0: for i in range(s0, a0): objId = globalShiftsMD.addObject() imgFn = locationToXmipp(i, getMovieFileName(movie)) globalShiftsMD.setValue(emlib.MDL_IMAGE, imgFn, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_X, 0.0, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_Y, 0.0, objId) for shiftX, shiftY in izip(shiftListX, shiftListY): if alFrame >= s0 and alFrame <= sN: objId = globalShiftsMD.addObject() imgFn = locationToXmipp(alFrame, getMovieFileName(movie)) globalShiftsMD.setValue(emlib.MDL_IMAGE, imgFn, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_X, shiftX, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_Y, shiftY, objId) alFrame += 1 if sN > aN: for j in range(aN, sN): objId = globalShiftsMD.addObject() imgFn = locationToXmipp(j+1, getMovieFileName(movie)) globalShiftsMD.setValue(emlib.MDL_IMAGE, imgFn, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_X, 0.0, objId) globalShiftsMD.setValue(emlib.MDL_SHIFT_Y, 0.0, objId) globalShiftsMD.write(xmdFn)
[docs]def makeEerFilename(eer: str, fractioning: int, supersampling: str, datatype: str) -> str: ARG_PREAMLE = '#' SEPARATOR = ',' return eer + ARG_PREAMLE + ('%d' % fractioning) + SEPARATOR + supersampling + SEPARATOR + datatype
[docs]def frameToRow(frame, row, firstFrame, eerFrames): frameFilename = ImageHandler.locationToXmipp(frame) if os.path.splitext(frameFilename)[-1] == '.eer': frameFilename = makeEerFilename(frameFilename, eerFrames, '4K', 'uint16') row.setValue(md.MDL_IMAGE, frameFilename)
[docs]def isEerMovie(movie: Union[Movie, SetOfMovies]): if movie is not None: files = movie.getFiles() if len(files) > 0: return next(iter(files)).endswith('.eer') return False
[docs]def writeMovieMd(movie, outXmd, f1, fN, useAlignment=False, eerFrames=40): movieMd = md.MetaData() frame = movie.clone() isEer = isEerMovie(movie) # get some info about the movie # problem is, that it can come from a movie set, and some # values might refer to different movie, e.g. no of frames :( firstFrame, _, frameIndex = movie.getFramesRange() # info from the movie set _, _ , lastFrame = movie.getDim() # actual no. of frame in the current movie lastFrame += 1 # (convert no. of frames to index, one-initiated) if lastFrame == 0: # this condition is for old SetOfMovies, that has lastFrame = 0. frames = movie.getNumberOfFrames() if frames is not None: lastFrame = frames if isEer: lastFrame = eerFrames if f1 < firstFrame or fN > lastFrame: raise ValueError(f"Frame range [{f1}-{fN}] could not be greater" f" than the movie one [{firstFrame}-{lastFrame}].") if useAlignment: alignment = movie.getAlignment() if alignment is None: raise ValueError("Can not write alignment for movie.") a0, aN = alignment.getRange() if a0 < firstFrame or aN > lastFrame: raise ValueError("Trying to write frames which have not been aligned.") shiftListX, shiftListY = alignment.getShifts() row = md.Row() stackIndex = frameIndex + (f1 - firstFrame) for i in range(f1, fN+1): frame.setIndex(stackIndex) frameToRow(frame, row, firstFrame=firstFrame, eerFrames=eerFrames) if useAlignment: shiftIndex = i - firstFrame row.setValue(emlib.MDL_SHIFT_X, shiftListX[shiftIndex]) row.setValue(emlib.MDL_SHIFT_Y, shiftListY[shiftIndex]) row.addToMd(movieMd) stackIndex += 1 movieMd.write(outXmd)
[docs]def createParamPhantomFile(filename, dimX, partSetMd, phFlip=False, ctfCorr=False): f = open(filename, 'w') str = "# XMIPP_STAR_1 *\n#\ndata_block1\n_dimensions2D '%d %d'\n" % \ (dimX, dimX) str += "_projAngleFile %s\n" % partSetMd str += "_ctfPhaseFlipped %d\n_ctfCorrected %d\n" % (phFlip, ctfCorr) str += "_applyShift 1\n_noisePixelLevel '0 0'\n" f.write(str) f.close()
[docs]def convertToMrc(self, fnVol, Ts, deleteVol=False): fnMrc = fnVol.replace(".vol",".mrc") self.runJob("xmipp_image_convert","-i %s -o %s -t vol"%(fnVol, fnMrc), numberOfMpi=1) self.runJob("xmipp_image_header","-i %s --sampling_rate %f"%(fnMrc, Ts), numberOfMpi=1) if deleteVol: cleanPath(fnVol) return fnMrc