Пример #1
0
class Angles(EMObject):
    """Represents a triplet of angles"""
    def __init__(self, **args):
        EMObject.__init__(self, **args)
        self._angleY = Float()
        self._angleY2 = Float()
        self._angleTilt = Float()

    def setAngles(self, angleY, angleY2, angleTilt):
        self._angleY.set(angleY)
        self._angleY2.set(angleY2)
        self._angleTilt.set(angleTilt)

    def getAngles(self):
        return (self._angleY.get(), self._angleY2.get(), self._angleTilt.get())
Пример #2
0
class Angles(EMObject):
    """Represents a triplet of angles"""

    def __init__(self, **args):
        EMObject.__init__(self, **args)
        self._angleY = Float()    
        self._angleY2 = Float()
        self._angleTilt = Float()
        
    def setAngles(self, angleY, angleY2, angleTilt):
        self._angleY.set(angleY)
        self._angleY2.set(angleY2)
        self._angleTilt.set(angleTilt)
        
    def getAngles(self):
        return (self._angleY.get(), self._angleY2.get(), self._angleTilt.get())
class XmippProtMonoTomo(ProtAnalysis3D):
    """
    Given a tomogram the protocol assigns local resolutions to each voxel of the tomogram.
    """
    _label = 'local Resolution MonoTomo'
    _lastUpdateVersion = VERSION_2_0

    def __init__(self, **args):
        ProtAnalysis3D.__init__(self, **args)
        self.min_res_init = Float()
        self.max_res_init = Float()

    # --------------------------- DEFINE param functions ----------------------
    def _defineParams(self, form):
        form.addSection(label='Input')

        form.addParam('half1', PointerParam, pointerClass='Tomogram',
                      label="Odd tomogram", important=True,
                      help='Select a volume for determining its '
                           'local resolution.')

        form.addParam('half2', PointerParam, pointerClass='Tomogram',
                      label="Even Tomogram", important=True,
                      help='Select a second volume for determining a '
                           'local resolution.')

        form.addParam('useMask', BooleanParam, default=False,
                      label="Use mask?",
                      help='The mask determines which points are specimen'
                           ' and which are not.')

        form.addParam('Mask', PointerParam, pointerClass='VolumeMask',
                      condition='useMask', allowsNull=True,
                      label="Binary Mask",
                      help='The mask determines which points are specimen'
                           ' and which are not')

        group = form.addGroup('Extra parameters')
        line = group.addLine('Resolution Range (Å)',
                             help="Resolution range (and step in expert mode) "
                                  "to evaluate the local resolution.")

        group.addParam('significance', FloatParam, default=0.95,
                       expertLevel=LEVEL_ADVANCED,
                       label="Significance",
                       help='Relution is computed using hipothesis tests, '
                            'this value determines the significance of that test')

        line.addParam('minRes', FloatParam, default=0, label='High')
        line.addParam('maxRes', FloatParam, allowsNull=True, label='Low')
        line.addParam('stepSize', FloatParam, allowsNull=True, default=0.25,
                      expertLevel=LEVEL_ADVANCED, label='Step')

        form.addParallelSection(threads=4, mpi=0)

    # --------------------------- INSERT steps functions --------------------------------------------

    def _createFilenameTemplates(self):
        """ Centralize how files are called """
        myDict = {
            FN_MEAN_VOL: self._getExtraPath('mean_Tomogram.mrc'),
            OUTPUT_RESOLUTION_FILE: self._getExtraPath('TomogramLocalResolution.mrc'),
            METADATA_MASK_FILE: self._getExtraPath('mask_data.xmd'),
            FN_METADATA_HISTOGRAM: self._getExtraPath('hist.xmd'),
            BINARY_MASK: self._getExtraPath('binarized_mask.mrc'),
            FN_GAUSSIAN_MAP: self._getExtraPath('gaussianfilted.mrc'),
        }
        self._updateFilenamesDict(myDict)

    def _insertAllSteps(self):
        # Convert input into xmipp Metadata format
        self._createFilenameTemplates()
        self._insertFunctionStep('convertInputStep')
        self._insertFunctionStep('resolutionMonoTomoStep')
        self._insertFunctionStep('createOutputStep')
        self._insertFunctionStep("createHistrogram")

    def convertInputStep(self):
        """ Read the input volume.
        """

        self.vol1Fn = self.half1.get().getFileName()
        self.vol2Fn = self.half2.get().getFileName()
        extVol1 = getExt(self.vol1Fn)
        extVol2 = getExt(self.vol2Fn)
        if (extVol1 == '.mrc') or (extVol1 == '.map'):
            self.vol1Fn = self.vol1Fn + ':mrc'
        if (extVol2 == '.mrc') or (extVol2 == '.map'):
            self.vol2Fn = self.vol2Fn + ':mrc'

        if self.useMask.get():
            if (not self.Mask.hasValue()):
                self.ifNomask(self.vol1Fn)
            else:
                self.maskFn = self.Mask.get().getFileName()

            extMask = getExt(self.maskFn)

            if (extMask == '.mrc') or (extMask == '.map'):
                self.maskFn = self.maskFn + ':mrc'

            if self.Mask.hasValue():
                params = ' -i %s' % self.maskFn
                params += ' -o %s' % self._getFileName(BINARY_MASK)
                params += ' --select below %f' % 0.5  # Mask threshold = 0.5 self.maskthreshold.get()
                params += ' --substitute binarize'

                self.runJob('xmipp_transform_threshold', params)

    def ifNomask(self, fnVol):
        xdim, _ydim, _zdim = self.half1.get().getDim()
        params = ' -i %s' % fnVol
        params += ' -o %s' % self._getFileName(FN_GAUSSIAN_MAP)
        setsize = 0.02 * xdim
        params += ' --fourier real_gaussian %f' % (setsize)

        self.runJob('xmipp_transform_filter', params)
        img = ImageHandler().read(self._getFileName(FN_GAUSSIAN_MAP))
        imgData = img.getData()
        max_val = np.amax(imgData) * 0.05

        params = ' -i %s' % self._getFileName(FN_GAUSSIAN_MAP)
        params += ' --select below %f' % max_val
        params += ' --substitute binarize'
        params += ' -o %s' % self._getFileName(BINARY_MASK)

        self.runJob('xmipp_transform_threshold', params)

        self.maskFn = self._getFileName(BINARY_MASK)

    def maskRadius(self):

        xdim, _ydim, _zdim = self.half1.get().getDim()
        xdim = xdim * 0.5

        return xdim

    def resolutionMonoTomoStep(self):
        # Number of frequencies
        max_ = self.maxRes.get()

        if self.stepSize.hasValue():
            freq_step = self.stepSize.get()
        else:
            freq_step = 0.25

        xdim = self.maskRadius()

        params = ' --vol %s' % self.vol1Fn
        params += ' --vol2 %s' % self.vol2Fn
        params += ' --meanVol %s' % self._getFileName(FN_MEAN_VOL)

        if self.useMask.get():
            params += ' --mask %s' % self._getFileName(BINARY_MASK)

        params += ' --sampling_rate %f' % self.half1.get().getSamplingRate()
        params += ' --minRes %f' % self.minRes.get()
        params += ' --maxRes %f' % max_
        params += ' --step %f' % freq_step
        params += ' -o %s' % self._getFileName(OUTPUT_RESOLUTION_FILE)
        params += ' --significance %f' % self.significance.get()

        self.runJob('xmipp_resolution_monotomo', params)

    def createHistrogram(self):

        freq_step = self.stepSize.get() if self.stepSize.hasValue() else 10

        M = float(self.max_res_init)
        m = float(self.min_res_init)
        range_res = round((M - m) / freq_step)

        params = ' -i %s' % self._getFileName(OUTPUT_RESOLUTION_FILE)

        if self.useMask.get():
            params += ' --mask binary_file %s' % self._getFileName(BINARY_MASK)

        params += ' --steps %f' % range_res
        params += ' --range %f %f' % (self.min_res_init.get(),
                                      (float(self.max_res_init.get()) - float(freq_step)))
        params += ' -o %s' % self._getFileName(FN_METADATA_HISTOGRAM)

        self.runJob('xmipp_image_histogram', params)

    def readMetaDataOutput(self):
        mData = md.MetaData(self._getFileName(METADATA_MASK_FILE))
        NvoxelsOriginalMask = float(mData.getValue(md.MDL_COUNT, mData.firstObject()))
        NvoxelsOutputMask = float(mData.getValue(md.MDL_COUNT2, mData.firstObject()))
        nvox = int(round(
            ((NvoxelsOriginalMask - NvoxelsOutputMask) / NvoxelsOriginalMask) * 100))
        return nvox

    def getMinMax(self, imageFile):
        img = ImageHandler().read(imageFile)
        imgData = img.getData()
        min_res = round(np.amin(imgData) * 100) / 100
        max_res = round(np.amax(imgData) * 100) / 100
        return min_res, max_res

    def createOutputStep(self):
        volume = Tomogram()
        volume.setFileName(self._getFileName(OUTPUT_RESOLUTION_FILE))

        volume.setSamplingRate(self.half1.get().getSamplingRate())
        self._defineOutputs(resolution_Volume=volume)
        self._defineSourceRelation(self.half1, volume)

        # Setting the min max for the summary
        imageFile = self._getFileName(OUTPUT_RESOLUTION_FILE)
        min_, max_ = self.getMinMax(imageFile)
        self.min_res_init.set(round(min_ * 100) / 100)
        self.max_res_init.set(round(max_ * 100) / 100)
        self._store(self.min_res_init)
        self._store(self.max_res_init)

    # --------------------------- INFO functions ------------------------------

    def _methods(self):
        messages = []
        if hasattr(self, 'LocalResolution_Tomogram'):
            messages.append(
                'Information about the method/article in ' + MONOTOMO_METHOD_URL)
        return messages

    def _summary(self):
        summary = []
        summary.append("Highest resolution %.2f Å,   "
                       "Lowest resolution %.2f Å. \n" % (self.min_res_init,
                                                         self.max_res_init))
        return summary

    def _citations(self):
        return ['Vilas2020']
