# *****************************************************************************
# *
# * Authors: Carlos Oscar Sanchez Sorzano (coss@cnb.csic.es), Sep 2013
# * Ported to Scipion:
# * Vahid Abrishami (vabrishami@cnb.csic.es), Oct 2014
# *
# * Refactored/Updated: Josue Gomez-Blanco (josue.gomez-blanco@mcgill.ca), Jun 2016
# *
# * 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'
# *
# *****************************************************************************
from collections import OrderedDict
from pwem.constants import ALIGN_NONE
import pyworkflow.protocol.params as params
from pwem.protocols import ProtOperateParticles, ProtOperateVolumes
from xmipp3.convert import (writeSetOfParticles, writeSetOfVolumes,
getImageLocation)
from .protocol_process import XmippProcessParticles, XmippProcessVolumes
# Operands enum
OP_PLUS = 0
OP_MINUS = 1
OP_MULTIPLY = 2
OP_DIVIDE = 3
OP_MINIMUM = 4
OP_MAXIMUM = 5
OP_DOTPRODUCT = 6
OP_LOG = 7
OP_LOG10 = 8
OP_SQRT = 9
OP_ABS = 10
OP_POW = 11
OP_SLICE = 12
OP_COLUNM = 13
OP_ROW = 14
OP_RADIAL = 15
OP_RESET = 16
OP_CHOICES = OrderedDict() #[-1]*(OP_RESET+1)
OP_CHOICES[OP_PLUS] = 'plus'
OP_CHOICES[OP_MINUS] = 'minus'
OP_CHOICES[OP_MULTIPLY] = 'multiply'
OP_CHOICES[OP_DIVIDE] = 'divide'
OP_CHOICES[OP_MINIMUM] = 'minimum'
OP_CHOICES[OP_MAXIMUM] = 'maximum'
OP_CHOICES[OP_DOTPRODUCT] = 'dot product'
OP_CHOICES[OP_LOG] = 'log'
OP_CHOICES[OP_LOG10] = 'log10'
OP_CHOICES[OP_SQRT] = 'sqrt'
OP_CHOICES[OP_ABS] = 'abs'
OP_CHOICES[OP_POW] = 'pow'
OP_CHOICES[OP_COLUNM] = 'colunm'
OP_CHOICES[OP_SLICE] = 'slice'
OP_CHOICES[OP_ROW] = 'row'
OP_CHOICES[OP_RADIAL] = 'radial average'
OP_CHOICES[OP_RESET] = 'reset'
binaryCondition = ('(operation == %d or operation == %d or operation == %d or '
'operation == %d or operation == %d or operation == %d) ' %
(OP_PLUS, OP_MINUS, OP_MULTIPLY,
OP_DIVIDE, OP_MINIMUM, OP_MAXIMUM))
#noValueCondition = '(operation == 7 or operation == 8 or operation == 9 or '\
# 'operation == 10 or operation == 15 or operation == 16) '
noValueCondition = '(operation == %d or operation == %d or operation == %d or '\
'operation == %d or operation == %d or operation == %d) '%\
(OP_LOG, OP_LOG10, OP_SQRT,\
OP_ABS, OP_POW, OP_RESET)
intValueCondition = '(operation == %d or operation == %d)'%(OP_COLUNM, OP_ROW)
dotCondition = 'operation == %d'%OP_DOTPRODUCT
powCondition = 'operation == %d'%OP_POW
operationDict = {OP_PLUS : ' --plus ', OP_MINUS : ' --minus ',
OP_MULTIPLY : ' --mult ', OP_DIVIDE : ' --divide ',
OP_MINIMUM : ' --min ', OP_MAXIMUM : ' --max ',
OP_DOTPRODUCT : ' --dot_product ', OP_LOG : ' --log ',
OP_LOG10 : ' --log10', OP_SQRT : ' --sqrt ',
OP_ABS : ' --abs ', OP_POW : ' --pow ',
OP_SLICE : ' --slice ', OP_RADIAL : ' --radial_avg ',
OP_RESET : ' --reset ', OP_COLUNM: '--column',
OP_ROW: '--row'}
[docs]class XmippOperateHelper():
""" Some image operations such as: Dot product or Summation. """
_label = 'image operate'
def __init__(self, **args):
self._program = "xmipp_image_operate"
#--------------------------- DEFINE param functions -----------------------
def _defineProcessParams(self, form):
form.addParam('operation', params.EnumParam, choices=list(OP_CHOICES.values()),
default=OP_PLUS,
label="Operation",
help="Binary operations: \n"
"*plus*: Sums two images, volumes or adds a "
"numerical value to an image. \n"
"*minus*: Subtracts two images, volumes or "
"subtracts a numerical value to an image. \n"
"*multiply*: Multiplies two images, volumes, or "
"multiplies per a given number. \n"
"*divide*: Divides two images, volumes, or divides "
"per a given number. \n"
"*minimum*: Minimum of two images, volumes, or "
"number (pixel-wise). \n"
"*maximum*: Maximum of two images, volumes, or "
"number (pixel-wise). \n"
"*dot product*: Dot product between two images or"
" volumes. \n"
"Unary operations: \n"
"*log*: Computes the natural logarithm of an "
"image. \n"
"*log10*: Computes the decimal logarithm of "
"an image. \n"
"*sqrt*: Computes the square root of an image \n"
"*abs*: Computes the absolute value of an image. \n"
"*pow*: Computes the power of an image. \n"
"*slice*: Extracts a given slice from a volume "
"(first slice=0). \n"
"*column*: Extracts a given column from a image "
"or volume. \n"
"*row*: Extracts a given row from a image or "
"volume. \n"
"*radial average*: Compute the radial average of "
"an image. \n"
"*reset*: Set the image to 0")
form.addParam('isValue', params.BooleanParam, default=False,
label="Second operand is a value?",
condition=binaryCondition,
help="Set to true if you want to use a "
"value of the second operand")
self._defineSpecificParams(form)
form.addParam('value', params.FloatParam,
allowNull=True,
condition='isValue and %s or %s' %
(binaryCondition, powCondition),
label='Input value ',
help = 'Set the desire float value')
form.addParam('intValue', params.IntParam,
allowNull=True,
condition=intValueCondition,
label='Input value ',
help = 'This value must be integer')
#--------------------------- INSERT STEPS functions ------------------------
def _insertProcessStep(self):
operationStr = operationDict[self.operation.get()]
self._insertFunctionStep("operationStep", operationStr)
#--------------------------- UTILS functions ------------------------------
def _isBinaryCond(self):
operation = self.operation.get()
return (operation == OP_PLUS or operation == OP_MINUS or
operation == OP_MULTIPLY or operation == OP_DIVIDE
or operation == OP_MINIMUM or operation == OP_MAXIMUM)
def _isNoValueCond(self):
operation = self.operation.get()
return (operation == OP_LOG or operation == OP_LOG10 or
operation == OP_SQRT or operation == OP_ABS or
operation == OP_RADIAL or operation == OP_RESET)
def _isPowCond(self):
operation = self.operation.get()
return operation == OP_POW
def _isDotCond(self):
operation = self.operation.get()
return operation == OP_DOTPRODUCT
def _isintValueCond(self):
operation = self.operation.get()
return (operation == OP_SLICE or operation == OP_COLUNM
or operation == OP_ROW)
def _getSecondSetFn(self):
return self._getTmpPath("images_to_apply.xmd")
[docs]class XmippProtImageOperateParticles(ProtOperateParticles,
XmippProcessParticles,
XmippOperateHelper):
"""Performs operations between two sets of particles or to a set of particles. The mathematical operations are: plus, minus multiply, divide, minimum, maximum, dot product, log, log10, sqrt, abs, pow, column, slice, row, mn, radial average or reset. This protocol supports comparative analysis or creation of difference maps between conditions. """
_label = 'operate particles'
def __init__(self, **args):
ProtOperateParticles.__init__(self, **args)
XmippProcessParticles.__init__(self)
XmippOperateHelper.__init__(self, **args)
self.allowMpi = False
self.allowThreads = False
#--------------------------- DEFINE param functions -----------------------
def _defineProcessParams(self, form):
XmippOperateHelper._defineProcessParams(self, form)
def _defineSpecificParams(self, form):
form.addParam('inputParticles2', params.PointerParam,
allowNull=True,
condition=(binaryCondition+' and (not isValue) or '
+dotCondition),
label='Input Particles (2nd)',
help = 'Set a SetOfParticles. The particles must be '
'the same dimensions as the input particles.',
pointerClass='SetOfParticles')
#--------------------------- STEPS functions ------------------------------
[docs] def convertInputStep(self):
""" convert to Xmipp image model"""
writeSetOfParticles(self.inputParticles.get(), self.inputFn,
alignType=ALIGN_NONE)
if self.inputParticles2.get() is not None:
writeSetOfParticles(self.inputParticles2.get(),
self._getSecondSetFn(),
alignType=ALIGN_NONE)
[docs] def operationStep(self, operationStr):
dictImgFn = {"inputFn" : self.inputFn}
args = self._args % dictImgFn + operationStr
if self._isBinaryCond():
if self.isValue:
args += ' %f' % self.value.get()
else:
args += ' %s' % self._getSecondSetFn()
elif self._isPowCond():
args += ' %f' % self.value.get()
elif self._isDotCond():
args += ' %s' % self._getSecondSetFn()
elif self._isintValueCond():
args += ' %d' % self.intValue.get()
args += " -o %s --save_metadata_stack %s" % (self.outputStk,
self.outputMd)
args += " --keep_input_columns"
self.runJob(self._program, args)
#--------------------------- INFO functions -------------------------------
def _validate(self):
errors = []
def _errorDimensions():
if not (self.inputParticles.get() is None and
self.inputParticles2.get() is None):
dim1 = self.inputParticles.get().getDimensions()
dim2 = self.inputParticles2.get().getDimensions()
if dim1 != dim2:
errors.append("The number of images in two operands are "
"not the same. ")
def _errorSize():
if not (self.inputParticles.get() is None and
self.inputParticles2.get() is None):
size1 = self.inputParticles.get().getSize()
size2 = self.inputParticles2.get().getSize()
if size1 != size2:
errors.append("Size of both *SetOfParticles* are not the"
" same. ")
if self._isBinaryCond():
if not self.isValue:
if not self.inputParticles2.get() is None:
_errorSize()
_errorDimensions()
elif self._isDotCond():
if not self.inputParticles2.get() is None:
_errorDimensions()
return errors
[docs]class XmippProtImageOperateVolumes(ProtOperateVolumes,
XmippProcessVolumes,
XmippOperateHelper):
""" A Executes arithmetic operations between two volumesor to a volume. The mathematical operations are: plus, minus multiply, divide, minimum, maximum, dot product, log, log10, sqrt, abs, pow, column, slice, row, mn, radial average or resetThis enables structural comparisons, highlighting conformational changes or shared features between datasets."""
_label = 'operate volumes'
def __init__(self, **args):
ProtOperateVolumes.__init__(self, **args)
XmippProcessVolumes.__init__(self)
XmippOperateHelper.__init__(self, **args)
self.allowMpi = False
self.allowThreads = False
#--------------------------- DEFINE param functions -----------------------
def _defineProcessParams(self, form):
XmippOperateHelper._defineProcessParams(self, form)
def _defineSpecificParams(self, form):
form.addParam('inputVolumes2', params.PointerParam,
allowNull=True,
condition=(binaryCondition + ' and (not isValue) or '
+ dotCondition),
label='Input Volumes (2nd)',
help = 'This parameter depends of the input volume(s). '
'If it is set a volume (or a SetOfVolumes) as '
'input, this must be a *Volume* (or '
'*SetOfVolumes*) object.',
pointerClass='Volume, SetOfVolumes')
#--------------------------- STEPS functions ------------------------------
[docs] def operationStep(self, operationStr):
dictImgFn = {"inputFn" : self.inputFn}
args = self._args % dictImgFn + operationStr
if self._isBinaryCond():
if self.isValue:
args += ' %f' % self.value.get()
else:
args += ' %s' % self._getSecondVolumeFn()
elif self._isPowCond():
args += ' %f' % self.value.get()
elif self._isDotCond():
args += ' %s' % self._getSecondVolumeFn()
elif self._isintValueCond():
args += ' %d' % self.intValue.get()
args += " -o %s " % self.outputStk
if not self._isSingleInput():
args += " --save_metadata_stack %s" \
" --keep_input_columns" % self.outputMd
self.runJob(self._program, args)
#--------------------------- INFO functions -------------------------------
def _validate(self):
errors = []
def _errorDimensions():
if not (self.inputVolumes.get() is None and
self.inputVolumes2.get() is None):
dim1 = self.inputVolumes.get().getDimensions()
dim2 = self.inputVolumes2.get().getDimensions()
if dim1 != dim2:
errors.append("The number of volumes in two operands are "
"not the same. ")
def _errorSize():
if (not (self.inputVolumes.get() is None and
self.inputVolumes2.get() is None) and
not self._isSingleInput()):
size1 = self.inputVolumes.get().getSize()
size2 = self.inputVolumes2.get().getSize()
if size1 != size2:
errors.append("Size of both volumes are not the same. ")
if self._isBinaryCond():
if not self.isValue:
if not self.inputVolumes2.get() is None:
_errorSize()
_errorDimensions()
elif self._isDotCond():
if not self.inputVolumes2.get() is None:
_errorDimensions()
return errors
#--------------------------- UTILS functions ------------------------------
def _getSecondVolumeFn(self):
if self._isSingleInput():
return getImageLocation(self.inputVolumes2.get())
else:
return self._getSecondSetFn()