コード例 #1
0
    def createOutputStep(self):
        imgSet = self.inputParticles.get()
        partSet = self._createSetOfParticles()
        partSet.copyInfo(imgSet)
        outImagesMd = self._getExtraPath('expanded_particles.star')

        # remove repeating rlnImageId column
        tableName = ''
        if Plugin.IS_GT30():
            tableName = 'particles'
            mdOptics = Table(fileName=outImagesMd, tableName='optics')

        mdOut = Table(fileName=outImagesMd, tableName=tableName)
        mdOut.removeColumns("rlnImageId")
        with open(outImagesMd, "w") as f:
            mdOut.writeStar(f, tableName=tableName)
            if Plugin.IS_GT30():
                mdOptics.writeStar(f, tableName='optics')

        reader = convert.createReader()
        reader.readSetOfParticles(
            outImagesMd, partSet,
            alignType=ALIGN_PROJ,
            postprocessImageRow=self._postprocessImageRow)

        self._defineOutputs(outputParticles=partSet)
        self._defineSourceRelation(imgSet, partSet)
コード例 #2
0
    def _insertReconstructStep(self):
        imgSet = self.inputParticles.get()

        params = ' --i %s' % self._getFileName('input_particles')
        params += ' --o %s' % self._getFileName('output_volume')
        params += ' --sym %s' % self.symmetryGroup.get()
        params += ' --angpix %0.5f' % imgSet.getSamplingRate()
        params += ' --maxres %0.3f' % self.maxRes.get()
        params += ' --pad %0.3f' % self.pad.get()

        subset = -1 if self.subset.get() == 0 else self.subset
        params += ' --subset %d' % subset

        if Plugin.IS_GT30():
            params += ' --class %d' % self.classNum.get()

        if self.doCTF:
            params += ' --ctf'
            if self.ctfIntactFirstPeak:
                params += ' --ctf_intact_first_peak'

            if imgSet.isPhaseFlipped():
                params += ' --ctf_phase_flipped'

        if self.extraParams.hasValue():
            params += " " + self.extraParams.get()

        self._insertFunctionStep('reconstructStep', params)
コード例 #3
0
    def _defineParamDict(self):
        """ Define all parameters to run relion_postprocess"""
        # It seems that in Relion3 now the input should be the map
        # filename and not the prefix as before
        inputFn = self._getFileName('half1')

        self.paramDict = {'--i': inputFn,
                          '--o': self._getExtraPath('postprocess'),
                          '--angpix': self._getOutputPixelSize(),
                          # Expert params
                          '--filter_edge_width': self.filterEdgeWidth.get(),
                          '--randomize_at_fsc': self.randomizeAtFsc.get(),
                          '--mask': self._getFileName('mask')
                          }

        mtfFile = self.mtf.get()
        if mtfFile:
            self.paramDict['--mtf'] = mtfFile

        if self.doAutoBfactor:
            self.paramDict['--auto_bfac'] = ''
            self.paramDict['--autob_lowres'] = self.bfactorLowRes.get()
            self.paramDict['--autob_highres'] = self.bfactorHighRes.get()
        else:
            self.paramDict['--adhoc_bfac'] = self.bfactor.get()

        if self.skipFscWeighting:
            self.paramDict['--skip_fsc_weighting'] = ''
            self.paramDict['--low_pass'] = self.lowRes.get()

        if Plugin.IS_GT30() and self.origPixelSize.get() != -1.0:
            self.paramDict['--mtf_angpix'] = self.origPixelSize.get()
コード例 #4
0
    def _getMdOut(self, it, prefix, ref3d):
        randomSet = self._getRandomSet(prefix)
        dataStar = self._getDataStar(prefix, it)
        tableName = 'particles' if Plugin.IS_GT30() else None
        mdOut = []

        table = Table(fileName=dataStar, tableName=tableName)
        for row in table:
            if 0 < randomSet < 3:
                if int(row.rlnRandomSubset) == randomSet and int(
                        row.rlnClassNumber) == ref3d:
                    mdOut.append(row)
            else:
                if int(row.rlnClassNumber) == ref3d:
                    mdOut.append(row)

        return mdOut
