parameters.py 124 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203
  1. """Classes and methods to use for parameters of augmenters.
  2. This module contains e.g. classes representing probability
  3. distributions (guassian, poisson etc.), classes representing noise sources
  4. and methods to normalize parameter-related user inputs.
  5. """
  6. from __future__ import print_function, division, absolute_import
  7. import copy as copy_module
  8. from collections import defaultdict
  9. from abc import ABCMeta, abstractmethod
  10. import tempfile
  11. import numpy as np
  12. import six
  13. import six.moves as sm
  14. import scipy
  15. import scipy.stats
  16. import imageio
  17. from . import imgaug as ia
  18. from . import dtypes as iadt
  19. from . import random as iarandom
  20. from .external.opensimplex import OpenSimplex
  21. def _check_value_range(value, name, value_range):
  22. if value_range is None:
  23. return True
  24. if isinstance(value_range, tuple):
  25. assert len(value_range) == 2, (
  26. "If 'value_range' is a tuple, it must contain exactly 2 entries, "
  27. "got %d." % (len(value_range),))
  28. if value_range[0] is None and value_range[1] is None:
  29. return True
  30. if value_range[0] is None:
  31. assert value <= value_range[1], (
  32. "Parameter '%s' is outside of the expected value "
  33. "range (x <= %.4f)" % (name, value_range[1]))
  34. return True
  35. if value_range[1] is None:
  36. assert value_range[0] <= value, (
  37. "Parameter '%s' is outside of the expected value "
  38. "range (%.4f <= x)" % (name, value_range[0]))
  39. return True
  40. assert value_range[0] <= value <= value_range[1], (
  41. "Parameter '%s' is outside of the expected value "
  42. "range (%.4f <= x <= %.4f)" % (
  43. name, value_range[0], value_range[1]))
  44. return True
  45. if ia.is_callable(value_range):
  46. value_range(value)
  47. return True
  48. raise Exception("Unexpected input for value_range, got %s." % (
  49. str(value_range),))
  50. # FIXME this uses _check_value_range, which checks for a<=x<=b, but a produced
  51. # Uniform parameter has value range a<=x<b.
  52. def handle_continuous_param(param, name, value_range=None,
  53. tuple_to_uniform=True, list_to_choice=True):
  54. if ia.is_single_number(param):
  55. _check_value_range(param, name, value_range)
  56. return Deterministic(param)
  57. if tuple_to_uniform and isinstance(param, tuple):
  58. assert len(param) == 2, (
  59. "Expected parameter '%s' with type tuple to have exactly two "
  60. "entries, but got %d." % (name, len(param)))
  61. assert all([ia.is_single_number(v) for v in param]), (
  62. "Expected parameter '%s' with type tuple to only contain "
  63. "numbers, got %s." % (name, [type(v) for v in param],))
  64. _check_value_range(param[0], name, value_range)
  65. _check_value_range(param[1], name, value_range)
  66. return Uniform(param[0], param[1])
  67. if (list_to_choice and ia.is_iterable(param)
  68. and not isinstance(param, tuple)):
  69. assert all([ia.is_single_number(v) for v in param]), (
  70. "Expected iterable parameter '%s' to only contain numbers, "
  71. "got %s." % (name, [type(v) for v in param],))
  72. for param_i in param:
  73. _check_value_range(param_i, name, value_range)
  74. return Choice(param)
  75. if isinstance(param, StochasticParameter):
  76. return param
  77. allowed_type = "number"
  78. list_str = ", list of %s" % (allowed_type,) if list_to_choice else ""
  79. raise Exception(
  80. "Expected %s, tuple of two %s%s or StochasticParameter for %s, "
  81. "got %s." % (
  82. allowed_type, allowed_type, list_str, name, type(param),))
  83. def handle_discrete_param(param, name, value_range=None, tuple_to_uniform=True,
  84. list_to_choice=True, allow_floats=True):
  85. if (ia.is_single_integer(param)
  86. or (allow_floats and ia.is_single_float(param))):
  87. _check_value_range(param, name, value_range)
  88. return Deterministic(int(param))
  89. if tuple_to_uniform and isinstance(param, tuple):
  90. assert len(param) == 2, (
  91. "Expected parameter '%s' with type tuple to have exactly two "
  92. "entries, but got %d." % (name, len(param)))
  93. is_valid_types = all([
  94. ia.is_single_number(v)
  95. if allow_floats else ia.is_single_integer(v)
  96. for v in param])
  97. assert is_valid_types, (
  98. "Expected parameter '%s' of type tuple to only contain %s, "
  99. "got %s." % (
  100. name,
  101. "number" if allow_floats else "integer",
  102. [type(v) for v in param],))
  103. _check_value_range(param[0], name, value_range)
  104. _check_value_range(param[1], name, value_range)
  105. return DiscreteUniform(int(param[0]), int(param[1]))
  106. if (list_to_choice and ia.is_iterable(param)
  107. and not isinstance(param, tuple)):
  108. is_valid_types = all([
  109. ia.is_single_number(v)
  110. if allow_floats else ia.is_single_integer(v)
  111. for v in param])
  112. assert is_valid_types, (
  113. "Expected iterable parameter '%s' to only contain %s, "
  114. "got %s." % (
  115. name,
  116. "number" if allow_floats else "integer",
  117. [type(v) for v in param],))
  118. for param_i in param:
  119. _check_value_range(param_i, name, value_range)
  120. return Choice([int(param_i) for param_i in param])
  121. if isinstance(param, StochasticParameter):
  122. return param
  123. allowed_type = "number" if allow_floats else "int"
  124. list_str = ", list of %s" % (allowed_type,) if list_to_choice else ""
  125. raise Exception(
  126. "Expected %s, tuple of two %s%s or StochasticParameter for %s, "
  127. "got %s." % (
  128. allowed_type, allowed_type, list_str, name, type(param),))
  129. # Added in 0.4.0.
  130. def handle_categorical_string_param(param, name, valid_values=None):
  131. if param == ia.ALL and valid_values is not None:
  132. return Choice(list(valid_values))
  133. if ia.is_string(param):
  134. if valid_values is not None:
  135. assert param in valid_values, (
  136. "Expected parameter '%s' to be one of: %s. Got: %s." % (
  137. name, ", ".join(list(valid_values)), param))
  138. return Deterministic(param)
  139. if isinstance(param, list):
  140. assert all([ia.is_string(val) for val in param]), (
  141. "Expected list provided for parameter '%s' to only contain "
  142. "strings, got types: %s." % (
  143. name, ", ".join([type(v).__name__ for v in param])))
  144. if valid_values is not None:
  145. assert all([val in valid_values for val in param]), (
  146. "Expected list provided for parameter '%s' to only contain "
  147. "the following allowed strings: %s. Got strings: %s." % (
  148. name, ", ".join(valid_values), ", ".join(param)
  149. ))
  150. return Choice(param)
  151. if isinstance(param, StochasticParameter):
  152. return param
  153. raise Exception(
  154. "Expected parameter '%s' to be%s a string, a list of "
  155. "strings or StochasticParameter, got %s." % (
  156. name,
  157. " imgaug.ALL," if valid_values is not None else "",
  158. type(param).__name__,))
  159. def handle_discrete_kernel_size_param(param, name, value_range=(1, None),
  160. allow_floats=True):
  161. if (ia.is_single_integer(param)
  162. or (allow_floats and ia.is_single_float(param))):
  163. _check_value_range(param, name, value_range)
  164. return Deterministic(int(param)), None
  165. if isinstance(param, tuple):
  166. assert len(param) == 2, (
  167. "Expected parameter '%s' with type tuple to have exactly two "
  168. "entries, but got %d." % (name, len(param)))
  169. if (all([ia.is_single_integer(param_i) for param_i in param])
  170. or (allow_floats and all([ia.is_single_float(param_i)
  171. for param_i in param]))):
  172. _check_value_range(param[0], name, value_range)
  173. _check_value_range(param[1], name, value_range)
  174. return DiscreteUniform(int(param[0]), int(param[1])), None
  175. if all([isinstance(param_i, StochasticParameter)
  176. for param_i in param]):
  177. return param[0], param[1]
  178. handled = (
  179. handle_discrete_param(
  180. param[0], "%s[0]" % (name,), value_range,
  181. allow_floats=allow_floats),
  182. handle_discrete_param(
  183. param[1], "%s[1]" % (name,), value_range,
  184. allow_floats=allow_floats)
  185. )
  186. return handled
  187. if ia.is_iterable(param) and not isinstance(param, tuple):
  188. is_valid_types = all([
  189. ia.is_single_number(v)
  190. if allow_floats else ia.is_single_integer(v)
  191. for v in param])
  192. assert is_valid_types, (
  193. "Expected iterable parameter '%s' to only contain %s, "
  194. "got %s." % (
  195. name,
  196. "number" if allow_floats else "integer",
  197. [type(v) for v in param],))
  198. for param_i in param:
  199. _check_value_range(param_i, name, value_range)
  200. return Choice([int(param_i) for param_i in param]), None
  201. if isinstance(param, StochasticParameter):
  202. return param, None
  203. raise Exception(
  204. "Expected int, tuple/list with 2 entries or StochasticParameter. "
  205. "Got %s." % (type(param),))
  206. def handle_probability_param(param, name, tuple_to_uniform=False,
  207. list_to_choice=False):
  208. eps = 1e-6
  209. if param in [True, False, 0, 1]:
  210. return Deterministic(int(param))
  211. if ia.is_single_number(param):
  212. assert 0.0 <= param <= 1.0, (
  213. "Expected probability of parameter '%s' to be in the interval "
  214. "[0.0, 1.0], got %.4f." % (name, param,))
  215. if 0.0-eps < param < 0.0+eps or 1.0-eps < param < 1.0+eps:
  216. return Deterministic(int(np.round(param)))
  217. return Binomial(param)
  218. if tuple_to_uniform and isinstance(param, tuple):
  219. assert all([ia.is_single_number(v) for v in param]), (
  220. "Expected parameter '%s' of type tuple to only contain numbers, "
  221. "got %s." % (name, [type(v) for v in param],))
  222. assert len(param) == 2, (
  223. "Expected parameter '%s' of type tuple to contain exactly 2 "
  224. "entries, got %d." % (name, len(param)))
  225. assert 0 <= param[0] <= 1.0 and 0 <= param[1] <= 1.0, (
  226. "Expected parameter '%s' of type tuple to contain two "
  227. "probabilities in the interval [0.0, 1.0]. "
  228. "Got values %.4f and %.4f." % (name, param[0], param[1]))
  229. return Binomial(Uniform(param[0], param[1]))
  230. if list_to_choice and ia.is_iterable(param):
  231. assert all([ia.is_single_number(v) for v in param]), (
  232. "Expected iterable parameter '%s' to only contain numbers, "
  233. "got %s." % (name, [type(v) for v in param],))
  234. assert all([0 <= p_i <= 1.0 for p_i in param]), (
  235. "Expected iterable parameter '%s' to only contain probabilities "
  236. "in the interval [0.0, 1.0], got values %s." % (
  237. name, ", ".join(["%.4f" % (p_i,) for p_i in param])))
  238. return Binomial(Choice(param))
  239. if isinstance(param, StochasticParameter):
  240. return param
  241. raise Exception(
  242. "Expected boolean or number or StochasticParameter for %s, "
  243. "got %s." % (name, type(param),))
  244. def force_np_float_dtype(val):
  245. if val.dtype.kind == "f":
  246. return val
  247. return val.astype(np.float64)
  248. def both_np_float_if_one_is_float(a, b):
  249. # pylint: disable=invalid-name
  250. a_f = a.dtype.type in ia.NP_FLOAT_TYPES
  251. b_f = b.dtype.type in ia.NP_FLOAT_TYPES
  252. if a_f and b_f:
  253. return a, b
  254. if a_f:
  255. return a, b.astype(np.float64)
  256. if b_f:
  257. return a.astype(np.float64), b
  258. return a.astype(np.float64), b.astype(np.float64)
  259. def draw_distributions_grid(params, rows=None, cols=None,
  260. graph_sizes=(350, 350), sample_sizes=None,
  261. titles=None):
  262. if titles is None:
  263. titles = [None] * len(params)
  264. elif titles is False:
  265. titles = [False] * len(params)
  266. if sample_sizes is not None:
  267. images = [
  268. param_i.draw_distribution_graph(size=size_i, title=title_i)
  269. for param_i, size_i, title_i in zip(params, sample_sizes, titles)]
  270. else:
  271. images = [
  272. param_i.draw_distribution_graph(title=title_i)
  273. for param_i, title_i in zip(params, titles)]
  274. images_rs = ia.imresize_many_images(images, sizes=graph_sizes)
  275. grid = ia.draw_grid(images_rs, rows=rows, cols=cols)
  276. return grid
  277. def show_distributions_grid(params, rows=None, cols=None,
  278. graph_sizes=(350, 350), sample_sizes=None,
  279. titles=None):
  280. ia.imshow(
  281. draw_distributions_grid(
  282. params,
  283. graph_sizes=graph_sizes,
  284. sample_sizes=sample_sizes,
  285. rows=rows,
  286. cols=cols,
  287. titles=titles
  288. )
  289. )
  290. @six.add_metaclass(ABCMeta)
  291. class StochasticParameter(object):
  292. """Abstract parent class for all stochastic parameters.
  293. Stochastic parameters are here all parameters from which values are
  294. supposed to be sampled. Usually the sampled values are to a degree random.
  295. E.g. a stochastic parameter may be the uniform distribution over the
  296. interval ``[-10, 10]``. Samples from that distribution (and therefore the
  297. stochastic parameter) could be ``5.2``, ``-3.7``, ``-9.7``, ``6.4``, etc.
  298. """
  299. def __init__(self):
  300. pass
  301. def draw_sample(self, random_state=None):
  302. """
  303. Draws a single sample value from this parameter.
  304. Parameters
  305. ----------
  306. 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
  307. A seed or random number generator to use during the sampling
  308. process. If ``None``, the global RNG will be used.
  309. See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`
  310. for a similar parameter with more details.
  311. Returns
  312. -------
  313. any
  314. A single sample value.
  315. """
  316. return self.draw_samples(1, random_state=random_state)[0]
  317. def draw_samples(self, size, random_state=None):
  318. """Draw one or more samples from the parameter.
  319. Parameters
  320. ----------
  321. size : tuple of int or int
  322. Number of samples by dimension.
  323. 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
  324. A seed or random number generator to use during the sampling
  325. process. If ``None``, the global RNG will be used.
  326. See also :func:`~imgaug.augmenters.meta.Augmenter.__init__`
  327. for a similar parameter with more details.
  328. Returns
  329. -------
  330. ndarray
  331. Sampled values. Usually a numpy ndarray of basically any dtype,
  332. though not strictly limited to numpy arrays. Its shape is expected
  333. to match `size`.
  334. """
  335. random_state = iarandom.RNG(random_state)
  336. samples = self._draw_samples(
  337. size if not ia.is_single_integer(size) else tuple([size]),
  338. random_state)
  339. random_state.advance_()
  340. return samples
  341. @abstractmethod
  342. def _draw_samples(self, size, random_state):
  343. raise NotImplementedError()
  344. def __add__(self, other):
  345. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  346. return Add(self, other)
  347. raise Exception(
  348. "Invalid datatypes in: StochasticParameter + %s. "
  349. "Expected second argument to be number or "
  350. "StochasticParameter." % (type(other),))
  351. def __sub__(self, other):
  352. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  353. return Subtract(self, other)
  354. raise Exception(
  355. "Invalid datatypes in: StochasticParameter - %s. "
  356. "Expected second argument to be number or "
  357. "StochasticParameter." % (type(other),))
  358. def __mul__(self, other):
  359. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  360. return Multiply(self, other)
  361. raise Exception(
  362. "Invalid datatypes in: StochasticParameter * %s. "
  363. "Expected second argument to be number or "
  364. "StochasticParameter." % (type(other),))
  365. def __pow__(self, other, z=None):
  366. if z is not None:
  367. raise NotImplementedError(
  368. "Modulo power is currently not supported by "
  369. "StochasticParameter.")
  370. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  371. return Power(self, other)
  372. raise Exception(
  373. "Invalid datatypes in: StochasticParameter ** %s. "
  374. "Expected second argument to be number or "
  375. "StochasticParameter." % (type(other),))
  376. def __div__(self, other):
  377. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  378. return Divide(self, other)
  379. raise Exception(
  380. "Invalid datatypes in: StochasticParameter / %s. "
  381. "Expected second argument to be number or "
  382. "StochasticParameter." % (type(other),))
  383. def __truediv__(self, other):
  384. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  385. return Divide(self, other)
  386. raise Exception(
  387. "Invalid datatypes in: StochasticParameter / %s (truediv). "
  388. "Expected second argument to be number or "
  389. "StochasticParameter." % (type(other),))
  390. def __floordiv__(self, other):
  391. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  392. return Discretize(Divide(self, other))
  393. raise Exception(
  394. "Invalid datatypes in: StochasticParameter // %s (floordiv). "
  395. "Expected second argument to be number or "
  396. "StochasticParameter." % (type(other),))
  397. def __radd__(self, other):
  398. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  399. return Add(other, self)
  400. raise Exception(
  401. "Invalid datatypes in: %s + StochasticParameter. "
  402. "Expected second argument to be number or "
  403. "StochasticParameter." % (type(other),))
  404. def __rsub__(self, other):
  405. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  406. return Subtract(other, self)
  407. raise Exception(
  408. "Invalid datatypes in: %s - StochasticParameter. "
  409. "Expected second argument to be number or "
  410. "StochasticParameter." % (type(other),))
  411. def __rmul__(self, other):
  412. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  413. return Multiply(other, self)
  414. raise Exception(
  415. "Invalid datatypes in: %s * StochasticParameter. "
  416. "Expected second argument to be number or "
  417. "StochasticParameter." % (type(other),))
  418. def __rpow__(self, other, z=None):
  419. if z is not None:
  420. raise NotImplementedError(
  421. "Modulo power is currently not supported by "
  422. "StochasticParameter.")
  423. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  424. return Power(other, self)
  425. raise Exception(
  426. "Invalid datatypes in: %s ** StochasticParameter. "
  427. "Expected second argument to be number or "
  428. "StochasticParameter." % (type(other),))
  429. def __rdiv__(self, other):
  430. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  431. return Divide(other, self)
  432. raise Exception(
  433. "Invalid datatypes in: %s / StochasticParameter. "
  434. "Expected second argument to be number or "
  435. "StochasticParameter." % (type(other),))
  436. def __rtruediv__(self, other):
  437. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  438. return Divide(other, self)
  439. raise Exception(
  440. "Invalid datatypes in: %s / StochasticParameter (rtruediv). "
  441. "Expected second argument to be number or "
  442. "StochasticParameter." % (type(other),))
  443. def __rfloordiv__(self, other):
  444. if ia.is_single_number(other) or isinstance(other, StochasticParameter):
  445. return Discretize(Divide(other, self))
  446. raise Exception(
  447. "Invalid datatypes in: StochasticParameter // %s (rfloordiv). "
  448. "Expected second argument to be number or "
  449. "StochasticParameter." % (type(other),))
  450. def copy(self):
  451. """Create a shallow copy of this parameter.
  452. Returns
  453. -------
  454. imgaug.parameters.StochasticParameter
  455. Shallow copy.
  456. """
  457. return copy_module.copy(self)
  458. def deepcopy(self):
  459. """Create a deep copy of this parameter.
  460. Returns
  461. -------
  462. imgaug.parameters.StochasticParameter
  463. Deep copy.
  464. """
  465. return copy_module.deepcopy(self)
  466. def draw_distribution_graph(self, title=None, size=(1000, 1000), bins=100):
  467. """Generate an image visualizing the parameter's sample distribution.
  468. Parameters
  469. ----------
  470. title : None or False or str, optional
  471. Title of the plot. ``None`` is automatically replaced by a title
  472. derived from ``str(param)``. If set to ``False``, no title will be
  473. shown.
  474. size : tuple of int
  475. Number of points to sample. This is always expected to have at
  476. least two values. The first defines the number of sampling runs,
  477. the second (and further) dimensions define the size assigned
  478. to each :func:`~imgaug.parameters.StochasticParameter.draw_samples`
  479. call. E.g. ``(10, 20, 15)`` will lead to ``10`` calls of
  480. ``draw_samples(size=(20, 15))``. The results will be merged to a
  481. single 1d array.
  482. bins : int
  483. Number of bins in the plot histograms.
  484. Returns
  485. -------
  486. data : (H,W,3) ndarray
  487. Image of the plot.
  488. """
  489. # import only when necessary (faster startup; optional dependency;
  490. # less fragile -- see issue #225)
  491. import matplotlib.pyplot as plt
  492. points = []
  493. for _ in sm.xrange(size[0]):
  494. points.append(self.draw_samples(size[1:]).flatten())
  495. points = np.concatenate(points)
  496. fig = plt.figure()
  497. fig.add_subplot(111)
  498. ax = fig.gca()
  499. heights, bins = np.histogram(points, bins=bins)
  500. heights = heights / sum(heights)
  501. ax.bar(bins[:-1], heights,
  502. width=(max(bins) - min(bins))/len(bins),
  503. color="blue",
  504. alpha=0.75)
  505. if title is None:
  506. title = str(self)
  507. if title is not False:
  508. # split long titles - otherwise matplotlib generates errors
  509. title_fragments = [title[i:i+50]
  510. for i in sm.xrange(0, len(title), 50)]
  511. ax.set_title("\n".join(title_fragments))
  512. fig.tight_layout(pad=0)
  513. with tempfile.NamedTemporaryFile(suffix=".png") as f:
  514. # we don't add bbox_inches='tight' here so that
  515. # draw_distributions_grid has an easier time combining many plots
  516. fig.savefig(f.name)
  517. data = imageio.imread(f)[..., 0:3]
  518. plt.close()
  519. return data
  520. class Deterministic(StochasticParameter):
  521. """Parameter that is a constant value.
  522. If ``N`` values are sampled from this parameter, it will return ``N`` times
  523. ``V``, where ``V`` is the constant value.
  524. Parameters
  525. ----------
  526. value : number or str or imgaug.parameters.StochasticParameter
  527. A constant value to use.
  528. A string may be provided to generate arrays of strings.
  529. If this is a StochasticParameter, a single value will be sampled
  530. from it exactly once and then used as the constant value.
  531. Examples
  532. --------
  533. >>> import imgaug.parameters as iap
  534. >>> param = iap.Deterministic(10)
  535. >>> param.draw_sample()
  536. 10
  537. Will always sample the value 10.
  538. """
  539. def __init__(self, value):
  540. super(Deterministic, self).__init__()
  541. if isinstance(value, StochasticParameter):
  542. self.value = value.draw_sample()
  543. elif ia.is_single_number(value) or ia.is_string(value):
  544. self.value = value
  545. else:
  546. raise Exception("Expected StochasticParameter object or number or "
  547. "string, got %s." % (type(value),))
  548. def _draw_samples(self, size, random_state):
  549. kwargs = {}
  550. if ia.is_single_integer(self.value):
  551. kwargs = {"dtype": np.int32}
  552. elif ia.is_single_float(self.value):
  553. kwargs = {"dtype": np.float32}
  554. return np.full(size, self.value, **kwargs)
  555. def __repr__(self):
  556. return self.__str__()
  557. def __str__(self):
  558. if ia.is_single_integer(self.value):
  559. return "Deterministic(int %d)" % (self.value,)
  560. if ia.is_single_float(self.value):
  561. return "Deterministic(float %.8f)" % (self.value,)
  562. return "Deterministic(%s)" % (str(self.value),)
  563. # TODO replace two-value parameters used in tests with this
  564. class DeterministicList(StochasticParameter):
  565. """Parameter that repeats elements from a list in the given order.
  566. E.g. of samples of shape ``(A, B, C)`` are requested, this parameter will
  567. return the first ``A*B*C`` elements, reshaped to ``(A, B, C)`` from the
  568. provided list. If the list contains less than ``A*B*C`` elements, it
  569. will (by default) be tiled until it is long enough (i.e. the sampling
  570. will start again at the first element, if necessary multiple times).
  571. Added in 0.4.0.
  572. Parameters
  573. ----------
  574. values : ndarray or iterable of number
  575. An iterable of values to sample from in the order within the iterable.
  576. """
  577. # Added in 0.4.0.
  578. def __init__(self, values):
  579. super(DeterministicList, self).__init__()
  580. assert ia.is_iterable(values), (
  581. "Expected to get an iterable as input, got type %s." % (
  582. type(values).__name__,))
  583. assert len(values) > 0, ("Expected to get at least one value, got "
  584. "zero.")
  585. if ia.is_np_array(values):
  586. # this would not be able to handle e.g. [[1, 2], [3]] and output
  587. # dtype object due to the non-regular shape, hence we have the
  588. # else block
  589. self.values = values.flatten()
  590. else:
  591. self.values = np.array(list(ia.flatten(values)))
  592. kind = self.values.dtype.kind
  593. # limit to 32bit instead of 64bit for efficiency
  594. if kind == "i":
  595. self.values = self.values.astype(np.int32)
  596. elif kind == "f":
  597. self.values = self.values.astype(np.float32)
  598. # Added in 0.4.0.
  599. def _draw_samples(self, size, random_state):
  600. nb_requested = int(np.prod(size))
  601. values = self.values
  602. if nb_requested > self.values.size:
  603. # we don't use itertools.cycle() here, as that would require
  604. # running through a loop potentially many times (as `size` can
  605. # be very large), which would be slow
  606. multiplier = int(np.ceil(nb_requested / values.size))
  607. values = np.tile(values, (multiplier,))
  608. return values[:nb_requested].reshape(size)
  609. # Added in 0.4.0.
  610. def __repr__(self):
  611. return self.__str__()
  612. # Added in 0.4.0.
  613. def __str__(self):
  614. if self.values.dtype.kind == "f":
  615. values = ["%.4f" % (value,) for value in self.values]
  616. return "DeterministicList([%s])" % (", ".join(values),)
  617. return "DeterministicList(%s)" % (str(self.values.tolist()),)
  618. class Choice(StochasticParameter):
  619. """Parameter that samples value from a list of allowed values.
  620. Parameters
  621. ----------
  622. a : iterable
  623. List of allowed values.
  624. Usually expected to be ``int`` s, ``float`` s or ``str`` s.
  625. May also contain ``StochasticParameter`` s. Each
  626. ``StochasticParameter`` that is randomly picked will automatically be
  627. replaced by a sample of itself (or by ``N`` samples if the parameter
  628. was picked ``N`` times).
  629. replace : bool, optional
  630. Whether to perform sampling with or without replacing.
  631. p : None or iterable of number, optional
  632. Probabilities of each element in `a`.
  633. Must have the same length as `a` (if provided).
  634. Examples
  635. --------
  636. >>> import imgaug.parameters as iap
  637. >>> param = iap.Choice([5, 17, 25], p=[0.25, 0.5, 0.25])
  638. >>> sample = param.draw_sample()
  639. >>> assert sample in [5, 17, 25]
  640. Create and sample from a parameter, which will produce with ``50%``
  641. probability the sample ``17`` and in the other ``50%`` of all cases the
  642. sample ``5`` or ``25``..
  643. """
  644. def __init__(self, a, replace=True, p=None):
  645. # pylint: disable=invalid-name
  646. super(Choice, self).__init__()
  647. assert ia.is_iterable(a), (
  648. "Expected a to be an iterable (e.g. list), got %s." % (type(a),))
  649. self.a = a
  650. self.replace = replace
  651. if p is not None:
  652. assert ia.is_iterable(p), (
  653. "Expected p to be None or an iterable, got %s." % (type(p),))
  654. assert len(p) == len(a), (
  655. "Expected lengths of a and p to be identical, "
  656. "got %d and %d." % (len(a), len(p)))
  657. self.p = p
  658. def _draw_samples(self, size, random_state):
  659. if any([isinstance(a_i, StochasticParameter) for a_i in self.a]):
  660. rngs = random_state.duplicate(1+len(self.a))
  661. samples = rngs[0].choice(
  662. self.a, np.prod(size), replace=self.replace, p=self.p)
  663. # collect the sampled parameters and how many samples must be taken
  664. # from each of them
  665. params_counter = defaultdict(lambda: 0)
  666. for sample in samples:
  667. if isinstance(sample, StochasticParameter):
  668. key = str(sample)
  669. params_counter[key] += 1
  670. # collect per parameter once the required number of samples
  671. # iterate here over self.a to always use the same seed for
  672. # the same parameter
  673. # TODO this might fail if the same parameter is added multiple
  674. # times to self.a?
  675. # TODO this will fail if a parameter cant handle size=(N,)
  676. param_to_samples = dict()
  677. for i, param in enumerate(self.a):
  678. key = str(param)
  679. if key in params_counter:
  680. param_to_samples[key] = param.draw_samples(
  681. size=(params_counter[key],),
  682. random_state=rngs[1+i]
  683. )
  684. # assign the values sampled from the parameters to the `samples`
  685. # array by replacing the respective parameter
  686. param_to_readcount = defaultdict(lambda: 0)
  687. for i, sample in enumerate(samples):
  688. if isinstance(sample, StochasticParameter):
  689. key = str(sample)
  690. readcount = param_to_readcount[key]
  691. samples[i] = param_to_samples[key][readcount]
  692. param_to_readcount[key] += 1
  693. samples = samples.reshape(size)
  694. else:
  695. samples = random_state.choice(self.a, size, replace=self.replace,
  696. p=self.p)
  697. dtype = samples.dtype
  698. if dtype.itemsize*8 > 32:
  699. # strings have kind "U"
  700. kind = dtype.kind
  701. if kind == "i":
  702. samples = samples.astype(np.int32)
  703. elif kind == "u":
  704. samples = samples.astype(np.uint32)
  705. elif kind == "f":
  706. samples = samples.astype(np.float32)
  707. return samples
  708. def __repr__(self):
  709. return self.__str__()
  710. def __str__(self):
  711. return "Choice(a=%s, replace=%s, p=%s)" % (
  712. str(self.a), str(self.replace), str(self.p),)
  713. class Binomial(StochasticParameter):
  714. """Binomial distribution.
  715. Parameters
  716. ----------
  717. p : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  718. Probability of the binomial distribution. Expected to be in the
  719. interval ``[0.0, 1.0]``.
  720. * If a single ``number``, this ``number`` will be used as a
  721. constant value.
  722. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  723. sampled from the continuous interval ``[a, b)`` once per call.
  724. * If a ``list`` of ``number``, a random value will be picked from
  725. the ``list`` once per call.
  726. * If a :class:`StochasticParameter`, that parameter will be
  727. queried once per call.
  728. "per call" denotes a call of :func:`Binomial.draw_sample` or
  729. :func:`Binomial.draw_samples`.
  730. Examples
  731. --------
  732. >>> import imgaug.parameters as iap
  733. >>> param = iap.Binomial(Uniform(0.01, 0.2))
  734. Create a binomial distribution that uses a varying probability between
  735. ``0.01`` and ``0.2``, randomly and uniformly estimated once per sampling
  736. call.
  737. """
  738. def __init__(self, p):
  739. super(Binomial, self).__init__()
  740. self.p = handle_continuous_param(p, "p")
  741. def _draw_samples(self, size, random_state):
  742. p = self.p.draw_sample(random_state=random_state)
  743. assert 0 <= p <= 1.0, (
  744. "Expected probability p to be in the interval [0.0, 1.0], "
  745. "got %.4f." % (p,))
  746. return random_state.binomial(1, p, size).astype(np.int32)
  747. def __repr__(self):
  748. return self.__str__()
  749. def __str__(self):
  750. return "Binomial(%s)" % (self.p,)
  751. class DiscreteUniform(StochasticParameter):
  752. """Uniform distribution over the discrete interval ``[a..b]``.
  753. Parameters
  754. ----------
  755. a : int or tuple of int or list of int or imgaug.parameters.StochasticParameter
  756. Lower bound of the interval.
  757. If ``a>b``, `a` and `b` will automatically be flipped.
  758. If ``a==b``, all generated values will be identical to `a`.
  759. * If a single ``int``, this ``int`` will be used as a
  760. constant value.
  761. * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
  762. sampled from the discrete interval ``[a..b]`` once per call.
  763. * If a ``list`` of ``int``, a random value will be picked from
  764. the ``list`` once per call.
  765. * If a :class:`StochasticParameter`, that parameter will be
  766. queried once per call.
  767. "per call" denotes a call of :func:`DiscreteUniform.draw_sample` or
  768. :func:`DiscreteUniform.draw_samples`.
  769. b : int or imgaug.parameters.StochasticParameter
  770. Upper bound of the interval. Analogous to `a`.
  771. Examples
  772. --------
  773. >>> import imgaug.parameters as iap
  774. >>> param = iap.DiscreteUniform(10, Choice([20, 30, 40]))
  775. >>> sample = param.draw_sample()
  776. >>> assert 10 <= sample <= 40
  777. Create a discrete uniform distribution which's interval differs between
  778. calls and can be ``[10..20]``, ``[10..30]`` or ``[10..40]``.
  779. """
  780. def __init__(self, a, b):
  781. # pylint: disable=invalid-name
  782. super(DiscreteUniform, self).__init__()
  783. self.a = handle_discrete_param(a, "a")
  784. self.b = handle_discrete_param(b, "b")
  785. def _draw_samples(self, size, random_state):
  786. # pylint: disable=invalid-name
  787. a = self.a.draw_sample(random_state=random_state)
  788. b = self.b.draw_sample(random_state=random_state)
  789. if a > b:
  790. a, b = b, a
  791. elif a == b:
  792. return np.full(size, a, dtype=np.int32)
  793. return random_state.integers(a, b + 1, size, dtype=np.int32)
  794. def __repr__(self):
  795. return self.__str__()
  796. def __str__(self):
  797. return "DiscreteUniform(%s, %s)" % (self.a, self.b)
  798. class Poisson(StochasticParameter):
  799. """Parameter that resembles a poisson distribution.
  800. A poisson distribution with ``lambda=0`` has its highest probability at
  801. point ``0`` and decreases quickly from there.
  802. Poisson distributions are discrete and never negative.
  803. Parameters
  804. ----------
  805. lam : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  806. Lambda parameter of the poisson distribution.
  807. * If a single ``number``, this ``number`` will be used as a
  808. constant value.
  809. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  810. sampled from the continuous interval ``[a, b)`` once per call.
  811. * If a ``list`` of ``number``, a random value will be picked from
  812. the ``list`` once per call.
  813. * If a :class:`StochasticParameter`, that parameter will be
  814. queried once per call.
  815. "per call" denotes a call of :func:`Poisson.draw_sample` or
  816. :func:`Poisson.draw_samples`.
  817. Examples
  818. --------
  819. >>> import imgaug.parameters as iap
  820. >>> param = iap.Poisson(1)
  821. >>> sample = param.draw_sample()
  822. >>> assert sample >= 0
  823. Create a poisson distribution with ``lambda=1`` and sample a value from
  824. it.
  825. """
  826. def __init__(self, lam):
  827. super(Poisson, self).__init__()
  828. self.lam = handle_continuous_param(lam, "lam")
  829. def _draw_samples(self, size, random_state):
  830. lam = self.lam.draw_sample(random_state=random_state)
  831. lam = max(lam, 0)
  832. return random_state.poisson(lam=lam, size=size).astype(np.int32)
  833. def __repr__(self):
  834. return self.__str__()
  835. def __str__(self):
  836. return "Poisson(%s)" % (self.lam,)
  837. class Normal(StochasticParameter):
  838. """Parameter that resembles a normal/gaussian distribution.
  839. Parameters
  840. ----------
  841. loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  842. The mean of the normal distribution.
  843. * If a single ``number``, this ``number`` will be used as a
  844. constant value.
  845. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  846. sampled from the continuous interval ``[a, b)`` once per call.
  847. * If a ``list`` of ``number``, a random value will be picked from
  848. the ``list`` once per call.
  849. * If a :class:`StochasticParameter`, that parameter will be
  850. queried once per call.
  851. "per call" denotes a call of :func:`Laplace.draw_sample` or
  852. :func:`Laplace.draw_samples`.
  853. scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  854. The standard deviation of the normal distribution.
  855. If this parameter reaches ``0``, the output array will be filled with
  856. `loc`.
  857. Datatype behaviour is the analogous to `loc`.
  858. Examples
  859. --------
  860. >>> import imgaug.parameters as iap
  861. >>> param = iap.Normal(Choice([-1.0, 1.0]), 1.0)
  862. Create a gaussian distribution with a mean that differs by call.
  863. Samples values may sometimes follow ``N(-1.0, 1.0)`` and sometimes
  864. ``N(1.0, 1.0)``.
  865. """
  866. def __init__(self, loc, scale):
  867. super(Normal, self).__init__()
  868. self.loc = handle_continuous_param(loc, "loc")
  869. self.scale = handle_continuous_param(scale, "scale",
  870. value_range=(0, None))
  871. def _draw_samples(self, size, random_state):
  872. loc = self.loc.draw_sample(random_state=random_state)
  873. scale = self.scale.draw_sample(random_state=random_state)
  874. assert scale >= 0, "Expected scale to be >=0, got %.4f." % (scale,)
  875. if scale == 0:
  876. return np.full(size, loc, dtype=np.float32)
  877. return random_state.normal(loc, scale, size=size).astype(np.float32)
  878. def __repr__(self):
  879. return self.__str__()
  880. def __str__(self):
  881. return "Normal(loc=%s, scale=%s)" % (self.loc, self.scale)
  882. # TODO docstring for parameters is outdated
  883. class TruncatedNormal(StochasticParameter):
  884. """Parameter that resembles a truncated normal distribution.
  885. A truncated normal distribution is similar to a normal distribution,
  886. except the domain is smoothly bounded to a min and max value.
  887. This is a wrapper around :func:`scipy.stats.truncnorm`.
  888. Parameters
  889. ----------
  890. loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  891. The mean of the normal distribution.
  892. * If a single ``number``, this ``number`` will be used as a
  893. constant value.
  894. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  895. sampled from the continuous interval ``[a, b)`` once per call.
  896. * If a ``list`` of ``number``, a random value will be picked from
  897. the ``list`` once per call.
  898. * If a :class:`StochasticParameter`, that parameter will be
  899. queried once per call.
  900. "per call" denotes a call of :func:`TruncatedNormal.draw_sample` or
  901. :func:`TruncatedNormal.draw_samples`.
  902. scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  903. The standard deviation of the normal distribution.
  904. If this parameter reaches ``0``, the output array will be filled with
  905. `loc`.
  906. Datatype behaviour is the same as for `loc`.
  907. low : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  908. The minimum value of the truncated normal distribution.
  909. Datatype behaviour is the same as for `loc`.
  910. high : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  911. The maximum value of the truncated normal distribution.
  912. Datatype behaviour is the same as for `loc`.
  913. Examples
  914. --------
  915. >>> import imgaug.parameters as iap
  916. >>> param = iap.TruncatedNormal(0, 5.0, low=-10, high=10)
  917. >>> samples = param.draw_samples(100, random_state=0)
  918. >>> assert np.all(samples >= -10)
  919. >>> assert np.all(samples <= 10)
  920. Create a truncated normal distribution with its minimum at ``-10.0``
  921. and its maximum at ``10.0``.
  922. """
  923. def __init__(self, loc, scale, low=-np.inf, high=np.inf):
  924. super(TruncatedNormal, self).__init__()
  925. self.loc = handle_continuous_param(loc, "loc")
  926. self.scale = handle_continuous_param(scale, "scale",
  927. value_range=(0, None))
  928. self.low = handle_continuous_param(low, "low")
  929. self.high = handle_continuous_param(high, "high")
  930. def _draw_samples(self, size, random_state):
  931. # pylint: disable=invalid-name
  932. loc = self.loc.draw_sample(random_state=random_state)
  933. scale = self.scale.draw_sample(random_state=random_state)
  934. low = self.low.draw_sample(random_state=random_state)
  935. high = self.high.draw_sample(random_state=random_state)
  936. seed = random_state.generate_seed_()
  937. if low > high:
  938. low, high = high, low
  939. assert scale >= 0, "Expected scale to be >=0, got %.4f." % (scale,)
  940. if scale == 0:
  941. return np.full(size, fill_value=loc, dtype=np.float32)
  942. a = (low - loc) / scale
  943. b = (high - loc) / scale
  944. tnorm = scipy.stats.truncnorm(a=a, b=b, loc=loc, scale=scale)
  945. # Using a seed here works with both np.random interfaces.
  946. # Last time tried, scipy crashed when providing just
  947. # random_state.generator on the new np.random interface.
  948. return tnorm.rvs(size=size, random_state=seed).astype(np.float32)
  949. def __repr__(self):
  950. return self.__str__()
  951. def __str__(self):
  952. return "TruncatedNormal(loc=%s, scale=%s, low=%s, high=%s)" % (
  953. self.loc, self.scale, self.low, self.high)
  954. class Laplace(StochasticParameter):
  955. """Parameter that resembles a (continuous) laplace distribution.
  956. This is a wrapper around numpy's :func:`numpy.random.laplace`.
  957. Parameters
  958. ----------
  959. loc : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  960. The position of the distribution peak, similar to the mean in normal
  961. distributions.
  962. * If a single ``number``, this ``number`` will be used as a
  963. constant value.
  964. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  965. sampled from the continuous interval ``[a, b)`` once per call.
  966. * If a ``list`` of ``number``, a random value will be picked from
  967. the ``list`` once per call.
  968. * If a :class:`StochasticParameter`, that parameter will be
  969. queried once per call.
  970. "per call" denotes a call of :func:`Laplace.draw_sample` or
  971. :func:`Laplace.draw_samples`.
  972. scale : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  973. The exponential decay factor, similar to the standard deviation in
  974. gaussian distributions.
  975. If this parameter reaches ``0``, the output array will be filled with
  976. `loc`.
  977. Datatype behaviour is the analogous to `loc`.
  978. Examples
  979. --------
  980. >>> import imgaug.parameters as iap
  981. >>> param = iap.Laplace(0, 1.0)
  982. Create a laplace distribution, which's peak is at ``0`` and decay is
  983. ``1.0``.
  984. """
  985. def __init__(self, loc, scale):
  986. super(Laplace, self).__init__()
  987. self.loc = handle_continuous_param(loc, "loc")
  988. self.scale = handle_continuous_param(scale, "scale",
  989. value_range=(0, None))
  990. def _draw_samples(self, size, random_state):
  991. loc = self.loc.draw_sample(random_state=random_state)
  992. scale = self.scale.draw_sample(random_state=random_state)
  993. assert scale >= 0, "Expected scale to be >=0, got %s." % (scale,)
  994. if scale == 0:
  995. return np.full(size, loc, dtype=np.float32)
  996. return random_state.laplace(loc, scale, size=size).astype(np.float32)
  997. def __repr__(self):
  998. return self.__str__()
  999. def __str__(self):
  1000. return "Laplace(loc=%s, scale=%s)" % (self.loc, self.scale)
  1001. class ChiSquare(StochasticParameter):
  1002. """Parameter that resembles a (continuous) chi-square distribution.
  1003. This is a wrapper around numpy's :func:`numpy.random.chisquare`.
  1004. Parameters
  1005. ----------
  1006. df : int or tuple of two int or list of int or imgaug.parameters.StochasticParameter
  1007. Degrees of freedom. Expected value range is ``[1, inf)``.
  1008. * If a single ``int``, this ``int`` will be used as a
  1009. constant value.
  1010. * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
  1011. sampled from the discrete interval ``[a..b]`` once per call.
  1012. * If a ``list`` of ``int``, a random value will be picked from
  1013. the ``list`` once per call.
  1014. * If a :class:`StochasticParameter`, that parameter will be
  1015. queried once per call.
  1016. "per call" denotes a call of :func:`ChiSquare.draw_sample` or
  1017. :func:`ChiSquare.draw_samples`.
  1018. Examples
  1019. --------
  1020. >>> import imgaug.parameters as iap
  1021. >>> param = iap.ChiSquare(df=2)
  1022. Create a chi-square distribution with two degrees of freedom.
  1023. """
  1024. def __init__(self, df):
  1025. # pylint: disable=invalid-name
  1026. super(ChiSquare, self).__init__()
  1027. self.df = handle_discrete_param(df, "df", value_range=(1, None))
  1028. def _draw_samples(self, size, random_state):
  1029. # pylint: disable=invalid-name
  1030. df = self.df.draw_sample(random_state=random_state)
  1031. assert df >= 1, "Expected df to be >=1, got %d." % (df,)
  1032. return random_state.chisquare(df, size=size).astype(np.float32)
  1033. def __repr__(self):
  1034. return self.__str__()
  1035. def __str__(self):
  1036. return "ChiSquare(df=%s)" % (self.df,)
  1037. class Weibull(StochasticParameter):
  1038. """
  1039. Parameter that resembles a (continuous) weibull distribution.
  1040. This is a wrapper around numpy's :func:`numpy.random.weibull`.
  1041. Parameters
  1042. ----------
  1043. a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1044. Shape parameter of the distribution.
  1045. * If a single ``number``, this ``number`` will be used as a
  1046. constant value.
  1047. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  1048. sampled from the continuous interval ``[a, b)`` once per call.
  1049. * If a ``list`` of ``number``, a random value will be picked from
  1050. the ``list`` once per call.
  1051. * If a :class:`StochasticParameter`, that parameter will be
  1052. queried once per call.
  1053. "per call" denotes a call of :func:`Weibull.draw_sample` or
  1054. :func:`Weibull.draw_samples`.
  1055. Examples
  1056. --------
  1057. >>> import imgaug.parameters as iap
  1058. >>> param = iap.Weibull(a=0.5)
  1059. Create a weibull distribution with shape 0.5.
  1060. """
  1061. def __init__(self, a):
  1062. # pylint: disable=invalid-name
  1063. super(Weibull, self).__init__()
  1064. self.a = handle_continuous_param(a, "a", value_range=(0.0001, None))
  1065. def _draw_samples(self, size, random_state):
  1066. # pylint: disable=invalid-name
  1067. a = self.a.draw_sample(random_state=random_state)
  1068. assert a > 0, "Expected a to be >0, got %.4f." % (a,)
  1069. return random_state.weibull(a, size=size).astype(np.float32)
  1070. def __repr__(self):
  1071. return self.__str__()
  1072. def __str__(self):
  1073. return "Weibull(a=%s)" % (self.a,)
  1074. # TODO rename (a, b) to (low, high) as in numpy?
  1075. class Uniform(StochasticParameter):
  1076. """Parameter that resembles a uniform distribution over ``[a, b)``.
  1077. Parameters
  1078. ----------
  1079. a : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1080. Lower bound of the interval.
  1081. If ``a>b``, `a` and `b` will automatically be flipped.
  1082. If ``a==b``, all generated values will be identical to `a`.
  1083. * If a single ``number``, this ``number`` will be used as a
  1084. constant value.
  1085. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  1086. sampled from the continuous interval ``[a, b)`` once per call.
  1087. * If a ``list`` of ``number``, a random value will be picked from
  1088. the ``list`` once per call.
  1089. * If a :class:`StochasticParameter`, that parameter will be
  1090. queried once per call.
  1091. "per call" denotes a call of :func:`Uniform.draw_sample` or
  1092. :func:`Uniform.draw_samples`.
  1093. b : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1094. Upper bound of the interval. Analogous to `a`.
  1095. Examples
  1096. --------
  1097. >>> import imgaug.parameters as iap
  1098. >>> param = iap.Uniform(0, 10.0)
  1099. >>> sample = param.draw_sample()
  1100. >>> assert 0 <= sample < 10.0
  1101. Create and sample from a uniform distribution over ``[0, 10.0)``.
  1102. """
  1103. def __init__(self, a, b):
  1104. # pylint: disable=invalid-name
  1105. super(Uniform, self).__init__()
  1106. self.a = handle_continuous_param(a, "a")
  1107. self.b = handle_continuous_param(b, "b")
  1108. def _draw_samples(self, size, random_state):
  1109. # pylint: disable=invalid-name
  1110. a = self.a.draw_sample(random_state=random_state)
  1111. b = self.b.draw_sample(random_state=random_state)
  1112. if a > b:
  1113. a, b = b, a
  1114. elif a == b:
  1115. return np.full(size, a, dtype=np.float32)
  1116. return random_state.uniform(a, b, size).astype(np.float32)
  1117. def __repr__(self):
  1118. return self.__str__()
  1119. def __str__(self):
  1120. return "Uniform(%s, %s)" % (self.a, self.b)
  1121. class Beta(StochasticParameter):
  1122. """Parameter that resembles a (continuous) beta distribution.
  1123. Parameters
  1124. ----------
  1125. alpha : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1126. alpha parameter of the beta distribution.
  1127. Expected value range is ``(0, inf)``. Values below ``0`` are
  1128. automatically clipped to ``0+epsilon``.
  1129. * If a single ``number``, this ``number`` will be used as a
  1130. constant value.
  1131. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  1132. sampled from the continuous interval ``[a, b)`` once per call.
  1133. * If a ``list`` of ``number``, a random value will be picked from
  1134. the ``list`` once per call.
  1135. * If a :class:`StochasticParameter`, that parameter will be
  1136. queried once per call.
  1137. "per call" denotes a call of :func:`Beta.draw_sample` or
  1138. :func:`Beta.draw_samples`.
  1139. beta : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1140. Beta parameter of the beta distribution. Analogous to `alpha`.
  1141. epsilon : number
  1142. Clipping parameter. If `alpha` or `beta` end up ``<=0``, they are clipped to ``0+epsilon``.
  1143. Examples
  1144. --------
  1145. >>> import imgaug.parameters as iap
  1146. >>> param = iap.Beta(0.4, 0.6)
  1147. Create a beta distribution with ``alpha=0.4`` and ``beta=0.6``.
  1148. """
  1149. def __init__(self, alpha, beta, epsilon=0.0001):
  1150. super(Beta, self).__init__()
  1151. self.alpha = handle_continuous_param(alpha, "alpha")
  1152. self.beta = handle_continuous_param(beta, "beta")
  1153. assert ia.is_single_number(epsilon), (
  1154. "Expected epsilon to a number, got type %s." % (type(epsilon),))
  1155. self.epsilon = epsilon
  1156. def _draw_samples(self, size, random_state):
  1157. alpha = self.alpha.draw_sample(random_state=random_state)
  1158. beta = self.beta.draw_sample(random_state=random_state)
  1159. alpha = max(alpha, self.epsilon)
  1160. beta = max(beta, self.epsilon)
  1161. return random_state.beta(alpha, beta, size=size).astype(np.float32)
  1162. def __repr__(self):
  1163. return self.__str__()
  1164. def __str__(self):
  1165. return "Beta(%s, %s)" % (self.alpha, self.beta)
  1166. class FromLowerResolution(StochasticParameter):
  1167. """Parameter to sample from other parameters at lower image resolutions.
  1168. This parameter is intended to be used with parameters that would usually
  1169. sample one value per pixel (or one value per pixel and channel). Instead
  1170. of sampling from the other parameter at full resolution, it samples at
  1171. lower resolution, e.g. ``0.5*H x 0.5*W`` with ``H`` being the height and
  1172. ``W`` being the width. After the low-resolution sampling this parameter
  1173. then upscales the result to ``HxW``.
  1174. This parameter is intended to produce coarse samples. E.g. combining
  1175. this with :class:`Binomial` can lead to large rectangular areas of
  1176. ``1`` s and ``0`` s.
  1177. Parameters
  1178. ----------
  1179. other_param : imgaug.parameters.StochasticParameter
  1180. The other parameter which is to be sampled on a coarser image.
  1181. size_percent : None or number or iterable of number or imgaug.parameters.StochasticParameter, optional
  1182. Size of the 2d sampling plane in percent of the requested size.
  1183. I.e. this is relative to the size provided in the call to
  1184. ``draw_samples(size)``. Lower values will result in smaller sampling
  1185. planes, which are then upsampled to `size`. This means that lower
  1186. values will result in larger rectangles. The size may be provided as
  1187. a constant value or a tuple ``(a, b)``, which will automatically be
  1188. converted to the continuous uniform range ``[a, b)`` or a
  1189. :class:`StochasticParameter`, which will be queried per call to
  1190. :func:`FromLowerResolution.draw_sample` and
  1191. :func:`FromLowerResolution.draw_samples`.
  1192. size_px : None or number or iterable of numbers or imgaug.parameters.StochasticParameter, optional
  1193. Size of the 2d sampling plane in pixels.
  1194. Lower values will result in smaller sampling planes, which are then
  1195. upsampled to the input `size` of ``draw_samples(size)``.
  1196. This means that lower values will result in larger rectangles.
  1197. The size may be provided as a constant value or a tuple ``(a, b)``,
  1198. which will automatically be converted to the discrete uniform
  1199. range ``[a..b]`` or a :class:`StochasticParameter`, which will be
  1200. queried once per call to :func:`FromLowerResolution.draw_sample` and
  1201. :func:`FromLowerResolution.draw_samples`.
  1202. method : str or int or imgaug.parameters.StochasticParameter, optional
  1203. Upsampling/interpolation method to use. This is used after the sampling
  1204. is finished and the low resolution plane has to be upsampled to the
  1205. requested `size` in ``draw_samples(size, ...)``. The method may be
  1206. the same as in :func:`~imgaug.imgaug.imresize_many_images`. Usually
  1207. ``nearest`` or ``linear`` are good choices. ``nearest`` will result
  1208. in rectangles with sharp edges and ``linear`` in rectangles with
  1209. blurry and round edges. The method may be provided as a
  1210. :class:`StochasticParameter`, which will be queried once per call to
  1211. :func:`FromLowerResolution.draw_sample` and
  1212. :func:`FromLowerResolution.draw_samples`.
  1213. min_size : int, optional
  1214. Minimum size in pixels of the low resolution sampling plane.
  1215. Examples
  1216. --------
  1217. >>> import imgaug.parameters as iap
  1218. >>> param = iap.FromLowerResolution(
  1219. >>> Binomial(0.05),
  1220. >>> size_px=(2, 16),
  1221. >>> method=Choice(["nearest", "linear"]))
  1222. Samples from a binomial distribution with ``p=0.05``. The sampling plane
  1223. will always have a size HxWxC with H and W being independently sampled
  1224. from ``[2..16]`` (i.e. it may range from ``2x2xC`` up to ``16x16xC`` max,
  1225. but may also be e.g. ``4x8xC``). The upsampling method will be ``nearest``
  1226. in ``50%`` of all cases and ``linear`` in the other 50 percent. The result
  1227. will sometimes be rectangular patches of sharp ``1`` s surrounded by
  1228. ``0`` s and sometimes blurry blobs of ``1``s, surrounded by values
  1229. ``<1.0``.
  1230. """
  1231. def __init__(self, other_param, size_percent=None, size_px=None,
  1232. method="nearest", min_size=1):
  1233. super(FromLowerResolution, self).__init__()
  1234. assert size_percent is not None or size_px is not None, (
  1235. "Expected either 'size_percent' or 'size_px' to be provided, "
  1236. "got neither of them.")
  1237. if size_percent is not None:
  1238. self.size_method = "percent"
  1239. self.size_px = None
  1240. if ia.is_single_number(size_percent):
  1241. self.size_percent = Deterministic(size_percent)
  1242. elif ia.is_iterable(size_percent):
  1243. assert len(size_percent) == 2, (
  1244. "Expected iterable 'size_percent' to contain exactly 2 "
  1245. "values, got %d." % (len(size_percent),))
  1246. self.size_percent = Uniform(size_percent[0], size_percent[1])
  1247. elif isinstance(size_percent, StochasticParameter):
  1248. self.size_percent = size_percent
  1249. else:
  1250. raise Exception(
  1251. "Expected int, float, tuple of two ints/floats or "
  1252. "StochasticParameter for size_percent, "
  1253. "got %s." % (type(size_percent),))
  1254. else: # = elif size_px is not None:
  1255. self.size_method = "px"
  1256. self.size_percent = None
  1257. if ia.is_single_integer(size_px):
  1258. self.size_px = Deterministic(size_px)
  1259. elif ia.is_iterable(size_px):
  1260. assert len(size_px) == 2, (
  1261. "Expected iterable 'size_px' to contain exactly 2 "
  1262. "values, got %d." % (len(size_px),))
  1263. self.size_px = DiscreteUniform(size_px[0], size_px[1])
  1264. elif isinstance(size_px, StochasticParameter):
  1265. self.size_px = size_px
  1266. else:
  1267. raise Exception(
  1268. "Expected int, float, tuple of two ints/floats or "
  1269. "StochasticParameter for size_px, "
  1270. "got %s." % (type(size_px),))
  1271. self.other_param = other_param
  1272. if ia.is_string(method) or ia.is_single_integer(method):
  1273. self.method = Deterministic(method)
  1274. elif isinstance(method, StochasticParameter):
  1275. self.method = method
  1276. else:
  1277. raise Exception("Expected string or StochasticParameter, "
  1278. "got %s." % (type(method),))
  1279. self.min_size = min_size
  1280. def _draw_samples(self, size, random_state):
  1281. if len(size) == 3:
  1282. n = 1
  1283. h, w, c = size
  1284. elif len(size) == 4:
  1285. n, h, w, c = size
  1286. else:
  1287. raise Exception("FromLowerResolution can only generate samples "
  1288. "of shape (H, W, C) or (N, H, W, C), "
  1289. "requested was %s." % (str(size),))
  1290. if self.size_method == "percent":
  1291. hw_percents = self.size_percent.draw_samples(
  1292. (n, 2), random_state=random_state)
  1293. hw_pxs = (hw_percents * np.array([h, w])).astype(np.int32)
  1294. else:
  1295. hw_pxs = self.size_px.draw_samples(
  1296. (n, 2), random_state=random_state)
  1297. methods = self.method.draw_samples((n,), random_state=random_state)
  1298. result = None
  1299. for i, (hw_px, method) in enumerate(zip(hw_pxs, methods)):
  1300. h_small = max(hw_px[0], self.min_size)
  1301. w_small = max(hw_px[1], self.min_size)
  1302. samples = self.other_param.draw_samples(
  1303. (1, h_small, w_small, c), random_state=random_state)
  1304. # This (1) makes sure that samples are of dtypes supported by
  1305. # imresize_many_images, and (2) forces samples to be float-kind
  1306. # if the requested interpolation is something else than nearest
  1307. # neighbour interpolation. (2) is a bit hacky and makes sure that
  1308. # continuous values are produced for e.g. cubic interpolation.
  1309. # This is particularly important for e.g. binomial distributios
  1310. # used in FromLowerResolution and thereby in e.g. CoarseDropout,
  1311. # where integer-kinds would lead to sharp edges despite using
  1312. # cubic interpolation.
  1313. if samples.dtype.kind == "f":
  1314. samples = iadt.restore_dtypes_(samples, np.float32)
  1315. elif samples.dtype.kind == "i":
  1316. if method == "nearest":
  1317. samples = iadt.restore_dtypes_(samples, np.int32)
  1318. else:
  1319. samples = iadt.restore_dtypes_(samples, np.float32)
  1320. else:
  1321. assert samples.dtype.kind == "u", (
  1322. "FromLowerResolution can only process outputs of kind "
  1323. "f (float), i (int) or u (uint), got %s." % (
  1324. samples.dtype.kind))
  1325. if method == "nearest":
  1326. samples = iadt.restore_dtypes_(samples, np.uint16)
  1327. else:
  1328. samples = iadt.restore_dtypes_(samples, np.float32)
  1329. samples_upscaled = ia.imresize_many_images(
  1330. samples, (h, w), interpolation=method)
  1331. if result is None:
  1332. result = np.zeros((n, h, w, c), dtype=samples_upscaled.dtype)
  1333. result[i] = samples_upscaled
  1334. if len(size) == 3:
  1335. return result[0]
  1336. return result
  1337. def __repr__(self):
  1338. return self.__str__()
  1339. def __str__(self):
  1340. if self.size_method == "percent":
  1341. pattern = (
  1342. "FromLowerResolution("
  1343. "size_percent=%s, method=%s, other_param=%s"
  1344. ")")
  1345. return pattern % (self.size_percent, self.method, self.other_param)
  1346. pattern = (
  1347. "FromLowerResolution("
  1348. "size_px=%s, method=%s, other_param=%s"
  1349. ")")
  1350. return pattern % (self.size_px, self.method, self.other_param)
  1351. class Clip(StochasticParameter):
  1352. """Clip another parameter to a defined value range.
  1353. Parameters
  1354. ----------
  1355. other_param : imgaug.parameters.StochasticParameter
  1356. The other parameter, which's values are to be clipped.
  1357. minval : None or number, optional
  1358. The minimum value to use.
  1359. If ``None``, no minimum will be used.
  1360. maxval : None or number, optional
  1361. The maximum value to use.
  1362. If ``None``, no maximum will be used.
  1363. Examples
  1364. --------
  1365. >>> import imgaug.parameters as iap
  1366. >>> param = iap.Clip(Normal(0, 1.0), minval=-2.0, maxval=2.0)
  1367. Create a standard gaussian distribution, which's values never go below
  1368. ``-2.0`` or above ``2.0``. Note that this will lead to small "bumps" of
  1369. higher probability at ``-2.0`` and ``2.0``, as values below/above these
  1370. will be clipped to them. For smoother limitations on gaussian
  1371. distributions, see :class:`TruncatedNormal`.
  1372. """
  1373. def __init__(self, other_param, minval=None, maxval=None):
  1374. super(Clip, self).__init__()
  1375. _assert_arg_is_stoch_param("other_param", other_param)
  1376. assert minval is None or ia.is_single_number(minval), (
  1377. "Expected 'minval' to be None or a number, got type %s." % (
  1378. type(minval),))
  1379. assert maxval is None or ia.is_single_number(maxval), (
  1380. "Expected 'maxval' to be None or a number, got type %s." % (
  1381. type(maxval),))
  1382. self.other_param = other_param
  1383. self.minval = minval
  1384. self.maxval = maxval
  1385. def _draw_samples(self, size, random_state):
  1386. samples = self.other_param.draw_samples(size, random_state=random_state)
  1387. if self.minval is not None or self.maxval is not None:
  1388. # Note that this would produce a warning if 'samples' is int64
  1389. # or uint64
  1390. samples = np.clip(samples, self.minval, self.maxval, out=samples)
  1391. return samples
  1392. def __repr__(self):
  1393. return self.__str__()
  1394. def __str__(self):
  1395. opstr = str(self.other_param)
  1396. if self.minval is not None and self.maxval is not None:
  1397. return "Clip(%s, %.6f, %.6f)" % (
  1398. opstr, float(self.minval), float(self.maxval))
  1399. if self.minval is not None:
  1400. return "Clip(%s, %.6f, None)" % (opstr, float(self.minval))
  1401. if self.maxval is not None:
  1402. return "Clip(%s, None, %.6f)" % (opstr, float(self.maxval))
  1403. return "Clip(%s, None, None)" % (opstr,)
  1404. class Discretize(StochasticParameter):
  1405. """Convert a continuous distribution to a discrete one.
  1406. This will round the values and then cast them to integers.
  1407. Values sampled from already discrete distributions are not changed.
  1408. Parameters
  1409. ----------
  1410. other_param : imgaug.parameters.StochasticParameter
  1411. The other parameter, which's values are to be discretized.
  1412. round : bool, optional
  1413. Whether to round before converting to integer dtype.
  1414. Added in 0.4.0.
  1415. Examples
  1416. --------
  1417. >>> import imgaug.parameters as iap
  1418. >>> param = iap.Discretize(iap.Normal(0, 1.0))
  1419. Create a discrete standard gaussian distribution.
  1420. """
  1421. def __init__(self, other_param, round=True):
  1422. # pylint: disable=redefined-builtin
  1423. super(Discretize, self).__init__()
  1424. _assert_arg_is_stoch_param("other_param", other_param)
  1425. self.other_param = other_param
  1426. self.round = round
  1427. def _draw_samples(self, size, random_state):
  1428. samples = self.other_param.draw_samples(size, random_state=random_state)
  1429. assert samples.dtype.kind in ["u", "i", "b", "f"], (
  1430. "Expected to get uint, int, bool or float dtype as samples in "
  1431. "Discretize(), but got dtype '%s' (kind '%s') instead." % (
  1432. samples.dtype.name, samples.dtype.kind))
  1433. if samples.dtype.kind in ["u", "i", "b"]:
  1434. return samples
  1435. # floats seem to reliably cover ints that have half the number of
  1436. # bits -- probably not the case for float128 though as that is
  1437. # really float96
  1438. bitsize = 8 * samples.dtype.itemsize // 2
  1439. # in case some weird system knows something like float8 we set a
  1440. # lower bound here -- shouldn't happen though
  1441. bitsize = max(bitsize, 8)
  1442. dtype = np.dtype("int%d" % (bitsize,))
  1443. if self.round:
  1444. samples = np.round(samples)
  1445. return samples.astype(dtype)
  1446. def __repr__(self):
  1447. return self.__str__()
  1448. def __str__(self):
  1449. opstr = str(self.other_param)
  1450. return "Discretize(%s, round=%s)" % (opstr, str(self.round))
  1451. class Multiply(StochasticParameter):
  1452. """Multiply the samples of another stochastic parameter.
  1453. Parameters
  1454. ----------
  1455. other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1456. Other parameter which's sampled values are to be multiplied with `val`.
  1457. Let ``S`` be the requested shape of samples, then the datatype
  1458. behaviour is as follows:
  1459. * If a single ``number``, this ``number`` will be used as a
  1460. constant value to fill an array of shape ``S``.
  1461. * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
  1462. shape ``S`` will be filled with uniformly sampled values from
  1463. the continuous interval ``[a, b)``.
  1464. * If a ``list`` of ``number``, an array of shape ``S`` will be
  1465. filled with randomly picked values from the ``list``.
  1466. * If a :class:`StochasticParameter`, that parameter will be
  1467. queried once per call to generate an array of shape ``S``.
  1468. "per call" denotes a call of :func:`Multiply.draw_sample` or
  1469. :func:`Multiply.draw_samples`.
  1470. val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1471. Multiplier to use.
  1472. Datatype behaviour is analogous to `other_param`, though if
  1473. ``elementwise=False`` (the default), only a single sample will be
  1474. generated per call instead of ``S``.
  1475. elementwise : bool, optional
  1476. Controls the sampling behaviour of `val`.
  1477. If set to ``False``, a single samples will be requested from `val` and
  1478. used as the constant multiplier.
  1479. If set to ``True``, samples of shape ``S`` will be requested from
  1480. `val` and multiplied elementwise with the samples of `other_param`.
  1481. Examples
  1482. --------
  1483. >>> import imgaug.parameters as iap
  1484. >>> param = iap.Multiply(iap.Uniform(0.0, 1.0), -1)
  1485. Convert a uniform distribution from ``[0.0, 1.0)`` to ``(-1.0, 0.0]``.
  1486. """
  1487. def __init__(self, other_param, val, elementwise=False):
  1488. super(Multiply, self).__init__()
  1489. self.other_param = handle_continuous_param(other_param, "other_param")
  1490. self.val = handle_continuous_param(val, "val")
  1491. self.elementwise = elementwise
  1492. def _draw_samples(self, size, random_state):
  1493. rngs = random_state.duplicate(2)
  1494. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1495. elementwise = (
  1496. self.elementwise
  1497. and not isinstance(self.val, Deterministic))
  1498. if elementwise:
  1499. val_samples = self.val.draw_samples(size, random_state=rngs[1])
  1500. else:
  1501. val_samples = self.val.draw_sample(random_state=rngs[1])
  1502. if elementwise:
  1503. return np.multiply(samples, val_samples)
  1504. return samples * val_samples
  1505. def __repr__(self):
  1506. return self.__str__()
  1507. def __str__(self):
  1508. return "Multiply(%s, %s, %s)" % (
  1509. str(self.other_param), str(self.val), self.elementwise)
  1510. class Divide(StochasticParameter):
  1511. """Divide the samples of another stochastic parameter.
  1512. This parameter will automatically prevent division by zero (uses 1.0)
  1513. as the denominator in these cases.
  1514. Parameters
  1515. ----------
  1516. other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1517. Other parameter which's sampled values are to be divided by `val`.
  1518. Let ``S`` be the requested shape of samples, then the datatype
  1519. behaviour is as follows:
  1520. * If a single ``number``, this ``number`` will be used as a
  1521. constant value to fill an array of shape ``S``.
  1522. * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
  1523. shape ``S`` will be filled with uniformly sampled values from
  1524. the continuous interval ``[a, b)``.
  1525. * If a ``list`` of ``number``, an array of shape ``S`` will be
  1526. filled with randomly picked values from the ``list``.
  1527. * If a :class:`StochasticParameter`, that parameter will be
  1528. queried once per call to generate an array of shape ``S``.
  1529. "per call" denotes a call of :func:`Divide.draw_sample` or
  1530. :func:`Divide.draw_samples`.
  1531. val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1532. Denominator to use.
  1533. Datatype behaviour is analogous to `other_param`, though if
  1534. ``elementwise=False`` (the default), only a single sample will be
  1535. generated per call instead of ``S``.
  1536. elementwise : bool, optional
  1537. Controls the sampling behaviour of `val`.
  1538. If set to ``False``, a single samples will be requested from `val` and
  1539. used as the constant denominator.
  1540. If set to ``True``, samples of shape ``S`` will be requested from
  1541. `val` and used to divide the samples of `other_param` elementwise.
  1542. Examples
  1543. --------
  1544. >>> import imgaug.parameters as iap
  1545. >>> param = iap.Divide(iap.Uniform(0.0, 1.0), 2)
  1546. Convert a uniform distribution ``[0.0, 1.0)`` to ``[0, 0.5)``.
  1547. """
  1548. def __init__(self, other_param, val, elementwise=False):
  1549. super(Divide, self).__init__()
  1550. self.other_param = handle_continuous_param(other_param, "other_param")
  1551. self.val = handle_continuous_param(val, "val")
  1552. self.elementwise = elementwise
  1553. def _draw_samples(self, size, random_state):
  1554. # pylint: disable=no-else-return
  1555. rngs = random_state.duplicate(2)
  1556. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1557. elementwise = (
  1558. self.elementwise
  1559. and not isinstance(self.val, Deterministic))
  1560. if elementwise:
  1561. val_samples = self.val.draw_samples(size, random_state=rngs[1])
  1562. # prevent division by zero
  1563. val_samples[val_samples == 0] = 1
  1564. return np.divide(
  1565. force_np_float_dtype(samples),
  1566. force_np_float_dtype(val_samples)
  1567. )
  1568. else:
  1569. val_sample = self.val.draw_sample(random_state=rngs[1])
  1570. # prevent division by zero
  1571. if val_sample == 0:
  1572. val_sample = 1
  1573. return force_np_float_dtype(samples) / float(val_sample)
  1574. def __repr__(self):
  1575. return self.__str__()
  1576. def __str__(self):
  1577. return "Divide(%s, %s, %s)" % (
  1578. str(self.other_param), str(self.val), self.elementwise)
  1579. # TODO sampling (N,) from something like 10+Uniform(0, 1) will return
  1580. # N times the same value as (N,) values will be sampled from 10, but only
  1581. # one from Uniform() unless elementwise=True is explicitly set. That
  1582. # seems unintuitive. How can this be prevented?
  1583. class Add(StochasticParameter):
  1584. """Add to the samples of another stochastic parameter.
  1585. Parameters
  1586. ----------
  1587. other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1588. Samples of `val` will be added to samples of this parameter.
  1589. Let ``S`` be the requested shape of samples, then the datatype
  1590. behaviour is as follows:
  1591. * If a single ``number``, this ``number`` will be used as a
  1592. constant value to fill an array of shape ``S``.
  1593. * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
  1594. shape ``S`` will be filled with uniformly sampled values from
  1595. the continuous interval ``[a, b)``.
  1596. * If a ``list`` of ``number``, an array of shape ``S`` will be
  1597. filled with randomly picked values from the ``list``.
  1598. * If a :class:`StochasticParameter`, that parameter will be
  1599. queried once per call to generate an array of shape ``S``.
  1600. "per call" denotes a call of :func:`Add.draw_sample` or
  1601. :func:`Add.draw_samples`.
  1602. val : number or tuple of two number or list of number or imgaug.parameters.StochasticParameter
  1603. Value to add to the samples of `other_param`.
  1604. Datatype behaviour is analogous to `other_param`, though if
  1605. ``elementwise=False`` (the default), only a single sample will be
  1606. generated per call instead of ``S``.
  1607. elementwise : bool, optional
  1608. Controls the sampling behaviour of `val`.
  1609. If set to ``False``, a single samples will be requested from `val` and
  1610. used as the constant multiplier.
  1611. If set to ``True``, samples of shape ``S`` will be requested from
  1612. `val` and added elementwise with the samples of `other_param`.
  1613. Examples
  1614. --------
  1615. >>> import imgaug.parameters as iap
  1616. >>> param = iap.Add(Uniform(0.0, 1.0), 1.0)
  1617. Convert a uniform distribution from ``[0.0, 1.0)`` to ``[1.0, 2.0)``.
  1618. """
  1619. def __init__(self, other_param, val, elementwise=False):
  1620. super(Add, self).__init__()
  1621. self.other_param = handle_continuous_param(other_param, "other_param")
  1622. self.val = handle_continuous_param(val, "val")
  1623. self.elementwise = elementwise
  1624. def _draw_samples(self, size, random_state):
  1625. rngs = random_state.duplicate(2)
  1626. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1627. elementwise = (
  1628. self.elementwise and not isinstance(self.val, Deterministic))
  1629. if elementwise:
  1630. val_samples = self.val.draw_samples(size, random_state=rngs[1])
  1631. else:
  1632. val_samples = self.val.draw_sample(random_state=rngs[1])
  1633. if elementwise:
  1634. return np.add(samples, val_samples)
  1635. return samples + val_samples
  1636. def __repr__(self):
  1637. return self.__str__()
  1638. def __str__(self):
  1639. return "Add(%s, %s, %s)" % (
  1640. str(self.other_param), str(self.val), self.elementwise)
  1641. class Subtract(StochasticParameter):
  1642. """Subtract from the samples of another stochastic parameter.
  1643. Parameters
  1644. ----------
  1645. other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1646. Samples of `val` will be subtracted from samples of this parameter.
  1647. Let ``S`` be the requested shape of samples, then the datatype
  1648. behaviour is as follows:
  1649. * If a single ``number``, this ``number`` will be used as a
  1650. constant value to fill an array of shape ``S``.
  1651. * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
  1652. shape ``S`` will be filled with uniformly sampled values from
  1653. the continuous interval ``[a, b)``.
  1654. * If a ``list`` of ``number``, an array of shape ``S`` will be
  1655. filled with randomly picked values from the ``list``.
  1656. * If a :class:`StochasticParameter`, that parameter will be
  1657. queried once per call to generate an array of shape ``S``.
  1658. "per call" denotes a call of :func:`Subtract.draw_sample` or
  1659. :func:`Subtract.draw_samples`.
  1660. val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1661. Value to subtract from the other parameter.
  1662. Datatype behaviour is analogous to `other_param`, though if
  1663. ``elementwise=False`` (the default), only a single sample will be
  1664. generated per call instead of ``S``.
  1665. elementwise : bool, optional
  1666. Controls the sampling behaviour of `val`.
  1667. If set to ``False``, a single samples will be requested from `val` and
  1668. used as the constant multiplier.
  1669. If set to ``True``, samples of shape ``S`` will be requested from
  1670. `val` and subtracted elementwise from the samples of `other_param`.
  1671. Examples
  1672. --------
  1673. >>> import imgaug.parameters as iap
  1674. >>> param = iap.Subtract(iap.Uniform(0.0, 1.0), 1.0)
  1675. Convert a uniform distribution from ``[0.0, 1.0)`` to ``[-1.0, 0.0)``.
  1676. """
  1677. def __init__(self, other_param, val, elementwise=False):
  1678. super(Subtract, self).__init__()
  1679. self.other_param = handle_continuous_param(other_param, "other_param")
  1680. self.val = handle_continuous_param(val, "val")
  1681. self.elementwise = elementwise
  1682. def _draw_samples(self, size, random_state):
  1683. rngs = random_state.duplicate(2)
  1684. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1685. elementwise = (self.elementwise
  1686. and not isinstance(self.val, Deterministic))
  1687. if elementwise:
  1688. val_samples = self.val.draw_samples(size, random_state=rngs[1])
  1689. else:
  1690. val_samples = self.val.draw_sample(random_state=rngs[1])
  1691. if elementwise:
  1692. return np.subtract(samples, val_samples)
  1693. return samples - val_samples
  1694. def __repr__(self):
  1695. return self.__str__()
  1696. def __str__(self):
  1697. return "Subtract(%s, %s, %s)" % (
  1698. str(self.other_param), str(self.val), self.elementwise)
  1699. class Power(StochasticParameter):
  1700. """Exponentiate the samples of another stochastic parameter.
  1701. Parameters
  1702. ----------
  1703. other_param : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1704. Other parameter which's sampled values are to be exponentiated by `val`.
  1705. Let ``S`` be the requested shape of samples, then the datatype
  1706. behaviour is as follows:
  1707. * If a single ``number``, this ``number`` will be used as a
  1708. constant value to fill an array of shape ``S``.
  1709. * If a ``tuple`` of two ``number`` s ``(a, b)``, an array of
  1710. shape ``S`` will be filled with uniformly sampled values from
  1711. the continuous interval ``[a, b)``.
  1712. * If a ``list`` of ``number``, an array of shape ``S`` will be
  1713. filled with randomly picked values from the ``list``.
  1714. * If a :class:`StochasticParameter`, that parameter will be
  1715. queried once per call to generate an array of shape ``S``.
  1716. "per call" denotes a call of :func:`Power.draw_sample` or
  1717. :func:`Power.draw_samples`.
  1718. val : number or tuple of number or list of number or imgaug.parameters.StochasticParameter
  1719. Value to use exponentiate the samples of `other_param`.
  1720. Datatype behaviour is analogous to `other_param`, though if
  1721. ``elementwise=False`` (the default), only a single sample will be
  1722. generated per call instead of ``S``.
  1723. elementwise : bool, optional
  1724. Controls the sampling behaviour of `val`.
  1725. If set to ``False``, a single samples will be requested from `val` and
  1726. used as the constant multiplier.
  1727. If set to ``True``, samples of shape ``S`` will be requested from
  1728. `val` and used to exponentiate elementwise the samples of `other_param`.
  1729. Examples
  1730. --------
  1731. >>> import imgaug.parameters as iap
  1732. >>> param = iap.Power(iap.Uniform(0.0, 1.0), 2)
  1733. Converts a uniform range ``[0.0, 1.0)`` to a distribution that is peaked
  1734. towards 1.0.
  1735. """
  1736. def __init__(self, other_param, val, elementwise=False):
  1737. super(Power, self).__init__()
  1738. self.other_param = handle_continuous_param(other_param, "other_param")
  1739. self.val = handle_continuous_param(val, "val")
  1740. self.elementwise = elementwise
  1741. def _draw_samples(self, size, random_state):
  1742. rngs = random_state.duplicate(2)
  1743. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1744. elementwise = (
  1745. self.elementwise
  1746. and not isinstance(self.val, Deterministic))
  1747. if elementwise:
  1748. exponents = self.val.draw_samples(size, random_state=rngs[1])
  1749. else:
  1750. exponents = self.val.draw_sample(random_state=rngs[1])
  1751. # without this we get int results in the case of
  1752. # Power(<int>, <stochastic float param>)
  1753. samples, exponents = both_np_float_if_one_is_float(samples, exponents)
  1754. samples_dtype = samples.dtype
  1755. # TODO switch to this as numpy>=1.15 is now a requirement
  1756. # float_power requires numpy>=1.12
  1757. # result = np.float_power(samples, exponents)
  1758. # TODO why was float32 type here replaced with complex number
  1759. # formulation?
  1760. result = np.power(samples.astype(np.complex), exponents).real
  1761. if result.dtype != samples_dtype:
  1762. result = result.astype(samples_dtype)
  1763. return result
  1764. def __repr__(self):
  1765. return self.__str__()
  1766. def __str__(self):
  1767. return "Power(%s, %s, %s)" % (
  1768. str(self.other_param), str(self.val), self.elementwise)
  1769. class Absolute(StochasticParameter):
  1770. """Convert the samples of another parameter to their absolute values.
  1771. Parameters
  1772. ----------
  1773. other_param : imgaug.parameters.StochasticParameter
  1774. Other parameter which's sampled values are to be modified.
  1775. Examples
  1776. --------
  1777. >>> import imgaug.parameters as iap
  1778. >>> param = iap.Absolute(iap.Uniform(-1.0, 1.0))
  1779. Convert a uniform distribution from ``[-1.0, 1.0)`` to ``[0.0, 1.0]``.
  1780. """
  1781. def __init__(self, other_param):
  1782. super(Absolute, self).__init__()
  1783. _assert_arg_is_stoch_param("other_param", other_param)
  1784. self.other_param = other_param
  1785. def _draw_samples(self, size, random_state):
  1786. samples = self.other_param.draw_samples(size, random_state=random_state)
  1787. return np.absolute(samples)
  1788. def __repr__(self):
  1789. return self.__str__()
  1790. def __str__(self):
  1791. opstr = str(self.other_param)
  1792. return "Absolute(%s)" % (opstr,)
  1793. class RandomSign(StochasticParameter):
  1794. """Convert a parameter's samples randomly to positive or negative values.
  1795. Parameters
  1796. ----------
  1797. other_param : imgaug.parameters.StochasticParameter
  1798. Other parameter which's sampled values are to be modified.
  1799. p_positive : number
  1800. Fraction of values that are supposed to be turned to positive values.
  1801. Examples
  1802. --------
  1803. >>> import imgaug.parameters as iap
  1804. >>> param = iap.RandomSign(iap.Poisson(1))
  1805. Create a poisson distribution with ``alpha=1`` that is mirrored/copied (not
  1806. flipped) at the y-axis.
  1807. """
  1808. def __init__(self, other_param, p_positive=0.5):
  1809. super(RandomSign, self).__init__()
  1810. _assert_arg_is_stoch_param("other_param", other_param)
  1811. assert ia.is_single_number(p_positive), (
  1812. "Expected 'p_positive' to be a number, got %s." % (
  1813. type(p_positive)))
  1814. assert 0.0 <= p_positive <= 1.0, (
  1815. "Expected 'p_positive' to be in the interval [0.0, 1.0], "
  1816. "got %.4f." % (p_positive,))
  1817. self.other_param = other_param
  1818. self.p_positive = p_positive
  1819. def _draw_samples(self, size, random_state):
  1820. rss = random_state.duplicate(2)
  1821. samples = self.other_param.draw_samples(size, random_state=rss[0])
  1822. # TODO add method to change from uint to int here instead of assert
  1823. assert samples.dtype.kind in ["f", "i"], (
  1824. "Expected to get samples of kind float or int, but got dtype %s "
  1825. "of kind %s." % (samples.dtype.name, samples.dtype.kind))
  1826. # TODO convert to same kind as samples
  1827. coinflips = rss[1].binomial(
  1828. 1, self.p_positive, size=size).astype(np.int8)
  1829. signs = coinflips * 2 - 1
  1830. # Add absolute here to guarantee that we get p_positive percent of
  1831. # positive values. Otherwise we would merely flip p_positive percent
  1832. # of all signs.
  1833. # TODO test if
  1834. # result[coinflips_mask] *= (-1)
  1835. # is faster (with protection against mask being empty?)
  1836. result = np.absolute(samples) * signs
  1837. return result
  1838. def __repr__(self):
  1839. return self.__str__()
  1840. def __str__(self):
  1841. opstr = str(self.other_param)
  1842. return "RandomSign(%s, %.2f)" % (opstr, self.p_positive)
  1843. class ForceSign(StochasticParameter):
  1844. """Convert a parameter's samples to either positive or negative values.
  1845. Parameters
  1846. ----------
  1847. other_param : imgaug.parameters.StochasticParameter
  1848. Other parameter which's sampled values are to be modified.
  1849. positive : bool
  1850. Whether to force all signs to be positive (``True``) or
  1851. negative (``False``).
  1852. mode : {'invert', 'reroll'}, optional
  1853. Method to change the signs. Valid values are ``invert`` and ``reroll``.
  1854. ``invert`` means that wrong signs are simply flipped.
  1855. ``reroll`` means that all samples with wrong signs are sampled again,
  1856. optionally many times, until they randomly end up having the correct
  1857. sign.
  1858. reroll_count_max : int, optional
  1859. If `mode` is set to ``reroll``, this determines how often values may
  1860. be rerolled before giving up and simply flipping the sign (as in
  1861. ``mode="invert"``). This shouldn't be set too high, as rerolling is
  1862. expensive.
  1863. Examples
  1864. --------
  1865. >>> import imgaug.parameters as iap
  1866. >>> param = iap.ForceSign(iap.Poisson(1), positive=False)
  1867. Create a poisson distribution with ``alpha=1`` that is flipped towards
  1868. negative values.
  1869. """
  1870. def __init__(self, other_param, positive, mode="invert",
  1871. reroll_count_max=2):
  1872. super(ForceSign, self).__init__()
  1873. _assert_arg_is_stoch_param("other_param", other_param)
  1874. self.other_param = other_param
  1875. assert positive in [True, False], (
  1876. "Expected 'positive' to be True or False, got type %s." % (
  1877. type(positive),))
  1878. self.positive = positive
  1879. assert mode in ["invert", "reroll"], (
  1880. "Expected 'mode' to be \"invert\" or \"reroll\", got %s." % (mode,))
  1881. self.mode = mode
  1882. assert ia.is_single_integer(reroll_count_max), (
  1883. "Expected 'reroll_count_max' to be an integer, got type %s." % (
  1884. type(reroll_count_max)))
  1885. self.reroll_count_max = reroll_count_max
  1886. def _draw_samples(self, size, random_state):
  1887. rngs = random_state.duplicate(1+self.reroll_count_max)
  1888. samples = self.other_param.draw_samples(size, random_state=rngs[0])
  1889. if self.mode == "invert":
  1890. if self.positive:
  1891. samples[samples < 0] *= (-1)
  1892. else:
  1893. samples[samples > 0] *= (-1)
  1894. else:
  1895. if self.positive:
  1896. bad_samples = np.where(samples < 0)[0]
  1897. else:
  1898. bad_samples = np.where(samples > 0)[0]
  1899. reroll_count = 0
  1900. while len(bad_samples) > 0 and reroll_count < self.reroll_count_max:
  1901. # This rerolls the full input size, even when only a tiny
  1902. # fraction of the values were wrong. That is done, because not
  1903. # all parameters necessarily support any number of dimensions
  1904. # for `size`, so we cant just resample size=N for N values
  1905. # with wrong signs.
  1906. # There is still quite some room for improvement here.
  1907. samples_reroll = self.other_param.draw_samples(
  1908. size,
  1909. random_state=rngs[1+reroll_count]
  1910. )
  1911. samples[bad_samples] = samples_reroll[bad_samples]
  1912. reroll_count += 1
  1913. if self.positive:
  1914. bad_samples = np.where(samples < 0)[0]
  1915. else:
  1916. bad_samples = np.where(samples > 0)[0]
  1917. if len(bad_samples) > 0:
  1918. samples[bad_samples] *= (-1)
  1919. return samples
  1920. def __repr__(self):
  1921. return self.__str__()
  1922. def __str__(self):
  1923. opstr = str(self.other_param)
  1924. return "ForceSign(%s, %s, %s, %d)" % (
  1925. opstr, str(self.positive), self.mode, self.reroll_count_max)
  1926. def Positive(other_param, mode="invert", reroll_count_max=2):
  1927. """Convert another parameter's results to positive values.
  1928. Parameters
  1929. ----------
  1930. other_param : imgaug.parameters.StochasticParameter
  1931. Other parameter which's sampled values are to be
  1932. modified.
  1933. mode : {'invert', 'reroll'}, optional
  1934. How to change the signs. Valid values are ``invert`` and ``reroll``.
  1935. ``invert`` means that wrong signs are simply flipped.
  1936. ``reroll`` means that all samples with wrong signs are sampled again,
  1937. optionally many times, until they randomly end up having the correct
  1938. sign.
  1939. reroll_count_max : int, optional
  1940. If `mode` is set to ``reroll``, this determines how often values may
  1941. be rerolled before giving up and simply flipping the sign (as in
  1942. ``mode="invert"``). This shouldn't be set too high, as rerolling is
  1943. expensive.
  1944. Examples
  1945. --------
  1946. >>> import imgaug.parameters as iap
  1947. >>> param = iap.Positive(iap.Normal(0, 1), mode="reroll")
  1948. Create a gaussian distribution that has only positive values.
  1949. If any negative value is sampled in the process, that sample is resampled
  1950. up to two times to get a positive one. If it isn't positive after the
  1951. second resampling step, the sign is simply flipped.
  1952. """
  1953. # pylint: disable=invalid-name
  1954. return ForceSign(
  1955. other_param=other_param,
  1956. positive=True,
  1957. mode=mode,
  1958. reroll_count_max=reroll_count_max
  1959. )
  1960. def Negative(other_param, mode="invert", reroll_count_max=2):
  1961. """Convert another parameter's results to negative values.
  1962. Parameters
  1963. ----------
  1964. other_param : imgaug.parameters.StochasticParameter
  1965. Other parameter which's sampled values are to be
  1966. modified.
  1967. mode : {'invert', 'reroll'}, optional
  1968. How to change the signs. Valid values are ``invert`` and ``reroll``.
  1969. ``invert`` means that wrong signs are simply flipped.
  1970. ``reroll`` means that all samples with wrong signs are sampled again,
  1971. optionally many times, until they randomly end up having the correct
  1972. sign.
  1973. reroll_count_max : int, optional
  1974. If `mode` is set to ``reroll``, this determines how often values may
  1975. be rerolled before giving up and simply flipping the sign (as in
  1976. ``mode="invert"``). This shouldn't be set too high, as rerolling is
  1977. expensive.
  1978. Examples
  1979. --------
  1980. >>> import imgaug.parameters as iap
  1981. >>> param = iap.Negative(iap.Normal(0, 1), mode="reroll")
  1982. Create a gaussian distribution that has only negative values.
  1983. If any positive value is sampled in the process, that sample is resampled
  1984. up to two times to get a negative one. If it isn't negative after the
  1985. second resampling step, the sign is simply flipped.
  1986. """
  1987. # pylint: disable=invalid-name
  1988. return ForceSign(
  1989. other_param=other_param,
  1990. positive=False,
  1991. mode=mode,
  1992. reroll_count_max=reroll_count_max
  1993. )
  1994. # TODO this always aggregates the result in high resolution space, instead of
  1995. # aggregating them in low resolution and then only upscaling the final
  1996. # image (for N iterations that would save up to N-1 upscales)
  1997. class IterativeNoiseAggregator(StochasticParameter):
  1998. """Aggregate multiple iterations of samples from another parameter.
  1999. This is supposed to be used in conjunction with :class:`SimplexNoise` or
  2000. :class:`FrequencyNoise`. If a shape ``S`` is requested, it will request
  2001. ``I`` times ``S`` samples from the underlying parameter, where ``I`` is
  2002. the number of iterations. The ``I`` arrays will be combined to a single
  2003. array of shape ``S`` using an aggregation method, e.g. simple averaging.
  2004. Parameters
  2005. ----------
  2006. other_param : StochasticParameter
  2007. The other parameter from which to sample one or more times.
  2008. iterations : int or iterable of int or list of int or imgaug.parameters.StochasticParameter, optional
  2009. The number of iterations.
  2010. * If a single ``int``, this ``int`` will be used as a
  2011. constant value.
  2012. * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
  2013. sampled from the discrete interval ``[a..b]`` once per call.
  2014. * If a ``list`` of ``int``, a random value will be picked from
  2015. the ``list`` once per call.
  2016. * If a :class:`StochasticParameter`, that parameter will be
  2017. queried once per call.
  2018. "per call" denotes a call of
  2019. :func:`IterativeNoiseAggregator.draw_sample` or
  2020. :func:`IterativeNoiseAggregator.draw_samples`.
  2021. aggregation_method : imgaug.ALL or {'min', 'avg', 'max'} or list of str or imgaug.parameters.StochasticParameter, optional
  2022. The method to use to aggregate the samples of multiple iterations
  2023. to a single output array. All methods combine several arrays of
  2024. shape ``S`` each to a single array of shape ``S`` and hence work
  2025. elementwise. Known methods are ``min`` (take the minimum over all
  2026. iterations), ``max`` (take the maximum) and ``avg`` (take the average).
  2027. * If an ``str``, it must be one of the described methods and
  2028. will be used for all calls..
  2029. * If a ``list`` of ``str``, it must contain one or more of the
  2030. described methods and a random one will be samples once per call.
  2031. * If ``imgaug.ALL``, then equivalent to the ``list``
  2032. ``["min", "max", "avg"]``.
  2033. * If :class:`StochasticParameter`, a value will be sampled from
  2034. that parameter once per call and must be one of the described
  2035. methods..
  2036. "per call" denotes a call of
  2037. :func:`IterativeNoiseAggregator.draw_sample` or
  2038. :func:`IterativeNoiseAggregator.draw_samples`.
  2039. Examples
  2040. --------
  2041. >>> import imgaug.parameters as iap
  2042. >>> noise = iap.IterativeNoiseAggregator(
  2043. >>> iap.SimplexNoise(),
  2044. >>> iterations=(2, 5),
  2045. >>> aggregation_method="max")
  2046. Create a parameter that -- upon each call -- generates ``2`` to ``5``
  2047. arrays of simplex noise with the same shape. Then it combines these
  2048. noise maps to a single map using elementwise maximum.
  2049. """
  2050. def __init__(self, other_param, iterations=(1, 3),
  2051. aggregation_method=["max", "avg"]):
  2052. # pylint: disable=dangerous-default-value
  2053. super(IterativeNoiseAggregator, self).__init__()
  2054. _assert_arg_is_stoch_param("other_param", other_param)
  2055. self.other_param = other_param
  2056. def _assert_within_bounds(_iterations):
  2057. assert all([1 <= val <= 10000 for val in _iterations]), (
  2058. "Expected 'iterations' to only contain values within "
  2059. "the interval [1, 1000], got values %s." % (
  2060. ", ".join([str(val) for val in _iterations]),))
  2061. if ia.is_single_integer(iterations):
  2062. _assert_within_bounds([iterations])
  2063. self.iterations = Deterministic(iterations)
  2064. elif isinstance(iterations, list):
  2065. assert len(iterations) > 0, (
  2066. "Expected 'iterations' of type list to contain at least one "
  2067. "entry, got %d." % (len(iterations),))
  2068. _assert_within_bounds(iterations)
  2069. self.iterations = Choice(iterations)
  2070. elif ia.is_iterable(iterations):
  2071. assert len(iterations) == 2, (
  2072. "Expected iterable non-list 'iteratons' to contain exactly "
  2073. "two entries, got %d." % (len(iterations),))
  2074. assert all([ia.is_single_integer(val) for val in iterations]), (
  2075. "Expected iterable non-list 'iterations' to only contain "
  2076. "integers, got types %s." % (
  2077. ", ".join([str(type(val)) for val in iterations]),))
  2078. _assert_within_bounds(iterations)
  2079. self.iterations = DiscreteUniform(iterations[0], iterations[1])
  2080. elif isinstance(iterations, StochasticParameter):
  2081. self.iterations = iterations
  2082. else:
  2083. raise Exception(
  2084. "Expected iterations to be int or tuple of two ints or "
  2085. "StochasticParameter, got %s." % (type(iterations),))
  2086. if aggregation_method == ia.ALL:
  2087. self.aggregation_method = Choice(["min", "max", "avg"])
  2088. elif ia.is_string(aggregation_method):
  2089. self.aggregation_method = Deterministic(aggregation_method)
  2090. elif isinstance(aggregation_method, list):
  2091. assert len(aggregation_method) >= 1, (
  2092. "Expected at least one aggregation method got %d." % (
  2093. len(aggregation_method),))
  2094. assert all([ia.is_string(val) for val in aggregation_method]), (
  2095. "Expected aggregation methods provided as strings, "
  2096. "got types %s." % (
  2097. ", ".join([str(type(v)) for v in aggregation_method])))
  2098. self.aggregation_method = Choice(aggregation_method)
  2099. elif isinstance(aggregation_method, StochasticParameter):
  2100. self.aggregation_method = aggregation_method
  2101. else:
  2102. raise Exception(
  2103. "Expected aggregation_method to be string or list of strings "
  2104. "or StochasticParameter, got %s." % (
  2105. type(aggregation_method),))
  2106. def _draw_samples(self, size, random_state):
  2107. rngs = random_state.duplicate(2)
  2108. aggregation_method = self.aggregation_method.draw_sample(
  2109. random_state=rngs[0])
  2110. iterations = self.iterations.draw_sample(random_state=rngs[1])
  2111. assert iterations > 0, (
  2112. "Expected to sample at least one iteration of aggregation. "
  2113. "Got %d." % (iterations,))
  2114. rngs_iterations = rngs[1].duplicate(iterations)
  2115. result = np.zeros(size, dtype=np.float32)
  2116. for i in sm.xrange(iterations):
  2117. noise_iter = self.other_param.draw_samples(
  2118. size, random_state=rngs_iterations[i])
  2119. if aggregation_method == "avg":
  2120. result += noise_iter
  2121. elif aggregation_method == "min":
  2122. if i == 0:
  2123. result = noise_iter
  2124. else:
  2125. result = np.minimum(result, noise_iter)
  2126. else: # self.aggregation_method == "max"
  2127. if i == 0:
  2128. result = noise_iter
  2129. else:
  2130. result = np.maximum(result, noise_iter)
  2131. if aggregation_method == "avg":
  2132. result = result / iterations
  2133. return result
  2134. def __repr__(self):
  2135. return self.__str__()
  2136. def __str__(self):
  2137. opstr = str(self.other_param)
  2138. return "IterativeNoiseAggregator(%s, %s, %s)" % (
  2139. opstr, str(self.iterations), str(self.aggregation_method))
  2140. class Sigmoid(StochasticParameter):
  2141. """Apply a sigmoid function to the outputs of another parameter.
  2142. This is intended to be used in combination with :class:`SimplexNoise` or
  2143. :class:`FrequencyNoise`. It pushes the noise values away from ``~0.5`` and
  2144. towards ``0.0`` or ``1.0``, making the noise maps more binary.
  2145. Parameters
  2146. ----------
  2147. other_param : imgaug.parameters.StochasticParameter
  2148. The other parameter to which the sigmoid will be applied.
  2149. threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional
  2150. Sets the value of the sigmoid's saddle point, i.e. where values
  2151. start to quickly shift from ``0.0`` to ``1.0``.
  2152. * If a single ``number``, this ``number`` will be used as a
  2153. constant value.
  2154. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  2155. sampled from the continuous interval ``[a, b)`` once per call.
  2156. * If a ``list`` of ``number``, a random value will be picked from
  2157. the ``list`` once per call.
  2158. * If a :class:`StochasticParameter`, that parameter will be
  2159. queried once per call.
  2160. "per call" denotes a call of :func:`Sigmoid.draw_sample` or
  2161. :func:`Sigmoid.draw_samples`.
  2162. activated : bool or number, optional
  2163. Defines whether the sigmoid is activated. If this is ``False``, the
  2164. results of `other_param` will not be altered. This may be set to a
  2165. ``float`` ``p`` in value range``[0.0, 1.0]``, which will result in
  2166. `activated` being ``True`` in ``p`` percent of all calls.
  2167. mul : number, optional
  2168. The results of `other_param` will be multiplied with this value before
  2169. applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this
  2170. should be set to about ``20``.
  2171. add : number, optional
  2172. This value will be added to the results of `other_param` before
  2173. applying the sigmoid. For noise values (range ``[0.0, 1.0]``) this
  2174. should be set to about ``-10.0``, provided `mul` was set to ``20``.
  2175. Examples
  2176. --------
  2177. >>> import imgaug.parameters as iap
  2178. >>> param = iap.Sigmoid(
  2179. >>> iap.SimplexNoise(),
  2180. >>> activated=0.5,
  2181. >>> mul=20,
  2182. >>> add=-10)
  2183. Applies a sigmoid to simplex noise in ``50%`` of all calls. The noise
  2184. results are modified to match the sigmoid's expected value range. The
  2185. sigmoid's outputs are in the range ``[0.0, 1.0]``.
  2186. """
  2187. def __init__(self, other_param, threshold=(-10, 10), activated=True,
  2188. mul=1, add=0):
  2189. super(Sigmoid, self).__init__()
  2190. _assert_arg_is_stoch_param("other_param", other_param)
  2191. self.other_param = other_param
  2192. self.threshold = handle_continuous_param(threshold, "threshold")
  2193. self.activated = handle_probability_param(activated, "activated")
  2194. assert ia.is_single_number(mul), (
  2195. "Expected 'mul' to be a number, got type %s." % (type(mul),))
  2196. assert mul > 0, (
  2197. "Expected 'mul' to be greater than zero, got %.4f." % (mul,))
  2198. self.mul = mul
  2199. assert ia.is_single_number(add), (
  2200. "Expected 'add' to be a number, got type %s." % (type(add),))
  2201. self.add = add
  2202. @staticmethod
  2203. def create_for_noise(other_param, threshold=(-10, 10), activated=True):
  2204. """Create a Sigmoid adjusted for noise parameters.
  2205. "noise" here denotes :class:`SimplexNoise` and :class:`FrequencyNoise`.
  2206. Parameters
  2207. ----------
  2208. other_param : imgaug.parameters.StochasticParameter
  2209. See :func:`~imgaug.parameters.Sigmoid.__init__`.
  2210. threshold : number or tuple of number or iterable of number or imgaug.parameters.StochasticParameter, optional
  2211. See :func:`~imgaug.parameters.Sigmoid.__init__`.
  2212. activated : bool or number, optional
  2213. See :func:`~imgaug.parameters.Sigmoid.__init__`.
  2214. Returns
  2215. -------
  2216. Sigmoid
  2217. A sigmoid adjusted to be used with noise.
  2218. """
  2219. return Sigmoid(other_param, threshold, activated, mul=20, add=-10)
  2220. def _draw_samples(self, size, random_state):
  2221. rngs = random_state.duplicate(3)
  2222. result = self.other_param.draw_samples(size, random_state=rngs[0])
  2223. if result.dtype.kind != "f":
  2224. result = result.astype(np.float32)
  2225. activated = self.activated.draw_sample(random_state=rngs[1])
  2226. threshold = self.threshold.draw_sample(random_state=rngs[2])
  2227. if activated > 0.5:
  2228. # threshold must be subtracted here, not added
  2229. # higher threshold = move threshold of sigmoid towards the right
  2230. # = make it harder to pass the threshold
  2231. # = more 0.0s / less 1.0s
  2232. # by subtracting a high value, it moves each x towards the left,
  2233. # leading to more values being left of the threshold, leading
  2234. # to more 0.0s
  2235. return 1 / (1 + np.exp(-(result * self.mul + self.add - threshold)))
  2236. return result
  2237. def __repr__(self):
  2238. return self.__str__()
  2239. def __str__(self):
  2240. opstr = str(self.other_param)
  2241. return "Sigmoid(%s, %s, %s, %s, %s)" % (
  2242. opstr, str(self.threshold), str(self.activated), str(self.mul),
  2243. str(self.add))
  2244. class SimplexNoise(StochasticParameter):
  2245. """Parameter that generates simplex noise of varying resolutions.
  2246. This parameter expects to sample noise for 2d planes, i.e. for
  2247. sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``
  2248. per spatial location in that plane.
  2249. The noise is sampled from low resolution planes and
  2250. upscaled to the requested height and width. The size of the low
  2251. resolution plane may be defined (large values can be slow) and the
  2252. interpolation method for upscaling can be set.
  2253. Parameters
  2254. ----------
  2255. size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  2256. Maximum height and width in pixels of the low resolution plane.
  2257. Upon any sampling call, the requested shape will be downscaled until
  2258. the height or width (whichever is larger) does not exceed this maximum
  2259. value anymore. Then the noise will be sampled at that shape and later
  2260. upscaled back to the requested shape.
  2261. * If a single ``int``, this ``int`` will be used as a
  2262. constant value.
  2263. * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
  2264. sampled from the discrete interval ``[a..b]`` once per call.
  2265. * If a ``list`` of ``int``, a random value will be picked from
  2266. the ``list`` once per call.
  2267. * If a :class:`StochasticParameter`, that parameter will be
  2268. queried once per call.
  2269. "per call" denotes a call of :func:`SimplexNoise.draw_sample` or
  2270. :func:`SimplexNoise.draw_samples`.
  2271. upscale_method : str or int or list of str or list of int or imgaug.parameters.StochasticParameter, optional
  2272. After generating the noise maps in low resolution environments, they
  2273. have to be upscaled to the originally requested shape (i.e. usually
  2274. the image size). This parameter controls the interpolation method to
  2275. use. See also :func:`~imgaug.imgaug.imresize_many_images` for a
  2276. description of possible values.
  2277. * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or
  2278. ``area`` or ``cubic`` is picked per iteration (all same
  2279. probability).
  2280. * If ``str``, then that value will always be used as the method
  2281. (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).
  2282. * If ``list`` of ``str``, then a random value will be picked from
  2283. that list per call.
  2284. * If :class:`StochasticParameter`, then a random value will be
  2285. sampled from that parameter per call.
  2286. Examples
  2287. --------
  2288. >>> import imgaug.parameters as iap
  2289. >>> param = iap.SimplexNoise(upscale_method="linear")
  2290. Create a parameter that produces smooth simplex noise of varying sizes.
  2291. >>> param = iap.SimplexNoise(
  2292. >>> size_px_max=(8, 16),
  2293. >>> upscale_method="nearest")
  2294. Create a parameter that produces rectangular simplex noise of rather
  2295. high detail.
  2296. """
  2297. def __init__(self, size_px_max=(2, 16),
  2298. upscale_method=["linear", "nearest"]):
  2299. # pylint: disable=dangerous-default-value
  2300. super(SimplexNoise, self).__init__()
  2301. self.size_px_max = handle_discrete_param(
  2302. size_px_max, "size_px_max", value_range=(1, 10000))
  2303. if upscale_method == ia.ALL:
  2304. self.upscale_method = Choice(["nearest", "linear", "area",
  2305. "cubic"])
  2306. elif ia.is_string(upscale_method):
  2307. self.upscale_method = Deterministic(upscale_method)
  2308. elif isinstance(upscale_method, list):
  2309. assert len(upscale_method) >= 1, (
  2310. "Expected at least one upscale method, "
  2311. "got %d." % (len(upscale_method),))
  2312. assert all([ia.is_string(val) for val in upscale_method]), (
  2313. "Expected all upscale methods to be strings, got types %s." % (
  2314. ", ".join([str(type(v)) for v in upscale_method])))
  2315. self.upscale_method = Choice(upscale_method)
  2316. elif isinstance(upscale_method, StochasticParameter):
  2317. self.upscale_method = upscale_method
  2318. else:
  2319. raise Exception(
  2320. "Expected upscale_method to be string or list of strings or "
  2321. "StochasticParameter, got %s." % (type(upscale_method),))
  2322. def _draw_samples(self, size, random_state):
  2323. assert len(size) in [2, 3], (
  2324. "Expected requested noise to have shape (H, W) or (H, W, C), "
  2325. "got shape %s." % (size,))
  2326. height, width = size[0:2]
  2327. nb_channels = 1 if len(size) == 2 else size[2]
  2328. channels = [self._draw_samples_hw(height, width, random_state)
  2329. for _ in np.arange(nb_channels)]
  2330. if len(size) == 2:
  2331. return channels[0]
  2332. return np.stack(channels, axis=-1)
  2333. def _draw_samples_hw(self, height, width, random_state):
  2334. iterations = 1
  2335. rngs = random_state.duplicate(1+iterations)
  2336. aggregation_method = "max"
  2337. upscale_methods = self.upscale_method.draw_samples(
  2338. (iterations,), random_state=rngs[0])
  2339. result = np.zeros((height, width), dtype=np.float32)
  2340. for i in sm.xrange(iterations):
  2341. noise_iter = self._draw_samples_iteration(
  2342. height, width, rngs[1+i], upscale_methods[i])
  2343. if aggregation_method == "avg":
  2344. result += noise_iter
  2345. elif aggregation_method == "min":
  2346. if i == 0:
  2347. result = noise_iter
  2348. else:
  2349. result = np.minimum(result, noise_iter)
  2350. else: # self.aggregation_method == "max"
  2351. if i == 0:
  2352. result = noise_iter
  2353. else:
  2354. result = np.maximum(result, noise_iter)
  2355. if aggregation_method == "avg":
  2356. result = result / iterations
  2357. return result
  2358. def _draw_samples_iteration(self, height, width, rng, upscale_method):
  2359. opensimplex_seed = rng.generate_seed_()
  2360. # we have to use int(.) here, otherwise we can get warnings about
  2361. # value overflows in OpenSimplex L103
  2362. generator = OpenSimplex(seed=int(opensimplex_seed))
  2363. maxlen = max(height, width)
  2364. size_px_max = self.size_px_max.draw_sample(random_state=rng)
  2365. if maxlen > size_px_max:
  2366. downscale_factor = size_px_max / maxlen
  2367. h_small = int(height * downscale_factor)
  2368. w_small = int(width * downscale_factor)
  2369. else:
  2370. h_small = height
  2371. w_small = width
  2372. # don't go below Hx1 or 1xW
  2373. h_small = max(h_small, 1)
  2374. w_small = max(w_small, 1)
  2375. noise = np.zeros((h_small, w_small), dtype=np.float32)
  2376. for y in sm.xrange(h_small):
  2377. for x in sm.xrange(w_small):
  2378. noise[y, x] = generator.noise2d(y=y, x=x)
  2379. # TODO this was previously (noise+0.5)/2, which was wrong as the noise
  2380. # here is in range [-1.0, 1.0], but this new normalization might
  2381. # lead to bad masks due to too many values being significantly
  2382. # above 0.0 instead of being clipped to 0?
  2383. noise_0to1 = (noise + 1.0) / 2
  2384. noise_0to1 = np.clip(noise_0to1, 0.0, 1.0)
  2385. if noise_0to1.shape != (height, width):
  2386. noise_0to1_uint8 = (noise_0to1 * 255).astype(np.uint8)
  2387. noise_0to1_3d = np.tile(
  2388. noise_0to1_uint8[..., np.newaxis], (1, 1, 3))
  2389. noise_0to1 = ia.imresize_single_image(
  2390. noise_0to1_3d, (height, width), interpolation=upscale_method)
  2391. noise_0to1 = (noise_0to1[..., 0] / 255.0).astype(np.float32)
  2392. return noise_0to1
  2393. def __repr__(self):
  2394. return self.__str__()
  2395. def __str__(self):
  2396. return "SimplexNoise(%s, %s)" % (
  2397. str(self.size_px_max),
  2398. str(self.upscale_method)
  2399. )
  2400. class FrequencyNoise(StochasticParameter):
  2401. """Parameter to generate noise of varying frequencies.
  2402. This parameter expects to sample noise for 2d planes, i.e. for
  2403. sizes ``(H, W, [C])`` and will return a value in the range ``[0.0, 1.0]``
  2404. per spatial location in that plane.
  2405. The exponent controls the frequencies and therefore noise patterns.
  2406. Small values (around ``-4.0``) will result in large blobs. Large values
  2407. (around ``4.0``) will result in small, repetitive patterns.
  2408. The noise is sampled from low resolution planes and
  2409. upscaled to the requested height and width. The size of the low
  2410. resolution plane may be defined (high values can be slow) and the
  2411. interpolation method for upscaling can be set.
  2412. Parameters
  2413. ----------
  2414. exponent : number or tuple of number or list of number or imgaug.parameters.StochasticParameter, optional
  2415. Exponent to use when scaling in the frequency domain.
  2416. Sane values are in the range ``-4`` (large blobs) to ``4`` (small
  2417. patterns). To generate cloud-like structures, use roughly ``-2``.
  2418. * If a single ``number``, this ``number`` will be used as a
  2419. constant value.
  2420. * If a ``tuple`` of two ``number`` s ``(a, b)``, the value will be
  2421. sampled from the continuous interval ``[a, b)`` once per call.
  2422. * If a ``list`` of ``number``, a random value will be picked from
  2423. the ``list`` once per call.
  2424. * If a :class:`StochasticParameter`, that parameter will be
  2425. queried once per call.
  2426. size_px_max : int or tuple of int or list of int or imgaug.parameters.StochasticParameter, optional
  2427. Maximum height and width in pixels of the low resolution plane.
  2428. Upon any sampling call, the requested shape will be downscaled until
  2429. the height or width (whichever is larger) does not exceed this maximum
  2430. value anymore. Then the noise will be sampled at that shape and later
  2431. upscaled back to the requested shape.
  2432. * If a single ``int``, this ``int`` will be used as a
  2433. constant value.
  2434. * If a ``tuple`` of two ``int`` s ``(a, b)``, the value will be
  2435. sampled from the discrete interval ``[a..b]`` once per call.
  2436. * If a ``list`` of ``int``, a random value will be picked from
  2437. the ``list`` once per call.
  2438. * If a :class:`StochasticParameter`, that parameter will be
  2439. queried once per call.
  2440. "per call" denotes a call of :func:`FrequencyNoise.draw_sample` or
  2441. :func:`FrequencyNoise.draw_samples`.
  2442. upscale_method : imgaug.ALL or str or list of str or imgaug.parameters.StochasticParameter, optional
  2443. After generating the noise maps in low resolution environments, they
  2444. have to be upscaled to the originally requested shape (i.e. usually
  2445. the image size). This parameter controls the interpolation method to
  2446. use. See also :func:`~imgaug.imgaug.imresize_many_images` for a
  2447. description of possible values.
  2448. * If ``imgaug.ALL``, then either ``nearest`` or ``linear`` or
  2449. ``area`` or ``cubic`` is picked per iteration (all same
  2450. probability).
  2451. * If ``str``, then that value will always be used as the method
  2452. (must be ``nearest`` or ``linear`` or ``area`` or ``cubic``).
  2453. * If ``list`` of ``str``, then a random value will be picked from
  2454. that list per call.
  2455. * If :class:`StochasticParameter`, then a random value will be
  2456. sampled from that parameter per call.
  2457. Examples
  2458. --------
  2459. >>> import imgaug.parameters as iap
  2460. >>> param = iap.FrequencyNoise(
  2461. >>> exponent=-2,
  2462. >>> size_px_max=(16, 32),
  2463. >>> upscale_method="linear")
  2464. Create a parameter that produces noise with cloud-like patterns.
  2465. """
  2466. def __init__(self, exponent=(-4, 4), size_px_max=(4, 32),
  2467. upscale_method=["linear", "nearest"]):
  2468. # pylint: disable=dangerous-default-value
  2469. super(FrequencyNoise, self).__init__()
  2470. self.exponent = handle_continuous_param(exponent, "exponent")
  2471. self.size_px_max = handle_discrete_param(
  2472. size_px_max, "size_px_max", value_range=(1, 10000))
  2473. if upscale_method == ia.ALL:
  2474. self.upscale_method = Choice(["nearest", "linear", "area",
  2475. "cubic"])
  2476. elif ia.is_string(upscale_method):
  2477. self.upscale_method = Deterministic(upscale_method)
  2478. elif isinstance(upscale_method, list):
  2479. assert len(upscale_method) >= 1, (
  2480. "Expected at least one upscale method, "
  2481. "got %d." % (len(upscale_method),))
  2482. assert all([ia.is_string(val) for val in upscale_method]), (
  2483. "Expected all upscale methods to be strings, got types %s." % (
  2484. ", ".join([str(type(v)) for v in upscale_method])))
  2485. self.upscale_method = Choice(upscale_method)
  2486. elif isinstance(upscale_method, StochasticParameter):
  2487. self.upscale_method = upscale_method
  2488. else:
  2489. raise Exception(
  2490. "Expected upscale_method to be string or list of strings or "
  2491. "StochasticParameter, got %s." % (type(upscale_method),))
  2492. # TODO this is the same as in SimplexNoise, make DRY
  2493. def _draw_samples(self, size, random_state):
  2494. # code here is similar to:
  2495. # http://www.redblobgames.com/articles/noise/2d/
  2496. # http://www.redblobgames.com/articles/noise/2d/2d-noise.js
  2497. assert len(size) in [2, 3], (
  2498. "Expected requested noise to have shape (H, W) or (H, W, C), "
  2499. "got shape %s." % (size,))
  2500. height, width = size[0:2]
  2501. nb_channels = 1 if len(size) == 2 else size[2]
  2502. channels = [self._draw_samples_hw(height, width, random_state)
  2503. for _ in np.arange(nb_channels)]
  2504. if len(size) == 2:
  2505. return channels[0]
  2506. return np.stack(channels, axis=-1)
  2507. def _draw_samples_hw(self, height, width, random_state):
  2508. rngs = random_state.duplicate(5)
  2509. maxlen = max(height, width)
  2510. size_px_max = self.size_px_max.draw_sample(random_state=rngs[0])
  2511. if maxlen > size_px_max:
  2512. downscale_factor = size_px_max / maxlen
  2513. h_small = int(height * downscale_factor)
  2514. w_small = int(width * downscale_factor)
  2515. else:
  2516. h_small = height
  2517. w_small = width
  2518. # don't go below Hx4 or 4xW
  2519. h_small = max(h_small, 4)
  2520. w_small = max(w_small, 4)
  2521. # generate random base matrix
  2522. # TODO use a single RNG with a single call here
  2523. wn_r = rngs[1].random(size=(h_small, w_small))
  2524. wn_a = rngs[2].random(size=(h_small, w_small))
  2525. wn_r = wn_r * (max(h_small, w_small) ** 2)
  2526. wn_a = wn_a * 2 * np.pi
  2527. wn_r = wn_r * np.cos(wn_a)
  2528. wn_a = wn_r * np.sin(wn_a)
  2529. # pronounce some frequencies
  2530. exponent = self.exponent.draw_sample(random_state=rngs[3])
  2531. # this has some similarity with a distance map from the center, but
  2532. # looks a bit more like a cross
  2533. f = self._create_distance_matrix((h_small, w_small))
  2534. f[0, 0] = 1 # necessary to prevent -inf from appearing
  2535. scale = f ** exponent
  2536. scale[0, 0] = 0
  2537. treal = wn_r * scale
  2538. timag = wn_a * scale
  2539. wn_freqs_mul = np.zeros(treal.shape, dtype=np.complex)
  2540. wn_freqs_mul.real = treal
  2541. wn_freqs_mul.imag = timag
  2542. wn_inv = np.fft.ifft2(wn_freqs_mul).real
  2543. # normalize to 0 to 1
  2544. wn_inv_min = np.min(wn_inv)
  2545. wn_inv_max = np.max(wn_inv)
  2546. noise_0to1 = (wn_inv - wn_inv_min) / (wn_inv_max - wn_inv_min)
  2547. # upscale from low resolution to image size
  2548. upscale_method = self.upscale_method.draw_sample(random_state=rngs[4])
  2549. if noise_0to1.shape != (height, width):
  2550. noise_0to1_uint8 = (noise_0to1 * 255).astype(np.uint8)
  2551. noise_0to1_3d = np.tile(
  2552. noise_0to1_uint8[..., np.newaxis], (1, 1, 3))
  2553. noise_0to1 = ia.imresize_single_image(
  2554. noise_0to1_3d,
  2555. (height, width),
  2556. interpolation=upscale_method)
  2557. noise_0to1 = (noise_0to1[..., 0] / 255.0).astype(np.float32)
  2558. return noise_0to1
  2559. @classmethod
  2560. def _create_distance_matrix(cls, size):
  2561. h, w = size
  2562. def _freq(yy, xx):
  2563. hdist = np.minimum(yy, h-yy)
  2564. wdist = np.minimum(xx, w-xx)
  2565. return np.sqrt(hdist**2 + wdist**2)
  2566. return np.fromfunction(_freq, (h, w))
  2567. def __repr__(self):
  2568. return self.__str__()
  2569. def __str__(self):
  2570. return "FrequencyNoise(%s, %s, %s)" % (
  2571. str(self.exponent),
  2572. str(self.size_px_max),
  2573. str(self.upscale_method))
  2574. def _assert_arg_is_stoch_param(arg_name, arg_value):
  2575. assert isinstance(arg_value, StochasticParameter), (
  2576. "Expected '%s' to be a StochasticParameter, "
  2577. "got type %s." % (arg_name, arg_value,))