# **************************************************************************
# *
# * Authors: J.M. De la Rosa Trevin (delarosatrevin@scilifelab.se)
# * Tapu Shaikh (shaikh@ceitec.muni.cz)
# *
# * 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 pwem.protocols import ProtFilterParticles
from pyworkflow.protocol.params import (EnumParam, BooleanParam,
DigFreqParam, FloatParam)
from pyworkflow.utils.path import removeBaseExt
from pyworkflow.constants import PROD
from ..constants import *
from ..utils import SpiderShell
from .protocol_base import SpiderProtocol
[docs]class SpiderProtFilter(ProtFilterParticles, SpiderProtocol):
""" Apply Fourier filters to an image or a volume using Spider FQ or FQ NP.
To improve boundary quality the image is padded
with the average value to twice the original size
during filtration if padding is selected.
See more documentation at:
[[http://spider.wadsworth.org/spider_doc/spider/docs/man/fq.html][SPIDER's FQ online manual]]
"""
_label = 'filter particles'
_devStatus = PROD
def __init__(self, **kwargs):
ProtFilterParticles.__init__(self, **kwargs)
SpiderProtocol.__init__(self, **kwargs)
# To avoid showing MPI box due to duplicated init
self.allowMpi = False
self._op = "FQ"
self._params = {'ext': 'stk',
'particles': 'particles_filtered',
'particlesSel': 'particles_filtered_sel'}
# --------------------------- DEFINE param functions ----------------------
def _defineProcessParams(self, form):
form.addParam('filterType', EnumParam,
choices=['Top-hat', 'Gaussian', 'Fermi',
'Butterworth', 'Raised cosine'],
label="Filter type", default=FILTER_BUTTERWORTH,
help="""Select what type of filter do you want to apply.
*Top-hat*: Filter is a "top-hat" function
that truncates the Fourier transform at spatial frequency.
*Gaussian*: Filter is the Gaussian function: EXP(-F**2 / (2 * SPF**2)),
where F is the frequency.
*Fermi*: Filter is: 1 / (1 + EXP((F - SPF) / T)) which negotiates
between "Top-hat" and Gaussian characteristics, depending on
the value of the temperature T.
*Butterworth* Filter is: 1 / (SQRT(1 + F / RAD)**(2 * ORDER))
The ORDER determines the filter fall off and RAD corresponds
to the cut-off radius.
*Raised cosine* Filter is: 0.5 * (COS(PI * (F - Flow) / (Flow - Fup)) + 1)
if Flow < F < Fup, 1 if F < Flow, and 0 if F > Fup
See detailed description of the filter at [[http://spider.wadsworth.org/spider_doc/spider/docs/man/fq.html][SPIDER's FQ online manual]]
""")
form.addParam('filterMode', EnumParam, choices=['low-pass', 'high-pass'],
label='Filter mode', default=FILTER_LOWPASS)
form.addParam('usePadding', BooleanParam, default=True,
label='Use padding?',
help='If set to *Yes*, to improve boundary quality\n'
'the image is padded with the average value to\n'
'twice the original size during filtration.\n\n'
'If *No* padding is applied, this may lead to\n'
'artifacts near boundary of image.')
form.addParam('filterRadius', DigFreqParam, default=0.12,
label='Filter radius (0 < f < 0.5)',
condition='filterType <= %d or filterType == %d' %
(FILTER_SPACE_REAL, FILTER_FERMI),
help='Low frequency cutoff to apply the filter.\n')
line = form.addLine('Frequency',
condition='filterType > %d' % FILTER_FERMI,
help='Range to apply the filter. Expected values between 0 and 0.5.')
line.addParam('lowFreq', DigFreqParam, default=0.1, label='Lowest')
line.addParam('highFreq', DigFreqParam, default=0.2, label='Highest')
form.addParam('temperature', FloatParam, default=0.3,
label='Temperature T:',
condition='filterType == %d' % FILTER_FERMI,
help='Enter a temperature parameter T The filter falls off roughly within \n'
'this reciprocal distance (in terms of frequency units).')
# --------------------------- INSERT steps functions ----------------------
def _insertAllSteps(self):
# Define some names
self.particlesStk = self._getPath('%(particles)s.%(ext)s' % self._params)
# Insert processing steps
self._insertFunctionStep('convertInput', 'inputParticles',
self._getFileName('particles'),
self._getFileName('particlesSel'))
self._insertFunctionStep('filterStep', self.filterType.get())
self._insertFunctionStep('createOutputStep')
# --------------------------- STEPS functions -----------------------------
[docs] def filterStep(self, filterType):
""" Apply the selected filter to particles.
Create the set of particles.
"""
particles = self.inputParticles.get()
n = particles.getSize()
OP = self._op
args = []
if not self.usePadding:
OP += ' NP'
if filterType <= FILTER_FERMI:
args.append(self.filterRadius.get())
else:
args.append('%f %f' % (self.lowFreq, self.highFreq))
if filterType == FILTER_FERMI:
args.append(self.temperature.get())
# Map to expected filter number in Spider for operation FQ
filterNumber = filterType * 2 + 1
# Consider low-pass or high-pass
filterNumber += self.filterMode.get()
self._enterWorkingDir() # Do operations inside the run working dir
spi = SpiderShell(ext=self.getExt()) # Create the Spider process to send commands
particlesStk = removeBaseExt(self.particlesStk)
# Run a loop for filtering
locStr = particlesStk + '@******[part]'
cmds = ['do lb5 [part] = 1,%d' % n,
OP, locStr, locStr, filterNumber] + args + ['lb5']
for c in cmds:
spi.runCmd(c)
spi.close()
self._leaveWorkingDir() # Go back to project dir
[docs] def createOutputStep(self):
particles = self.inputParticles.get()
imgSet = self._createSetOfParticles()
imgSet.copyInfo(particles)
updateItem = lambda p, i: p.setLocation(i, self.particlesStk)
imgSet.copyItems(particles,
updateItemCallback=updateItem,
itemDataIterator=iter(range(1, particles.getSize()+1)))
self._defineOutputs(outputParticles=imgSet)
self._defineTransformRelation(particles, imgSet)
# --------------------------- INFO functions ----------------------------------
def _validate(self):
errors = []
return errors
def _citations(self):
cites = []
return cites
def _summary(self):
pixelSize = self.inputParticles.get().getSamplingRate()
summary = list()
summary.append('Used filter: *%s %s*' %
(self.getEnumText('filterType'),
self.getEnumText('filterMode')))
if self.filterType <= FILTER_FERMI:
summary.append('Filter radius: *%s px^-1*' % self.filterRadius)
radiusAngstroms = pixelSize / self.filterRadius.get()
summary.append('Filter radius: *%s Angstroms*' % radiusAngstroms)
else:
summary.append('Frequency range: *%s - %s*' % (self.lowFreq,
self.highFreq))
if self.filterType == FILTER_FERMI:
summary.append('Temperature factor: *%s*' % self.temperature)
summary.append('Padding set to: *%s*' % self.usePadding)
return summary
def _methods(self):
methods = []
msg = '\nInput particles %s were %s filtered using a %s filter' %\
(self.getObjectTag('inputParticles'),
self.getEnumText('filterMode'),
self.getEnumText('filterType'))
if self.filterType <= FILTER_FERMI:
msg += ', using a radius of %s px^-1' % self.filterRadius
else:
msg += ', using a frequency range of %s to %s px^-1' %\
(self.lowFreq, self.highFreq)
if self.filterType == FILTER_FERMI:
msg += ' and a temperature factor of of %s px^-1' % self.temperature
if self.usePadding:
msg += ', padding the images by a factor of two.'
else:
msg += ' with no padding.'
methods.append(msg)
methods.append('Output particles: %s' %
self.getObjectTag('outputParticles'))
return methods