コード例 #5
0
    def test_fromStar(self):
        if not Plugin.IS_GT30():
            print("Skipping test (required Relion > 3.1)")
            return

        partsStar = self.ds.getFile("Extract/job018/particles.star")

        print("<<< Reading optics groups from file: \n   %s\n" % partsStar)
        og = OpticsGroups.fromStar(partsStar)
        fog = og.first()

        # test hasColumn method
        for colName in ['rlnMtfFileName', 'rlnOpticsGroupName']:
            self.assertTrue(og.hasColumn(colName))

        # acq = first.getAcquisition()
        self.assertEqual(fog.rlnMtfFileName, 'mtf_k2_200kV.star')
        self.assertEqual(fog.rlnOpticsGroupName, 'opticsGroup1')
        self.assertEqual(og['opticsGroup1'], fog)
コード例 #6
0
    def _defineParams(self, form):
        form.addSection(label='Input')

        form.addParam('inputParticles', PointerParam,
                      pointerClass='SetOfParticles',
                      pointerCondition='hasAlignmentProj',
                      label="Input particles",
                      help='Select the input images from the project.')
        form.addParam('symmetryGroup', StringParam, default='c1',
                      label="Symmetry group",
                      help='See [[Relion Symmetry][http://www2.mrc-lmb.cam.ac.uk/'
                           'relion/index.php/Conventions_%26_File_formats#Symmetry]] '
                           'page for a description of the symmetry format '
                           'accepted by Relion')
        form.addParam('maxRes', FloatParam, default=-1,
                      label="Maximum resolution (A)",  
                      help='Maximum resolution (in Angstrom) to consider \n'
                           'in Fourier space (default Nyquist).')
        form.addParam('pad', FloatParam, default=2,
                      label="Padding factor")
        form.addParam('subset', EnumParam, default=0,
                      choices=['all', 'half1', 'half2'],
                      display=EnumParam.DISPLAY_HLIST,
                      label='Subset to reconstruct',
                      help='Subset of images to consider.')
        if Plugin.IS_GT30():
            form.addParam('classNum', IntParam, default=-1,
                          label='Use only this class',
                          help='Consider only this class (-1: use all classes)')
        
        form.addParam('extraParams', StringParam, default='',
                      expertLevel=LEVEL_ADVANCED,
                      label='Extra parameters: ', 
                      help='Extra parameters to *relion_reconstruct* program. '
                           'Address to Relion to see full list of options.')
        form.addSection('CTF')
        form.addParam('doCTF', BooleanParam, default=False,
                      label='Apply CTF correction?')
        form.addParam('ctfIntactFirstPeak', BooleanParam, default=False,
                      condition='doCTF',
                      label='Leave CTFs intact until first peak?')
        
        form.addParallelSection(threads=0, mpi=1)
コード例 #7
0
    def test_readSetOfParticles(self):
        if not Plugin.IS_GT30():
            print("Skipping test (required Relion > 3.1)")
            return

        partsSet = self.__readParticles(
            self.ds.getFile("Extract/job018/particles.star"),
            extraLabels=['rlnNrOfSignificantSamples'])
        partsSet.write()

        first = partsSet.getFirstItem()
        first.printAll()

        self.assertAlmostEqual(first.getSamplingRate(), 1.244531)
        self.assertEqual(first.getClassId(), 4)
        self.assertTrue(hasattr(first, '_rlnNrOfSignificantSamples'))

        fog = OpticsGroups.fromImages(partsSet).first()
        self.assertEqual(fog.rlnMtfFileName, 'mtf_k2_200kV.star')
        self.assertEqual(fog.rlnOpticsGroupName, 'opticsGroup1')
