Permalink
Browse files

Add #76 (radd/rsub/rmul/rpow/rdiv for params)

  • Loading branch information...
aleju committed Nov 19, 2017
1 parent 4c269f6 commit 776d03dc8faa63a037ae1d559ded371ddd33b0b2
Showing with 134 additions and 61 deletions.
  1. +124 −59 imgaug/parameters.py
  2. +10 −2 tests/check_parameters.py
View
@@ -10,6 +10,8 @@
import numbers
from collections import defaultdict
NP_FLOAT_TYPES = set(np.sctypes["float"])
def handle_continuous_param(param, name, value_range=None, tuple_to_uniform=True, list_to_choice=True):
def check_value_range(v):
if value_range is None:
@@ -99,6 +101,24 @@ def check_value_range(v):
else:
raise Exception("Expected int, tuple of two int, list of int or StochasticParameter for %s, got %s." % (name, type(param),))
def force_np_float_dtype(val):
if val.dtype in NP_FLOAT_TYPES:
return val
else:
return val.astype(np.float32)
def both_np_float_if_one_is_float(a, b):
a_f = a.dtype in NP_FLOAT_TYPES
b_f = b.dtype in NP_FLOAT_TYPES
if a_f and b_f:
return a, b
elif a_f:
return a, b.astype(np.float32)
elif b_f:
return a.astype(np.float32), b
else:
return a.astype(np.float32), b.astype(np.float32)
def draw_distributions_grid(params, rows=None, cols=None, graph_sizes=(350, 350), sample_sizes=None, titles=None):
if titles is None:
titles = [None] * len(params)
@@ -209,7 +229,7 @@ def __mul__(self, other):
def __pow__(self, other, z=None):
if z is not None:
raise NotImplementedError("Modulo power is currently supported by StochasticParameter.")
raise NotImplementedError("Modulo power is currently not supported by StochasticParameter.")
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Power(self, other)
else:
@@ -227,6 +247,44 @@ def __truediv__(self, other):
else:
raise Exception("Invalid datatypes in: StochasticParameter / %s (truediv). Expected second argument to be number or StochasticParameter." % (type(other),))
def __radd__(self, other):
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Add(other, self)
else:
raise Exception("Invalid datatypes in: %s + StochasticParameter. Expected second argument to be number or StochasticParameter." % (type(other),))
def __rsub__(self, other):
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Subtract(other, self)
else:
raise Exception("Invalid datatypes in: %s - StochasticParameter. Expected second argument to be number or StochasticParameter." % (type(other),))
def __rmul__(self, other):
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Multiply(other, self)
else:
raise Exception("Invalid datatypes in: %s * StochasticParameter. Expected second argument to be number or StochasticParameter." % (type(other),))
def __rpow__(self, other, z=None):
if z is not None:
raise NotImplementedError("Modulo power is currently not supported by StochasticParameter.")
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Power(other, self)
else:
raise Exception("Invalid datatypes in: %s ** StochasticParameter. Expected second argument to be number or StochasticParameter." % (type(other),))
def __rdiv__(self, other):
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Divide(other, self)
else:
raise Exception("Invalid datatypes in: %s / StochasticParameter. Expected second argument to be number or StochasticParameter." % (type(other),))
def __rtruediv__(self, other):
if ia.is_single_number(other) or isinstance(other, StochasticParameter):
return Divide(other, self)
else:
raise Exception("Invalid datatypes in: %s / StochasticParameter (truediv). Expected second argument to be number or StochasticParameter." % (type(other),))
def copy(self):
"""
Create a shallow copy of this parameter.
@@ -1200,7 +1258,7 @@ class Multiply(StochasticParameter):
Parameters
----------
other_param : StochasticParameter
other_param : number or tuple of two number or list of number or StochasticParameter
Other parameter which's sampled values are to be
multiplied.
@@ -1227,29 +1285,31 @@ class Multiply(StochasticParameter):
def __init__(self, other_param, val, elementwise=False):
super(Multiply, self).__init__()
assert isinstance(other_param, StochasticParameter)
self.other_param = other_param
self.other_param = handle_continuous_param(other_param, "other_param")
self.val = handle_continuous_param(val, "val")
self.elementwise = elementwise
def _draw_samples(self, size, random_state):
seed = random_state.randint(0, 10**6, 1)[0]
samples = self.other_param.draw_samples(size, random_state=ia.new_random_state(seed))
if self.elementwise and not isinstance(self.val, Deterministic):
return np.multiply(
samples,
self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
)
elementwise = self.elementwise and not isinstance(self.val, Deterministic)
if elementwise:
val_samples = self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
else:
return samples * self.val.draw_sample(random_state=ia.new_random_state(seed+1))
val_samples = self.val.draw_sample(random_state=ia.new_random_state(seed+1))
if elementwise:
return np.multiply(samples, val_samples)
else:
return samples * val_samples
def __repr__(self):
return self.__str__()
def __str__(self):
opstr = str(self.other_param)
return "Multiply(%s, %s, %s)" % (opstr, str(self.val), self.elementwise)
return "Multiply(%s, %s, %s)" % (str(self.other_param), str(self.val), self.elementwise)
class Divide(StochasticParameter):
"""
@@ -1260,7 +1320,7 @@ class Divide(StochasticParameter):
Parameters
----------
other_param : StochasticParameter
other_param : number or tuple of two number or list of number or StochasticParameter
Other parameter which's sampled values are to be
divided.
@@ -1287,16 +1347,17 @@ class Divide(StochasticParameter):
def __init__(self, other_param, val, elementwise=False):
super(Divide, self).__init__()
assert isinstance(other_param, StochasticParameter)
self.other_param = other_param
self.other_param = handle_continuous_param(other_param, "other_param")
self.val = handle_continuous_param(val, "val")
self.elementwise = elementwise
def _draw_samples(self, size, random_state):
seed = random_state.randint(0, 10**6, 1)[0]
samples = self.other_param.draw_samples(size, random_state=ia.new_random_state(seed))
if self.elementwise and not isinstance(self.val, Deterministic):
elementwise = self.elementwise and not isinstance(self.val, Deterministic)
if elementwise:
val_samples = self.val.draw_samples(
size,
random_state=ia.new_random_state(seed+1)
@@ -1306,8 +1367,8 @@ def _draw_samples(self, size, random_state):
val_samples[val_samples == 0] = 1
return np.multiply(
samples,
val_samples.astype(np.float32)
force_np_float_dtype(samples),
force_np_float_dtype(val_samples)
)
else:
val_sample = self.val.draw_sample(
@@ -1318,22 +1379,22 @@ def _draw_samples(self, size, random_state):
if val_sample == 0:
val_sample = 1
return samples / float(val_sample)
return force_np_float_dtype(samples) / float(val_sample)
def __repr__(self):
return self.__str__()
def __str__(self):
opstr = str(self.other_param)
return "Divide(%s, %s, %s)" % (opstr, str(self.val), self.elementwise)
return "Divide(%s, %s, %s)" % (str(self.other_param), str(self.val), self.elementwise)
class Add(StochasticParameter):
"""
Parameter to add to other parameter's results.
Parameters
----------
other_param : StochasticParameter
other_param : number or tuple of two number or list of number or StochasticParameter
Other parameter which's sampled values are to be
modified.
@@ -1359,37 +1420,39 @@ class Add(StochasticParameter):
def __init__(self, other_param, val, elementwise=False):
super(Add, self).__init__()
assert isinstance(other_param, StochasticParameter)
self.other_param = other_param
self.other_param = handle_continuous_param(other_param, "other_param")
self.val = handle_continuous_param(val, "val")
self.elementwise = elementwise
def _draw_samples(self, size, random_state):
seed = random_state.randint(0, 10**6, 1)[0]
samples = self.other_param.draw_samples(size, random_state=ia.new_random_state(seed))
if self.elementwise and not isinstance(self.val, Deterministic):
return np.add(
samples,
self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
)
elementwise = self.elementwise and not isinstance(self.val, Deterministic)
if elementwise:
val_samples = self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
else:
val_samples = self.val.draw_sample(random_state=ia.new_random_state(seed+1))
if elementwise:
return np.add(samples, val_samples)
else:
return samples + self.val.draw_sample(random_state=ia.new_random_state(seed+1))
return samples + val_samples
def __repr__(self):
return self.__str__()
def __str__(self):
opstr = str(self.other_param)
return "Add(%s, %s, %s)" % (opstr, str(self.val), self.elementwise)
return "Add(%s, %s, %s)" % (str(self.other_param), str(self.val), self.elementwise)
class Subtract(StochasticParameter):
"""
Parameter to subtract from another parameter's results.
Parameters
----------
other_param : StochasticParameter
other_param : number or tuple of two number or list of number or StochasticParameter
Other parameter which's sampled values are to be
modified.
@@ -1415,37 +1478,39 @@ class Subtract(StochasticParameter):
def __init__(self, other_param, val, elementwise=False):
super(Subtract, self).__init__()
assert isinstance(other_param, StochasticParameter)
self.other_param = other_param
self.other_param = handle_continuous_param(other_param, "other_param")
self.val = handle_continuous_param(val, "val")
self.elementwise = elementwise
def _draw_samples(self, size, random_state):
seed = random_state.randint(0, 10**6, 1)[0]
samples = self.other_param.draw_samples(size, random_state=ia.new_random_state(seed))
if self.elementwise and not isinstance(self.val, Deterministic):
return np.subtract(
samples,
self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
)
elementwise = self.elementwise and not isinstance(self.val, Deterministic)
if elementwise:
val_samples = self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
else:
return samples - self.val.draw_sample(random_state=ia.new_random_state(seed+1))
val_samples = self.val.draw_sample(random_state=ia.new_random_state(seed+1))
if elementwise:
return np.subtract(samples, val_samples)
else:
return samples - val_samples
def __repr__(self):
return self.__str__()
def __str__(self):
opstr = str(self.other_param)
return "Subtract(%s, %s, %s)" % (opstr, str(self.val), self.elementwise)
return "Subtract(%s, %s, %s)" % (str(self.other_param), str(self.val), self.elementwise)
class Power(StochasticParameter):
"""
Parameter to exponentiate another parameter's results with.
Parameters
----------
other_param : StochasticParameter
other_param : number or tuple of two number or list of number or StochasticParameter
Other parameter which's sampled values are to be
modified.
@@ -1473,41 +1538,41 @@ class Power(StochasticParameter):
def __init__(self, other_param, val, elementwise=False):
super(Power, self).__init__()
assert isinstance(other_param, StochasticParameter)
self.other_param = other_param
self.other_param = handle_continuous_param(other_param, "other_param")
self.val = handle_continuous_param(val, "val")
self.elementwise = elementwise
def _draw_samples(self, size, random_state):
seed = random_state.randint(0, 10**6, 1)[0]
samples = self.other_param.draw_samples(size, random_state=ia.new_random_state(seed))
samples_dtype = samples.dtype
if self.elementwise and not isinstance(self.val, Deterministic):
elementwise = self.elementwise and not isinstance(self.val, Deterministic)
if elementwise:
exponents = self.val.draw_samples(size, random_state=ia.new_random_state(seed+1))
else:
exponents = self.val.draw_sample(random_state=ia.new_random_state(seed+1))
#print("[power] samples", samples[0:100])
#print("[power] exponents", exponents[0:100] if ia.is_np_array(exponents) else exponents)
# without this we get int results in the case of
# Power(<int>, <stochastic float param>)
samples, exponents = both_np_float_if_one_is_float(samples, exponents)
samples_dtype = samples.dtype
# float_power requires numpy>=1.12
#result = np.float_power(samples, exponents)
#result = np.power(samples.astype(np.float64), exponents)
# TODO why was float32 type here replaced with complex number
# formulation?
result = np.power(samples.astype(np.complex), exponents).real
#print("[power] result", result[0:100])
if result.dtype != samples_dtype:
result = result.astype(samples_dtype)
#print("[power] result cast", result[0:100])
return result
def __repr__(self):
return self.__str__()
def __str__(self):
opstr = str(self.other_param)
return "Power(%s, %s, %s)" % (opstr, str(self.val), self.elementwise)
return "Power(%s, %s, %s)" % (str(self.other_param), str(self.val), self.elementwise)
class Absolute(StochasticParameter):
"""
View
@@ -4,7 +4,7 @@
from imgaug.parameters import (
Binomial, Choice, DiscreteUniform, Poisson, Normal, Laplace, ChiSquare,
Weibull, Uniform, Beta, Deterministic, Clip, Discretize, Multiply, Add,
Absolute, RandomSign, ForceSign, Positive, Negative,
Divide, Power, Absolute, RandomSign, ForceSign, Positive, Negative,
SimplexNoise, FrequencyNoise, Sigmoid
)
from scipy import misc
@@ -49,9 +49,17 @@ def main():
params_arithmetic = [
("Normal(0, 1.0)", Normal(0.0, 1.0)),
("Normal(0, 1.0) + 5", Normal(0.0, 1.0) + 5),
("5 + Normal(0, 1.0)", 5 + Normal(0.0, 1.0)),
("5 + Normal(0, 1.0)", Add(5, Normal(0.0, 1.0), elementwise=True)),
("Normal(0, 1.0) * 10", Normal(0.0, 1.0) * 10),
("10 * Normal(0, 1.0)", 10 * Normal(0.0, 1.0)),
("10 * Normal(0, 1.0)", Multiply(10, Normal(0.0, 1.0), elementwise=True)),
("Normal(0, 1.0) / 10", Normal(0.0, 1.0) / 10),
("Normal(0, 1.0) ** 2", Normal(0.0, 1.0) ** 2)
("10 / Normal(0, 1.0)", 10 / Normal(0.0, 1.0)),
("10 / Normal(0, 1.0)", Divide(10, Normal(0.0, 1.0), elementwise=True)),
("Normal(0, 1.0) ** 2", Normal(0.0, 1.0) ** 2),
("2 ** Normal(0, 1.0)", 2 ** Normal(0.0, 1.0)),
("2 ** Normal(0, 1.0)", Power(2, Normal(0.0, 1.0), elementwise=True))
]
params_noise = [

0 comments on commit 776d03d

Please sign in to comment.