# *****************************************************************************
# *
# * 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):
""" Apply an operation to two sets of particles """
_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):
""" Apply an operation to two sets of volumes """
_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()