コード例 #8
0
    def test_readSetOfParticlesAfterCtf(self):
        if not Plugin.IS_GT30():
            print("Skipping test (required Relion > 3.1)")
            return

        starFile = self.ds.getFile(
            "CtfRefine/job023/particles_ctf_refine.star")
        partsReader = Table.Reader(starFile, tableName='particles')
        firstRow = partsReader.getRow()

        partsSet = self.__readParticles(starFile)
        first = partsSet.getFirstItem()

        ogLabels = ['rlnBeamTiltX', 'rlnBeamTiltY']
        extraLabels = ['rlnCtfBfactor', 'rlnCtfScalefactor', 'rlnPhaseShift']
        for l in extraLabels:
            value = getattr(first, '_%s' % l)
            self.assertIsNotNone(value, "Missing label: %s" % l)
            self.assertAlmostEqual(getattr(firstRow, l), value)

        fog = OpticsGroups.fromImages(partsSet).first()
        self.assertTrue(all(hasattr(fog, l) for l in ogLabels))

        # Also test writing and preserving extra labels
        outputStar = self.getOutputPath('particles.star')
        print(">>> Writing to particles star: %s" % outputStar)
        starWriter = convert.createWriter()
        starWriter.writeSetOfParticles(partsSet, outputStar)

        fog = OpticsGroups.fromStar(outputStar).first()
        self.assertTrue(all(hasattr(fog, l) for l in ogLabels))

        partsReader = Table.Reader(outputStar, tableName='particles')
        firstRow = partsReader.getRow()
        for l in extraLabels:
            value = getattr(first, '_%s' % l)
            self.assertIsNotNone(value, "Missing label: %s" % l)
            self.assertAlmostEqual(getattr(firstRow, l), value)
コード例 #9
0
    def test_string(self):
        if not Plugin.IS_GT30():
            print("Skipping test (required Relion > 3.1)")
            return

        og = OpticsGroups.create(rlnMtfFileName='mtf_k2_200kV.star')
        fog = og.first()

        # acq = first.getAcquisition()
        self.assertEqual(fog.rlnMtfFileName, 'mtf_k2_200kV.star')
        self.assertEqual(fog.rlnOpticsGroupName, 'opticsGroup1')
        self.assertEqual(og['opticsGroup1'], fog)

        # try update by id
        og.update(1, rlnMtfFileName="new_mtf_k2.star")
        # try update by name
        og.update('opticsGroup1', rlnImageSize=512)

        fog = og.first()
        # acq = first.getAcquisition()
        self.assertEqual(fog.rlnMtfFileName, 'new_mtf_k2.star')
        self.assertEqual(fog.rlnImageSize, 512)
        self.assertEqual(fog.rlnOpticsGroupName, 'opticsGroup1')
        self.assertEqual(og['opticsGroup1'], fog)
