Source code for spider.protocols.protocol_custommask

# **************************************************************************
# *
# * Authors:     J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se)
# *
# * 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 3 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
# * 02111-1307  USA
# *
# *  All comments concerning this program package may be sent to the
# *  e-mail address 'scipion@cnb.csic.es'
# *
# **************************************************************************

from pyworkflow.constants import PROD
from pyworkflow.protocol.params import PointerParam, FloatParam
from pwem.protocols import ProtCreateMask2D
from pwem.objects import Mask
from pwem.emlib.image import ImageHandler

from ..utils import runCustomMaskScript
from .protocol_base import SpiderProtocol


[docs]class SpiderProtCustomMask(ProtCreateMask2D, SpiderProtocol): """ This protocol creates a 2D mask using SPIDER. In the step following this one, dimension-reduction, the covariance of the pixels in all images will be computed. Only pixels under a given mask will be analyzed. If this step is performed, a mask that follows closely the contour the particle of interest will be used. Absent a custom-made mask, a circular mask will be used. For non-globular structures, this customized mask will reduce computational demand and the likelihood of numerical inaccuracy in the next dimension-reduction step. On the other hand, given the power of modern computers, this step may be unnecessary. """ _label = 'create 2d mask' _devStatus = PROD def __init__(self, **kwargs): ProtCreateMask2D.__init__(self, **kwargs) SpiderProtocol.__init__(self, **kwargs) # To avoid showing MPI box due to duplicated init self.allowMpi = False self._params = {'ext': 'stk', 'inputImage': 'input_image', 'outputMask': 'output_mask' } # --------------------------- DEFINE param functions ---------------------- def _defineParams(self, form): form.addSection(label='Input') form.addParam('inputImage', PointerParam, label="Input image", important=True, pointerClass='Particle', help='Select the input image to create the mask. \n' 'It is recommended to used an average image.') form.addParam('filterRadius1', FloatParam, default=0.1, label='Fourier radius for input image ' '(range: 0 - 0.5 px^-1)', help='The input image will be low-pass filtered to ' 'smooth any jagged edges.') form.addParam('sdFactor', FloatParam, default=0.6, label='First threshold (units of standard deviations)', help='The filtered image will be thresholded at the ' 'average plus this number * st.dev.') form.addParam('filterRadius2', FloatParam, default=0.1, label='Fourier radius for intermediate mask ' '(range: 0 - 0.5)', help='The intermediate thresholded mask will be again ' 'filtered for further smoothing.') form.addParam('maskThreshold', FloatParam, default=0.01, label='Mask threshold (range: approx. 0 - 1)', help='The filtered intermediate mask will be thresholded ' 'to generate the final mask.') # --------------------------- INSERT steps functions ---------------------- def _insertAllSteps(self): # Store references of input converted image filename # and the input image object self.outFn = self._getFileName('inputImage') self.inputImg = self.inputImage.get() # Convert the input image to Spider format self._insertFunctionStep('convertInputStep', self.inputImg.getLocation(), self._getFileName('inputImage')) # Run Spider script to generate the custom mask self._insertFunctionStep('createMaskStep', self.filterRadius1.get(), self.sdFactor.get(), self.filterRadius2.get(), self.maskThreshold.get()) # Create the output Mask object self._insertFunctionStep('createOutputStep') # --------------------------- STEPS functions -----------------------------
[docs] def convertInputStep(self, inputLoc, outputFn): """ Convert the input image to a Spider (with stk extension). """ ImageHandler().convert(inputLoc, (1, outputFn))
[docs] def createMaskStep(self, filterRadius1, sdFactor, filterRadius2, maskThreshold): """ Apply the selected filter to particles. Create the set of particles. """ runCustomMaskScript(filterRadius1, sdFactor, filterRadius2, maskThreshold, workingDir=self._getPath(), ext=self.getExt(), inputImage=self._params['inputImage']+'@1', outputMask=self._params['outputMask'])
[docs] def createOutputStep(self): maskFn = self._getFileName('outputMask') mask = Mask() mask.copyInfo(self.inputImg) mask.setLocation(4, maskFn) self._defineOutputs(outputMask=mask) self._defineSourceRelation(self.inputImage, mask)
# --------------------------- INFO functions ------------------------------ def _summary(self): summary = [] if self.inputImage.hasValue(): pixelSize = self.inputImage.get().getSamplingRate() filter1Angstroms = pixelSize / self.filterRadius1.get() filter2Angstroms = pixelSize / self.filterRadius2.get() summary.append('Radius for initial Fourier filter: *%s px^-1*' % self.filterRadius1) summary.append(' On the object scale: *%s Angstroms*' % filter1Angstroms) summary.append('Threshold for filtered image: *avg + %s s.d.*' % self.sdFactor) summary.append('Radius for Fourier filter for intermediate mask: *%s px^-1*' % self.filterRadius2) summary.append(' On the object scale: *%s Angstroms*' % filter2Angstroms) summary.append('Threshold for filtered mask: *%s*' % self.maskThreshold) else: summary.append('Input image not selected yet.') return summary def _methods(self): if self.inputImage.hasValue(): pixelSize = self.inputImage.get().getSamplingRate() filter1Angstroms = pixelSize/self.filterRadius1.get() filter2Angstroms = pixelSize/self.filterRadius2.get() msg = "We low-pass filtered the average image %s" %\ self.getObjectTag('inputImage') msg += "to 1/%s Angstroms^-1, " % filter1Angstroms msg += "thresholded it at its average plus %s * s.d., "\ % self.sdFactor msg += "low-pass filtered this intermediate mask " msg += "to 1/%s Angstroms^-1, " % filter2Angstroms msg += "and finally thresholded this filtered mask at a value of %s." % self.maskThreshold msg += 'For multivariate data analysis, a custom mask was generated: %s.' %\ self.getObjectTag('outputMask') else: msg = 'Input image not selected yet.' return [msg]