| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203 |
- """Classes and methods to use for parameters of augmenters.
- This module contains e.g. classes representing probability
- distributions (guassian, poisson etc.), classes representing noise sources
- and methods to normalize parameter-related user inputs.
- """
- from __future__ import print_function, division, absolute_import
- import copy as copy_module
- from collections import defaultdict
- from abc import ABCMeta, abstractmethod
- import tempfile
- import numpy as np
- import six
- import six.moves as sm
- import scipy
- import scipy.stats
- import imageio
- from . import imgaug as ia
- from . import dtypes as iadt
- from . import random as iarandom
- from .external.opensimplex import OpenSimplex
- def _check_value_range(value, name, value_range):
- if value_range is None:
- return True
- if isinstance(value_range, tuple):
- assert len(value_range) == 2, (
- "If 'value_range' is a tuple, it must contain exactly 2 entries, "
- "got %d." % (len(value_range),))
- if value_range[0] is None and value_range[1] is None:
- return True
- if value_range[0] is None:
- assert value <= value_range[1], (
- "Parameter '%s' is outside of the expected value "
- "range (x <= %.4f)" % (name, value_range[1]))
- return True
- if value_range[1] is None:
- assert value_range[0] <= value, (
- "Parameter '%s' is outside of the expected value "
- "range (%.4f <= x)" % (name, value_range[0]))
- return True
- assert value_range[0] <= value <= value_range[1], (
- "Parameter '%s' is outside of the expected value "
- "range (%.4f <= x <= %.4f)" % (
- name, value_range[0], value_range[1]))
- return True
- if ia.is_callable(value_range):
- value_range(value)
- return True
- raise Exception("Unexpected input for value_range, got %s." % (
- str(value_range),))
- # FIXME this uses _check_value_range, which checks for a<=x<=b, but a produced
- # Uniform parameter has value range a<=x<b.
- def handle_continuous_param(param, name, value_range=None,
- tuple_to_uniform=True, list_to_choice=True):
- if ia.is_single_number(param):
- _check_value_range(param, name, value_range)
- return Deterministic(param)
- if tuple_to_uniform and isinstance(param, tuple):
- assert len(param) == 2, (
- "Expected parameter '%s' with type tuple to have exactly two "
- "entries, but got %d." % (name, len(param)))
- assert all([ia.is_single_number(v) for v in param]), (
- "Expected parameter '%s' with type tuple to only contain "
- "numbers, got %s." % (name, [type(v) for v in param],))
- _check_value_range(param[0], name, value_range)
- _check_value_range(param[1], name, value_range)
- return Uniform(param[0], param[1])
- if (list_to_choice and ia.is_iterable(param)
- and not isinstance(param, tuple)):
- assert all([ia.is_single_number(v) for v in param]), (
- "Expected iterable parameter '%s' to only contain numbers, "
- "got %s." % (name, [type(v) for v in param],))
- for param_i in param:
- _check_value_range(param_i, name, value_range)
- return Choice(param)
- if isinstance(param, StochasticParameter):
- return param
- allowed_type = "number"
- list_str = ", list of %s" % (allowed_type,) if list_to_choice else ""
- raise Exception(
- "Expected %s, tuple of two %s%s or StochasticParameter for %s, "
- "got %s." % (
- allowed_type, allowed_type, list_str, name, type(param),))
- def handle_discrete_param(param, name, value_range=None, tuple_to_uniform=True,
- list_to_choice=True, allow_floats=True):
- if (ia.is_single_integer(param)
- or (allow_floats and ia.is_single_float(param))):
- _check_value_range(param, name, value_range)
- return Deterministic(int(param))
- if tuple_to_uniform and isinstance(param, tuple):
- assert len(param) == 2, (
- "Expected parameter '%s' with type tuple to have exactly two "
- "entries, but got %d." % (name, len(param)))
- is_valid_types = all([
- ia.is_single_number(v)
- if allow_floats else ia.is_single_integer(v)
- for v in param])
- assert is_valid_types, (
- "Expected parameter '%s' of type tuple to only contain %s, "
- "got %s." % (
- name,
- "number" if allow_floats else "integer",
- [type(v) for v in param],))
- _check_value_range(param[0], name, value_range)
- _check_value_range(param[1], name, value_range)
- return DiscreteUniform(int(param[0]), int(param[1]))
- if (list_to_choice and ia.is_iterable(param)
- and not isinstance(param, tuple)):
- is_valid_types = all([
- ia.is_single_number(v)
- if allow_floats else ia.is_single_integer(v)
- for v in param])
- assert is_valid_types, (
- "Expected iterable parameter '%s' to only contain %s, "
- "got %s." % (
- name,
- "number" if allow_floats else "integer",
- [type(v) for v in param],))
- for param_i in param:
- _check_value_range(param_i, name, value_range)
- return Choice([int(param_i) for param_i in param])
- if isinstance(param, StochasticParameter):
- return param
- allowed_type = "number" if allow_floats else "int"
- list_str = ", list of %s" % (allowed_type,) if list_to_choice else ""
- raise Exception(
- "Expected %s, tuple of two %s%s or StochasticParameter for %s, "
- "got %s." % (
- allowed_type, allowed_type, list_str, name, type(param),))
- # Added in 0.4.0.
- def handle_categorical_string_param(param, name, valid_values=None):
- if param == ia.ALL and valid_values is not None:
- return Choice(list(valid_values))
- if ia.is_string(param):
- if valid_values is not None:
- assert param in valid_values, (
- "Expected parameter '%s' to be one of: %s. Got: %s." % (
- name, ", ".join(list(valid_values)), param))
- return Deterministic(param)
- if isinstance(param, list):
- assert all([ia.is_string(val) for val in param]), (
- "Expected list provided for parameter '%s' to only contain "
- "strings, got types: %s." % (
- name, ", ".join([type(v).__name__ for v in param])))
- if valid_values is not None:
- assert all([val in valid_values for val in param]), (
- "Expected list provided for parameter '%s' to only contain "
- "the following allowed strings: %s. Got strings: %s." % (
- name, ", ".join(valid_values), ", ".join(param)
- ))
- return Choice(param)
- if isinstance(param, StochasticParameter):
- return param
- raise Exception(
- "Expected parameter '%s' to be%s a string, a list of "
- "strings or StochasticParameter, got %s." % (
- name,
- " imgaug.ALL," if valid_values is not None else "",
- type(param).__name__,))
- def handle_discrete_kernel_size_param(param, name, value_range=(1, None),
- allow_floats=True):
- if (ia.is_single_integer(param)
- or (allow_floats and ia.is_single_float(param))):
- _check_value_range(param, name, value_range)
- return Deterministic(int(param)), None
- if isinstance(param, tuple):
- assert len(param) == 2, (
- "Expected parameter '%s' with type tuple to have exactly two "
- "entries, but got %d." % (name, len(param)))
- if (all([ia.is_single_integer(param_i) for param_i in param])
- or (allow_floats and all([ia.is_single_float(param_i)
- for param_i in param]))):
- _check_value_range(param[0], name, value_range)
- _check_value_range(param[1], name, value_range)
- return DiscreteUniform(int(param[0]), int(param[1])), None
- if all([isinstance(param_i, StochasticParameter)
- for param_i in param]):
- return param[0], param[1]
- handled = (
- handle_discrete_param(
- param[0], "%s[0]" % (name,), value_range,
- allow_floats=allow_floats),
- handle_discrete_param(
- param[1], "%s[1]" % (name,), value_range,
- allow_floats=allow_floats)
- )
- return handled
- if ia.is_iterable(param) and not isinstance(param, tuple):
- is_valid_types = all([
- ia.is_single_number(v)
- if allow_floats else ia.is_single_integer(v)
- for v in param])
- assert is_valid_types, (
- "Expected iterable parameter '%s' to only contain %s, "
- "got %s." % (
- name,
- "number" if allow_floats else "integer",
- [type(v) for v in param],))
- for param_i in param:
- _check_value_range(param_i, name, value_range)
- return Choice([int(param_i) for param_i in param]), None
- if isinstance(param, StochasticParameter):
- return param, None
- raise Exception(
- "Expected int, tuple/list with 2 entries or StochasticParameter. "
- "Got %s." % (type(param),))
- def handle_probability_param(param, name, tuple_to_uniform=False,
- list_to_choice=False):
- eps = 1e-6
- if param in [True, False, 0, 1]:
- return Deterministic(int(param))
- if ia.is_single_number(param):
- assert 0.0 <= param <= 1.0, (
- "Expected probability of parameter '%s' to be in the interval "
- "[0.0, 1.0], got %.4f." % (name, param,))
- if 0.0-eps < param < 0.0+eps or 1.0-eps < param < 1.0+eps:
- return Deterministic(int(np.round(param)))
- return Binomial(param)
- if tuple_to_uniform and isinstance(param, tuple):
- assert all([ia.is_single_number(v) for v in param]), (
- "Expected parameter '%s' of type tuple to only contain numbers, "
- "got %s." % (name, [type(v) for v in param],))
- assert len(param) == 2, (
- "Expected parameter '%s' of type tuple to contain exactly 2 "
- "entries, got %d." % (name, len(param)))
- assert 0 <= param[0] <= 1.0 and 0 <= param[1] <= 1.0, (
- "Expected parameter '%s' of type tuple to contain two "
- "probabilities in the interval [0.0, 1.0]. "
- "Got values %.4f and %.4f." % (name, param[0], param[1]))
- return Binomial(Uniform(param[0], param[1]))
- if list_to_choice and ia.is_iterable(param):
- assert all([ia.is_single_number(v) for v in param]), (
- "Expected iterable parameter '%s' to only contain numbers, "
- "got %s." % (name, [type(v) for v in param],))
- assert all([0 <= p_i <= 1.0 for p_i in param]), (
- "Expected iterable parameter '%s' to only contain probabilities "
- "in the interval [0.0, 1.0], got values %s." % (
- name, ", ".join(["%.4f" % (p_i,) for p_i in param])))
- return Binomial(Choice(param))
- if isinstance(param, StochasticParameter):
- return param
- raise Exception(
- "Expected boolean or number or StochasticParameter for %s, "
- "got %s." % (name, type(param),))
- def force_np_float_dtype(val):
- if val.dtype.kind == "f":
- return val
- return val.astype(np.float64)
- def both_np_float_if_one_is_float(a, b):
- # pylint: disable=invalid-name
- a_f = a.dtype.type in ia.NP_FLOAT_TYPES
- b_f = b.dtype.type in ia.NP_FLOAT_TYPES
- if a_f and b_f:
- return a, b
- if a_f:
- return a, b.astype(np.float64)
- if b_f:
- return a.astype(np.float64), b
- return a.astype(np.float64), b.astype(np.float64)
- 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)
- elif titles is False:
- titles = [False] * len(params)
- if sample_sizes is not None:
- images = [
- param_i.draw_distribution_graph(size=size_i, title=title_i)
- for param_i, size_i, title_i in zip(params, sample_sizes, titles)]
- else:
- images = [
- param_i.draw_distribution_graph(title=title_i)
- for param_i, title_i in zip(params, titles)]
- images_rs = ia.imresize_many_images(images, sizes=graph_sizes)
- grid = ia.draw_grid(images_rs, rows=rows, cols=cols)
- return grid
- def show_distributions_grid(params, rows=None, cols=None,
- graph_sizes=(350, 350), sample_sizes=None,
- titles=None):
- ia.imshow(
- draw_distributions_grid(
- params,
- graph_sizes=graph_sizes,
- sample_sizes=sample_sizes,
- rows=rows,
- cols=cols,
- titles=titles
- )
- )
- @six.add_metaclass(ABCMeta)
- class StochasticParameter(object):
- """Abstract parent class for all stochastic parameters.
- Stochastic parameters are here all parameters from which values are
- supposed to be sampled. Usually the sampled values are to a degree random.
- E.g. a stochastic parameter may be the uniform distribution over the
- interval ``[-10, 10]``. Samples from that distribution (and therefore the
- stochastic parameter) could be ``5.2``, ``-3.7``, ``-9.7``, ``6.4``, etc.
- """
- def __init__(self):
- pass
- def draw_sample(self, random_state=None):
- """
- Draws a single sample value from this parameter.
- Parameters
- ----------
- random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
- A seed or random number generator to use during the sampling
- process. If ``None``, the global RNG will be used.
- See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`
- for a similar parameter with more details.
- Returns
- -------
- any
- A single sample value.
- """
- return self.draw_samples(1, random_state=random_state)[0]
- def draw_samples(self, size, random_state=None):
- """Draw one or more samples from the parameter.
- Parameters
- ----------
- size : tuple of int or int
- Number of samples by dimension.
- random_state : None or int or imgaug.random.RNG or numpy.random.Generator or numpy.random.BitGenerator or numpy.random.SeedSequence or numpy.random.RandomState, optional
- A seed or random number generator to use during the sampling
- process. If ``None``, the global RNG will be used.
- See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`
- for a similar parameter with more details.
- Returns
- -------
- ndarray
- Sampled values. Usually a numpy ndarray of basically any dtype,
- though not strictly limited to numpy arrays. Its shape is expected
- to match `size`.
- """
- random_state = iarandom.RNG(random_state)
- samples = self._draw_samples(
- size if not ia.is_single_integer(size) else tuple([size]),
- random_state)
- random_state.advance_()
- return samples
- @abstractmethod
- def _draw_samples(self, size, random_state):
- raise NotImplementedError()
- def __add__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Add(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter + %s. "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __sub__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Subtract(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter - %s. "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __mul__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Multiply(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter * %s. "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __pow__(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(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter ** %s. "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __div__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Divide(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter / %s. "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __truediv__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Divide(self, other)
- raise Exception(
- "Invalid datatypes in: StochasticParameter / %s (truediv). "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __floordiv__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Discretize(Divide(self, other))
- raise Exception(
- "Invalid datatypes in: StochasticParameter // %s (floordiv). "
- "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)
- 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)
- 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)
- 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)
- 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)
- 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)
- raise Exception(
- "Invalid datatypes in: %s / StochasticParameter (rtruediv). "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def __rfloordiv__(self, other):
- if ia.is_single_number(other) or isinstance(other, StochasticParameter):
- return Discretize(Divide(other, self))
- raise Exception(
- "Invalid datatypes in: StochasticParameter // %s (rfloordiv). "
- "Expected second argument to be number or "
- "StochasticParameter." % (type(other),))
- def copy(self):
- """Create a shallow copy of this parameter.
- Returns
- -------
- imgaug.parameters.StochasticParameter
- Shallow copy.
- """
- return copy_module.copy(self)
- def deepcopy(self):
- """Create a deep copy of this parameter.
- Returns
- -------
- imgaug.parameters.StochasticParameter
- Deep copy.
- """
- return copy_module.deepcopy(self)
- def draw_distribution_graph(self, title=None, size=(1000, 1000), bins=100):
- """Generate an image visualizing the parameter's sample distribution.
- Parameters
- ----------
- title : None or False or str, optional
- Title of the plot. ``None`` is automatically replaced by a title
- derived from ``str(param)``. If set to ``False``, no title will be
- shown.
- size : tuple of int
- Number of points to sample. This is always expected to have at
- least two values. The first defines the number of sampling runs,
- the second (and further) dimensions define the size assigned
- to each :func:`~imgaug.parameters.StochasticParameter.draw_samples`
- call. E.g. ``(10, 20, 15)`` will lead to ``10`` calls of
- ``draw_samples(size=(20, 15))``. The results will be merged to a
- single 1d array.
- bins : int
- Number of bins in the plot histograms.
- Returns
- -------
- data : (H,W,3) ndarray
- Image of the plot.
- """
- # import only when necessary (faster startup; optional dependency;
- # less fragile -- see issue #225)
- import matplotlib.pyplot as plt
- points = []
- for _ in sm.xrange(size[0]):
- points.append(self.draw_samples(size[1:]).flatten())
- points = np.concatenate(points)
- fig = plt.figure()
- fig.add_subplot(111)
- ax = fig.gca()
- heights, bins = np.histogram(points, bins=bins)
- heights = heights / sum(heights)
- ax.bar(bins[:-1], heights,
- width=(max(bins) - min(bins))/len(bins),
- color="blue",
- alpha=0.75)
- if title is None:
- title = str(self)
- if title is not False:
- # split long titles - otherwise matplotlib generates errors
- title_fragments = [title[i:i+50]
- for i in sm.xrange(0, len(title), 50)]
- ax.set_title("\n".join(title_fragments))
- fig.tight_layout(pad=0)
- with tempfile.NamedTemporaryFile(suffix=".png") as f:
- # we don't add bbox_inches='tight' here so that
- # draw_distributions_grid has an easier time combining many plots
- fig.savefig(f.name)
- data = imageio.imread(f)[..., 0:3]
- plt.close()
- return data
- class Deterministic(StochasticParameter):
- """Parameter that is a constant value.
- If ``N`` values are sampled from this parameter, it will return ``N`` times
- ``V``, where ``V`` is the constant value.
- Parameters
- ----------
- value : number or str or imgaug.parameters.StochasticParameter
- A constant value to use.
- A string may be provided to generate arrays of strings.
- If this is a StochasticParameter, a single value will be sampled
- from it exactly once and then used as the constant value.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Deterministic(10)
- >>> param.draw_sample()
- 10
- Will always sample the value 10.
- """
- def __init__(self, value):
- super(Deterministic, self).__init__()
- if isinstance(value, StochasticParameter):
- self.value = value.draw_sample()
- elif ia.is_single_number(value) or ia.is_string(value):
- self.value = value
- else:
- raise Exception("Expected StochasticParameter object or number or "
- "string, got %s." % (type(value),))
- def _draw_samples(self, size, random_state):
- kwargs = {}
- if ia.is_single_integer(self.value):
- kwargs = {"dtype": np.int32}
- elif ia.is_single_float(self.value):
- kwargs = {"dtype": np.float32}
- return np.full(size, self.value, **kwargs)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- if ia.is_single_integer(self.value):
- return "Deterministic(int %d)" % (self.value,)
- if ia.is_single_float(self.value):
- return "Deterministic(float %.8f)" % (self.value,)
- return "Deterministic(%s)" % (str(self.value),)
- # TODO replace two-value parameters used in tests with this
- class DeterministicList(StochasticParameter):
- """Parameter that repeats elements from a list in the given order.
- E.g. of samples of shape ``(A, B, C)`` are requested, this parameter will
- return the first ``A*B*C`` elements, reshaped to ``(A, B, C)`` from the
- provided list. If the list contains less than ``A*B*C`` elements, it
- will (by default) be tiled until it is long enough (i.e. the sampling
- will start again at the first element, if necessary multiple times).
- Added in 0.4.0.
- Parameters
- ----------
- values : ndarray or iterable of number
- An iterable of values to sample from in the order within the iterable.
- """
- # Added in 0.4.0.
- def __init__(self, values):
- super(DeterministicList, self).__init__()
- assert ia.is_iterable(values), (
- "Expected to get an iterable as input, got type %s." % (
- type(values).__name__,))
- assert len(values) > 0, ("Expected to get at least one value, got "
- "zero.")
- if ia.is_np_array(values):
- # this would not be able to handle e.g. [[1, 2], [3]] and output
- # dtype object due to the non-regular shape, hence we have the
- # else block
- self.values = values.flatten()
- else:
- self.values = np.array(list(ia.flatten(values)))
- kind = self.values.dtype.kind
- # limit to 32bit instead of 64bit for efficiency
- if kind == "i":
- self.values = self.values.astype(np.int32)
- elif kind == "f":
- self.values = self.values.astype(np.float32)
- # Added in 0.4.0.
- def _draw_samples(self, size, random_state):
- nb_requested = int(np.prod(size))
- values = self.values
- if nb_requested > self.values.size:
- # we don't use itertools.cycle() here, as that would require
- # running through a loop potentially many times (as `size` can
- # be very large), which would be slow
- multiplier = int(np.ceil(nb_requested / values.size))
- values = np.tile(values, (multiplier,))
- return values[:nb_requested].reshape(size)
- # Added in 0.4.0.
- def __repr__(self):
- return self.__str__()
- # Added in 0.4.0.
- def __str__(self):
- if self.values.dtype.kind == "f":
- values = ["%.4f" % (value,) for value in self.values]
- return "DeterministicList([%s])" % (", ".join(values),)
- return "DeterministicList(%s)" % (str(self.values.tolist()),)
- class Choice(StochasticParameter):
- """Parameter that samples value from a list of allowed values.
- Parameters
- ----------
- a : iterable
- List of allowed values.
- Usually expected to be ``int`` s, ``float`` s or ``str`` s.
- May also contain ``StochasticParameter`` s. Each
- ``StochasticParameter`` that is randomly picked will automatically be
- replaced by a sample of itself (or by ``N`` samples if the parameter
- was picked ``N`` times).
- replace : bool, optional
- Whether to perform sampling with or without replacing.
- p : None or iterable of number, optional
- Probabilities of each element in `a`.
- Must have the same length as `a` (if provided).
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Choice([5, 17, 25], p=[0.25, 0.5, 0.25])
- >>> sample = param.draw_sample()
- >>> assert sample in [5, 17, 25]
- Create and sample from a parameter, which will produce with ``50%``
- probability the sample ``17`` and in the other ``50%`` of all cases the
- sample ``5`` or ``25``..
- """
- def __init__(self, a, replace=True, p=None):
- # pylint: disable=invalid-name
- super(Choice, self).__init__()
- assert ia.is_iterable(a), (
- "Expected a to be an iterable (e.g. list), got %s." % (type(a),))
- self.a = a
- self.replace = replace
- if p is not None:
- assert ia.is_iterable(p), (
- "Expected p to be None or an iterable, got %s." % (type(p),))
- assert len(p) == len(a), (
- "Expected lengths of a and p to be identical, "
- "got %d and %d." % (len(a), len(p)))
- self.p = p
- def _draw_samples(self, size, random_state):
- if any([isinstance(a_i, StochasticParameter) for a_i in self.a]):
- rngs = random_state.duplicate(1+len(self.a))
- samples = rngs[0].choice(
- self.a, np.prod(size), replace=self.replace, p=self.p)
- # collect the sampled parameters and how many samples must be taken
- # from each of them
- params_counter = defaultdict(lambda: 0)
- for sample in samples:
- if isinstance(sample, StochasticParameter):
- key = str(sample)
- params_counter[key] += 1
- # collect per parameter once the required number of samples
- # iterate here over self.a to always use the same seed for
- # the same parameter
- # TODO this might fail if the same parameter is added multiple
- # times to self.a?
- # TODO this will fail if a parameter cant handle size=(N,)
- param_to_samples = dict()
- for i, param in enumerate(self.a):
- key = str(param)
- if key in params_counter:
- param_to_samples[key] = param.draw_samples(
- size=(params_counter[key],),
- random_state=rngs[1+i]
- )
- # assign the values sampled from the parameters to the `samples`
- # array by replacing the respective parameter
- param_to_readcount = defaultdict(lambda: 0)
- for i, sample in enumerate(samples):
- if isinstance(sample, StochasticParameter):
- key = str(sample)
- readcount = param_to_readcount[key]
- samples[i] = param_to_samples[key][readcount]
- param_to_readcount[key] += 1
- samples = samples.reshape(size)
- else:
- samples = random_state.choice(self.a, size, replace=self.replace,
- p=self.p)
- dtype = samples.dtype
- if dtype.itemsize*8 > 32:
- # strings have kind "U"
- kind = dtype.kind
- if kind == "i":
- samples = samples.astype(np.int32)
- elif kind == "u":
- samples = samples.astype(np.uint32)
- elif kind == "f":
- samples = samples.astype(np.float32)
- return samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Choice(a=%s, replace=%s, p=%s)" % (
- str(self.a), str(self.replace), str(self.p),)
- class Binomial(StochasticParameter):
- """Binomial distribution.
- Parameters
- ----------
- p : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Probability of the binomial distribution. Expected to be in the
- interval ``[0.0, 1.0]``.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Binomial.draw_sample` or
- :func:`Binomial.draw_samples`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Binomial(Uniform(0.01, 0.2))
- Create a binomial distribution that uses a varying probability between
- ``0.01`` and ``0.2``, randomly and uniformly estimated once per sampling
- call.
- """
- def __init__(self, p):
- super(Binomial, self).__init__()
- self.p = handle_continuous_param(p, "p")
- def _draw_samples(self, size, random_state):
- p = self.p.draw_sample(random_state=random_state)
- assert 0 <= p <= 1.0, (
- "Expected probability p to be in the interval [0.0, 1.0], "
- "got %.4f." % (p,))
- return random_state.binomial(1, p, size).astype(np.int32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Binomial(%s)" % (self.p,)
- class DiscreteUniform(StochasticParameter):
- """Uniform distribution over the discrete interval ``[a..b]``.
- Parameters
- ----------
- a : int or tuple of int or list of int or imgaug.parameters.StochasticParameter
- Lower bound of the interval.
- If ``a>b``, `a` and `b` will automatically be flipped.
- If ``a==b``, all generated values will be identical to `a`.
- * If a single ``int``, this ``int`` will be used as a
- constant value.
- * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
- sampled from the discrete interval ``[a..b]`` once per call.
- * If a ``list`` of ``int``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`DiscreteUniform.draw_sample` or
- :func:`DiscreteUniform.draw_samples`.
- b : int or imgaug.parameters.StochasticParameter
- Upper bound of the interval. Analogous to `a`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.DiscreteUniform(10, Choice([20, 30, 40]))
- >>> sample = param.draw_sample()
- >>> assert 10 <= sample <= 40
- Create a discrete uniform distribution which's interval differs between
- calls and can be ``[10..20]``, ``[10..30]`` or ``[10..40]``.
- """
- def __init__(self, a, b):
- # pylint: disable=invalid-name
- super(DiscreteUniform, self).__init__()
- self.a = handle_discrete_param(a, "a")
- self.b = handle_discrete_param(b, "b")
- def _draw_samples(self, size, random_state):
- # pylint: disable=invalid-name
- a = self.a.draw_sample(random_state=random_state)
- b = self.b.draw_sample(random_state=random_state)
- if a > b:
- a, b = b, a
- elif a == b:
- return np.full(size, a, dtype=np.int32)
- return random_state.integers(a, b + 1, size, dtype=np.int32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "DiscreteUniform(%s, %s)" % (self.a, self.b)
- class Poisson(StochasticParameter):
- """Parameter that resembles a poisson distribution.
- A poisson distribution with ``lambda=0`` has its highest probability at
- point ``0`` and decreases quickly from there.
- Poisson distributions are discrete and never negative.
- Parameters
- ----------
- lam : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Lambda parameter of the poisson distribution.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Poisson.draw_sample` or
- :func:`Poisson.draw_samples`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Poisson(1)
- >>> sample = param.draw_sample()
- >>> assert sample >= 0
- Create a poisson distribution with ``lambda=1`` and sample a value from
- it.
- """
- def __init__(self, lam):
- super(Poisson, self).__init__()
- self.lam = handle_continuous_param(lam, "lam")
- def _draw_samples(self, size, random_state):
- lam = self.lam.draw_sample(random_state=random_state)
- lam = max(lam, 0)
- return random_state.poisson(lam=lam, size=size).astype(np.int32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Poisson(%s)" % (self.lam,)
- class Normal(StochasticParameter):
- """Parameter that resembles a normal/gaussian distribution.
- Parameters
- ----------
- loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The mean of the normal distribution.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Laplace.draw_sample` or
- :func:`Laplace.draw_samples`.
- scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The standard deviation of the normal distribution.
- If this parameter reaches ``0``, the output array will be filled with
- `loc`.
- Datatype behaviour is the analogous to `loc`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Normal(Choice([-1.0, 1.0]), 1.0)
- Create a gaussian distribution with a mean that differs by call.
- Samples values may sometimes follow ``N(-1.0, 1.0)`` and sometimes
- ``N(1.0, 1.0)``.
- """
- def __init__(self, loc, scale):
- super(Normal, self).__init__()
- self.loc = handle_continuous_param(loc, "loc")
- self.scale = handle_continuous_param(scale, "scale",
- value_range=(0, None))
- def _draw_samples(self, size, random_state):
- loc = self.loc.draw_sample(random_state=random_state)
- scale = self.scale.draw_sample(random_state=random_state)
- assert scale >= 0, "Expected scale to be >=0, got %.4f." % (scale,)
- if scale == 0:
- return np.full(size, loc, dtype=np.float32)
- return random_state.normal(loc, scale, size=size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Normal(loc=%s, scale=%s)" % (self.loc, self.scale)
- # TODO docstring for parameters is outdated
- class TruncatedNormal(StochasticParameter):
- """Parameter that resembles a truncated normal distribution.
- A truncated normal distribution is similar to a normal distribution,
- except the domain is smoothly bounded to a min and max value.
- This is a wrapper around :func:`scipy.stats.truncnorm`.
- Parameters
- ----------
- loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The mean of the normal distribution.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`TruncatedNormal.draw_sample` or
- :func:`TruncatedNormal.draw_samples`.
- scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The standard deviation of the normal distribution.
- If this parameter reaches ``0``, the output array will be filled with
- `loc`.
- Datatype behaviour is the same as for `loc`.
- low : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The minimum value of the truncated normal distribution.
- Datatype behaviour is the same as for `loc`.
- high : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The maximum value of the truncated normal distribution.
- Datatype behaviour is the same as for `loc`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.TruncatedNormal(0, 5.0, low=-10, high=10)
- >>> samples = param.draw_samples(100, random_state=0)
- >>> assert np.all(samples >= -10)
- >>> assert np.all(samples <= 10)
- Create a truncated normal distribution with its minimum at ``-10.0``
- and its maximum at ``10.0``.
- """
- def __init__(self, loc, scale, low=-np.inf, high=np.inf):
- super(TruncatedNormal, self).__init__()
- self.loc = handle_continuous_param(loc, "loc")
- self.scale = handle_continuous_param(scale, "scale",
- value_range=(0, None))
- self.low = handle_continuous_param(low, "low")
- self.high = handle_continuous_param(high, "high")
- def _draw_samples(self, size, random_state):
- # pylint: disable=invalid-name
- loc = self.loc.draw_sample(random_state=random_state)
- scale = self.scale.draw_sample(random_state=random_state)
- low = self.low.draw_sample(random_state=random_state)
- high = self.high.draw_sample(random_state=random_state)
- seed = random_state.generate_seed_()
- if low > high:
- low, high = high, low
- assert scale >= 0, "Expected scale to be >=0, got %.4f." % (scale,)
- if scale == 0:
- return np.full(size, fill_value=loc, dtype=np.float32)
- a = (low - loc) / scale
- b = (high - loc) / scale
- tnorm = scipy.stats.truncnorm(a=a, b=b, loc=loc, scale=scale)
- # Using a seed here works with both np.random interfaces.
- # Last time tried, scipy crashed when providing just
- # random_state.generator on the new np.random interface.
- return tnorm.rvs(size=size, random_state=seed).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "TruncatedNormal(loc=%s, scale=%s, low=%s, high=%s)" % (
- self.loc, self.scale, self.low, self.high)
- class Laplace(StochasticParameter):
- """Parameter that resembles a (continuous) laplace distribution.
- This is a wrapper around numpy's :func:`numpy.random.laplace`.
- Parameters
- ----------
- loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The position of the distribution peak, similar to the mean in normal
- distributions.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Laplace.draw_sample` or
- :func:`Laplace.draw_samples`.
- scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- The exponential decay factor, similar to the standard deviation in
- gaussian distributions.
- If this parameter reaches ``0``, the output array will be filled with
- `loc`.
- Datatype behaviour is the analogous to `loc`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Laplace(0, 1.0)
- Create a laplace distribution, which's peak is at ``0`` and decay is
- ``1.0``.
- """
- def __init__(self, loc, scale):
- super(Laplace, self).__init__()
- self.loc = handle_continuous_param(loc, "loc")
- self.scale = handle_continuous_param(scale, "scale",
- value_range=(0, None))
- def _draw_samples(self, size, random_state):
- loc = self.loc.draw_sample(random_state=random_state)
- scale = self.scale.draw_sample(random_state=random_state)
- assert scale >= 0, "Expected scale to be >=0, got %s." % (scale,)
- if scale == 0:
- return np.full(size, loc, dtype=np.float32)
- return random_state.laplace(loc, scale, size=size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Laplace(loc=%s, scale=%s)" % (self.loc, self.scale)
- class ChiSquare(StochasticParameter):
- """Parameter that resembles a (continuous) chi-square distribution.
- This is a wrapper around numpy's :func:`numpy.random.chisquare`.
- Parameters
- ----------
- df : int or tuple of two int or list of int or imgaug.parameters.StochasticParameter
- Degrees of freedom. Expected value range is ``[1, inf)``.
- * If a single ``int``, this ``int`` will be used as a
- constant value.
- * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
- sampled from the discrete interval ``[a..b]`` once per call.
- * If a ``list`` of ``int``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`ChiSquare.draw_sample` or
- :func:`ChiSquare.draw_samples`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.ChiSquare(df=2)
- Create a chi-square distribution with two degrees of freedom.
- """
- def __init__(self, df):
- # pylint: disable=invalid-name
- super(ChiSquare, self).__init__()
- self.df = handle_discrete_param(df, "df", value_range=(1, None))
- def _draw_samples(self, size, random_state):
- # pylint: disable=invalid-name
- df = self.df.draw_sample(random_state=random_state)
- assert df >= 1, "Expected df to be >=1, got %d." % (df,)
- return random_state.chisquare(df, size=size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "ChiSquare(df=%s)" % (self.df,)
- class Weibull(StochasticParameter):
- """
- Parameter that resembles a (continuous) weibull distribution.
- This is a wrapper around numpy's :func:`numpy.random.weibull`.
- Parameters
- ----------
- a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Shape parameter of the distribution.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Weibull.draw_sample` or
- :func:`Weibull.draw_samples`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Weibull(a=0.5)
- Create a weibull distribution with shape 0.5.
- """
- def __init__(self, a):
- # pylint: disable=invalid-name
- super(Weibull, self).__init__()
- self.a = handle_continuous_param(a, "a", value_range=(0.0001, None))
- def _draw_samples(self, size, random_state):
- # pylint: disable=invalid-name
- a = self.a.draw_sample(random_state=random_state)
- assert a > 0, "Expected a to be >0, got %.4f." % (a,)
- return random_state.weibull(a, size=size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Weibull(a=%s)" % (self.a,)
- # TODO rename (a, b) to (low, high) as in numpy?
- class Uniform(StochasticParameter):
- """Parameter that resembles a uniform distribution over ``[a, b)``.
- Parameters
- ----------
- a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Lower bound of the interval.
- If ``a>b``, `a` and `b` will automatically be flipped.
- If ``a==b``, all generated values will be identical to `a`.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Uniform.draw_sample` or
- :func:`Uniform.draw_samples`.
- b : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Upper bound of the interval. Analogous to `a`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Uniform(0, 10.0)
- >>> sample = param.draw_sample()
- >>> assert 0 <= sample < 10.0
- Create and sample from a uniform distribution over ``[0, 10.0)``.
- """
- def __init__(self, a, b):
- # pylint: disable=invalid-name
- super(Uniform, self).__init__()
- self.a = handle_continuous_param(a, "a")
- self.b = handle_continuous_param(b, "b")
- def _draw_samples(self, size, random_state):
- # pylint: disable=invalid-name
- a = self.a.draw_sample(random_state=random_state)
- b = self.b.draw_sample(random_state=random_state)
- if a > b:
- a, b = b, a
- elif a == b:
- return np.full(size, a, dtype=np.float32)
- return random_state.uniform(a, b, size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Uniform(%s, %s)" % (self.a, self.b)
- class Beta(StochasticParameter):
- """Parameter that resembles a (continuous) beta distribution.
- Parameters
- ----------
- alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- alpha parameter of the beta distribution.
- Expected value range is ``(0, inf)``. Values below ``0`` are
- automatically clipped to ``0+epsilon``.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Beta.draw_sample` or
- :func:`Beta.draw_samples`.
- beta : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Beta parameter of the beta distribution. Analogous to `alpha`.
- epsilon : number
- Clipping parameter. If `alpha` or `beta` end up ``<=0``, they are clipped to ``0+epsilon``.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Beta(0.4, 0.6)
- Create a beta distribution with ``alpha=0.4`` and ``beta=0.6``.
- """
- def __init__(self, alpha, beta, epsilon=0.0001):
- super(Beta, self).__init__()
- self.alpha = handle_continuous_param(alpha, "alpha")
- self.beta = handle_continuous_param(beta, "beta")
- assert ia.is_single_number(epsilon), (
- "Expected epsilon to a number, got type %s." % (type(epsilon),))
- self.epsilon = epsilon
- def _draw_samples(self, size, random_state):
- alpha = self.alpha.draw_sample(random_state=random_state)
- beta = self.beta.draw_sample(random_state=random_state)
- alpha = max(alpha, self.epsilon)
- beta = max(beta, self.epsilon)
- return random_state.beta(alpha, beta, size=size).astype(np.float32)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Beta(%s, %s)" % (self.alpha, self.beta)
- class FromLowerResolution(StochasticParameter):
- """Parameter to sample from other parameters at lower image resolutions.
- This parameter is intended to be used with parameters that would usually
- sample one value per pixel (or one value per pixel and channel). Instead
- of sampling from the other parameter at full resolution, it samples at
- lower resolution, e.g. ``0.5*H x 0.5*W`` with ``H`` being the height and
- ``W`` being the width. After the low-resolution sampling this parameter
- then upscales the result to ``HxW``.
- This parameter is intended to produce coarse samples. E.g. combining
- this with :class:`Binomial` can lead to large rectangular areas of
- ``1`` s and ``0`` s.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- The other parameter which is to be sampled on a coarser image.
- size_percent : None or number or iterable of number or imgaug.parameters.StochasticParameter, optional
- Size of the 2d sampling plane in percent of the requested size.
- I.e. this is relative to the size provided in the call to
- ``draw_samples(size)``. Lower values will result in smaller sampling
- planes, which are then upsampled to `size`. This means that lower
- values will result in larger rectangles. The size may be provided as
- a constant value or a tuple ``(a, b)``, which will automatically be
- converted to the continuous uniform range ``[a, b)`` or a
- :class:`StochasticParameter`, which will be queried per call to
- :func:`FromLowerResolution.draw_sample` and
- :func:`FromLowerResolution.draw_samples`.
- size_px : None or number or iterable of numbers or imgaug.parameters.StochasticParameter, optional
- Size of the 2d sampling plane in pixels.
- Lower values will result in smaller sampling planes, which are then
- upsampled to the input `size` of ``draw_samples(size)``.
- This means that lower values will result in larger rectangles.
- The size may be provided as a constant value or a tuple ``(a, b)``,
- which will automatically be converted to the discrete uniform
- range ``[a..b]`` or a :class:`StochasticParameter`, which will be
- queried once per call to :func:`FromLowerResolution.draw_sample` and
- :func:`FromLowerResolution.draw_samples`.
- method : str or int or imgaug.parameters.StochasticParameter, optional
- Upsampling/interpolation method to use. This is used after the sampling
- is finished and the low resolution plane has to be upsampled to the
- requested `size` in ``draw_samples(size, ...)``. The method may be
- the same as in :func:`~imgaug.imgaug.imresize_many_images`. Usually
- ``nearest`` or ``linear`` are good choices. ``nearest`` will result
- in rectangles with sharp edges and ``linear`` in rectangles with
- blurry and round edges. The method may be provided as a
- :class:`StochasticParameter`, which will be queried once per call to
- :func:`FromLowerResolution.draw_sample` and
- :func:`FromLowerResolution.draw_samples`.
- min_size : int, optional
- Minimum size in pixels of the low resolution sampling plane.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.FromLowerResolution(
- >>> Binomial(0.05),
- >>> size_px=(2, 16),
- >>> method=Choice(["nearest", "linear"]))
- Samples from a binomial distribution with ``p=0.05``. The sampling plane
- will always have a size HxWxC with H and W being independently sampled
- from ``[2..16]`` (i.e. it may range from ``2x2xC`` up to ``16x16xC`` max,
- but may also be e.g. ``4x8xC``). The upsampling method will be ``nearest``
- in ``50%`` of all cases and ``linear`` in the other 50 percent. The result
- will sometimes be rectangular patches of sharp ``1`` s surrounded by
- ``0`` s and sometimes blurry blobs of ``1``s, surrounded by values
- ``<1.0``.
- """
- def __init__(self, other_param, size_percent=None, size_px=None,
- method="nearest", min_size=1):
- super(FromLowerResolution, self).__init__()
- assert size_percent is not None or size_px is not None, (
- "Expected either 'size_percent' or 'size_px' to be provided, "
- "got neither of them.")
- if size_percent is not None:
- self.size_method = "percent"
- self.size_px = None
- if ia.is_single_number(size_percent):
- self.size_percent = Deterministic(size_percent)
- elif ia.is_iterable(size_percent):
- assert len(size_percent) == 2, (
- "Expected iterable 'size_percent' to contain exactly 2 "
- "values, got %d." % (len(size_percent),))
- self.size_percent = Uniform(size_percent[0], size_percent[1])
- elif isinstance(size_percent, StochasticParameter):
- self.size_percent = size_percent
- else:
- raise Exception(
- "Expected int, float, tuple of two ints/floats or "
- "StochasticParameter for size_percent, "
- "got %s." % (type(size_percent),))
- else: # = elif size_px is not None:
- self.size_method = "px"
- self.size_percent = None
- if ia.is_single_integer(size_px):
- self.size_px = Deterministic(size_px)
- elif ia.is_iterable(size_px):
- assert len(size_px) == 2, (
- "Expected iterable 'size_px' to contain exactly 2 "
- "values, got %d." % (len(size_px),))
- self.size_px = DiscreteUniform(size_px[0], size_px[1])
- elif isinstance(size_px, StochasticParameter):
- self.size_px = size_px
- else:
- raise Exception(
- "Expected int, float, tuple of two ints/floats or "
- "StochasticParameter for size_px, "
- "got %s." % (type(size_px),))
- self.other_param = other_param
- if ia.is_string(method) or ia.is_single_integer(method):
- self.method = Deterministic(method)
- elif isinstance(method, StochasticParameter):
- self.method = method
- else:
- raise Exception("Expected string or StochasticParameter, "
- "got %s." % (type(method),))
- self.min_size = min_size
- def _draw_samples(self, size, random_state):
- if len(size) == 3:
- n = 1
- h, w, c = size
- elif len(size) == 4:
- n, h, w, c = size
- else:
- raise Exception("FromLowerResolution can only generate samples "
- "of shape (H, W, C) or (N, H, W, C), "
- "requested was %s." % (str(size),))
- if self.size_method == "percent":
- hw_percents = self.size_percent.draw_samples(
- (n, 2), random_state=random_state)
- hw_pxs = (hw_percents * np.array([h, w])).astype(np.int32)
- else:
- hw_pxs = self.size_px.draw_samples(
- (n, 2), random_state=random_state)
- methods = self.method.draw_samples((n,), random_state=random_state)
- result = None
- for i, (hw_px, method) in enumerate(zip(hw_pxs, methods)):
- h_small = max(hw_px[0], self.min_size)
- w_small = max(hw_px[1], self.min_size)
- samples = self.other_param.draw_samples(
- (1, h_small, w_small, c), random_state=random_state)
- # This (1) makes sure that samples are of dtypes supported by
- # imresize_many_images, and (2) forces samples to be float-kind
- # if the requested interpolation is something else than nearest
- # neighbour interpolation. (2) is a bit hacky and makes sure that
- # continuous values are produced for e.g. cubic interpolation.
- # This is particularly important for e.g. binomial distributios
- # used in FromLowerResolution and thereby in e.g. CoarseDropout,
- # where integer-kinds would lead to sharp edges despite using
- # cubic interpolation.
- if samples.dtype.kind == "f":
- samples = iadt.restore_dtypes_(samples, np.float32)
- elif samples.dtype.kind == "i":
- if method == "nearest":
- samples = iadt.restore_dtypes_(samples, np.int32)
- else:
- samples = iadt.restore_dtypes_(samples, np.float32)
- else:
- assert samples.dtype.kind == "u", (
- "FromLowerResolution can only process outputs of kind "
- "f (float), i (int) or u (uint), got %s." % (
- samples.dtype.kind))
- if method == "nearest":
- samples = iadt.restore_dtypes_(samples, np.uint16)
- else:
- samples = iadt.restore_dtypes_(samples, np.float32)
- samples_upscaled = ia.imresize_many_images(
- samples, (h, w), interpolation=method)
- if result is None:
- result = np.zeros((n, h, w, c), dtype=samples_upscaled.dtype)
- result[i] = samples_upscaled
- if len(size) == 3:
- return result[0]
- return result
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- if self.size_method == "percent":
- pattern = (
- "FromLowerResolution("
- "size_percent=%s, method=%s, other_param=%s"
- ")")
- return pattern % (self.size_percent, self.method, self.other_param)
- pattern = (
- "FromLowerResolution("
- "size_px=%s, method=%s, other_param=%s"
- ")")
- return pattern % (self.size_px, self.method, self.other_param)
- class Clip(StochasticParameter):
- """Clip another parameter to a defined value range.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- The other parameter, which's values are to be clipped.
- minval : None or number, optional
- The minimum value to use.
- If ``None``, no minimum will be used.
- maxval : None or number, optional
- The maximum value to use.
- If ``None``, no maximum will be used.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Clip(Normal(0, 1.0), minval=-2.0, maxval=2.0)
- Create a standard gaussian distribution, which's values never go below
- ``-2.0`` or above ``2.0``. Note that this will lead to small "bumps" of
- higher probability at ``-2.0`` and ``2.0``, as values below/above these
- will be clipped to them. For smoother limitations on gaussian
- distributions, see :class:`TruncatedNormal`.
- """
- def __init__(self, other_param, minval=None, maxval=None):
- super(Clip, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- assert minval is None or ia.is_single_number(minval), (
- "Expected 'minval' to be None or a number, got type %s." % (
- type(minval),))
- assert maxval is None or ia.is_single_number(maxval), (
- "Expected 'maxval' to be None or a number, got type %s." % (
- type(maxval),))
- self.other_param = other_param
- self.minval = minval
- self.maxval = maxval
- def _draw_samples(self, size, random_state):
- samples = self.other_param.draw_samples(size, random_state=random_state)
- if self.minval is not None or self.maxval is not None:
- # Note that this would produce a warning if 'samples' is int64
- # or uint64
- samples = np.clip(samples, self.minval, self.maxval, out=samples)
- return samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- if self.minval is not None and self.maxval is not None:
- return "Clip(%s, %.6f, %.6f)" % (
- opstr, float(self.minval), float(self.maxval))
- if self.minval is not None:
- return "Clip(%s, %.6f, None)" % (opstr, float(self.minval))
- if self.maxval is not None:
- return "Clip(%s, None, %.6f)" % (opstr, float(self.maxval))
- return "Clip(%s, None, None)" % (opstr,)
- class Discretize(StochasticParameter):
- """Convert a continuous distribution to a discrete one.
- This will round the values and then cast them to integers.
- Values sampled from already discrete distributions are not changed.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- The other parameter, which's values are to be discretized.
- round : bool, optional
- Whether to round before converting to integer dtype.
- Added in 0.4.0.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Discretize(iap.Normal(0, 1.0))
- Create a discrete standard gaussian distribution.
- """
- def __init__(self, other_param, round=True):
- # pylint: disable=redefined-builtin
- super(Discretize, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- self.other_param = other_param
- self.round = round
- def _draw_samples(self, size, random_state):
- samples = self.other_param.draw_samples(size, random_state=random_state)
- assert samples.dtype.kind in ["u", "i", "b", "f"], (
- "Expected to get uint, int, bool or float dtype as samples in "
- "Discretize(), but got dtype '%s' (kind '%s') instead." % (
- samples.dtype.name, samples.dtype.kind))
- if samples.dtype.kind in ["u", "i", "b"]:
- return samples
- # floats seem to reliably cover ints that have half the number of
- # bits -- probably not the case for float128 though as that is
- # really float96
- bitsize = 8 * samples.dtype.itemsize // 2
- # in case some weird system knows something like float8 we set a
- # lower bound here -- shouldn't happen though
- bitsize = max(bitsize, 8)
- dtype = np.dtype("int%d" % (bitsize,))
- if self.round:
- samples = np.round(samples)
- return samples.astype(dtype)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "Discretize(%s, round=%s)" % (opstr, str(self.round))
- class Multiply(StochasticParameter):
- """Multiply the samples of another stochastic parameter.
- Parameters
- ----------
- other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be multiplied with `val`.
- Let ``S`` be the requested shape of samples, then the datatype
- behaviour is as follows:
- * If a single ``number``, this ``number`` will be used as a
- constant value to fill an array of shape ``S``.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
- shape ``S`` will be filled with uniformly sampled values from
- the continuous interval ``[a, b)``.
- * If a ``list`` of ``number``, an array of shape ``S`` will be
- filled with randomly picked values from the ``list``.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call to generate an array of shape ``S``.
- "per call" denotes a call of :func:`Multiply.draw_sample` or
- :func:`Multiply.draw_samples`.
- val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Multiplier to use.
- Datatype behaviour is analogous to `other_param`, though if
- ``elementwise=False`` (the default), only a single sample will be
- generated per call instead of ``S``.
- elementwise : bool, optional
- Controls the sampling behaviour of `val`.
- If set to ``False``, a single samples will be requested from `val` and
- used as the constant multiplier.
- If set to ``True``, samples of shape ``S`` will be requested from
- `val` and multiplied elementwise with the samples of `other_param`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Multiply(iap.Uniform(0.0, 1.0), -1)
- Convert a uniform distribution from ``[0.0, 1.0)`` to ``(-1.0, 0.0]``.
- """
- def __init__(self, other_param, val, elementwise=False):
- super(Multiply, self).__init__()
- 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):
- rngs = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- elementwise = (
- self.elementwise
- and not isinstance(self.val, Deterministic))
- if elementwise:
- val_samples = self.val.draw_samples(size, random_state=rngs[1])
- else:
- val_samples = self.val.draw_sample(random_state=rngs[1])
- if elementwise:
- return np.multiply(samples, val_samples)
- return samples * val_samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Multiply(%s, %s, %s)" % (
- str(self.other_param), str(self.val), self.elementwise)
- class Divide(StochasticParameter):
- """Divide the samples of another stochastic parameter.
- This parameter will automatically prevent division by zero (uses 1.0)
- as the denominator in these cases.
- Parameters
- ----------
- other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be divided by `val`.
- Let ``S`` be the requested shape of samples, then the datatype
- behaviour is as follows:
- * If a single ``number``, this ``number`` will be used as a
- constant value to fill an array of shape ``S``.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
- shape ``S`` will be filled with uniformly sampled values from
- the continuous interval ``[a, b)``.
- * If a ``list`` of ``number``, an array of shape ``S`` will be
- filled with randomly picked values from the ``list``.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call to generate an array of shape ``S``.
- "per call" denotes a call of :func:`Divide.draw_sample` or
- :func:`Divide.draw_samples`.
- val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Denominator to use.
- Datatype behaviour is analogous to `other_param`, though if
- ``elementwise=False`` (the default), only a single sample will be
- generated per call instead of ``S``.
- elementwise : bool, optional
- Controls the sampling behaviour of `val`.
- If set to ``False``, a single samples will be requested from `val` and
- used as the constant denominator.
- If set to ``True``, samples of shape ``S`` will be requested from
- `val` and used to divide the samples of `other_param` elementwise.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Divide(iap.Uniform(0.0, 1.0), 2)
- Convert a uniform distribution ``[0.0, 1.0)`` to ``[0, 0.5)``.
- """
- def __init__(self, other_param, val, elementwise=False):
- super(Divide, self).__init__()
- 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):
- # pylint: disable=no-else-return
- rngs = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- elementwise = (
- self.elementwise
- and not isinstance(self.val, Deterministic))
- if elementwise:
- val_samples = self.val.draw_samples(size, random_state=rngs[1])
- # prevent division by zero
- val_samples[val_samples == 0] = 1
- return np.divide(
- force_np_float_dtype(samples),
- force_np_float_dtype(val_samples)
- )
- else:
- val_sample = self.val.draw_sample(random_state=rngs[1])
- # prevent division by zero
- if val_sample == 0:
- val_sample = 1
- return force_np_float_dtype(samples) / float(val_sample)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Divide(%s, %s, %s)" % (
- str(self.other_param), str(self.val), self.elementwise)
- # TODO sampling (N,) from something like 10+Uniform(0, 1) will return
- # N times the same value as (N,) values will be sampled from 10, but only
- # one from Uniform() unless elementwise=True is explicitly set. That
- # seems unintuitive. How can this be prevented?
- class Add(StochasticParameter):
- """Add to the samples of another stochastic parameter.
- Parameters
- ----------
- other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Samples of `val` will be added to samples of this parameter.
- Let ``S`` be the requested shape of samples, then the datatype
- behaviour is as follows:
- * If a single ``number``, this ``number`` will be used as a
- constant value to fill an array of shape ``S``.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
- shape ``S`` will be filled with uniformly sampled values from
- the continuous interval ``[a, b)``.
- * If a ``list`` of ``number``, an array of shape ``S`` will be
- filled with randomly picked values from the ``list``.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call to generate an array of shape ``S``.
- "per call" denotes a call of :func:`Add.draw_sample` or
- :func:`Add.draw_samples`.
- val : number or tuple of two number or list of number or imgaug.parameters.StochasticParameter
- Value to add to the samples of `other_param`.
- Datatype behaviour is analogous to `other_param`, though if
- ``elementwise=False`` (the default), only a single sample will be
- generated per call instead of ``S``.
- elementwise : bool, optional
- Controls the sampling behaviour of `val`.
- If set to ``False``, a single samples will be requested from `val` and
- used as the constant multiplier.
- If set to ``True``, samples of shape ``S`` will be requested from
- `val` and added elementwise with the samples of `other_param`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Add(Uniform(0.0, 1.0), 1.0)
- Convert a uniform distribution from ``[0.0, 1.0)`` to ``[1.0, 2.0)``.
- """
- def __init__(self, other_param, val, elementwise=False):
- super(Add, self).__init__()
- 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):
- rngs = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- elementwise = (
- self.elementwise and not isinstance(self.val, Deterministic))
- if elementwise:
- val_samples = self.val.draw_samples(size, random_state=rngs[1])
- else:
- val_samples = self.val.draw_sample(random_state=rngs[1])
- if elementwise:
- return np.add(samples, val_samples)
- return samples + val_samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Add(%s, %s, %s)" % (
- str(self.other_param), str(self.val), self.elementwise)
- class Subtract(StochasticParameter):
- """Subtract from the samples of another stochastic parameter.
- Parameters
- ----------
- other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Samples of `val` will be subtracted from samples of this parameter.
- Let ``S`` be the requested shape of samples, then the datatype
- behaviour is as follows:
- * If a single ``number``, this ``number`` will be used as a
- constant value to fill an array of shape ``S``.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
- shape ``S`` will be filled with uniformly sampled values from
- the continuous interval ``[a, b)``.
- * If a ``list`` of ``number``, an array of shape ``S`` will be
- filled with randomly picked values from the ``list``.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call to generate an array of shape ``S``.
- "per call" denotes a call of :func:`Subtract.draw_sample` or
- :func:`Subtract.draw_samples`.
- val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Value to subtract from the other parameter.
- Datatype behaviour is analogous to `other_param`, though if
- ``elementwise=False`` (the default), only a single sample will be
- generated per call instead of ``S``.
- elementwise : bool, optional
- Controls the sampling behaviour of `val`.
- If set to ``False``, a single samples will be requested from `val` and
- used as the constant multiplier.
- If set to ``True``, samples of shape ``S`` will be requested from
- `val` and subtracted elementwise from the samples of `other_param`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Subtract(iap.Uniform(0.0, 1.0), 1.0)
- Convert a uniform distribution from ``[0.0, 1.0)`` to ``[-1.0, 0.0)``.
- """
- def __init__(self, other_param, val, elementwise=False):
- super(Subtract, self).__init__()
- 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):
- rngs = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- elementwise = (self.elementwise
- and not isinstance(self.val, Deterministic))
- if elementwise:
- val_samples = self.val.draw_samples(size, random_state=rngs[1])
- else:
- val_samples = self.val.draw_sample(random_state=rngs[1])
- if elementwise:
- return np.subtract(samples, val_samples)
- return samples - val_samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Subtract(%s, %s, %s)" % (
- str(self.other_param), str(self.val), self.elementwise)
- class Power(StochasticParameter):
- """Exponentiate the samples of another stochastic parameter.
- Parameters
- ----------
- other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be exponentiated by `val`.
- Let ``S`` be the requested shape of samples, then the datatype
- behaviour is as follows:
- * If a single ``number``, this ``number`` will be used as a
- constant value to fill an array of shape ``S``.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
- shape ``S`` will be filled with uniformly sampled values from
- the continuous interval ``[a, b)``.
- * If a ``list`` of ``number``, an array of shape ``S`` will be
- filled with randomly picked values from the ``list``.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call to generate an array of shape ``S``.
- "per call" denotes a call of :func:`Power.draw_sample` or
- :func:`Power.draw_samples`.
- val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
- Value to use exponentiate the samples of `other_param`.
- Datatype behaviour is analogous to `other_param`, though if
- ``elementwise=False`` (the default), only a single sample will be
- generated per call instead of ``S``.
- elementwise : bool, optional
- Controls the sampling behaviour of `val`.
- If set to ``False``, a single samples will be requested from `val` and
- used as the constant multiplier.
- If set to ``True``, samples of shape ``S`` will be requested from
- `val` and used to exponentiate elementwise the samples of `other_param`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Power(iap.Uniform(0.0, 1.0), 2)
- Converts a uniform range ``[0.0, 1.0)`` to a distribution that is peaked
- towards 1.0.
- """
- def __init__(self, other_param, val, elementwise=False):
- super(Power, self).__init__()
- 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):
- rngs = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- elementwise = (
- self.elementwise
- and not isinstance(self.val, Deterministic))
- if elementwise:
- exponents = self.val.draw_samples(size, random_state=rngs[1])
- else:
- exponents = self.val.draw_sample(random_state=rngs[1])
- # 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
- # TODO switch to this as numpy>=1.15 is now a requirement
- # float_power requires numpy>=1.12
- # result = np.float_power(samples, exponents)
- # TODO why was float32 type here replaced with complex number
- # formulation?
- result = np.power(samples.astype(np.complex), exponents).real
- if result.dtype != samples_dtype:
- result = result.astype(samples_dtype)
- return result
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "Power(%s, %s, %s)" % (
- str(self.other_param), str(self.val), self.elementwise)
- class Absolute(StochasticParameter):
- """Convert the samples of another parameter to their absolute values.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be modified.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Absolute(iap.Uniform(-1.0, 1.0))
- Convert a uniform distribution from ``[-1.0, 1.0)`` to ``[0.0, 1.0]``.
- """
- def __init__(self, other_param):
- super(Absolute, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- self.other_param = other_param
- def _draw_samples(self, size, random_state):
- samples = self.other_param.draw_samples(size, random_state=random_state)
- return np.absolute(samples)
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "Absolute(%s)" % (opstr,)
- class RandomSign(StochasticParameter):
- """Convert a parameter's samples randomly to positive or negative values.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be modified.
- p_positive : number
- Fraction of values that are supposed to be turned to positive values.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.RandomSign(iap.Poisson(1))
- Create a poisson distribution with ``alpha=1`` that is mirrored/copied (not
- flipped) at the y-axis.
- """
- def __init__(self, other_param, p_positive=0.5):
- super(RandomSign, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- assert ia.is_single_number(p_positive), (
- "Expected 'p_positive' to be a number, got %s." % (
- type(p_positive)))
- assert 0.0 <= p_positive <= 1.0, (
- "Expected 'p_positive' to be in the interval [0.0, 1.0], "
- "got %.4f." % (p_positive,))
- self.other_param = other_param
- self.p_positive = p_positive
- def _draw_samples(self, size, random_state):
- rss = random_state.duplicate(2)
- samples = self.other_param.draw_samples(size, random_state=rss[0])
- # TODO add method to change from uint to int here instead of assert
- assert samples.dtype.kind in ["f", "i"], (
- "Expected to get samples of kind float or int, but got dtype %s "
- "of kind %s." % (samples.dtype.name, samples.dtype.kind))
- # TODO convert to same kind as samples
- coinflips = rss[1].binomial(
- 1, self.p_positive, size=size).astype(np.int8)
- signs = coinflips * 2 - 1
- # Add absolute here to guarantee that we get p_positive percent of
- # positive values. Otherwise we would merely flip p_positive percent
- # of all signs.
- # TODO test if
- # result[coinflips_mask] *= (-1)
- # is faster (with protection against mask being empty?)
- result = np.absolute(samples) * signs
- return result
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "RandomSign(%s, %.2f)" % (opstr, self.p_positive)
- class ForceSign(StochasticParameter):
- """Convert a parameter's samples to either positive or negative values.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be modified.
- positive : bool
- Whether to force all signs to be positive (``True``) or
- negative (``False``).
- mode : {'invert', 'reroll'}, optional
- Method to change the signs. Valid values are ``invert`` and ``reroll``.
- ``invert`` means that wrong signs are simply flipped.
- ``reroll`` means that all samples with wrong signs are sampled again,
- optionally many times, until they randomly end up having the correct
- sign.
- reroll_count_max : int, optional
- If `mode` is set to ``reroll``, this determines how often values may
- be rerolled before giving up and simply flipping the sign (as in
- ``mode="invert"``). This shouldn't be set too high, as rerolling is
- expensive.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.ForceSign(iap.Poisson(1), positive=False)
- Create a poisson distribution with ``alpha=1`` that is flipped towards
- negative values.
- """
- def __init__(self, other_param, positive, mode="invert",
- reroll_count_max=2):
- super(ForceSign, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- self.other_param = other_param
- assert positive in [True, False], (
- "Expected 'positive' to be True or False, got type %s." % (
- type(positive),))
- self.positive = positive
- assert mode in ["invert", "reroll"], (
- "Expected 'mode' to be \"invert\" or \"reroll\", got %s." % (mode,))
- self.mode = mode
- assert ia.is_single_integer(reroll_count_max), (
- "Expected 'reroll_count_max' to be an integer, got type %s." % (
- type(reroll_count_max)))
- self.reroll_count_max = reroll_count_max
- def _draw_samples(self, size, random_state):
- rngs = random_state.duplicate(1+self.reroll_count_max)
- samples = self.other_param.draw_samples(size, random_state=rngs[0])
- if self.mode == "invert":
- if self.positive:
- samples[samples < 0] *= (-1)
- else:
- samples[samples > 0] *= (-1)
- else:
- if self.positive:
- bad_samples = np.where(samples < 0)[0]
- else:
- bad_samples = np.where(samples > 0)[0]
- reroll_count = 0
- while len(bad_samples) > 0 and reroll_count < self.reroll_count_max:
- # This rerolls the full input size, even when only a tiny
- # fraction of the values were wrong. That is done, because not
- # all parameters necessarily support any number of dimensions
- # for `size`, so we cant just resample size=N for N values
- # with wrong signs.
- # There is still quite some room for improvement here.
- samples_reroll = self.other_param.draw_samples(
- size,
- random_state=rngs[1+reroll_count]
- )
- samples[bad_samples] = samples_reroll[bad_samples]
- reroll_count += 1
- if self.positive:
- bad_samples = np.where(samples < 0)[0]
- else:
- bad_samples = np.where(samples > 0)[0]
- if len(bad_samples) > 0:
- samples[bad_samples] *= (-1)
- return samples
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "ForceSign(%s, %s, %s, %d)" % (
- opstr, str(self.positive), self.mode, self.reroll_count_max)
- def Positive(other_param, mode="invert", reroll_count_max=2):
- """Convert another parameter's results to positive values.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be
- modified.
- mode : {'invert', 'reroll'}, optional
- How to change the signs. Valid values are ``invert`` and ``reroll``.
- ``invert`` means that wrong signs are simply flipped.
- ``reroll`` means that all samples with wrong signs are sampled again,
- optionally many times, until they randomly end up having the correct
- sign.
- reroll_count_max : int, optional
- If `mode` is set to ``reroll``, this determines how often values may
- be rerolled before giving up and simply flipping the sign (as in
- ``mode="invert"``). This shouldn't be set too high, as rerolling is
- expensive.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Positive(iap.Normal(0, 1), mode="reroll")
- Create a gaussian distribution that has only positive values.
- If any negative value is sampled in the process, that sample is resampled
- up to two times to get a positive one. If it isn't positive after the
- second resampling step, the sign is simply flipped.
- """
- # pylint: disable=invalid-name
- return ForceSign(
- other_param=other_param,
- positive=True,
- mode=mode,
- reroll_count_max=reroll_count_max
- )
- def Negative(other_param, mode="invert", reroll_count_max=2):
- """Convert another parameter's results to negative values.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- Other parameter which's sampled values are to be
- modified.
- mode : {'invert', 'reroll'}, optional
- How to change the signs. Valid values are ``invert`` and ``reroll``.
- ``invert`` means that wrong signs are simply flipped.
- ``reroll`` means that all samples with wrong signs are sampled again,
- optionally many times, until they randomly end up having the correct
- sign.
- reroll_count_max : int, optional
- If `mode` is set to ``reroll``, this determines how often values may
- be rerolled before giving up and simply flipping the sign (as in
- ``mode="invert"``). This shouldn't be set too high, as rerolling is
- expensive.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Negative(iap.Normal(0, 1), mode="reroll")
- Create a gaussian distribution that has only negative values.
- If any positive value is sampled in the process, that sample is resampled
- up to two times to get a negative one. If it isn't negative after the
- second resampling step, the sign is simply flipped.
- """
- # pylint: disable=invalid-name
- return ForceSign(
- other_param=other_param,
- positive=False,
- mode=mode,
- reroll_count_max=reroll_count_max
- )
- # TODO this always aggregates the result in high resolution space, instead of
- # aggregating them in low resolution and then only upscaling the final
- # image (for N iterations that would save up to N-1 upscales)
- class IterativeNoiseAggregator(StochasticParameter):
- """Aggregate multiple iterations of samples from another parameter.
- This is supposed to be used in conjunction with :class:`SimplexNoise` or
- :class:`FrequencyNoise`. If a shape ``S`` is requested, it will request
- ``I`` times ``S`` samples from the underlying parameter, where ``I`` is
- the number of iterations. The ``I`` arrays will be combined to a single
- array of shape ``S`` using an aggregation method, e.g. simple averaging.
- Parameters
- ----------
- other_param : StochasticParameter
- The other parameter from which to sample one or more times.
- iterations : int or iterable of int or list of int or imgaug.parameters.StochasticParameter, optional
- The number of iterations.
- * If a single ``int``, this ``int`` will be used as a
- constant value.
- * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
- sampled from the discrete interval ``[a..b]`` once per call.
- * If a ``list`` of ``int``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of
- :func:`IterativeNoiseAggregator.draw_sample` or
- :func:`IterativeNoiseAggregator.draw_samples`.
- aggregation_method : imgaug.ALL or {'min', 'avg', 'max'} or list of str or imgaug.parameters.StochasticParameter, optional
- The method to use to aggregate the samples of multiple iterations
- to a single output array. All methods combine several arrays of
- shape ``S`` each to a single array of shape ``S`` and hence work
- elementwise. Known methods are ``min`` (take the minimum over all
- iterations), ``max`` (take the maximum) and ``avg`` (take the average).
- * If an ``str``, it must be one of the described methods and
- will be used for all calls..
- * If a ``list`` of ``str``, it must contain one or more of the
- described methods and a random one will be samples once per call.
- * If ``imgaug.ALL``, then equivalent to the ``list``
- ``["min", "max", "avg"]``.
- * If :class:`StochasticParameter`, a value will be sampled from
- that parameter once per call and must be one of the described
- methods..
- "per call" denotes a call of
- :func:`IterativeNoiseAggregator.draw_sample` or
- :func:`IterativeNoiseAggregator.draw_samples`.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> noise = iap.IterativeNoiseAggregator(
- >>> iap.SimplexNoise(),
- >>> iterations=(2, 5),
- >>> aggregation_method="max")
- Create a parameter that -- upon each call -- generates ``2`` to ``5``
- arrays of simplex noise with the same shape. Then it combines these
- noise maps to a single map using elementwise maximum.
- """
- def __init__(self, other_param, iterations=(1, 3),
- aggregation_method=["max", "avg"]):
- # pylint: disable=dangerous-default-value
- super(IterativeNoiseAggregator, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- self.other_param = other_param
- def _assert_within_bounds(_iterations):
- assert all([1 <= val <= 10000 for val in _iterations]), (
- "Expected 'iterations' to only contain values within "
- "the interval [1, 1000], got values %s." % (
- ", ".join([str(val) for val in _iterations]),))
- if ia.is_single_integer(iterations):
- _assert_within_bounds([iterations])
- self.iterations = Deterministic(iterations)
- elif isinstance(iterations, list):
- assert len(iterations) > 0, (
- "Expected 'iterations' of type list to contain at least one "
- "entry, got %d." % (len(iterations),))
- _assert_within_bounds(iterations)
- self.iterations = Choice(iterations)
- elif ia.is_iterable(iterations):
- assert len(iterations) == 2, (
- "Expected iterable non-list 'iteratons' to contain exactly "
- "two entries, got %d." % (len(iterations),))
- assert all([ia.is_single_integer(val) for val in iterations]), (
- "Expected iterable non-list 'iterations' to only contain "
- "integers, got types %s." % (
- ", ".join([str(type(val)) for val in iterations]),))
- _assert_within_bounds(iterations)
- self.iterations = DiscreteUniform(iterations[0], iterations[1])
- elif isinstance(iterations, StochasticParameter):
- self.iterations = iterations
- else:
- raise Exception(
- "Expected iterations to be int or tuple of two ints or "
- "StochasticParameter, got %s." % (type(iterations),))
- if aggregation_method == ia.ALL:
- self.aggregation_method = Choice(["min", "max", "avg"])
- elif ia.is_string(aggregation_method):
- self.aggregation_method = Deterministic(aggregation_method)
- elif isinstance(aggregation_method, list):
- assert len(aggregation_method) >= 1, (
- "Expected at least one aggregation method got %d." % (
- len(aggregation_method),))
- assert all([ia.is_string(val) for val in aggregation_method]), (
- "Expected aggregation methods provided as strings, "
- "got types %s." % (
- ", ".join([str(type(v)) for v in aggregation_method])))
- self.aggregation_method = Choice(aggregation_method)
- elif isinstance(aggregation_method, StochasticParameter):
- self.aggregation_method = aggregation_method
- else:
- raise Exception(
- "Expected aggregation_method to be string or list of strings "
- "or StochasticParameter, got %s." % (
- type(aggregation_method),))
- def _draw_samples(self, size, random_state):
- rngs = random_state.duplicate(2)
- aggregation_method = self.aggregation_method.draw_sample(
- random_state=rngs[0])
- iterations = self.iterations.draw_sample(random_state=rngs[1])
- assert iterations > 0, (
- "Expected to sample at least one iteration of aggregation. "
- "Got %d." % (iterations,))
- rngs_iterations = rngs[1].duplicate(iterations)
- result = np.zeros(size, dtype=np.float32)
- for i in sm.xrange(iterations):
- noise_iter = self.other_param.draw_samples(
- size, random_state=rngs_iterations[i])
- if aggregation_method == "avg":
- result += noise_iter
- elif aggregation_method == "min":
- if i == 0:
- result = noise_iter
- else:
- result = np.minimum(result, noise_iter)
- else: # self.aggregation_method == "max"
- if i == 0:
- result = noise_iter
- else:
- result = np.maximum(result, noise_iter)
- if aggregation_method == "avg":
- result = result / iterations
- return result
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "IterativeNoiseAggregator(%s, %s, %s)" % (
- opstr, str(self.iterations), str(self.aggregation_method))
- class Sigmoid(StochasticParameter):
- """Apply a sigmoid function to the outputs of another parameter.
- This is intended to be used in combination with :class:`SimplexNoise` or
- :class:`FrequencyNoise`. It pushes the noise values away from ``~0.5`` and
- towards ``0.0`` or ``1.0``, making the noise maps more binary.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- The other parameter to which the sigmoid will be applied.
- threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional
- Sets the value of the sigmoid's saddle point, i.e. where values
- start to quickly shift from ``0.0`` to ``1.0``.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`Sigmoid.draw_sample` or
- :func:`Sigmoid.draw_samples`.
- activated : bool or number, optional
- Defines whether the sigmoid is activated. If this is ``False``, the
- results of `other_param` will not be altered. This may be set to a
- ``float`` ``p`` in value range``[0.0, 1.0]``, which will result in
- `activated` being ``True`` in ``p`` percent of all calls.
- mul : number, optional
- The results of `other_param` will be multiplied with this value before
- applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this
- should be set to about ``20``.
- add : number, optional
- This value will be added to the results of `other_param` before
- applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this
- should be set to about ``-10.0``, provided `mul` was set to ``20``.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.Sigmoid(
- >>> iap.SimplexNoise(),
- >>> activated=0.5,
- >>> mul=20,
- >>> add=-10)
- Applies a sigmoid to simplex noise in ``50%`` of all calls. The noise
- results are modified to match the sigmoid's expected value range. The
- sigmoid's outputs are in the range ``[0.0, 1.0]``.
- """
- def __init__(self, other_param, threshold=(-10, 10), activated=True,
- mul=1, add=0):
- super(Sigmoid, self).__init__()
- _assert_arg_is_stoch_param("other_param", other_param)
- self.other_param = other_param
- self.threshold = handle_continuous_param(threshold, "threshold")
- self.activated = handle_probability_param(activated, "activated")
- assert ia.is_single_number(mul), (
- "Expected 'mul' to be a number, got type %s." % (type(mul),))
- assert mul > 0, (
- "Expected 'mul' to be greater than zero, got %.4f." % (mul,))
- self.mul = mul
- assert ia.is_single_number(add), (
- "Expected 'add' to be a number, got type %s." % (type(add),))
- self.add = add
- @staticmethod
- def create_for_noise(other_param, threshold=(-10, 10), activated=True):
- """Create a Sigmoid adjusted for noise parameters.
- "noise" here denotes :class:`SimplexNoise` and :class:`FrequencyNoise`.
- Parameters
- ----------
- other_param : imgaug.parameters.StochasticParameter
- See :func:`~imgaug.parameters.Sigmoid.__init__`.
- threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional
- See :func:`~imgaug.parameters.Sigmoid.__init__`.
- activated : bool or number, optional
- See :func:`~imgaug.parameters.Sigmoid.__init__`.
- Returns
- -------
- Sigmoid
- A sigmoid adjusted to be used with noise.
- """
- return Sigmoid(other_param, threshold, activated, mul=20, add=-10)
- def _draw_samples(self, size, random_state):
- rngs = random_state.duplicate(3)
- result = self.other_param.draw_samples(size, random_state=rngs[0])
- if result.dtype.kind != "f":
- result = result.astype(np.float32)
- activated = self.activated.draw_sample(random_state=rngs[1])
- threshold = self.threshold.draw_sample(random_state=rngs[2])
- if activated > 0.5:
- # threshold must be subtracted here, not added
- # higher threshold = move threshold of sigmoid towards the right
- # = make it harder to pass the threshold
- # = more 0.0s / less 1.0s
- # by subtracting a high value, it moves each x towards the left,
- # leading to more values being left of the threshold, leading
- # to more 0.0s
- return 1 / (1 + np.exp(-(result * self.mul + self.add - threshold)))
- return result
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- opstr = str(self.other_param)
- return "Sigmoid(%s, %s, %s, %s, %s)" % (
- opstr, str(self.threshold), str(self.activated), str(self.mul),
- str(self.add))
- class SimplexNoise(StochasticParameter):
- """Parameter that generates simplex noise of varying resolutions.
- This parameter expects to sample noise for 2d planes, i.e. for
- sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``
- per spatial location in that plane.
- The noise is sampled from low resolution planes and
- upscaled to the requested height and width. The size of the low
- resolution plane may be defined (large values can be slow) and the
- interpolation method for upscaling can be set.
- Parameters
- ----------
- size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
- Maximum height and width in pixels of the low resolution plane.
- Upon any sampling call, the requested shape will be downscaled until
- the height or width (whichever is larger) does not exceed this maximum
- value anymore. Then the noise will be sampled at that shape and later
- upscaled back to the requested shape.
- * If a single ``int``, this ``int`` will be used as a
- constant value.
- * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
- sampled from the discrete interval ``[a..b]`` once per call.
- * If a ``list`` of ``int``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`SimplexNoise.draw_sample` or
- :func:`SimplexNoise.draw_samples`.
- upscale_method : str or int or list of str or list of int or imgaug.parameters.StochasticParameter, optional
- After generating the noise maps in low resolution environments, they
- have to be upscaled to the originally requested shape (i.e. usually
- the image size). This parameter controls the interpolation method to
- use. See also :func:`~imgaug.imgaug.imresize_many_images` for a
- description of possible values.
- * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or
- ``area`` or ``cubic`` is picked per iteration (all same
- probability).
- * If ``str``, then that value will always be used as the method
- (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).
- * If ``list`` of ``str``, then a random value will be picked from
- that list per call.
- * If :class:`StochasticParameter`, then a random value will be
- sampled from that parameter per call.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.SimplexNoise(upscale_method="linear")
- Create a parameter that produces smooth simplex noise of varying sizes.
- >>> param = iap.SimplexNoise(
- >>> size_px_max=(8, 16),
- >>> upscale_method="nearest")
- Create a parameter that produces rectangular simplex noise of rather
- high detail.
- """
- def __init__(self, size_px_max=(2, 16),
- upscale_method=["linear", "nearest"]):
- # pylint: disable=dangerous-default-value
- super(SimplexNoise, self).__init__()
- self.size_px_max = handle_discrete_param(
- size_px_max, "size_px_max", value_range=(1, 10000))
- if upscale_method == ia.ALL:
- self.upscale_method = Choice(["nearest", "linear", "area",
- "cubic"])
- elif ia.is_string(upscale_method):
- self.upscale_method = Deterministic(upscale_method)
- elif isinstance(upscale_method, list):
- assert len(upscale_method) >= 1, (
- "Expected at least one upscale method, "
- "got %d." % (len(upscale_method),))
- assert all([ia.is_string(val) for val in upscale_method]), (
- "Expected all upscale methods to be strings, got types %s." % (
- ", ".join([str(type(v)) for v in upscale_method])))
- self.upscale_method = Choice(upscale_method)
- elif isinstance(upscale_method, StochasticParameter):
- self.upscale_method = upscale_method
- else:
- raise Exception(
- "Expected upscale_method to be string or list of strings or "
- "StochasticParameter, got %s." % (type(upscale_method),))
- def _draw_samples(self, size, random_state):
- assert len(size) in [2, 3], (
- "Expected requested noise to have shape (H, W) or (H, W, C), "
- "got shape %s." % (size,))
- height, width = size[0:2]
- nb_channels = 1 if len(size) == 2 else size[2]
- channels = [self._draw_samples_hw(height, width, random_state)
- for _ in np.arange(nb_channels)]
- if len(size) == 2:
- return channels[0]
- return np.stack(channels, axis=-1)
- def _draw_samples_hw(self, height, width, random_state):
- iterations = 1
- rngs = random_state.duplicate(1+iterations)
- aggregation_method = "max"
- upscale_methods = self.upscale_method.draw_samples(
- (iterations,), random_state=rngs[0])
- result = np.zeros((height, width), dtype=np.float32)
- for i in sm.xrange(iterations):
- noise_iter = self._draw_samples_iteration(
- height, width, rngs[1+i], upscale_methods[i])
- if aggregation_method == "avg":
- result += noise_iter
- elif aggregation_method == "min":
- if i == 0:
- result = noise_iter
- else:
- result = np.minimum(result, noise_iter)
- else: # self.aggregation_method == "max"
- if i == 0:
- result = noise_iter
- else:
- result = np.maximum(result, noise_iter)
- if aggregation_method == "avg":
- result = result / iterations
- return result
- def _draw_samples_iteration(self, height, width, rng, upscale_method):
- opensimplex_seed = rng.generate_seed_()
- # we have to use int(.) here, otherwise we can get warnings about
- # value overflows in OpenSimplex L103
- generator = OpenSimplex(seed=int(opensimplex_seed))
- maxlen = max(height, width)
- size_px_max = self.size_px_max.draw_sample(random_state=rng)
- if maxlen > size_px_max:
- downscale_factor = size_px_max / maxlen
- h_small = int(height * downscale_factor)
- w_small = int(width * downscale_factor)
- else:
- h_small = height
- w_small = width
- # don't go below Hx1 or 1xW
- h_small = max(h_small, 1)
- w_small = max(w_small, 1)
- noise = np.zeros((h_small, w_small), dtype=np.float32)
- for y in sm.xrange(h_small):
- for x in sm.xrange(w_small):
- noise[y, x] = generator.noise2d(y=y, x=x)
- # TODO this was previously (noise+0.5)/2, which was wrong as the noise
- # here is in range [-1.0, 1.0], but this new normalization might
- # lead to bad masks due to too many values being significantly
- # above 0.0 instead of being clipped to 0?
- noise_0to1 = (noise + 1.0) / 2
- noise_0to1 = np.clip(noise_0to1, 0.0, 1.0)
- if noise_0to1.shape != (height, width):
- noise_0to1_uint8 = (noise_0to1 * 255).astype(np.uint8)
- noise_0to1_3d = np.tile(
- noise_0to1_uint8[..., np.newaxis], (1, 1, 3))
- noise_0to1 = ia.imresize_single_image(
- noise_0to1_3d, (height, width), interpolation=upscale_method)
- noise_0to1 = (noise_0to1[..., 0] / 255.0).astype(np.float32)
- return noise_0to1
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "SimplexNoise(%s, %s)" % (
- str(self.size_px_max),
- str(self.upscale_method)
- )
- class FrequencyNoise(StochasticParameter):
- """Parameter to generate noise of varying frequencies.
- This parameter expects to sample noise for 2d planes, i.e. for
- sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``
- per spatial location in that plane.
- The exponent controls the frequencies and therefore noise patterns.
- Small values (around ``-4.0``) will result in large blobs. Large values
- (around ``4.0``) will result in small, repetitive patterns.
- The noise is sampled from low resolution planes and
- upscaled to the requested height and width. The size of the low
- resolution plane may be defined (high values can be slow) and the
- interpolation method for upscaling can be set.
- Parameters
- ----------
- exponent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
- Exponent to use when scaling in the frequency domain.
- Sane values are in the range ``-4`` (large blobs) to ``4`` (small
- patterns). To generate cloud-like structures, use roughly ``-2``.
- * If a single ``number``, this ``number`` will be used as a
- constant value.
- * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
- sampled from the continuous interval ``[a, b)`` once per call.
- * If a ``list`` of ``number``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
- Maximum height and width in pixels of the low resolution plane.
- Upon any sampling call, the requested shape will be downscaled until
- the height or width (whichever is larger) does not exceed this maximum
- value anymore. Then the noise will be sampled at that shape and later
- upscaled back to the requested shape.
- * If a single ``int``, this ``int`` will be used as a
- constant value.
- * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
- sampled from the discrete interval ``[a..b]`` once per call.
- * If a ``list`` of ``int``, a random value will be picked from
- the ``list`` once per call.
- * If a :class:`StochasticParameter`, that parameter will be
- queried once per call.
- "per call" denotes a call of :func:`FrequencyNoise.draw_sample` or
- :func:`FrequencyNoise.draw_samples`.
- upscale_method : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional
- After generating the noise maps in low resolution environments, they
- have to be upscaled to the originally requested shape (i.e. usually
- the image size). This parameter controls the interpolation method to
- use. See also :func:`~imgaug.imgaug.imresize_many_images` for a
- description of possible values.
- * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or
- ``area`` or ``cubic`` is picked per iteration (all same
- probability).
- * If ``str``, then that value will always be used as the method
- (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).
- * If ``list`` of ``str``, then a random value will be picked from
- that list per call.
- * If :class:`StochasticParameter`, then a random value will be
- sampled from that parameter per call.
- Examples
- --------
- >>> import imgaug.parameters as iap
- >>> param = iap.FrequencyNoise(
- >>> exponent=-2,
- >>> size_px_max=(16, 32),
- >>> upscale_method="linear")
- Create a parameter that produces noise with cloud-like patterns.
- """
- def __init__(self, exponent=(-4, 4), size_px_max=(4, 32),
- upscale_method=["linear", "nearest"]):
- # pylint: disable=dangerous-default-value
- super(FrequencyNoise, self).__init__()
- self.exponent = handle_continuous_param(exponent, "exponent")
- self.size_px_max = handle_discrete_param(
- size_px_max, "size_px_max", value_range=(1, 10000))
- if upscale_method == ia.ALL:
- self.upscale_method = Choice(["nearest", "linear", "area",
- "cubic"])
- elif ia.is_string(upscale_method):
- self.upscale_method = Deterministic(upscale_method)
- elif isinstance(upscale_method, list):
- assert len(upscale_method) >= 1, (
- "Expected at least one upscale method, "
- "got %d." % (len(upscale_method),))
- assert all([ia.is_string(val) for val in upscale_method]), (
- "Expected all upscale methods to be strings, got types %s." % (
- ", ".join([str(type(v)) for v in upscale_method])))
- self.upscale_method = Choice(upscale_method)
- elif isinstance(upscale_method, StochasticParameter):
- self.upscale_method = upscale_method
- else:
- raise Exception(
- "Expected upscale_method to be string or list of strings or "
- "StochasticParameter, got %s." % (type(upscale_method),))
- # TODO this is the same as in SimplexNoise, make DRY
- def _draw_samples(self, size, random_state):
- # code here is similar to:
- # http://www.redblobgames.com/articles/noise/2d/
- # http://www.redblobgames.com/articles/noise/2d/2d-noise.js
- assert len(size) in [2, 3], (
- "Expected requested noise to have shape (H, W) or (H, W, C), "
- "got shape %s." % (size,))
- height, width = size[0:2]
- nb_channels = 1 if len(size) == 2 else size[2]
- channels = [self._draw_samples_hw(height, width, random_state)
- for _ in np.arange(nb_channels)]
- if len(size) == 2:
- return channels[0]
- return np.stack(channels, axis=-1)
- def _draw_samples_hw(self, height, width, random_state):
- rngs = random_state.duplicate(5)
- maxlen = max(height, width)
- size_px_max = self.size_px_max.draw_sample(random_state=rngs[0])
- if maxlen > size_px_max:
- downscale_factor = size_px_max / maxlen
- h_small = int(height * downscale_factor)
- w_small = int(width * downscale_factor)
- else:
- h_small = height
- w_small = width
- # don't go below Hx4 or 4xW
- h_small = max(h_small, 4)
- w_small = max(w_small, 4)
- # generate random base matrix
- # TODO use a single RNG with a single call here
- wn_r = rngs[1].random(size=(h_small, w_small))
- wn_a = rngs[2].random(size=(h_small, w_small))
- wn_r = wn_r * (max(h_small, w_small) ** 2)
- wn_a = wn_a * 2 * np.pi
- wn_r = wn_r * np.cos(wn_a)
- wn_a = wn_r * np.sin(wn_a)
- # pronounce some frequencies
- exponent = self.exponent.draw_sample(random_state=rngs[3])
- # this has some similarity with a distance map from the center, but
- # looks a bit more like a cross
- f = self._create_distance_matrix((h_small, w_small))
- f[0, 0] = 1 # necessary to prevent -inf from appearing
- scale = f ** exponent
- scale[0, 0] = 0
- treal = wn_r * scale
- timag = wn_a * scale
- wn_freqs_mul = np.zeros(treal.shape, dtype=np.complex)
- wn_freqs_mul.real = treal
- wn_freqs_mul.imag = timag
- wn_inv = np.fft.ifft2(wn_freqs_mul).real
- # normalize to 0 to 1
- wn_inv_min = np.min(wn_inv)
- wn_inv_max = np.max(wn_inv)
- noise_0to1 = (wn_inv - wn_inv_min) / (wn_inv_max - wn_inv_min)
- # upscale from low resolution to image size
- upscale_method = self.upscale_method.draw_sample(random_state=rngs[4])
- if noise_0to1.shape != (height, width):
- noise_0to1_uint8 = (noise_0to1 * 255).astype(np.uint8)
- noise_0to1_3d = np.tile(
- noise_0to1_uint8[..., np.newaxis], (1, 1, 3))
- noise_0to1 = ia.imresize_single_image(
- noise_0to1_3d,
- (height, width),
- interpolation=upscale_method)
- noise_0to1 = (noise_0to1[..., 0] / 255.0).astype(np.float32)
- return noise_0to1
- @classmethod
- def _create_distance_matrix(cls, size):
- h, w = size
- def _freq(yy, xx):
- hdist = np.minimum(yy, h-yy)
- wdist = np.minimum(xx, w-xx)
- return np.sqrt(hdist**2 + wdist**2)
- return np.fromfunction(_freq, (h, w))
- def __repr__(self):
- return self.__str__()
- def __str__(self):
- return "FrequencyNoise(%s, %s, %s)" % (
- str(self.exponent),
- str(self.size_px_max),
- str(self.upscale_method))
- def _assert_arg_is_stoch_param(arg_name, arg_value):
- assert isinstance(arg_value, StochasticParameter), (
- "Expected '%s' to be a StochasticParameter, "
- "got type %s." % (arg_name, arg_value,))
|