コード例 #10
0
class ProtRelionRefine3D(ProtRefine3D, ProtRelionBase):
    """ Protocol to refine a 3D map using Relion.

Relion employs an empirical Bayesian approach to refinement
of (multiple) 3D reconstructions
or 2D class averages in electron cryo-microscopy (cryo-EM). Many
parameters of a statistical model are learned from the data,which
leads to objective and high-quality results.
    """    
    _label = '3D auto-refine'
    IS_CLASSIFY = False
    CHANGE_LABELS = ['rlnChangesOptimalOrientations',
                     'rlnChangesOptimalOffsets',
                     'rlnOverallAccuracyRotations',
                     'rlnOverallAccuracyTranslationsAngst' if Plugin.IS_GT30() else 'rlnOverallAccuracyTranslations']

    PREFIXES = ['half1_', 'half2_']
    
    def __init__(self, **args):        
        ProtRelionBase.__init__(self, **args)
        
    def _initialize(self):
        """ This function is mean to be called after the 
        working dir for the protocol have been set.
        (maybe after recovery from mapper)
        """
        ProtRelionBase._initialize(self)
        self.ClassFnTemplate = '%(ref)03d@%(rootDir)s/relion_it%(iter)03d_classes.mrcs'

    # -------------------------- INSERT steps functions -----------------------
    def _setSamplingArgs(self, args):
        """ Set sampling related params"""
        args['--auto_local_healpix_order'] = self.localSearchAutoSamplingDeg.get()
        
        if not self.doContinue:
            args['--healpix_order'] = self.angularSamplingDeg.get()
            args['--offset_range'] = self.offsetSearchRangePix.get()
            f = self._getSamplingFactor()
            args['--offset_step'] = self.offsetSearchStepPix.get() * f
            args['--auto_refine'] = ''
            args['--split_random_halves'] = ''
            
            joinHalves = "--low_resol_join_halves"
            if joinHalves not in self.extraParams.get():
                args['--low_resol_join_halves'] = 40

            if self.IS_GT30() and self.useFinerSamplingFaster:
                args['--auto_ignore_angles'] = ''
                args['--auto_resol_angles'] = ''

    # -------------------------- STEPS functions ------------------------------
    def createOutputStep(self):
        imgSet = self._getInputParticles()
        vol = Volume()
        vol.setFileName(self._getExtraPath('relion_class001.mrc'))
        vol.setSamplingRate(imgSet.getSamplingRate())
        half1 = self._getFileName("final_half1_volume", ref3d=1)
        half2 = self._getFileName("final_half2_volume", ref3d=1)
        vol.setHalfMaps([half1, half2])

        outImgSet = self._createSetOfParticles()
        outImgSet.copyInfo(imgSet)
        self._fillDataFromIter(outImgSet, self._lastIter())

        self._defineOutputs(outputVolume=vol)
        self._defineSourceRelation(self.inputParticles, vol)
        self._defineOutputs(outputParticles=outImgSet)
        self._defineTransformRelation(self.inputParticles, outImgSet)

        fsc = FSC(objLabel=self.getRunName())
        fn = self._getExtraPath("relion_model.star")
        table = Table(fileName=fn, tableName='model_class_1')
        resolution_inv = table.getColumnValues('rlnResolution')
        frc = table.getColumnValues('rlnGoldStandardFsc')
        fsc.setData(resolution_inv, frc)

        self._defineOutputs(outputFSC=fsc)
        self._defineSourceRelation(vol, fsc)

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

        if self.IS_3D and self.solventFscMask and not self.referenceMask.get():
            errors.append('When using solvent-corrected FSCs, '
                          'please provide a reference mask.')

        return errors
    
    def _validateContinue(self):
        errors = []
        continueRun = self.continueRun.get()
        continueRun._initialize()
        lastIter = continueRun._lastIter()
        
        if self.continueIter.get() == 'last':
            continueIter = lastIter
        else:
            continueIter = int(self.continueIter.get())
        
        if continueIter > lastIter:
            errors += ["You can continue only from the iteration %01d or less" % lastIter]
        
        return errors
    
    def _summaryNormal(self):
        summary = []
        if not hasattr(self, 'outputVolume'):
            summary.append("Output volume not ready yet.")
            it = self._lastIter() or -1
            if it >= 1 and it > self._getContinueIter():
                table = Table(fileName=self._getFileName('half1_model', iter=it),
                              tableName='model_general')
                row = table[0]
                resol = float(row.rlnCurrentResolution)
                summary.append("Current resolution: *%0.2f A*" % resol)
        else:
            table = Table(fileName=self._getFileName('modelFinal'),
                          tableName='model_general')
            row = table[0]
            resol = float(row.rlnCurrentResolution)
            summary.append("Final resolution: *%0.2f A*" % resol)

        return summary
    
    def _summaryContinue(self):
        return ["Continue from iteration %01d" % self._getContinueIter()]

    # -------------------------- UTILS functions ------------------------------
    def _fillDataFromIter(self, imgSet, iteration):
        tableName = 'particles@' if self.IS_GT30() else ''
        outImgsFn = self._getFileName('data', iter=iteration)
        imgSet.setAlignmentProj()
        self.reader = convert.createReader(alignType=ALIGN_PROJ,
                                           pixelSize=imgSet.getSamplingRate())

        mdIter = Table.iterRows(tableName + outImgsFn, key='rlnImageId')
        imgSet.copyItems(self._getInputParticles(), doClone=False,
                         updateItemCallback=self._updateParticle,
                         itemDataIterator=mdIter)

    def _updateParticle(self, particle, row):
        self.reader.setParticleTransform(particle, row)

        if getattr(self, '__updatingFirst', True):
            self.reader.createExtraLabels(particle, row, PARTICLE_EXTRA_LABELS)
            self.__updatingFirst = False
        else:
            self.reader.setExtraLabels(particle, row)
コード例 #11
0
 def IS_GT30(self):
     return Plugin.IS_GT30()