class BsoftProtBlocres(ProtAnalysis3D):
    """
    Bsoft program: blocres
    It calculates the local resolution map from to half maps.
    The method is based on a local measurement inside a mobile window.
    """
    _label = 'blocres'

    def __init__(self, **args):
        ProtAnalysis3D.__init__(self, **args)
        self.min_res_init = Float()
        self.max_res_init = Float()
        self.halfVolumes = True

    # --------------------------- DEFINE param functions -----------------------
    def _defineParams(self, form):
        form.addSection(label='Input')
        form.addParam('inputVolume',
                      params.PointerParam,
                      pointerClass='Volume',
                      label="Half 1",
                      help="Select first half volume to compute the "
                      "local resolution.")
        form.addParam('inputVolume2',
                      params.PointerParam,
                      pointerClass='Volume',
                      label="Half 2",
                      help="Select first half volume to compute the "
                      "local resolution.")
        form.addParam('mask',
                      params.PointerParam,
                      allowsNull=True,
                      pointerClass='Volume',
                      label='Mask',
                      help="Mask file to use for limiting the analysis to a "
                      "defined region and level value to use "
                      "(optional, default: all but zero).")
        form.addParam('method',
                      params.BooleanParam,
                      default=True,
                      label='Use Box',
                      help="The local (box) and shell (shell) resolution "
                      "calculations are mutually exclusive.")
        form.addParam('box',
                      params.IntParam,
                      default=20,
                      condition='method',
                      label='Box',
                      help="Kernel size for determining "
                      "local resolution (pixels/voxels).")
        form.addParam('shell',
                      params.IntParam,
                      default=20,
                      condition='not method',
                      label='Shell',
                      help="Shell width for determining "
                      "radial resolution (pixels/voxels).")

        form.addParam('cutoff',
                      params.FloatParam,
                      default=0.5,
                      label='Cutoff',
                      help="Resolution cutoff for FSC."
                      "(default: 0.5).")

        form.addSection(label='Parameters')
        form.addParam('step',
                      params.IntParam,
                      default=1,
                      label='Step',
                      help="Interval between voxel samples or shells for "
                      "resolution analysis (pixels, default: 1)")
        form.addParam(
            'maxresolution',
            params.FloatParam,
            default=2,
            label='Maximum Resolution',
            help="Maximum frequency available in the data (angstrom).")
        form.addParam('fill',
                      params.IntParam,
                      allowsNull=True,
                      label='Fill',
                      help="Value to fill the background "
                      "(non-masked regions; default 0).")
        form.addParam('pad',
                      params.EnumParam,
                      choices=['None', 'Box', 'Shell'],
                      default=1,
                      label='Padding Factor',
                      help="Resolution box padding factor "
                      "(0 = none, default: 1 (box) and 0 (shell)).")
        form.addParam('symmetry',
                      params.StringParam,
                      allowsNull=True,
                      default='',
                      label='Symmetry',
                      help="Point group symmetry.")
        form.addParam('smooth',
                      params.BooleanParam,
                      default=True,
                      label='Smooth',
                      help="Smooth the shell edge.")

    def _createFilenameTemplates(self):
        """ Centralize how files are called """
        myDict = {
            FN_HALF1: self._getTmpPath("half1.map"),
            FN_HALF2: self._getTmpPath("half2.map"),
            FN_MASKVOL: self._getTmpPath("mask.map"),
            FN_RESOLMAP: self._getExtraPath("resolutionMap.map")
        }
        self._updateFilenamesDict(myDict)

    # --------------------------- INSERT steps functions -----------------------
    def _insertAllSteps(self):
        # Insert processing steps
        self._createFilenameTemplates()
        self._insertFunctionStep('convertInputStep')
        self._insertFunctionStep('resolutionStep')
        self._insertFunctionStep('createOutputStep')

    # --------------------------- STEPS functions ------------------------------
    def convertInputStep(self):
        # blocres works with .map
        vol1Fn = self.inputVolume.get().getFileName()
        vol2Fn = self.inputVolume2.get().getFileName()
        if self.mask.get().getFileName() != '':
            maskFn = self.mask.get().getFileName()

        self.fnvol1 = self._getFileName("half1")
        self.fnvol2 = self._getFileName("half2")
        ImageHandler().convert(vol1Fn, self.fnvol1)
        ImageHandler().convert(vol2Fn, self.fnvol2)
        if self.mask.get().getFileName() != '':
            self.fnmask = self._getFileName("maskvol")
            ImageHandler().convert(maskFn, self.fnmask)
        else:
            self.fnmask = self.maks.get().getFileName()

    def resolutionStep(self):
        """ blocres parameters. """
        sampling = self.inputVolume.get().getSamplingRate()
        # Actions
        params = ' -v 1'  # No Verbose
        if self.method:
            params += ' -box %i' % self.box.get()
        else:
            params += ' -shell %i' % self.shell.get()

        # Parameters
        params += ' -sampling %f,%f,%f' % (sampling, sampling, sampling)
        params += ' -step %f' % self.step.get()
        params += ' -maxresolution %f' % self.maxresolution.get()
        params += ' -cutoff %f' % self.cutoff.get()
        if self.fill.get() != '':
            params += ' -fill %i' % self.fill.get()

        # Parameters for local resolution
        params += ' -pad %f' % self.pad.get()
        if self.symmetry.get() != '':
            params += ' -symmetry %s' % self.symmetry.get()
        if self.smooth.get():
            params += ' -smooth'
        if self.mask.get().getFileName() != '':
            params += ' -Mask %s' % self.fnmask

        # input halves and output map
        params += ' %s %s %s' % (self.fnvol1, self.fnvol2,
                                 self._getFileName(FN_RESOLMAP))

        self.runJob(bsoft.Plugin.getProgram('blocres'),
                    params,
                    env=bsoft.Plugin.getEnviron())

    def createOutputStep(self):
        volume = Volume()
        volume.setFileName(self._getFileName(FN_RESOLMAP))
        volume.setSamplingRate(self.inputVolume.get().getSamplingRate())
        self._defineOutputs(resolution_Volume=volume)
        self._defineSourceRelation(self.inputVolume, volume)

        imageFile = self._getFileName(FN_RESOLMAP)
        min_, max_ = self.getMinMax(imageFile)
        self.min_res_init.set(round(min_ * 100) / 100)
        self.max_res_init.set(round(max_ * 100) / 100)
        self._store(self.min_res_init)
        self._store(self.max_res_init)

    def getMinMax(self, imageFile):
        img = ImageHandler().read(imageFile)
        imgData = img.getData()
        # Remove 0's
        imgData = imgData[np.nonzero(imgData)]
        min_res = round(np.amin(imgData) * 100) / 100
        max_res = round(np.amax(imgData) * 100) / 100
        return min_res, max_res

    # --------------------------- INFO functions -----------------------------------
    def _validate(self):
        errors = []
        return errors

    def _citations(self):
        cites = ['Cardone2013']
        return cites

    def _summary(self):
        summary = []
        summary.append("Highest resolution %.2f A,   "
                       "Lowest resolution %.2f A." %
                       (self.min_res_init.get(), self.max_res_init.get()))

    def _methods(self):
        methods = []
        return methods