コード例 #12
0
class ProtRelionClassify3D(ProtClassify3D, ProtRelionBase):
    """
    Protocol to classify 3D using Relion Bayesian approach.
    Relion employs an empirical Bayesian approach to refinement of (multiple)
    3D reconstructions or 2D class averages in electron cryo-EM. Many
    parameters of a statistical model are learned from the data, which
    leads to objective and high-quality results.
    """

    _label = '3D classification'
    CHANGE_LABELS = ['rlnChangesOptimalOrientations',
                     'rlnChangesOptimalOffsets',
                     'rlnOverallAccuracyRotations',
                     'rlnOverallAccuracyTranslationsAngst' if Plugin.IS_GT30() else 'rlnOverallAccuracyTranslations',
                     'rlnChangesOptimalClasses']
    
    def __init__(self, **args):        
        ProtRelionBase.__init__(self, **args)
        
    def _initialize(self):
        """ This function is mean to be called after the 
        working dir for the protocol have been set.
        (maybe after recovery from mapper)
        """
        ProtRelionBase._initialize(self)
    
    # -------------------------- INSERT steps functions -----------------------
    def _setSamplingArgs(self, args):
        """ Set sampling related params. """
        if self.doImageAlignment:
            args['--healpix_order'] = self.angularSamplingDeg.get()
            args['--offset_range'] = self.offsetSearchRangePix.get()
            args['--offset_step'] = self.offsetSearchStepPix.get() * self._getSamplingFactor()

            if self.localAngularSearch:
                args['--sigma_ang'] = self.localAngularSearchRange.get() / 3.

            if relion.Plugin.IS_GT30() and self.allowCoarserSampling:
                args['--allow_coarser_sampling'] = ''

        else:
            args['--skip_align'] = ''
    
    # -------------------------- STEPS functions ------------------------------
    def createOutputStep(self):
        partSet = self.inputParticles.get()
        classes3D = self._createSetOfClasses3D(partSet)
        self._fillClassesFromIter(classes3D, self._lastIter())
        
        self._defineOutputs(outputClasses=classes3D)
        self._defineSourceRelation(self.inputParticles, classes3D)

        # create a SetOfVolumes and define its relations
        volumes = self._createSetOfVolumes()
        volumes.setSamplingRate(partSet.getSamplingRate())
        
        for class3D in classes3D:
            vol = class3D.getRepresentative()
            vol.setObjId(class3D.getObjId())
            volumes.append(vol)
        
        self._defineOutputs(outputVolumes=volumes)
        self._defineSourceRelation(self.inputParticles, volumes)
        
        if not self.doContinue:
            self._defineSourceRelation(self.referenceVolume, classes3D)
            self._defineSourceRelation(self.referenceVolume, volumes)
    
    # -------------------------- INFO functions -------------------------------
    def _validateNormal(self):
        errors = []
        return errors
    
    def _validateContinue(self):
        errors = []
        continueRun = self.continueRun.get()
        continueRun._initialize()
        lastIter = continueRun._lastIter()
        
        if self.continueIter.get() == 'last':
            continueIter = lastIter
        else:
            continueIter = int(self.continueIter.get())
        
        if continueIter > lastIter:
            errors += ["You can continue only from the iteration %01d or less" % lastIter]
        
        return errors
    
    def _summaryNormal(self):
        summary = []
        it = self._lastIter() or -1
        if it >= 1:
            table = Table(fileName=self._getFileName('model', iter=it),
                          tableName='model_general')
            row = table[0]
            resol = float(row.rlnCurrentResolution)
            summary.append("Current resolution: *%0.2f A*" % resol)

        inputParts = self.inputParticles.get()
        sizeStr = 'None' if inputParts is None else inputParts.getSize()
        summary.append("Input Particles: *%s*\n"
                       "Classified into *%d* 3D classes\n"
                       % (sizeStr, self.numberOfClasses))
        
        return summary
    
    def _summaryContinue(self):
        summary = list()
        summary.append("Continue from iteration %01d" % self._getContinueIter())
        return summary
    
    def _methods(self):
        strline = ''
        if hasattr(self, 'outputClasses'):
            strline += 'We classified %d particles into %d 3D classes using Relion Classify3d. ' %\
                           (self.inputParticles.get().getSize(), self.numberOfClasses.get())
        return [strline]
    
    # -------------------------- UTILS functions ------------------------------
    def _fillClassesFromIter(self, clsSet, iteration):
        """ Create the SetOfClasses3D from a given iteration. """
        classLoader = convert.ClassesLoader(self, ALIGN_PROJ)
        classLoader.fillClassesFromIter(clsSet, iteration)
コード例 #13
0
    def _plotClassDistribution(self, paramName=None):
        labels = ["rlnClassDistribution", "rlnAccuracyRotations"]
        if Plugin.IS_GT30():
            labels.append("rlnAccuracyTranslationsAngst")
        else:
            labels.append("rlnAccuracyTranslations")

        iterations = range(self.firstIter, self.lastIter + 1)
        classInfo = {}

        for it in iterations:
            modelStar = self.protocol._getFileName('model', iter=it)
            table = Table(fileName=modelStar, tableName='model_classes')
            for row in table:
                i, fn = relionToLocation(row.rlnReferenceImage)
                if i == NO_INDEX:  # the case for 3D classes
                    # NOTE: Since there is not an proper ID value in
                    # the classes metadata, we are assuming that class X
                    # has a filename *_classXXX.mrc (as it is in Relion)
                    # and we take the ID from there
                    index = int(fn[-7:-4])
                else:
                    index = i

                if index not in classInfo:
                    classInfo[index] = {}
                    for l in labels:
                        classInfo[index][l] = []

                for l in labels:
                    classInfo[index][l].append(float(getattr(row, l)))

        xplotter = RelionPlotter()
        xplotter.createSubPlot("Classes distribution over iterations",
                               "Iterations", "Classes Distribution")

        # Empty list for each iteration
        iters = [[]] * len(iterations)

        l = labels[0]
        for index in sorted(classInfo.keys()):
            for it, value in enumerate(classInfo[index][l]):
                iters[it].append(value)

        ax = xplotter.getLastSubPlot()

        n = len(iterations)
        ind = range(n)
        bottomValues = [0] * n
        width = 0.45  # the width of the bars: can also be len(x) sequence

        def get_cmap(N):
            import matplotlib.cm as cmx
            import matplotlib.colors as colors
            """Returns a function that maps each index in 0, 1, ... N-1 to a distinct
            RGB color."""
            color_norm = colors.Normalize(vmin=0, vmax=N)  # -1)
            scalar_map = cmx.ScalarMappable(norm=color_norm, cmap='hsv')

            def map_index_to_rgb_color(ind):
                return scalar_map.to_rgba(ind)

            return map_index_to_rgb_color

        cmap = get_cmap(len(classInfo))

        for classId in sorted(classInfo.keys()):
            values = classInfo[classId][l]
            ax.bar(ind,
                   values,
                   width,
                   label='class %s' % classId,
                   bottom=bottomValues,
                   color=cmap(classId))
            bottomValues = [a + b for a, b in zip(bottomValues, values)]

        ax.get_xaxis().set_ticks([i + 0.25 for i in ind])
        ax.get_xaxis().set_ticklabels([str(i) for i in ind])
        ax.legend(loc='upper left', fontsize='xx-small')

        return [xplotter]