class XmippProtScreenParticles(ProtProcessParticles):
    """ Attach different merit values to every particle in order to
        prune the set.
        zScore evaluates the similarity of a particles with an average
        (lower zScore -> high similarity).
        SSNR evaluates the signal/noise ration in the Fourier space.
        Variance evaluates the varaince on the micrographs context where
        the particle was picked.
    """

    _label = 'screen particles'

    # Automatic Particle rejection enum
    ZSCORE_CHOICES = ['None', 'MaxZscore', 'Percentage']
    SSNR_CHOICES = ['None', 'Percentage']
    VAR_CHOICES = ['None', 'Variance', 'Var. and Gini']
    REJ_NONE = 0
    REJ_MAXZSCORE = 1
    REJ_PERCENTAGE = 2
    REJ_PERCENTAGE_SSNR = 1
    REJ_VARIANCE = 1
    REJ_VARGINI = 2

    # --------------------------- DEFINE param functions ---------------------
    def _defineProcessParams(self, form):
        # --- zScore rejection ---
        form.addParam('autoParRejection', EnumParam,
                      choices=self.ZSCORE_CHOICES,
                      label="Automatic rejection by Zscore",
                      default=self.REJ_NONE,
                      display=EnumParam.DISPLAY_COMBO,
                      help='zScore evaluates the similarity of a particles '
                           'with an average. The rejection can be:\n'
                           '  None (no rejection)\n'
                           '  MaxZscore (reject a particle if its zScore '
                           'is larger than this value).\n '
                           '  Percentage (reject a given percentage for '
                           'this criteria).')
        form.addParam('maxZscore', FloatParam, default=3, validators=[Positive],
                      condition='autoParRejection==1', label='zScore threshold',
                      help='Maximum Zscore.')
        form.addParam('percentage', IntParam, default=5, label='Percentage (%)',
                      condition='autoParRejection==2',
                      help='The worse percentage of particles according to '
                           'metadata labels: ZScoreShape1, ZScoreShape2, '
                           'ZScoreSNR1, ZScoreSNR2, ZScoreHistogram are '
                           'automatically disabled.',
                      validators=[Range(0, 100, error="Percentage must be "
                                                      "between 0 and 100.")])
        # --- SSNR rejection ---
        form.addParam('autoParRejectionSSNR', EnumParam,
                      choices=self.SSNR_CHOICES,
                      label="Automatic rejection by SSNR",
                      default=self.REJ_NONE, display=EnumParam.DISPLAY_COMBO,
                      help='SSNR evaluates the signal/noise ration in the '
                           'Fourier space. The rejection can be:\n'
                           '  None (no rejection)\n'
                           '  Percentage (reject a given percentage of the '
                           'lowest SSNRs).')
        form.addParam('percentageSSNR', IntParam, default=5,
                      condition='autoParRejectionSSNR==1',
                      label='Percentage (%)',
                      help='The worse percentage of particles according to '
                           'SSNR are automatically disabled.',
                      validators=[Range(0, 100, error="Percentage must be "
                                                      "between 0 and 100.")])
        # --- Variance rejection ---
        form.addParam('autoParRejectionVar', EnumParam, default=self.REJ_NONE,
                      choices=self.VAR_CHOICES,
                      label='Automatic rejection by Variance',
                      help='Variance evaluates the varaince on the micrographs '
                           'context where the particle was picked. '
                           'The rejection can be:\n'
                           '  None (no rejection)\n'
                           '  Variance (taking into account only the variance)\n'
                           '  Var. and Gini (taking into account also the Gini '
                           'coeff.)')
        # --- Add features ---
        form.addParam('addFeatures', BooleanParam, default=False,
                      label='Add features', expertLevel=LEVEL_ADVANCED,
                      help='Add features used for the ranking to each one '
                           'of the input particles')

        form.addParallelSection(threads=0, mpi=0)

    def _getDefaultParallel(self):
        """This protocol doesn't have mpi version"""
        return (0, 0)

    #--------------------------- INSERT steps functions ----------------------
    def _insertAllSteps(self):
        self._initializeZscores()
        self.outputSize = 0
        self.inputSize = 0
        self.check = None
        self.fnInputMd = self._getExtraPath("input.xmd")
        self.fnInputOldMd = self._getExtraPath("inputOld.xmd")
        self.fnOutputMd = self._getExtraPath("output.xmd")

        self.inputSize, self.streamClosed = self._loadInput()
        partsSteps = self._insertNewPartsSteps()
        self._insertFunctionStep('createOutputStep',
                                 prerequisites=partsSteps, wait=True)

    def _getFirstJoinStep(self):
        for s in self._steps:
            if s.funcName == self._getFirstJoinStepName():
                return s
        return None

    def _getFirstJoinStepName(self):
        # This function will be used for streaming, to check which is
        # the first function that need to wait for all micrographs
        # to have completed, this can be overriden in subclasses
        # (e.g., in Xmipp 'sortPSDStep')
        return 'createOutputStep'

    def createOutputStep(self):
        pass

    def _insertNewPartsSteps(self):
        deps = []
        stepId = self._insertFunctionStep('sortImagesStep', prerequisites=[])
        deps.append(stepId)
        return deps

    def _stepsCheck(self):
        # Input particles set can be loaded or None when checked for new inputs
        # If None, we load it
        self._checkNewInput()
        self._checkNewOutput()

    def _checkNewInput(self):
        # Check if there are new particles to process from the input set
        partsFile = self.inputParticles.get().getFileName()
        mTime = datetime.fromtimestamp(os.path.getmtime(partsFile))
        # If the input movies.sqlite have not changed since our last check,
        # it does not make sense to check for new input data
        if self.lastCheck > mTime:
            return None

        self.inputSize, self.streamClosed = self._loadInput()
        if not isEmpty(self.fnInputMd):
            fDeps = self._insertNewPartsSteps()
            outputStep = self._getFirstJoinStep()
            if outputStep is not None:
                outputStep.addPrerequisites(*fDeps)
            self.updateSteps()

    def _loadInput(self):
        self.lastCheck = datetime.now()
        partsFile = self.inputParticles.get().getFileName()
        inPartsSet = SetOfParticles(filename=partsFile)
        inPartsSet.loadAllProperties()

        check = None
        for p in inPartsSet.iterItems(orderBy='creation', direction='DESC'):
            check = p.getObjCreation()
            break
        if self.check is None:
            writeSetOfParticles(inPartsSet, self.fnInputMd,
                                alignType=ALIGN_NONE, orderBy='creation')
        else:
            writeSetOfParticles(inPartsSet, self.fnInputMd,
                                alignType=ALIGN_NONE, orderBy='creation',
                                where='creation>"' + str(self.check) + '"')
            writeSetOfParticles(inPartsSet, self.fnInputOldMd,
                                alignType=ALIGN_NONE, orderBy='creation',
                                where='creation<"' + str(self.check) + '"')
        self.check = check

        streamClosed = inPartsSet.isStreamClosed()
        inputSize = inPartsSet.getSize()

        inPartsSet.close()

        return inputSize, streamClosed

    def _checkNewOutput(self):
        if getattr(self, 'finished', False):
            return
        self.finished = self.streamClosed and \
                        self.outputSize == self.inputSize

        streamMode = Set.STREAM_CLOSED if self.finished else Set.STREAM_OPEN

        newData = os.path.exists(self.fnOutputMd)
        lastToClose = self.finished and hasattr(self, 'outputParticles')
        if newData or lastToClose:

            outSet = self._loadOutputSet(SetOfParticles, 'outputParticles.sqlite')

            if newData:
                partsSet = self._createSetOfParticles()
                readSetOfParticles(self.fnOutputMd, partsSet)
                outSet.copyItems(partsSet)
                for item in partsSet:
                    self._calculateSummaryValues(item)
                self._store()

                writeSetOfParticles(outSet.iterItems(orderBy='_xmipp_zScore'),
                                    self._getPath("images.xmd"),
                                    alignType=ALIGN_NONE)
                cleanPath(self.fnOutputMd)

            self._updateOutputSet('outputParticles', outSet, streamMode)

        if self.finished:  # Unlock createOutputStep if finished all jobs
            outputStep = self._getFirstJoinStep()
            if outputStep and outputStep.isWaiting():
                outputStep.setStatus(cons.STATUS_NEW)

    def _loadOutputSet(self, SetClass, baseName):
        setFile = self._getPath(baseName)
        if os.path.exists(setFile):
            outputSet = SetClass(filename=setFile)
            outputSet.loadAllProperties()
            outputSet.enableAppend()
        else:
            outputSet = SetClass(filename=setFile)
            outputSet.setStreamState(outputSet.STREAM_OPEN)
            self._store(outputSet)
            self._defineTransformRelation(self.inputParticles, outputSet)

        outputSet.copyInfo(self.inputParticles.get())

        return outputSet

    #--------------------------- STEP functions -----------------------------
    def sortImagesStep(self):
        args = "-i Particles@%s -o %s --addToInput " % (self.fnInputMd,
                                                        self.fnOutputMd)
        if os.path.exists(self.fnInputOldMd):
            args += "-t Particles@%s " % self.fnInputOldMd

        if self.autoParRejection == self.REJ_MAXZSCORE:
            args += "--zcut " + str(self.maxZscore.get())
        elif self.autoParRejection == self.REJ_PERCENTAGE:
            args += "--percent " + str(self.percentage.get())

        if self.addFeatures:
            args += "--addFeatures "

        self.runJob("xmipp_image_sort_by_statistics", args)

        args = "-i Particles@%s -o %s" % (self.fnInputMd, self.fnOutputMd)
        if self.autoParRejectionSSNR == self.REJ_PERCENTAGE_SSNR:
            args += " --ssnrpercent " + str(self.percentageSSNR.get())
        self.runJob("xmipp_image_ssnr", args)

        if self.autoParRejectionVar != self.REJ_NONE:
            print('Rejecting by variance:')
            if self.outputSize == 0:
                varList = []
                giniList = []
                print('  - Reading metadata')
                mdata = emlib.MetaData(self.fnInputMd)
                for objId in mdata:
                    varList.append(mdata.getValue(emlib.MDL_SCORE_BY_VAR, objId))
                    giniList.append(mdata.getValue(emlib.MDL_SCORE_BY_GINI, objId))

                if self.autoParRejectionVar == self.REJ_VARIANCE:
                    valuesList = varList
                    self.mdLabels = [emlib.MDL_SCORE_BY_VAR]
                else:  # not working pretty well
                    valuesList = [var*(1-gini) for var, gini in zip(varList, giniList)]
                    self.mdLabels = [emlib.MDL_SCORE_BY_VAR, emlib.MDL_SCORE_BY_GINI]

                self.varThreshold.set(histThresholding(valuesList))
                print('  - Variance threshold: %f' % self.varThreshold)

            rejectByVariance(self.fnInputMd, self.fnOutputMd, self.varThreshold,
                             self.autoParRejectionVar)

        # update the processed particles
        self.outputSize += getSize(self.fnInputMd)

    def _initializeZscores(self):
        # Store the set for later access , ;-(
        self.minZScore = Float()
        self.maxZScore = Float()
        self.sumZScore = Float()
        self.varThreshold = Float()
        self._store()

    def _calculateSummaryValues(self, particle):
        zScore = particle._xmipp_zScore.get()

        self.minZScore.set(min(zScore, self.minZScore.get(1000)))
        self.maxZScore.set(max(zScore, self.maxZScore.get(0)))
        self.sumZScore.set(self.sumZScore.get(0) + zScore)

    # -------------------------- INFO functions ------------------------------
    def _summary(self):
        summary = []

        sumRejMet = {}  # A dict with the form choices
        if self.autoParRejection is not None:
            metStr = self.ZSCORE_CHOICES[self.autoParRejection.get()]
            if self.autoParRejection.get() == self.REJ_MAXZSCORE:
                metStr += " = %.2f" % self.maxZscore.get()
            elif self.autoParRejection.get() == self.REJ_PERCENTAGE:
                metStr += " = %.2f" % self.percentage.get()
            sumRejMet['Zscore'] = ("Zscore rejection method: " + metStr)

        if self.autoParRejectionSSNR is not None:
            metStr = self.SSNR_CHOICES[self.autoParRejectionSSNR.get()]
            if self.autoParRejectionSSNR.get() == self.REJ_PERCENTAGE_SSNR:
                metStr += " = %.2f" % self.percentageSSNR.get()
            sumRejMet['SSNR'] = ("SSNR rejection method: " + metStr)

        if self.autoParRejectionVar is not None:
            sumRejMet['Var'] = ("Variance rejection method: " +
                               self.VAR_CHOICES[self.autoParRejectionVar.get()])

        # If no output yet, just the form choices are shown plus a no-ready text
        if not hasattr(self, 'outputParticles'):
            summary += sumRejMet.values()
            summary.append("Output particles not ready yet.")
        else:
            summary.append(sumRejMet['Zscore'])
            if hasattr(self, 'sumZScore'):
                summary.append(" - The minimum ZScore is %.2f" % self.minZScore)
                summary.append(" - The maximum ZScore is %.2f" % self.maxZScore)
                meanZScore = self.sumZScore.get() * 1.0 / len(self.outputParticles)
                summary.append(" - The mean ZScore is %.2f" % meanZScore)
            else:
                summary.append(
                    "Summary values not calculated during processing.")
            summary.append(sumRejMet['SSNR'])
            summary.append(sumRejMet['Var'])
            if self.autoParRejectionVar != self.REJ_NONE:
                if hasattr(self, 'varThreshold'):
                    summary.append(" - Variance threshold: %.2f" % self.varThreshold)
                else:
                    summary.append(" - Variance threshold not calculed yet.")
        return summary
    
    def _validate(self):
        validateMsgs = []
        if self.autoParRejectionVar != self.REJ_NONE:
            part = self.inputParticles.get().getFirstItem()
            if not part.hasAttribute('_xmipp_scoreByVariance'):
                validateMsgs.append('The auto-rejection by Variance can not be '
                                    'done because the particles have not the '
                                    'scoreByVariance attribute. Use Xmipp to '
                                    'extract the particles.')
        return validateMsgs
        
    def _citations(self):
        return ['Vargas2013b']
    
    def _methods(self):
        methods = []
        if hasattr(self, 'outputParticles'):
            outParticles = (len(self.outputParticles) if self.outputParticles
                                                         is not None else None)
            particlesRejected = (len(self.inputParticles.get())-outParticles
                                 if outParticles is not None else None)
            particlesRejectedText = (' ('+str(particlesRejected)+')' if
                                     particlesRejected is not None else '')
            rejectionText = ['',  # REJ_NONE
                             ' and removing those not reaching %s%s'
                             % (str(self.maxZscore.get()),
                                particlesRejectedText),  # REJ_MAXZSCORE
                             ' and removing worst %s percent %s'
                             % (str(self.percentage.get()),
                                particlesRejectedText)  # REJ_PERCENTAGE
                             ]
            methods.append('Input dataset %s of %s particles was sorted by'
                           ' its ZScore using xmipp_image_sort_by_statistics'
                           ' program%s. '
                           % (self.getObjectTag('inputParticles'),
                              len(self.inputParticles.get()),
                              rejectionText[self.autoParRejection.get()]))
            methods.append('Output set is %s.'
                           % self.getObjectTag('outputParticles'))
        return methods