コード例 #14
0
    def _defineParams(self, form):
        form.addSection(label='Input')
        form.addParam('protRefine', params.PointerParam,
                      pointerClass="ProtRefine3D",
                      label='Select a previous refinement protocol',
                      help='Select any previous refinement protocol to get the '
                           '3D half maps. Note that it is recommended that the '
                           'refinement protocol uses a gold-standard method.')
        form.addParam('solventMask', params.PointerParam,
                      pointerClass="VolumeMask",
                      label='Solvent mask',
                      help="Provide a soft mask where the protein is white "
                           "(1) and the solvent is black (0). Often, the "
                           "softer the mask the higher resolution estimates "
                           "you will get. A soft edge of 5-10 pixels is often "
                           "a good edge width.")
        form.addParam('calibratedPixelSize', params.FloatParam, default=0,
                      label='Calibrated pixel size (A)',
                      help="Provide the final, calibrated pixel size in "
                           "Angstroms. If 0, the input pixel size will be used. "
                           "This value may be different from the pixel-size "
                           "used thus far, e.g. when you have recalibrated "
                           "the pixel size using the fit to a PDB model. "
                           "The X-axis of the output FSC plot will use this "
                           "calibrated value.")

        form.addSection(label='Sharpening')
        group = form.addGroup('MTF')
        group.addParam('mtf', params.FileParam,
                       label='MTF of the detector',
                       help='User-provided STAR-file with the MTF-curve '
                            'of the detector. Use the wizard to load one '
                            'of the predefined ones provided at:\n'
                            '- [[https://www3.mrc-lmb.cam.ac.uk/relion/index.php/'
                            'FAQs#Where_can_I_find_MTF_curves_for_typical_detectors.3F]'
                            '[Relion\'s Wiki FAQs]]\n'
                            ' - [[https://www.gatan.com/techniques/cryo-em#MTF][Gatan\'s website]]\n\n'
                            'Relion param: *--mtf*')
        if Plugin.IS_GT30():
            group.addParam('origPixelSize', params.FloatParam,
                           default=-1.0,
                           label='Original detector pixel size (A)',
                           help='This is the original pixel size (in Angstroms)'
                                ' in the raw (non-super-resolution!) micrographs')

        form.addParam('doAutoBfactor', params.BooleanParam, default=True,
                      label='Estimate B-factor automatically?',
                      help='If set to Yes, then the program will use the '
                           'automated procedure described by Rosenthal and '
                           'Henderson (2003, JMB) to estimate an overall '
                           'B-factor for your map, and sharpen it accordingly.')
        line = form.addLine('B-factor resolution (A): ',
                            condition='doAutoBfactor',
                            help='There are the frequency (in Angstroms), '
                                 'lowest and highest, that will be included in '
                                 'the linear fit of the Guinier plot as '
                                 'described in Rosenthal and Henderson '
                                 '(2003, JMB).')
        line.addParam('bfactorLowRes', params.FloatParam,
                      default=10.0, label='low')
        line.addParam('bfactorHighRes', params.FloatParam,
                      default=0.0, label='high')
        form.addParam('bfactor', params.FloatParam, default=-350,
                      condition='not doAutoBfactor',
                      label='Provide B-factor:',
                      help='User-provided B-factor (in A^2) for map '
                           'sharpening, e.g. -400. Use negative values for '
                           'sharpening. Be careful: if you over-sharpen\n'
                           'your map, you may end up interpreting noise for '
                           'signal!\n'
                           'Relion param: *--adhoc_bfac*')

        form.addSection(label='Filtering')
        form.addParam('skipFscWeighting', params.BooleanParam, default=False,
                      label='Skip FSC-weighting for sharpening?',
                      help='If set to No (the default), then the output map '
                           'will be low-pass filtered according to the '
                           'mask-corrected, gold-standard FSC-curve. '
                           'Sometimes, it is also useful to provide an ad-hoc '
                           'low-pass filter (option below), as due to local '
                           'resolution variations some parts of the map may '
                           'be better and other parts may be worse than the '
                           'overall resolution as measured by the FSC. In '
                           'such  cases, set this option to Yes and provide '
                           'an ad-hoc filter as described below.')
        form.addParam('lowRes', params.FloatParam, default=5,
                      condition='skipFscWeighting',
                      label='Ad-hoc low-pass filter (A):',
                      help='This option allows one to low-pass filter the map '
                           'at a user-provided frequency (in Angstroms). When '
                           'using a resolution that is higher than the '
                           'gold-standard FSC-reported resolution, take care '
                           'not to interpret noise in the map for signal...')
        form.addParam('filterEdgeWidth', params.IntParam, default=2,
                      expertLevel=params.LEVEL_ADVANCED,
                      label='Low-pass filter edge width:',
                      help='Width of the raised cosine on the low-pass filter '
                           'edge (in resolution shells)\n'
                           'Relion param: *--filter_edge_width*')
        form.addParam('randomizeAtFsc', params.FloatParam, default=0.8,
                      expertLevel=params.LEVEL_ADVANCED,
                      label='Randomize phases threshold',
                      help='Randomize phases from the resolution where FSC '
                           'drops below this value\n'
                           'Relion param: *--randomize_at_fsc*')

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