_morphology.py 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342
  1. # Copyright (C) 2003-2005 Peter J. Verveer
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions
  5. # are met:
  6. #
  7. # 1. Redistributions of source code must retain the above copyright
  8. # notice, this list of conditions and the following disclaimer.
  9. #
  10. # 2. Redistributions in binary form must reproduce the above
  11. # copyright notice, this list of conditions and the following
  12. # disclaimer in the documentation and/or other materials provided
  13. # with the distribution.
  14. #
  15. # 3. The name of the author may not be used to endorse or promote
  16. # products derived from this software without specific prior
  17. # written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
  20. # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  23. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  25. # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26. # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  27. # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  28. # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  29. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. import warnings
  31. import operator
  32. import numpy
  33. from . import _ni_support
  34. from . import _nd_image
  35. from . import _filters
  36. __all__ = ['iterate_structure', 'generate_binary_structure', 'binary_erosion',
  37. 'binary_dilation', 'binary_opening', 'binary_closing',
  38. 'binary_hit_or_miss', 'binary_propagation', 'binary_fill_holes',
  39. 'grey_erosion', 'grey_dilation', 'grey_opening', 'grey_closing',
  40. 'morphological_gradient', 'morphological_laplace', 'white_tophat',
  41. 'black_tophat', 'distance_transform_bf', 'distance_transform_cdt',
  42. 'distance_transform_edt']
  43. def _center_is_true(structure, origin):
  44. structure = numpy.array(structure)
  45. coor = tuple([oo + ss // 2 for ss, oo in zip(structure.shape,
  46. origin)])
  47. return bool(structure[coor])
  48. def iterate_structure(structure, iterations, origin=None):
  49. """
  50. Iterate a structure by dilating it with itself.
  51. Parameters
  52. ----------
  53. structure : array_like
  54. Structuring element (an array of bools, for example), to be dilated with
  55. itself.
  56. iterations : int
  57. number of dilations performed on the structure with itself
  58. origin : optional
  59. If origin is None, only the iterated structure is returned. If
  60. not, a tuple of the iterated structure and the modified origin is
  61. returned.
  62. Returns
  63. -------
  64. iterate_structure : ndarray of bools
  65. A new structuring element obtained by dilating `structure`
  66. (`iterations` - 1) times with itself.
  67. See also
  68. --------
  69. generate_binary_structure
  70. Examples
  71. --------
  72. >>> from scipy import ndimage
  73. >>> struct = ndimage.generate_binary_structure(2, 1)
  74. >>> struct.astype(int)
  75. array([[0, 1, 0],
  76. [1, 1, 1],
  77. [0, 1, 0]])
  78. >>> ndimage.iterate_structure(struct, 2).astype(int)
  79. array([[0, 0, 1, 0, 0],
  80. [0, 1, 1, 1, 0],
  81. [1, 1, 1, 1, 1],
  82. [0, 1, 1, 1, 0],
  83. [0, 0, 1, 0, 0]])
  84. >>> ndimage.iterate_structure(struct, 3).astype(int)
  85. array([[0, 0, 0, 1, 0, 0, 0],
  86. [0, 0, 1, 1, 1, 0, 0],
  87. [0, 1, 1, 1, 1, 1, 0],
  88. [1, 1, 1, 1, 1, 1, 1],
  89. [0, 1, 1, 1, 1, 1, 0],
  90. [0, 0, 1, 1, 1, 0, 0],
  91. [0, 0, 0, 1, 0, 0, 0]])
  92. """
  93. structure = numpy.asarray(structure)
  94. if iterations < 2:
  95. return structure.copy()
  96. ni = iterations - 1
  97. shape = [ii + ni * (ii - 1) for ii in structure.shape]
  98. pos = [ni * (structure.shape[ii] // 2) for ii in range(len(shape))]
  99. slc = tuple(slice(pos[ii], pos[ii] + structure.shape[ii], None)
  100. for ii in range(len(shape)))
  101. out = numpy.zeros(shape, bool)
  102. out[slc] = structure != 0
  103. out = binary_dilation(out, structure, iterations=ni)
  104. if origin is None:
  105. return out
  106. else:
  107. origin = _ni_support._normalize_sequence(origin, structure.ndim)
  108. origin = [iterations * o for o in origin]
  109. return out, origin
  110. def generate_binary_structure(rank, connectivity):
  111. """
  112. Generate a binary structure for binary morphological operations.
  113. Parameters
  114. ----------
  115. rank : int
  116. Number of dimensions of the array to which the structuring element
  117. will be applied, as returned by `np.ndim`.
  118. connectivity : int
  119. `connectivity` determines which elements of the output array belong
  120. to the structure, i.e., are considered as neighbors of the central
  121. element. Elements up to a squared distance of `connectivity` from
  122. the center are considered neighbors. `connectivity` may range from 1
  123. (no diagonal elements are neighbors) to `rank` (all elements are
  124. neighbors).
  125. Returns
  126. -------
  127. output : ndarray of bools
  128. Structuring element which may be used for binary morphological
  129. operations, with `rank` dimensions and all dimensions equal to 3.
  130. See also
  131. --------
  132. iterate_structure, binary_dilation, binary_erosion
  133. Notes
  134. -----
  135. `generate_binary_structure` can only create structuring elements with
  136. dimensions equal to 3, i.e., minimal dimensions. For larger structuring
  137. elements, that are useful e.g., for eroding large objects, one may either
  138. use `iterate_structure`, or create directly custom arrays with
  139. numpy functions such as `numpy.ones`.
  140. Examples
  141. --------
  142. >>> from scipy import ndimage
  143. >>> import numpy as np
  144. >>> struct = ndimage.generate_binary_structure(2, 1)
  145. >>> struct
  146. array([[False, True, False],
  147. [ True, True, True],
  148. [False, True, False]], dtype=bool)
  149. >>> a = np.zeros((5,5))
  150. >>> a[2, 2] = 1
  151. >>> a
  152. array([[ 0., 0., 0., 0., 0.],
  153. [ 0., 0., 0., 0., 0.],
  154. [ 0., 0., 1., 0., 0.],
  155. [ 0., 0., 0., 0., 0.],
  156. [ 0., 0., 0., 0., 0.]])
  157. >>> b = ndimage.binary_dilation(a, structure=struct).astype(a.dtype)
  158. >>> b
  159. array([[ 0., 0., 0., 0., 0.],
  160. [ 0., 0., 1., 0., 0.],
  161. [ 0., 1., 1., 1., 0.],
  162. [ 0., 0., 1., 0., 0.],
  163. [ 0., 0., 0., 0., 0.]])
  164. >>> ndimage.binary_dilation(b, structure=struct).astype(a.dtype)
  165. array([[ 0., 0., 1., 0., 0.],
  166. [ 0., 1., 1., 1., 0.],
  167. [ 1., 1., 1., 1., 1.],
  168. [ 0., 1., 1., 1., 0.],
  169. [ 0., 0., 1., 0., 0.]])
  170. >>> struct = ndimage.generate_binary_structure(2, 2)
  171. >>> struct
  172. array([[ True, True, True],
  173. [ True, True, True],
  174. [ True, True, True]], dtype=bool)
  175. >>> struct = ndimage.generate_binary_structure(3, 1)
  176. >>> struct # no diagonal elements
  177. array([[[False, False, False],
  178. [False, True, False],
  179. [False, False, False]],
  180. [[False, True, False],
  181. [ True, True, True],
  182. [False, True, False]],
  183. [[False, False, False],
  184. [False, True, False],
  185. [False, False, False]]], dtype=bool)
  186. """
  187. if connectivity < 1:
  188. connectivity = 1
  189. if rank < 1:
  190. return numpy.array(True, dtype=bool)
  191. output = numpy.fabs(numpy.indices([3] * rank) - 1)
  192. output = numpy.add.reduce(output, 0)
  193. return output <= connectivity
  194. def _binary_erosion(input, structure, iterations, mask, output,
  195. border_value, origin, invert, brute_force):
  196. try:
  197. iterations = operator.index(iterations)
  198. except TypeError as e:
  199. raise TypeError('iterations parameter should be an integer') from e
  200. input = numpy.asarray(input)
  201. if numpy.iscomplexobj(input):
  202. raise TypeError('Complex type not supported')
  203. if structure is None:
  204. structure = generate_binary_structure(input.ndim, 1)
  205. else:
  206. structure = numpy.asarray(structure, dtype=bool)
  207. if structure.ndim != input.ndim:
  208. raise RuntimeError('structure and input must have same dimensionality')
  209. if not structure.flags.contiguous:
  210. structure = structure.copy()
  211. if numpy.prod(structure.shape, axis=0) < 1:
  212. raise RuntimeError('structure must not be empty')
  213. if mask is not None:
  214. mask = numpy.asarray(mask)
  215. if mask.shape != input.shape:
  216. raise RuntimeError('mask and input must have equal sizes')
  217. origin = _ni_support._normalize_sequence(origin, input.ndim)
  218. cit = _center_is_true(structure, origin)
  219. if isinstance(output, numpy.ndarray):
  220. if numpy.iscomplexobj(output):
  221. raise TypeError('Complex output type not supported')
  222. else:
  223. output = bool
  224. output = _ni_support._get_output(output, input)
  225. temp_needed = numpy.may_share_memory(input, output)
  226. if temp_needed:
  227. # input and output arrays cannot share memory
  228. temp = output
  229. output = _ni_support._get_output(output.dtype, input)
  230. if iterations == 1:
  231. _nd_image.binary_erosion(input, structure, mask, output,
  232. border_value, origin, invert, cit, 0)
  233. elif cit and not brute_force:
  234. changed, coordinate_list = _nd_image.binary_erosion(
  235. input, structure, mask, output,
  236. border_value, origin, invert, cit, 1)
  237. structure = structure[tuple([slice(None, None, -1)] *
  238. structure.ndim)]
  239. for ii in range(len(origin)):
  240. origin[ii] = -origin[ii]
  241. if not structure.shape[ii] & 1:
  242. origin[ii] -= 1
  243. if mask is not None:
  244. mask = numpy.asarray(mask, dtype=numpy.int8)
  245. if not structure.flags.contiguous:
  246. structure = structure.copy()
  247. _nd_image.binary_erosion2(output, structure, mask, iterations - 1,
  248. origin, invert, coordinate_list)
  249. else:
  250. tmp_in = numpy.empty_like(input, dtype=bool)
  251. tmp_out = output
  252. if iterations >= 1 and not iterations & 1:
  253. tmp_in, tmp_out = tmp_out, tmp_in
  254. changed = _nd_image.binary_erosion(
  255. input, structure, mask, tmp_out,
  256. border_value, origin, invert, cit, 0)
  257. ii = 1
  258. while ii < iterations or (iterations < 1 and changed):
  259. tmp_in, tmp_out = tmp_out, tmp_in
  260. changed = _nd_image.binary_erosion(
  261. tmp_in, structure, mask, tmp_out,
  262. border_value, origin, invert, cit, 0)
  263. ii += 1
  264. if temp_needed:
  265. temp[...] = output
  266. output = temp
  267. return output
  268. def binary_erosion(input, structure=None, iterations=1, mask=None, output=None,
  269. border_value=0, origin=0, brute_force=False):
  270. """
  271. Multidimensional binary erosion with a given structuring element.
  272. Binary erosion is a mathematical morphology operation used for image
  273. processing.
  274. Parameters
  275. ----------
  276. input : array_like
  277. Binary image to be eroded. Non-zero (True) elements form
  278. the subset to be eroded.
  279. structure : array_like, optional
  280. Structuring element used for the erosion. Non-zero elements are
  281. considered True. If no structuring element is provided, an element
  282. is generated with a square connectivity equal to one.
  283. iterations : int, optional
  284. The erosion is repeated `iterations` times (one, by default).
  285. If iterations is less than 1, the erosion is repeated until the
  286. result does not change anymore.
  287. mask : array_like, optional
  288. If a mask is given, only those elements with a True value at
  289. the corresponding mask element are modified at each iteration.
  290. output : ndarray, optional
  291. Array of the same shape as input, into which the output is placed.
  292. By default, a new array is created.
  293. border_value : int (cast to 0 or 1), optional
  294. Value at the border in the output array.
  295. origin : int or tuple of ints, optional
  296. Placement of the filter, by default 0.
  297. brute_force : boolean, optional
  298. Memory condition: if False, only the pixels whose value was changed in
  299. the last iteration are tracked as candidates to be updated (eroded) in
  300. the current iteration; if True all pixels are considered as candidates
  301. for erosion, regardless of what happened in the previous iteration.
  302. False by default.
  303. Returns
  304. -------
  305. binary_erosion : ndarray of bools
  306. Erosion of the input by the structuring element.
  307. See also
  308. --------
  309. grey_erosion, binary_dilation, binary_closing, binary_opening,
  310. generate_binary_structure
  311. Notes
  312. -----
  313. Erosion [1]_ is a mathematical morphology operation [2]_ that uses a
  314. structuring element for shrinking the shapes in an image. The binary
  315. erosion of an image by a structuring element is the locus of the points
  316. where a superimposition of the structuring element centered on the point
  317. is entirely contained in the set of non-zero elements of the image.
  318. References
  319. ----------
  320. .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29
  321. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  322. Examples
  323. --------
  324. >>> from scipy import ndimage
  325. >>> import numpy as np
  326. >>> a = np.zeros((7,7), dtype=int)
  327. >>> a[1:6, 2:5] = 1
  328. >>> a
  329. array([[0, 0, 0, 0, 0, 0, 0],
  330. [0, 0, 1, 1, 1, 0, 0],
  331. [0, 0, 1, 1, 1, 0, 0],
  332. [0, 0, 1, 1, 1, 0, 0],
  333. [0, 0, 1, 1, 1, 0, 0],
  334. [0, 0, 1, 1, 1, 0, 0],
  335. [0, 0, 0, 0, 0, 0, 0]])
  336. >>> ndimage.binary_erosion(a).astype(a.dtype)
  337. array([[0, 0, 0, 0, 0, 0, 0],
  338. [0, 0, 0, 0, 0, 0, 0],
  339. [0, 0, 0, 1, 0, 0, 0],
  340. [0, 0, 0, 1, 0, 0, 0],
  341. [0, 0, 0, 1, 0, 0, 0],
  342. [0, 0, 0, 0, 0, 0, 0],
  343. [0, 0, 0, 0, 0, 0, 0]])
  344. >>> #Erosion removes objects smaller than the structure
  345. >>> ndimage.binary_erosion(a, structure=np.ones((5,5))).astype(a.dtype)
  346. array([[0, 0, 0, 0, 0, 0, 0],
  347. [0, 0, 0, 0, 0, 0, 0],
  348. [0, 0, 0, 0, 0, 0, 0],
  349. [0, 0, 0, 0, 0, 0, 0],
  350. [0, 0, 0, 0, 0, 0, 0],
  351. [0, 0, 0, 0, 0, 0, 0],
  352. [0, 0, 0, 0, 0, 0, 0]])
  353. """
  354. return _binary_erosion(input, structure, iterations, mask,
  355. output, border_value, origin, 0, brute_force)
  356. def binary_dilation(input, structure=None, iterations=1, mask=None,
  357. output=None, border_value=0, origin=0,
  358. brute_force=False):
  359. """
  360. Multidimensional binary dilation with the given structuring element.
  361. Parameters
  362. ----------
  363. input : array_like
  364. Binary array_like to be dilated. Non-zero (True) elements form
  365. the subset to be dilated.
  366. structure : array_like, optional
  367. Structuring element used for the dilation. Non-zero elements are
  368. considered True. If no structuring element is provided an element
  369. is generated with a square connectivity equal to one.
  370. iterations : int, optional
  371. The dilation is repeated `iterations` times (one, by default).
  372. If iterations is less than 1, the dilation is repeated until the
  373. result does not change anymore. Only an integer of iterations is
  374. accepted.
  375. mask : array_like, optional
  376. If a mask is given, only those elements with a True value at
  377. the corresponding mask element are modified at each iteration.
  378. output : ndarray, optional
  379. Array of the same shape as input, into which the output is placed.
  380. By default, a new array is created.
  381. border_value : int (cast to 0 or 1), optional
  382. Value at the border in the output array.
  383. origin : int or tuple of ints, optional
  384. Placement of the filter, by default 0.
  385. brute_force : boolean, optional
  386. Memory condition: if False, only the pixels whose value was changed in
  387. the last iteration are tracked as candidates to be updated (dilated)
  388. in the current iteration; if True all pixels are considered as
  389. candidates for dilation, regardless of what happened in the previous
  390. iteration. False by default.
  391. Returns
  392. -------
  393. binary_dilation : ndarray of bools
  394. Dilation of the input by the structuring element.
  395. See also
  396. --------
  397. grey_dilation, binary_erosion, binary_closing, binary_opening,
  398. generate_binary_structure
  399. Notes
  400. -----
  401. Dilation [1]_ is a mathematical morphology operation [2]_ that uses a
  402. structuring element for expanding the shapes in an image. The binary
  403. dilation of an image by a structuring element is the locus of the points
  404. covered by the structuring element, when its center lies within the
  405. non-zero points of the image.
  406. References
  407. ----------
  408. .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
  409. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  410. Examples
  411. --------
  412. >>> from scipy import ndimage
  413. >>> import numpy as np
  414. >>> a = np.zeros((5, 5))
  415. >>> a[2, 2] = 1
  416. >>> a
  417. array([[ 0., 0., 0., 0., 0.],
  418. [ 0., 0., 0., 0., 0.],
  419. [ 0., 0., 1., 0., 0.],
  420. [ 0., 0., 0., 0., 0.],
  421. [ 0., 0., 0., 0., 0.]])
  422. >>> ndimage.binary_dilation(a)
  423. array([[False, False, False, False, False],
  424. [False, False, True, False, False],
  425. [False, True, True, True, False],
  426. [False, False, True, False, False],
  427. [False, False, False, False, False]], dtype=bool)
  428. >>> ndimage.binary_dilation(a).astype(a.dtype)
  429. array([[ 0., 0., 0., 0., 0.],
  430. [ 0., 0., 1., 0., 0.],
  431. [ 0., 1., 1., 1., 0.],
  432. [ 0., 0., 1., 0., 0.],
  433. [ 0., 0., 0., 0., 0.]])
  434. >>> # 3x3 structuring element with connectivity 1, used by default
  435. >>> struct1 = ndimage.generate_binary_structure(2, 1)
  436. >>> struct1
  437. array([[False, True, False],
  438. [ True, True, True],
  439. [False, True, False]], dtype=bool)
  440. >>> # 3x3 structuring element with connectivity 2
  441. >>> struct2 = ndimage.generate_binary_structure(2, 2)
  442. >>> struct2
  443. array([[ True, True, True],
  444. [ True, True, True],
  445. [ True, True, True]], dtype=bool)
  446. >>> ndimage.binary_dilation(a, structure=struct1).astype(a.dtype)
  447. array([[ 0., 0., 0., 0., 0.],
  448. [ 0., 0., 1., 0., 0.],
  449. [ 0., 1., 1., 1., 0.],
  450. [ 0., 0., 1., 0., 0.],
  451. [ 0., 0., 0., 0., 0.]])
  452. >>> ndimage.binary_dilation(a, structure=struct2).astype(a.dtype)
  453. array([[ 0., 0., 0., 0., 0.],
  454. [ 0., 1., 1., 1., 0.],
  455. [ 0., 1., 1., 1., 0.],
  456. [ 0., 1., 1., 1., 0.],
  457. [ 0., 0., 0., 0., 0.]])
  458. >>> ndimage.binary_dilation(a, structure=struct1,\\
  459. ... iterations=2).astype(a.dtype)
  460. array([[ 0., 0., 1., 0., 0.],
  461. [ 0., 1., 1., 1., 0.],
  462. [ 1., 1., 1., 1., 1.],
  463. [ 0., 1., 1., 1., 0.],
  464. [ 0., 0., 1., 0., 0.]])
  465. """
  466. input = numpy.asarray(input)
  467. if structure is None:
  468. structure = generate_binary_structure(input.ndim, 1)
  469. origin = _ni_support._normalize_sequence(origin, input.ndim)
  470. structure = numpy.asarray(structure)
  471. structure = structure[tuple([slice(None, None, -1)] *
  472. structure.ndim)]
  473. for ii in range(len(origin)):
  474. origin[ii] = -origin[ii]
  475. if not structure.shape[ii] & 1:
  476. origin[ii] -= 1
  477. return _binary_erosion(input, structure, iterations, mask,
  478. output, border_value, origin, 1, brute_force)
  479. def binary_opening(input, structure=None, iterations=1, output=None,
  480. origin=0, mask=None, border_value=0, brute_force=False):
  481. """
  482. Multidimensional binary opening with the given structuring element.
  483. The *opening* of an input image by a structuring element is the
  484. *dilation* of the *erosion* of the image by the structuring element.
  485. Parameters
  486. ----------
  487. input : array_like
  488. Binary array_like to be opened. Non-zero (True) elements form
  489. the subset to be opened.
  490. structure : array_like, optional
  491. Structuring element used for the opening. Non-zero elements are
  492. considered True. If no structuring element is provided an element
  493. is generated with a square connectivity equal to one (i.e., only
  494. nearest neighbors are connected to the center, diagonally-connected
  495. elements are not considered neighbors).
  496. iterations : int, optional
  497. The erosion step of the opening, then the dilation step are each
  498. repeated `iterations` times (one, by default). If `iterations` is
  499. less than 1, each operation is repeated until the result does
  500. not change anymore. Only an integer of iterations is accepted.
  501. output : ndarray, optional
  502. Array of the same shape as input, into which the output is placed.
  503. By default, a new array is created.
  504. origin : int or tuple of ints, optional
  505. Placement of the filter, by default 0.
  506. mask : array_like, optional
  507. If a mask is given, only those elements with a True value at
  508. the corresponding mask element are modified at each iteration.
  509. .. versionadded:: 1.1.0
  510. border_value : int (cast to 0 or 1), optional
  511. Value at the border in the output array.
  512. .. versionadded:: 1.1.0
  513. brute_force : boolean, optional
  514. Memory condition: if False, only the pixels whose value was changed in
  515. the last iteration are tracked as candidates to be updated in the
  516. current iteration; if true all pixels are considered as candidates for
  517. update, regardless of what happened in the previous iteration.
  518. False by default.
  519. .. versionadded:: 1.1.0
  520. Returns
  521. -------
  522. binary_opening : ndarray of bools
  523. Opening of the input by the structuring element.
  524. See also
  525. --------
  526. grey_opening, binary_closing, binary_erosion, binary_dilation,
  527. generate_binary_structure
  528. Notes
  529. -----
  530. *Opening* [1]_ is a mathematical morphology operation [2]_ that
  531. consists in the succession of an erosion and a dilation of the
  532. input with the same structuring element. Opening, therefore, removes
  533. objects smaller than the structuring element.
  534. Together with *closing* (`binary_closing`), opening can be used for
  535. noise removal.
  536. References
  537. ----------
  538. .. [1] https://en.wikipedia.org/wiki/Opening_%28morphology%29
  539. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  540. Examples
  541. --------
  542. >>> from scipy import ndimage
  543. >>> import numpy as np
  544. >>> a = np.zeros((5,5), dtype=int)
  545. >>> a[1:4, 1:4] = 1; a[4, 4] = 1
  546. >>> a
  547. array([[0, 0, 0, 0, 0],
  548. [0, 1, 1, 1, 0],
  549. [0, 1, 1, 1, 0],
  550. [0, 1, 1, 1, 0],
  551. [0, 0, 0, 0, 1]])
  552. >>> # Opening removes small objects
  553. >>> ndimage.binary_opening(a, structure=np.ones((3,3))).astype(int)
  554. array([[0, 0, 0, 0, 0],
  555. [0, 1, 1, 1, 0],
  556. [0, 1, 1, 1, 0],
  557. [0, 1, 1, 1, 0],
  558. [0, 0, 0, 0, 0]])
  559. >>> # Opening can also smooth corners
  560. >>> ndimage.binary_opening(a).astype(int)
  561. array([[0, 0, 0, 0, 0],
  562. [0, 0, 1, 0, 0],
  563. [0, 1, 1, 1, 0],
  564. [0, 0, 1, 0, 0],
  565. [0, 0, 0, 0, 0]])
  566. >>> # Opening is the dilation of the erosion of the input
  567. >>> ndimage.binary_erosion(a).astype(int)
  568. array([[0, 0, 0, 0, 0],
  569. [0, 0, 0, 0, 0],
  570. [0, 0, 1, 0, 0],
  571. [0, 0, 0, 0, 0],
  572. [0, 0, 0, 0, 0]])
  573. >>> ndimage.binary_dilation(ndimage.binary_erosion(a)).astype(int)
  574. array([[0, 0, 0, 0, 0],
  575. [0, 0, 1, 0, 0],
  576. [0, 1, 1, 1, 0],
  577. [0, 0, 1, 0, 0],
  578. [0, 0, 0, 0, 0]])
  579. """
  580. input = numpy.asarray(input)
  581. if structure is None:
  582. rank = input.ndim
  583. structure = generate_binary_structure(rank, 1)
  584. tmp = binary_erosion(input, structure, iterations, mask, None,
  585. border_value, origin, brute_force)
  586. return binary_dilation(tmp, structure, iterations, mask, output,
  587. border_value, origin, brute_force)
  588. def binary_closing(input, structure=None, iterations=1, output=None,
  589. origin=0, mask=None, border_value=0, brute_force=False):
  590. """
  591. Multidimensional binary closing with the given structuring element.
  592. The *closing* of an input image by a structuring element is the
  593. *erosion* of the *dilation* of the image by the structuring element.
  594. Parameters
  595. ----------
  596. input : array_like
  597. Binary array_like to be closed. Non-zero (True) elements form
  598. the subset to be closed.
  599. structure : array_like, optional
  600. Structuring element used for the closing. Non-zero elements are
  601. considered True. If no structuring element is provided an element
  602. is generated with a square connectivity equal to one (i.e., only
  603. nearest neighbors are connected to the center, diagonally-connected
  604. elements are not considered neighbors).
  605. iterations : int, optional
  606. The dilation step of the closing, then the erosion step are each
  607. repeated `iterations` times (one, by default). If iterations is
  608. less than 1, each operations is repeated until the result does
  609. not change anymore. Only an integer of iterations is accepted.
  610. output : ndarray, optional
  611. Array of the same shape as input, into which the output is placed.
  612. By default, a new array is created.
  613. origin : int or tuple of ints, optional
  614. Placement of the filter, by default 0.
  615. mask : array_like, optional
  616. If a mask is given, only those elements with a True value at
  617. the corresponding mask element are modified at each iteration.
  618. .. versionadded:: 1.1.0
  619. border_value : int (cast to 0 or 1), optional
  620. Value at the border in the output array.
  621. .. versionadded:: 1.1.0
  622. brute_force : boolean, optional
  623. Memory condition: if False, only the pixels whose value was changed in
  624. the last iteration are tracked as candidates to be updated in the
  625. current iteration; if true al pixels are considered as candidates for
  626. update, regardless of what happened in the previous iteration.
  627. False by default.
  628. .. versionadded:: 1.1.0
  629. Returns
  630. -------
  631. binary_closing : ndarray of bools
  632. Closing of the input by the structuring element.
  633. See also
  634. --------
  635. grey_closing, binary_opening, binary_dilation, binary_erosion,
  636. generate_binary_structure
  637. Notes
  638. -----
  639. *Closing* [1]_ is a mathematical morphology operation [2]_ that
  640. consists in the succession of a dilation and an erosion of the
  641. input with the same structuring element. Closing therefore fills
  642. holes smaller than the structuring element.
  643. Together with *opening* (`binary_opening`), closing can be used for
  644. noise removal.
  645. References
  646. ----------
  647. .. [1] https://en.wikipedia.org/wiki/Closing_%28morphology%29
  648. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  649. Examples
  650. --------
  651. >>> from scipy import ndimage
  652. >>> import numpy as np
  653. >>> a = np.zeros((5,5), dtype=int)
  654. >>> a[1:-1, 1:-1] = 1; a[2,2] = 0
  655. >>> a
  656. array([[0, 0, 0, 0, 0],
  657. [0, 1, 1, 1, 0],
  658. [0, 1, 0, 1, 0],
  659. [0, 1, 1, 1, 0],
  660. [0, 0, 0, 0, 0]])
  661. >>> # Closing removes small holes
  662. >>> ndimage.binary_closing(a).astype(int)
  663. array([[0, 0, 0, 0, 0],
  664. [0, 1, 1, 1, 0],
  665. [0, 1, 1, 1, 0],
  666. [0, 1, 1, 1, 0],
  667. [0, 0, 0, 0, 0]])
  668. >>> # Closing is the erosion of the dilation of the input
  669. >>> ndimage.binary_dilation(a).astype(int)
  670. array([[0, 1, 1, 1, 0],
  671. [1, 1, 1, 1, 1],
  672. [1, 1, 1, 1, 1],
  673. [1, 1, 1, 1, 1],
  674. [0, 1, 1, 1, 0]])
  675. >>> ndimage.binary_erosion(ndimage.binary_dilation(a)).astype(int)
  676. array([[0, 0, 0, 0, 0],
  677. [0, 1, 1, 1, 0],
  678. [0, 1, 1, 1, 0],
  679. [0, 1, 1, 1, 0],
  680. [0, 0, 0, 0, 0]])
  681. >>> a = np.zeros((7,7), dtype=int)
  682. >>> a[1:6, 2:5] = 1; a[1:3,3] = 0
  683. >>> a
  684. array([[0, 0, 0, 0, 0, 0, 0],
  685. [0, 0, 1, 0, 1, 0, 0],
  686. [0, 0, 1, 0, 1, 0, 0],
  687. [0, 0, 1, 1, 1, 0, 0],
  688. [0, 0, 1, 1, 1, 0, 0],
  689. [0, 0, 1, 1, 1, 0, 0],
  690. [0, 0, 0, 0, 0, 0, 0]])
  691. >>> # In addition to removing holes, closing can also
  692. >>> # coarsen boundaries with fine hollows.
  693. >>> ndimage.binary_closing(a).astype(int)
  694. array([[0, 0, 0, 0, 0, 0, 0],
  695. [0, 0, 1, 0, 1, 0, 0],
  696. [0, 0, 1, 1, 1, 0, 0],
  697. [0, 0, 1, 1, 1, 0, 0],
  698. [0, 0, 1, 1, 1, 0, 0],
  699. [0, 0, 1, 1, 1, 0, 0],
  700. [0, 0, 0, 0, 0, 0, 0]])
  701. >>> ndimage.binary_closing(a, structure=np.ones((2,2))).astype(int)
  702. array([[0, 0, 0, 0, 0, 0, 0],
  703. [0, 0, 1, 1, 1, 0, 0],
  704. [0, 0, 1, 1, 1, 0, 0],
  705. [0, 0, 1, 1, 1, 0, 0],
  706. [0, 0, 1, 1, 1, 0, 0],
  707. [0, 0, 1, 1, 1, 0, 0],
  708. [0, 0, 0, 0, 0, 0, 0]])
  709. """
  710. input = numpy.asarray(input)
  711. if structure is None:
  712. rank = input.ndim
  713. structure = generate_binary_structure(rank, 1)
  714. tmp = binary_dilation(input, structure, iterations, mask, None,
  715. border_value, origin, brute_force)
  716. return binary_erosion(tmp, structure, iterations, mask, output,
  717. border_value, origin, brute_force)
  718. def binary_hit_or_miss(input, structure1=None, structure2=None,
  719. output=None, origin1=0, origin2=None):
  720. """
  721. Multidimensional binary hit-or-miss transform.
  722. The hit-or-miss transform finds the locations of a given pattern
  723. inside the input image.
  724. Parameters
  725. ----------
  726. input : array_like (cast to booleans)
  727. Binary image where a pattern is to be detected.
  728. structure1 : array_like (cast to booleans), optional
  729. Part of the structuring element to be fitted to the foreground
  730. (non-zero elements) of `input`. If no value is provided, a
  731. structure of square connectivity 1 is chosen.
  732. structure2 : array_like (cast to booleans), optional
  733. Second part of the structuring element that has to miss completely
  734. the foreground. If no value is provided, the complementary of
  735. `structure1` is taken.
  736. output : ndarray, optional
  737. Array of the same shape as input, into which the output is placed.
  738. By default, a new array is created.
  739. origin1 : int or tuple of ints, optional
  740. Placement of the first part of the structuring element `structure1`,
  741. by default 0 for a centered structure.
  742. origin2 : int or tuple of ints, optional
  743. Placement of the second part of the structuring element `structure2`,
  744. by default 0 for a centered structure. If a value is provided for
  745. `origin1` and not for `origin2`, then `origin2` is set to `origin1`.
  746. Returns
  747. -------
  748. binary_hit_or_miss : ndarray
  749. Hit-or-miss transform of `input` with the given structuring
  750. element (`structure1`, `structure2`).
  751. See also
  752. --------
  753. binary_erosion
  754. References
  755. ----------
  756. .. [1] https://en.wikipedia.org/wiki/Hit-or-miss_transform
  757. Examples
  758. --------
  759. >>> from scipy import ndimage
  760. >>> import numpy as np
  761. >>> a = np.zeros((7,7), dtype=int)
  762. >>> a[1, 1] = 1; a[2:4, 2:4] = 1; a[4:6, 4:6] = 1
  763. >>> a
  764. array([[0, 0, 0, 0, 0, 0, 0],
  765. [0, 1, 0, 0, 0, 0, 0],
  766. [0, 0, 1, 1, 0, 0, 0],
  767. [0, 0, 1, 1, 0, 0, 0],
  768. [0, 0, 0, 0, 1, 1, 0],
  769. [0, 0, 0, 0, 1, 1, 0],
  770. [0, 0, 0, 0, 0, 0, 0]])
  771. >>> structure1 = np.array([[1, 0, 0], [0, 1, 1], [0, 1, 1]])
  772. >>> structure1
  773. array([[1, 0, 0],
  774. [0, 1, 1],
  775. [0, 1, 1]])
  776. >>> # Find the matches of structure1 in the array a
  777. >>> ndimage.binary_hit_or_miss(a, structure1=structure1).astype(int)
  778. array([[0, 0, 0, 0, 0, 0, 0],
  779. [0, 0, 0, 0, 0, 0, 0],
  780. [0, 0, 1, 0, 0, 0, 0],
  781. [0, 0, 0, 0, 0, 0, 0],
  782. [0, 0, 0, 0, 1, 0, 0],
  783. [0, 0, 0, 0, 0, 0, 0],
  784. [0, 0, 0, 0, 0, 0, 0]])
  785. >>> # Change the origin of the filter
  786. >>> # origin1=1 is equivalent to origin1=(1,1) here
  787. >>> ndimage.binary_hit_or_miss(a, structure1=structure1,\\
  788. ... origin1=1).astype(int)
  789. array([[0, 0, 0, 0, 0, 0, 0],
  790. [0, 0, 0, 0, 0, 0, 0],
  791. [0, 0, 0, 0, 0, 0, 0],
  792. [0, 0, 0, 1, 0, 0, 0],
  793. [0, 0, 0, 0, 0, 0, 0],
  794. [0, 0, 0, 0, 0, 1, 0],
  795. [0, 0, 0, 0, 0, 0, 0]])
  796. """
  797. input = numpy.asarray(input)
  798. if structure1 is None:
  799. structure1 = generate_binary_structure(input.ndim, 1)
  800. if structure2 is None:
  801. structure2 = numpy.logical_not(structure1)
  802. origin1 = _ni_support._normalize_sequence(origin1, input.ndim)
  803. if origin2 is None:
  804. origin2 = origin1
  805. else:
  806. origin2 = _ni_support._normalize_sequence(origin2, input.ndim)
  807. tmp1 = _binary_erosion(input, structure1, 1, None, None, 0, origin1,
  808. 0, False)
  809. inplace = isinstance(output, numpy.ndarray)
  810. result = _binary_erosion(input, structure2, 1, None, output, 0,
  811. origin2, 1, False)
  812. if inplace:
  813. numpy.logical_not(output, output)
  814. numpy.logical_and(tmp1, output, output)
  815. else:
  816. numpy.logical_not(result, result)
  817. return numpy.logical_and(tmp1, result)
  818. def binary_propagation(input, structure=None, mask=None,
  819. output=None, border_value=0, origin=0):
  820. """
  821. Multidimensional binary propagation with the given structuring element.
  822. Parameters
  823. ----------
  824. input : array_like
  825. Binary image to be propagated inside `mask`.
  826. structure : array_like, optional
  827. Structuring element used in the successive dilations. The output
  828. may depend on the structuring element, especially if `mask` has
  829. several connex components. If no structuring element is
  830. provided, an element is generated with a squared connectivity equal
  831. to one.
  832. mask : array_like, optional
  833. Binary mask defining the region into which `input` is allowed to
  834. propagate.
  835. output : ndarray, optional
  836. Array of the same shape as input, into which the output is placed.
  837. By default, a new array is created.
  838. border_value : int (cast to 0 or 1), optional
  839. Value at the border in the output array.
  840. origin : int or tuple of ints, optional
  841. Placement of the filter, by default 0.
  842. Returns
  843. -------
  844. binary_propagation : ndarray
  845. Binary propagation of `input` inside `mask`.
  846. Notes
  847. -----
  848. This function is functionally equivalent to calling binary_dilation
  849. with the number of iterations less than one: iterative dilation until
  850. the result does not change anymore.
  851. The succession of an erosion and propagation inside the original image
  852. can be used instead of an *opening* for deleting small objects while
  853. keeping the contours of larger objects untouched.
  854. References
  855. ----------
  856. .. [1] http://cmm.ensmp.fr/~serra/cours/pdf/en/ch6en.pdf, slide 15.
  857. .. [2] I.T. Young, J.J. Gerbrands, and L.J. van Vliet, "Fundamentals of
  858. image processing", 1998
  859. ftp://qiftp.tudelft.nl/DIPimage/docs/FIP2.3.pdf
  860. Examples
  861. --------
  862. >>> from scipy import ndimage
  863. >>> import numpy as np
  864. >>> input = np.zeros((8, 8), dtype=int)
  865. >>> input[2, 2] = 1
  866. >>> mask = np.zeros((8, 8), dtype=int)
  867. >>> mask[1:4, 1:4] = mask[4, 4] = mask[6:8, 6:8] = 1
  868. >>> input
  869. array([[0, 0, 0, 0, 0, 0, 0, 0],
  870. [0, 0, 0, 0, 0, 0, 0, 0],
  871. [0, 0, 1, 0, 0, 0, 0, 0],
  872. [0, 0, 0, 0, 0, 0, 0, 0],
  873. [0, 0, 0, 0, 0, 0, 0, 0],
  874. [0, 0, 0, 0, 0, 0, 0, 0],
  875. [0, 0, 0, 0, 0, 0, 0, 0],
  876. [0, 0, 0, 0, 0, 0, 0, 0]])
  877. >>> mask
  878. array([[0, 0, 0, 0, 0, 0, 0, 0],
  879. [0, 1, 1, 1, 0, 0, 0, 0],
  880. [0, 1, 1, 1, 0, 0, 0, 0],
  881. [0, 1, 1, 1, 0, 0, 0, 0],
  882. [0, 0, 0, 0, 1, 0, 0, 0],
  883. [0, 0, 0, 0, 0, 0, 0, 0],
  884. [0, 0, 0, 0, 0, 0, 1, 1],
  885. [0, 0, 0, 0, 0, 0, 1, 1]])
  886. >>> ndimage.binary_propagation(input, mask=mask).astype(int)
  887. array([[0, 0, 0, 0, 0, 0, 0, 0],
  888. [0, 1, 1, 1, 0, 0, 0, 0],
  889. [0, 1, 1, 1, 0, 0, 0, 0],
  890. [0, 1, 1, 1, 0, 0, 0, 0],
  891. [0, 0, 0, 0, 0, 0, 0, 0],
  892. [0, 0, 0, 0, 0, 0, 0, 0],
  893. [0, 0, 0, 0, 0, 0, 0, 0],
  894. [0, 0, 0, 0, 0, 0, 0, 0]])
  895. >>> ndimage.binary_propagation(input, mask=mask,\\
  896. ... structure=np.ones((3,3))).astype(int)
  897. array([[0, 0, 0, 0, 0, 0, 0, 0],
  898. [0, 1, 1, 1, 0, 0, 0, 0],
  899. [0, 1, 1, 1, 0, 0, 0, 0],
  900. [0, 1, 1, 1, 0, 0, 0, 0],
  901. [0, 0, 0, 0, 1, 0, 0, 0],
  902. [0, 0, 0, 0, 0, 0, 0, 0],
  903. [0, 0, 0, 0, 0, 0, 0, 0],
  904. [0, 0, 0, 0, 0, 0, 0, 0]])
  905. >>> # Comparison between opening and erosion+propagation
  906. >>> a = np.zeros((6,6), dtype=int)
  907. >>> a[2:5, 2:5] = 1; a[0, 0] = 1; a[5, 5] = 1
  908. >>> a
  909. array([[1, 0, 0, 0, 0, 0],
  910. [0, 0, 0, 0, 0, 0],
  911. [0, 0, 1, 1, 1, 0],
  912. [0, 0, 1, 1, 1, 0],
  913. [0, 0, 1, 1, 1, 0],
  914. [0, 0, 0, 0, 0, 1]])
  915. >>> ndimage.binary_opening(a).astype(int)
  916. array([[0, 0, 0, 0, 0, 0],
  917. [0, 0, 0, 0, 0, 0],
  918. [0, 0, 0, 1, 0, 0],
  919. [0, 0, 1, 1, 1, 0],
  920. [0, 0, 0, 1, 0, 0],
  921. [0, 0, 0, 0, 0, 0]])
  922. >>> b = ndimage.binary_erosion(a)
  923. >>> b.astype(int)
  924. array([[0, 0, 0, 0, 0, 0],
  925. [0, 0, 0, 0, 0, 0],
  926. [0, 0, 0, 0, 0, 0],
  927. [0, 0, 0, 1, 0, 0],
  928. [0, 0, 0, 0, 0, 0],
  929. [0, 0, 0, 0, 0, 0]])
  930. >>> ndimage.binary_propagation(b, mask=a).astype(int)
  931. array([[0, 0, 0, 0, 0, 0],
  932. [0, 0, 0, 0, 0, 0],
  933. [0, 0, 1, 1, 1, 0],
  934. [0, 0, 1, 1, 1, 0],
  935. [0, 0, 1, 1, 1, 0],
  936. [0, 0, 0, 0, 0, 0]])
  937. """
  938. return binary_dilation(input, structure, -1, mask, output,
  939. border_value, origin)
  940. def binary_fill_holes(input, structure=None, output=None, origin=0):
  941. """
  942. Fill the holes in binary objects.
  943. Parameters
  944. ----------
  945. input : array_like
  946. N-D binary array with holes to be filled
  947. structure : array_like, optional
  948. Structuring element used in the computation; large-size elements
  949. make computations faster but may miss holes separated from the
  950. background by thin regions. The default element (with a square
  951. connectivity equal to one) yields the intuitive result where all
  952. holes in the input have been filled.
  953. output : ndarray, optional
  954. Array of the same shape as input, into which the output is placed.
  955. By default, a new array is created.
  956. origin : int, tuple of ints, optional
  957. Position of the structuring element.
  958. Returns
  959. -------
  960. out : ndarray
  961. Transformation of the initial image `input` where holes have been
  962. filled.
  963. See also
  964. --------
  965. binary_dilation, binary_propagation, label
  966. Notes
  967. -----
  968. The algorithm used in this function consists in invading the complementary
  969. of the shapes in `input` from the outer boundary of the image,
  970. using binary dilations. Holes are not connected to the boundary and are
  971. therefore not invaded. The result is the complementary subset of the
  972. invaded region.
  973. References
  974. ----------
  975. .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
  976. Examples
  977. --------
  978. >>> from scipy import ndimage
  979. >>> import numpy as np
  980. >>> a = np.zeros((5, 5), dtype=int)
  981. >>> a[1:4, 1:4] = 1
  982. >>> a[2,2] = 0
  983. >>> a
  984. array([[0, 0, 0, 0, 0],
  985. [0, 1, 1, 1, 0],
  986. [0, 1, 0, 1, 0],
  987. [0, 1, 1, 1, 0],
  988. [0, 0, 0, 0, 0]])
  989. >>> ndimage.binary_fill_holes(a).astype(int)
  990. array([[0, 0, 0, 0, 0],
  991. [0, 1, 1, 1, 0],
  992. [0, 1, 1, 1, 0],
  993. [0, 1, 1, 1, 0],
  994. [0, 0, 0, 0, 0]])
  995. >>> # Too big structuring element
  996. >>> ndimage.binary_fill_holes(a, structure=np.ones((5,5))).astype(int)
  997. array([[0, 0, 0, 0, 0],
  998. [0, 1, 1, 1, 0],
  999. [0, 1, 0, 1, 0],
  1000. [0, 1, 1, 1, 0],
  1001. [0, 0, 0, 0, 0]])
  1002. """
  1003. mask = numpy.logical_not(input)
  1004. tmp = numpy.zeros(mask.shape, bool)
  1005. inplace = isinstance(output, numpy.ndarray)
  1006. if inplace:
  1007. binary_dilation(tmp, structure, -1, mask, output, 1, origin)
  1008. numpy.logical_not(output, output)
  1009. else:
  1010. output = binary_dilation(tmp, structure, -1, mask, None, 1,
  1011. origin)
  1012. numpy.logical_not(output, output)
  1013. return output
  1014. def grey_erosion(input, size=None, footprint=None, structure=None,
  1015. output=None, mode="reflect", cval=0.0, origin=0):
  1016. """
  1017. Calculate a greyscale erosion, using either a structuring element,
  1018. or a footprint corresponding to a flat structuring element.
  1019. Grayscale erosion is a mathematical morphology operation. For the
  1020. simple case of a full and flat structuring element, it can be viewed
  1021. as a minimum filter over a sliding window.
  1022. Parameters
  1023. ----------
  1024. input : array_like
  1025. Array over which the grayscale erosion is to be computed.
  1026. size : tuple of ints
  1027. Shape of a flat and full structuring element used for the grayscale
  1028. erosion. Optional if `footprint` or `structure` is provided.
  1029. footprint : array of ints, optional
  1030. Positions of non-infinite elements of a flat structuring element
  1031. used for the grayscale erosion. Non-zero values give the set of
  1032. neighbors of the center over which the minimum is chosen.
  1033. structure : array of ints, optional
  1034. Structuring element used for the grayscale erosion. `structure`
  1035. may be a non-flat structuring element.
  1036. output : array, optional
  1037. An array used for storing the output of the erosion may be provided.
  1038. mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
  1039. The `mode` parameter determines how the array borders are
  1040. handled, where `cval` is the value when mode is equal to
  1041. 'constant'. Default is 'reflect'
  1042. cval : scalar, optional
  1043. Value to fill past edges of input if `mode` is 'constant'. Default
  1044. is 0.0.
  1045. origin : scalar, optional
  1046. The `origin` parameter controls the placement of the filter.
  1047. Default 0
  1048. Returns
  1049. -------
  1050. output : ndarray
  1051. Grayscale erosion of `input`.
  1052. See also
  1053. --------
  1054. binary_erosion, grey_dilation, grey_opening, grey_closing
  1055. generate_binary_structure, minimum_filter
  1056. Notes
  1057. -----
  1058. The grayscale erosion of an image input by a structuring element s defined
  1059. over a domain E is given by:
  1060. (input+s)(x) = min {input(y) - s(x-y), for y in E}
  1061. In particular, for structuring elements defined as
  1062. s(y) = 0 for y in E, the grayscale erosion computes the minimum of the
  1063. input image inside a sliding window defined by E.
  1064. Grayscale erosion [1]_ is a *mathematical morphology* operation [2]_.
  1065. References
  1066. ----------
  1067. .. [1] https://en.wikipedia.org/wiki/Erosion_%28morphology%29
  1068. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  1069. Examples
  1070. --------
  1071. >>> from scipy import ndimage
  1072. >>> import numpy as np
  1073. >>> a = np.zeros((7,7), dtype=int)
  1074. >>> a[1:6, 1:6] = 3
  1075. >>> a[4,4] = 2; a[2,3] = 1
  1076. >>> a
  1077. array([[0, 0, 0, 0, 0, 0, 0],
  1078. [0, 3, 3, 3, 3, 3, 0],
  1079. [0, 3, 3, 1, 3, 3, 0],
  1080. [0, 3, 3, 3, 3, 3, 0],
  1081. [0, 3, 3, 3, 2, 3, 0],
  1082. [0, 3, 3, 3, 3, 3, 0],
  1083. [0, 0, 0, 0, 0, 0, 0]])
  1084. >>> ndimage.grey_erosion(a, size=(3,3))
  1085. array([[0, 0, 0, 0, 0, 0, 0],
  1086. [0, 0, 0, 0, 0, 0, 0],
  1087. [0, 0, 1, 1, 1, 0, 0],
  1088. [0, 0, 1, 1, 1, 0, 0],
  1089. [0, 0, 3, 2, 2, 0, 0],
  1090. [0, 0, 0, 0, 0, 0, 0],
  1091. [0, 0, 0, 0, 0, 0, 0]])
  1092. >>> footprint = ndimage.generate_binary_structure(2, 1)
  1093. >>> footprint
  1094. array([[False, True, False],
  1095. [ True, True, True],
  1096. [False, True, False]], dtype=bool)
  1097. >>> # Diagonally-connected elements are not considered neighbors
  1098. >>> ndimage.grey_erosion(a, size=(3,3), footprint=footprint)
  1099. array([[0, 0, 0, 0, 0, 0, 0],
  1100. [0, 0, 0, 0, 0, 0, 0],
  1101. [0, 0, 1, 1, 1, 0, 0],
  1102. [0, 0, 3, 1, 2, 0, 0],
  1103. [0, 0, 3, 2, 2, 0, 0],
  1104. [0, 0, 0, 0, 0, 0, 0],
  1105. [0, 0, 0, 0, 0, 0, 0]])
  1106. """
  1107. if size is None and footprint is None and structure is None:
  1108. raise ValueError("size, footprint, or structure must be specified")
  1109. return _filters._min_or_max_filter(input, size, footprint, structure,
  1110. output, mode, cval, origin, 1)
  1111. def grey_dilation(input, size=None, footprint=None, structure=None,
  1112. output=None, mode="reflect", cval=0.0, origin=0):
  1113. """
  1114. Calculate a greyscale dilation, using either a structuring element,
  1115. or a footprint corresponding to a flat structuring element.
  1116. Grayscale dilation is a mathematical morphology operation. For the
  1117. simple case of a full and flat structuring element, it can be viewed
  1118. as a maximum filter over a sliding window.
  1119. Parameters
  1120. ----------
  1121. input : array_like
  1122. Array over which the grayscale dilation is to be computed.
  1123. size : tuple of ints
  1124. Shape of a flat and full structuring element used for the grayscale
  1125. dilation. Optional if `footprint` or `structure` is provided.
  1126. footprint : array of ints, optional
  1127. Positions of non-infinite elements of a flat structuring element
  1128. used for the grayscale dilation. Non-zero values give the set of
  1129. neighbors of the center over which the maximum is chosen.
  1130. structure : array of ints, optional
  1131. Structuring element used for the grayscale dilation. `structure`
  1132. may be a non-flat structuring element.
  1133. output : array, optional
  1134. An array used for storing the output of the dilation may be provided.
  1135. mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
  1136. The `mode` parameter determines how the array borders are
  1137. handled, where `cval` is the value when mode is equal to
  1138. 'constant'. Default is 'reflect'
  1139. cval : scalar, optional
  1140. Value to fill past edges of input if `mode` is 'constant'. Default
  1141. is 0.0.
  1142. origin : scalar, optional
  1143. The `origin` parameter controls the placement of the filter.
  1144. Default 0
  1145. Returns
  1146. -------
  1147. grey_dilation : ndarray
  1148. Grayscale dilation of `input`.
  1149. See also
  1150. --------
  1151. binary_dilation, grey_erosion, grey_closing, grey_opening
  1152. generate_binary_structure, maximum_filter
  1153. Notes
  1154. -----
  1155. The grayscale dilation of an image input by a structuring element s defined
  1156. over a domain E is given by:
  1157. (input+s)(x) = max {input(y) + s(x-y), for y in E}
  1158. In particular, for structuring elements defined as
  1159. s(y) = 0 for y in E, the grayscale dilation computes the maximum of the
  1160. input image inside a sliding window defined by E.
  1161. Grayscale dilation [1]_ is a *mathematical morphology* operation [2]_.
  1162. References
  1163. ----------
  1164. .. [1] https://en.wikipedia.org/wiki/Dilation_%28morphology%29
  1165. .. [2] https://en.wikipedia.org/wiki/Mathematical_morphology
  1166. Examples
  1167. --------
  1168. >>> from scipy import ndimage
  1169. >>> import numpy as np
  1170. >>> a = np.zeros((7,7), dtype=int)
  1171. >>> a[2:5, 2:5] = 1
  1172. >>> a[4,4] = 2; a[2,3] = 3
  1173. >>> a
  1174. array([[0, 0, 0, 0, 0, 0, 0],
  1175. [0, 0, 0, 0, 0, 0, 0],
  1176. [0, 0, 1, 3, 1, 0, 0],
  1177. [0, 0, 1, 1, 1, 0, 0],
  1178. [0, 0, 1, 1, 2, 0, 0],
  1179. [0, 0, 0, 0, 0, 0, 0],
  1180. [0, 0, 0, 0, 0, 0, 0]])
  1181. >>> ndimage.grey_dilation(a, size=(3,3))
  1182. array([[0, 0, 0, 0, 0, 0, 0],
  1183. [0, 1, 3, 3, 3, 1, 0],
  1184. [0, 1, 3, 3, 3, 1, 0],
  1185. [0, 1, 3, 3, 3, 2, 0],
  1186. [0, 1, 1, 2, 2, 2, 0],
  1187. [0, 1, 1, 2, 2, 2, 0],
  1188. [0, 0, 0, 0, 0, 0, 0]])
  1189. >>> ndimage.grey_dilation(a, footprint=np.ones((3,3)))
  1190. array([[0, 0, 0, 0, 0, 0, 0],
  1191. [0, 1, 3, 3, 3, 1, 0],
  1192. [0, 1, 3, 3, 3, 1, 0],
  1193. [0, 1, 3, 3, 3, 2, 0],
  1194. [0, 1, 1, 2, 2, 2, 0],
  1195. [0, 1, 1, 2, 2, 2, 0],
  1196. [0, 0, 0, 0, 0, 0, 0]])
  1197. >>> s = ndimage.generate_binary_structure(2,1)
  1198. >>> s
  1199. array([[False, True, False],
  1200. [ True, True, True],
  1201. [False, True, False]], dtype=bool)
  1202. >>> ndimage.grey_dilation(a, footprint=s)
  1203. array([[0, 0, 0, 0, 0, 0, 0],
  1204. [0, 0, 1, 3, 1, 0, 0],
  1205. [0, 1, 3, 3, 3, 1, 0],
  1206. [0, 1, 1, 3, 2, 1, 0],
  1207. [0, 1, 1, 2, 2, 2, 0],
  1208. [0, 0, 1, 1, 2, 0, 0],
  1209. [0, 0, 0, 0, 0, 0, 0]])
  1210. >>> ndimage.grey_dilation(a, size=(3,3), structure=np.ones((3,3)))
  1211. array([[1, 1, 1, 1, 1, 1, 1],
  1212. [1, 2, 4, 4, 4, 2, 1],
  1213. [1, 2, 4, 4, 4, 2, 1],
  1214. [1, 2, 4, 4, 4, 3, 1],
  1215. [1, 2, 2, 3, 3, 3, 1],
  1216. [1, 2, 2, 3, 3, 3, 1],
  1217. [1, 1, 1, 1, 1, 1, 1]])
  1218. """
  1219. if size is None and footprint is None and structure is None:
  1220. raise ValueError("size, footprint, or structure must be specified")
  1221. if structure is not None:
  1222. structure = numpy.asarray(structure)
  1223. structure = structure[tuple([slice(None, None, -1)] *
  1224. structure.ndim)]
  1225. if footprint is not None:
  1226. footprint = numpy.asarray(footprint)
  1227. footprint = footprint[tuple([slice(None, None, -1)] *
  1228. footprint.ndim)]
  1229. input = numpy.asarray(input)
  1230. origin = _ni_support._normalize_sequence(origin, input.ndim)
  1231. for ii in range(len(origin)):
  1232. origin[ii] = -origin[ii]
  1233. if footprint is not None:
  1234. sz = footprint.shape[ii]
  1235. elif structure is not None:
  1236. sz = structure.shape[ii]
  1237. elif numpy.isscalar(size):
  1238. sz = size
  1239. else:
  1240. sz = size[ii]
  1241. if not sz & 1:
  1242. origin[ii] -= 1
  1243. return _filters._min_or_max_filter(input, size, footprint, structure,
  1244. output, mode, cval, origin, 0)
  1245. def grey_opening(input, size=None, footprint=None, structure=None,
  1246. output=None, mode="reflect", cval=0.0, origin=0):
  1247. """
  1248. Multidimensional grayscale opening.
  1249. A grayscale opening consists in the succession of a grayscale erosion,
  1250. and a grayscale dilation.
  1251. Parameters
  1252. ----------
  1253. input : array_like
  1254. Array over which the grayscale opening is to be computed.
  1255. size : tuple of ints
  1256. Shape of a flat and full structuring element used for the grayscale
  1257. opening. Optional if `footprint` or `structure` is provided.
  1258. footprint : array of ints, optional
  1259. Positions of non-infinite elements of a flat structuring element
  1260. used for the grayscale opening.
  1261. structure : array of ints, optional
  1262. Structuring element used for the grayscale opening. `structure`
  1263. may be a non-flat structuring element.
  1264. output : array, optional
  1265. An array used for storing the output of the opening may be provided.
  1266. mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
  1267. The `mode` parameter determines how the array borders are
  1268. handled, where `cval` is the value when mode is equal to
  1269. 'constant'. Default is 'reflect'
  1270. cval : scalar, optional
  1271. Value to fill past edges of input if `mode` is 'constant'. Default
  1272. is 0.0.
  1273. origin : scalar, optional
  1274. The `origin` parameter controls the placement of the filter.
  1275. Default 0
  1276. Returns
  1277. -------
  1278. grey_opening : ndarray
  1279. Result of the grayscale opening of `input` with `structure`.
  1280. See also
  1281. --------
  1282. binary_opening, grey_dilation, grey_erosion, grey_closing
  1283. generate_binary_structure
  1284. Notes
  1285. -----
  1286. The action of a grayscale opening with a flat structuring element amounts
  1287. to smoothen high local maxima, whereas binary opening erases small objects.
  1288. References
  1289. ----------
  1290. .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
  1291. Examples
  1292. --------
  1293. >>> from scipy import ndimage
  1294. >>> import numpy as np
  1295. >>> a = np.arange(36).reshape((6,6))
  1296. >>> a[3, 3] = 50
  1297. >>> a
  1298. array([[ 0, 1, 2, 3, 4, 5],
  1299. [ 6, 7, 8, 9, 10, 11],
  1300. [12, 13, 14, 15, 16, 17],
  1301. [18, 19, 20, 50, 22, 23],
  1302. [24, 25, 26, 27, 28, 29],
  1303. [30, 31, 32, 33, 34, 35]])
  1304. >>> ndimage.grey_opening(a, size=(3,3))
  1305. array([[ 0, 1, 2, 3, 4, 4],
  1306. [ 6, 7, 8, 9, 10, 10],
  1307. [12, 13, 14, 15, 16, 16],
  1308. [18, 19, 20, 22, 22, 22],
  1309. [24, 25, 26, 27, 28, 28],
  1310. [24, 25, 26, 27, 28, 28]])
  1311. >>> # Note that the local maximum a[3,3] has disappeared
  1312. """
  1313. if (size is not None) and (footprint is not None):
  1314. warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
  1315. tmp = grey_erosion(input, size, footprint, structure, None, mode,
  1316. cval, origin)
  1317. return grey_dilation(tmp, size, footprint, structure, output, mode,
  1318. cval, origin)
  1319. def grey_closing(input, size=None, footprint=None, structure=None,
  1320. output=None, mode="reflect", cval=0.0, origin=0):
  1321. """
  1322. Multidimensional grayscale closing.
  1323. A grayscale closing consists in the succession of a grayscale dilation,
  1324. and a grayscale erosion.
  1325. Parameters
  1326. ----------
  1327. input : array_like
  1328. Array over which the grayscale closing is to be computed.
  1329. size : tuple of ints
  1330. Shape of a flat and full structuring element used for the grayscale
  1331. closing. Optional if `footprint` or `structure` is provided.
  1332. footprint : array of ints, optional
  1333. Positions of non-infinite elements of a flat structuring element
  1334. used for the grayscale closing.
  1335. structure : array of ints, optional
  1336. Structuring element used for the grayscale closing. `structure`
  1337. may be a non-flat structuring element.
  1338. output : array, optional
  1339. An array used for storing the output of the closing may be provided.
  1340. mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
  1341. The `mode` parameter determines how the array borders are
  1342. handled, where `cval` is the value when mode is equal to
  1343. 'constant'. Default is 'reflect'
  1344. cval : scalar, optional
  1345. Value to fill past edges of input if `mode` is 'constant'. Default
  1346. is 0.0.
  1347. origin : scalar, optional
  1348. The `origin` parameter controls the placement of the filter.
  1349. Default 0
  1350. Returns
  1351. -------
  1352. grey_closing : ndarray
  1353. Result of the grayscale closing of `input` with `structure`.
  1354. See also
  1355. --------
  1356. binary_closing, grey_dilation, grey_erosion, grey_opening,
  1357. generate_binary_structure
  1358. Notes
  1359. -----
  1360. The action of a grayscale closing with a flat structuring element amounts
  1361. to smoothen deep local minima, whereas binary closing fills small holes.
  1362. References
  1363. ----------
  1364. .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
  1365. Examples
  1366. --------
  1367. >>> from scipy import ndimage
  1368. >>> import numpy as np
  1369. >>> a = np.arange(36).reshape((6,6))
  1370. >>> a[3,3] = 0
  1371. >>> a
  1372. array([[ 0, 1, 2, 3, 4, 5],
  1373. [ 6, 7, 8, 9, 10, 11],
  1374. [12, 13, 14, 15, 16, 17],
  1375. [18, 19, 20, 0, 22, 23],
  1376. [24, 25, 26, 27, 28, 29],
  1377. [30, 31, 32, 33, 34, 35]])
  1378. >>> ndimage.grey_closing(a, size=(3,3))
  1379. array([[ 7, 7, 8, 9, 10, 11],
  1380. [ 7, 7, 8, 9, 10, 11],
  1381. [13, 13, 14, 15, 16, 17],
  1382. [19, 19, 20, 20, 22, 23],
  1383. [25, 25, 26, 27, 28, 29],
  1384. [31, 31, 32, 33, 34, 35]])
  1385. >>> # Note that the local minimum a[3,3] has disappeared
  1386. """
  1387. if (size is not None) and (footprint is not None):
  1388. warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
  1389. tmp = grey_dilation(input, size, footprint, structure, None, mode,
  1390. cval, origin)
  1391. return grey_erosion(tmp, size, footprint, structure, output, mode,
  1392. cval, origin)
  1393. def morphological_gradient(input, size=None, footprint=None, structure=None,
  1394. output=None, mode="reflect", cval=0.0, origin=0):
  1395. """
  1396. Multidimensional morphological gradient.
  1397. The morphological gradient is calculated as the difference between a
  1398. dilation and an erosion of the input with a given structuring element.
  1399. Parameters
  1400. ----------
  1401. input : array_like
  1402. Array over which to compute the morphlogical gradient.
  1403. size : tuple of ints
  1404. Shape of a flat and full structuring element used for the mathematical
  1405. morphology operations. Optional if `footprint` or `structure` is
  1406. provided. A larger `size` yields a more blurred gradient.
  1407. footprint : array of ints, optional
  1408. Positions of non-infinite elements of a flat structuring element
  1409. used for the morphology operations. Larger footprints
  1410. give a more blurred morphological gradient.
  1411. structure : array of ints, optional
  1412. Structuring element used for the morphology operations.
  1413. `structure` may be a non-flat structuring element.
  1414. output : array, optional
  1415. An array used for storing the output of the morphological gradient
  1416. may be provided.
  1417. mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
  1418. The `mode` parameter determines how the array borders are
  1419. handled, where `cval` is the value when mode is equal to
  1420. 'constant'. Default is 'reflect'
  1421. cval : scalar, optional
  1422. Value to fill past edges of input if `mode` is 'constant'. Default
  1423. is 0.0.
  1424. origin : scalar, optional
  1425. The `origin` parameter controls the placement of the filter.
  1426. Default 0
  1427. Returns
  1428. -------
  1429. morphological_gradient : ndarray
  1430. Morphological gradient of `input`.
  1431. See also
  1432. --------
  1433. grey_dilation, grey_erosion, gaussian_gradient_magnitude
  1434. Notes
  1435. -----
  1436. For a flat structuring element, the morphological gradient
  1437. computed at a given point corresponds to the maximal difference
  1438. between elements of the input among the elements covered by the
  1439. structuring element centered on the point.
  1440. References
  1441. ----------
  1442. .. [1] https://en.wikipedia.org/wiki/Mathematical_morphology
  1443. Examples
  1444. --------
  1445. >>> from scipy import ndimage
  1446. >>> import numpy as np
  1447. >>> a = np.zeros((7,7), dtype=int)
  1448. >>> a[2:5, 2:5] = 1
  1449. >>> ndimage.morphological_gradient(a, size=(3,3))
  1450. array([[0, 0, 0, 0, 0, 0, 0],
  1451. [0, 1, 1, 1, 1, 1, 0],
  1452. [0, 1, 1, 1, 1, 1, 0],
  1453. [0, 1, 1, 0, 1, 1, 0],
  1454. [0, 1, 1, 1, 1, 1, 0],
  1455. [0, 1, 1, 1, 1, 1, 0],
  1456. [0, 0, 0, 0, 0, 0, 0]])
  1457. >>> # The morphological gradient is computed as the difference
  1458. >>> # between a dilation and an erosion
  1459. >>> ndimage.grey_dilation(a, size=(3,3)) -\\
  1460. ... ndimage.grey_erosion(a, size=(3,3))
  1461. array([[0, 0, 0, 0, 0, 0, 0],
  1462. [0, 1, 1, 1, 1, 1, 0],
  1463. [0, 1, 1, 1, 1, 1, 0],
  1464. [0, 1, 1, 0, 1, 1, 0],
  1465. [0, 1, 1, 1, 1, 1, 0],
  1466. [0, 1, 1, 1, 1, 1, 0],
  1467. [0, 0, 0, 0, 0, 0, 0]])
  1468. >>> a = np.zeros((7,7), dtype=int)
  1469. >>> a[2:5, 2:5] = 1
  1470. >>> a[4,4] = 2; a[2,3] = 3
  1471. >>> a
  1472. array([[0, 0, 0, 0, 0, 0, 0],
  1473. [0, 0, 0, 0, 0, 0, 0],
  1474. [0, 0, 1, 3, 1, 0, 0],
  1475. [0, 0, 1, 1, 1, 0, 0],
  1476. [0, 0, 1, 1, 2, 0, 0],
  1477. [0, 0, 0, 0, 0, 0, 0],
  1478. [0, 0, 0, 0, 0, 0, 0]])
  1479. >>> ndimage.morphological_gradient(a, size=(3,3))
  1480. array([[0, 0, 0, 0, 0, 0, 0],
  1481. [0, 1, 3, 3, 3, 1, 0],
  1482. [0, 1, 3, 3, 3, 1, 0],
  1483. [0, 1, 3, 2, 3, 2, 0],
  1484. [0, 1, 1, 2, 2, 2, 0],
  1485. [0, 1, 1, 2, 2, 2, 0],
  1486. [0, 0, 0, 0, 0, 0, 0]])
  1487. """
  1488. tmp = grey_dilation(input, size, footprint, structure, None, mode,
  1489. cval, origin)
  1490. if isinstance(output, numpy.ndarray):
  1491. grey_erosion(input, size, footprint, structure, output, mode,
  1492. cval, origin)
  1493. return numpy.subtract(tmp, output, output)
  1494. else:
  1495. return (tmp - grey_erosion(input, size, footprint, structure,
  1496. None, mode, cval, origin))
  1497. def morphological_laplace(input, size=None, footprint=None,
  1498. structure=None, output=None,
  1499. mode="reflect", cval=0.0, origin=0):
  1500. """
  1501. Multidimensional morphological laplace.
  1502. Parameters
  1503. ----------
  1504. input : array_like
  1505. Input.
  1506. size : int or sequence of ints, optional
  1507. See `structure`.
  1508. footprint : bool or ndarray, optional
  1509. See `structure`.
  1510. structure : structure, optional
  1511. Either `size`, `footprint`, or the `structure` must be provided.
  1512. output : ndarray, optional
  1513. An output array can optionally be provided.
  1514. mode : {'reflect','constant','nearest','mirror', 'wrap'}, optional
  1515. The mode parameter determines how the array borders are handled.
  1516. For 'constant' mode, values beyond borders are set to be `cval`.
  1517. Default is 'reflect'.
  1518. cval : scalar, optional
  1519. Value to fill past edges of input if mode is 'constant'.
  1520. Default is 0.0
  1521. origin : origin, optional
  1522. The origin parameter controls the placement of the filter.
  1523. Returns
  1524. -------
  1525. morphological_laplace : ndarray
  1526. Output
  1527. """
  1528. tmp1 = grey_dilation(input, size, footprint, structure, None, mode,
  1529. cval, origin)
  1530. if isinstance(output, numpy.ndarray):
  1531. grey_erosion(input, size, footprint, structure, output, mode,
  1532. cval, origin)
  1533. numpy.add(tmp1, output, output)
  1534. numpy.subtract(output, input, output)
  1535. return numpy.subtract(output, input, output)
  1536. else:
  1537. tmp2 = grey_erosion(input, size, footprint, structure, None, mode,
  1538. cval, origin)
  1539. numpy.add(tmp1, tmp2, tmp2)
  1540. numpy.subtract(tmp2, input, tmp2)
  1541. numpy.subtract(tmp2, input, tmp2)
  1542. return tmp2
  1543. def white_tophat(input, size=None, footprint=None, structure=None,
  1544. output=None, mode="reflect", cval=0.0, origin=0):
  1545. """
  1546. Multidimensional white tophat filter.
  1547. Parameters
  1548. ----------
  1549. input : array_like
  1550. Input.
  1551. size : tuple of ints
  1552. Shape of a flat and full structuring element used for the filter.
  1553. Optional if `footprint` or `structure` is provided.
  1554. footprint : array of ints, optional
  1555. Positions of elements of a flat structuring element
  1556. used for the white tophat filter.
  1557. structure : array of ints, optional
  1558. Structuring element used for the filter. `structure`
  1559. may be a non-flat structuring element.
  1560. output : array, optional
  1561. An array used for storing the output of the filter may be provided.
  1562. mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
  1563. The `mode` parameter determines how the array borders are
  1564. handled, where `cval` is the value when mode is equal to
  1565. 'constant'. Default is 'reflect'
  1566. cval : scalar, optional
  1567. Value to fill past edges of input if `mode` is 'constant'.
  1568. Default is 0.0.
  1569. origin : scalar, optional
  1570. The `origin` parameter controls the placement of the filter.
  1571. Default is 0.
  1572. Returns
  1573. -------
  1574. output : ndarray
  1575. Result of the filter of `input` with `structure`.
  1576. Examples
  1577. --------
  1578. Subtract gray background from a bright peak.
  1579. >>> from scipy.ndimage import generate_binary_structure, white_tophat
  1580. >>> import numpy as np
  1581. >>> square = generate_binary_structure(rank=2, connectivity=3)
  1582. >>> bright_on_gray = np.array([[2, 3, 3, 3, 2],
  1583. ... [3, 4, 5, 4, 3],
  1584. ... [3, 5, 9, 5, 3],
  1585. ... [3, 4, 5, 4, 3],
  1586. ... [2, 3, 3, 3, 2]])
  1587. >>> white_tophat(input=bright_on_gray, structure=square)
  1588. array([[0, 0, 0, 0, 0],
  1589. [0, 0, 1, 0, 0],
  1590. [0, 1, 5, 1, 0],
  1591. [0, 0, 1, 0, 0],
  1592. [0, 0, 0, 0, 0]])
  1593. See also
  1594. --------
  1595. black_tophat
  1596. """
  1597. if (size is not None) and (footprint is not None):
  1598. warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
  1599. tmp = grey_erosion(input, size, footprint, structure, None, mode,
  1600. cval, origin)
  1601. tmp = grey_dilation(tmp, size, footprint, structure, output, mode,
  1602. cval, origin)
  1603. if tmp is None:
  1604. tmp = output
  1605. if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_:
  1606. numpy.bitwise_xor(input, tmp, out=tmp)
  1607. else:
  1608. numpy.subtract(input, tmp, out=tmp)
  1609. return tmp
  1610. def black_tophat(input, size=None, footprint=None,
  1611. structure=None, output=None, mode="reflect",
  1612. cval=0.0, origin=0):
  1613. """
  1614. Multidimensional black tophat filter.
  1615. Parameters
  1616. ----------
  1617. input : array_like
  1618. Input.
  1619. size : tuple of ints, optional
  1620. Shape of a flat and full structuring element used for the filter.
  1621. Optional if `footprint` or `structure` is provided.
  1622. footprint : array of ints, optional
  1623. Positions of non-infinite elements of a flat structuring element
  1624. used for the black tophat filter.
  1625. structure : array of ints, optional
  1626. Structuring element used for the filter. `structure`
  1627. may be a non-flat structuring element.
  1628. output : array, optional
  1629. An array used for storing the output of the filter may be provided.
  1630. mode : {'reflect', 'constant', 'nearest', 'mirror', 'wrap'}, optional
  1631. The `mode` parameter determines how the array borders are
  1632. handled, where `cval` is the value when mode is equal to
  1633. 'constant'. Default is 'reflect'
  1634. cval : scalar, optional
  1635. Value to fill past edges of input if `mode` is 'constant'. Default
  1636. is 0.0.
  1637. origin : scalar, optional
  1638. The `origin` parameter controls the placement of the filter.
  1639. Default 0
  1640. Returns
  1641. -------
  1642. black_tophat : ndarray
  1643. Result of the filter of `input` with `structure`.
  1644. Examples
  1645. --------
  1646. Change dark peak to bright peak and subtract background.
  1647. >>> from scipy.ndimage import generate_binary_structure, black_tophat
  1648. >>> import numpy as np
  1649. >>> square = generate_binary_structure(rank=2, connectivity=3)
  1650. >>> dark_on_gray = np.array([[7, 6, 6, 6, 7],
  1651. ... [6, 5, 4, 5, 6],
  1652. ... [6, 4, 0, 4, 6],
  1653. ... [6, 5, 4, 5, 6],
  1654. ... [7, 6, 6, 6, 7]])
  1655. >>> black_tophat(input=dark_on_gray, structure=square)
  1656. array([[0, 0, 0, 0, 0],
  1657. [0, 0, 1, 0, 0],
  1658. [0, 1, 5, 1, 0],
  1659. [0, 0, 1, 0, 0],
  1660. [0, 0, 0, 0, 0]])
  1661. See also
  1662. --------
  1663. white_tophat, grey_opening, grey_closing
  1664. """
  1665. if (size is not None) and (footprint is not None):
  1666. warnings.warn("ignoring size because footprint is set", UserWarning, stacklevel=2)
  1667. tmp = grey_dilation(input, size, footprint, structure, None, mode,
  1668. cval, origin)
  1669. tmp = grey_erosion(tmp, size, footprint, structure, output, mode,
  1670. cval, origin)
  1671. if tmp is None:
  1672. tmp = output
  1673. if input.dtype == numpy.bool_ and tmp.dtype == numpy.bool_:
  1674. numpy.bitwise_xor(tmp, input, out=tmp)
  1675. else:
  1676. numpy.subtract(tmp, input, out=tmp)
  1677. return tmp
  1678. def distance_transform_bf(input, metric="euclidean", sampling=None,
  1679. return_distances=True, return_indices=False,
  1680. distances=None, indices=None):
  1681. """
  1682. Distance transform function by a brute force algorithm.
  1683. This function calculates the distance transform of the `input`, by
  1684. replacing each foreground (non-zero) element, with its
  1685. shortest distance to the background (any zero-valued element).
  1686. In addition to the distance transform, the feature transform can
  1687. be calculated. In this case the index of the closest background
  1688. element to each foreground element is returned in a separate array.
  1689. Parameters
  1690. ----------
  1691. input : array_like
  1692. Input
  1693. metric : {'euclidean', 'taxicab', 'chessboard'}, optional
  1694. 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'.
  1695. The default is 'euclidean'.
  1696. sampling : float, or sequence of float, optional
  1697. This parameter is only used when `metric` is 'euclidean'.
  1698. Spacing of elements along each dimension. If a sequence, must be of
  1699. length equal to the input rank; if a single number, this is used for
  1700. all axes. If not specified, a grid spacing of unity is implied.
  1701. return_distances : bool, optional
  1702. Whether to calculate the distance transform.
  1703. Default is True.
  1704. return_indices : bool, optional
  1705. Whether to calculate the feature transform.
  1706. Default is False.
  1707. distances : ndarray, optional
  1708. An output array to store the calculated distance transform, instead of
  1709. returning it.
  1710. `return_distances` must be True.
  1711. It must be the same shape as `input`, and of type float64 if `metric`
  1712. is 'euclidean', uint32 otherwise.
  1713. indices : int32 ndarray, optional
  1714. An output array to store the calculated feature transform, instead of
  1715. returning it.
  1716. `return_indicies` must be True.
  1717. Its shape must be `(input.ndim,) + input.shape`.
  1718. Returns
  1719. -------
  1720. distances : ndarray, optional
  1721. The calculated distance transform. Returned only when
  1722. `return_distances` is True and `distances` is not supplied.
  1723. It will have the same shape as the input array.
  1724. indices : int32 ndarray, optional
  1725. The calculated feature transform. It has an input-shaped array for each
  1726. dimension of the input. See distance_transform_edt documentation for an
  1727. example.
  1728. Returned only when `return_indices` is True and `indices` is not
  1729. supplied.
  1730. Notes
  1731. -----
  1732. This function employs a slow brute force algorithm, see also the
  1733. function distance_transform_cdt for more efficient taxicab and
  1734. chessboard algorithms.
  1735. """
  1736. ft_inplace = isinstance(indices, numpy.ndarray)
  1737. dt_inplace = isinstance(distances, numpy.ndarray)
  1738. _distance_tranform_arg_check(
  1739. dt_inplace, ft_inplace, return_distances, return_indices
  1740. )
  1741. tmp1 = numpy.asarray(input) != 0
  1742. struct = generate_binary_structure(tmp1.ndim, tmp1.ndim)
  1743. tmp2 = binary_dilation(tmp1, struct)
  1744. tmp2 = numpy.logical_xor(tmp1, tmp2)
  1745. tmp1 = tmp1.astype(numpy.int8) - tmp2.astype(numpy.int8)
  1746. metric = metric.lower()
  1747. if metric == 'euclidean':
  1748. metric = 1
  1749. elif metric in ['taxicab', 'cityblock', 'manhattan']:
  1750. metric = 2
  1751. elif metric == 'chessboard':
  1752. metric = 3
  1753. else:
  1754. raise RuntimeError('distance metric not supported')
  1755. if sampling is not None:
  1756. sampling = _ni_support._normalize_sequence(sampling, tmp1.ndim)
  1757. sampling = numpy.asarray(sampling, dtype=numpy.float64)
  1758. if not sampling.flags.contiguous:
  1759. sampling = sampling.copy()
  1760. if return_indices:
  1761. ft = numpy.zeros(tmp1.shape, dtype=numpy.int32)
  1762. else:
  1763. ft = None
  1764. if return_distances:
  1765. if distances is None:
  1766. if metric == 1:
  1767. dt = numpy.zeros(tmp1.shape, dtype=numpy.float64)
  1768. else:
  1769. dt = numpy.zeros(tmp1.shape, dtype=numpy.uint32)
  1770. else:
  1771. if distances.shape != tmp1.shape:
  1772. raise RuntimeError('distances array has wrong shape')
  1773. if metric == 1:
  1774. if distances.dtype.type != numpy.float64:
  1775. raise RuntimeError('distances array must be float64')
  1776. else:
  1777. if distances.dtype.type != numpy.uint32:
  1778. raise RuntimeError('distances array must be uint32')
  1779. dt = distances
  1780. else:
  1781. dt = None
  1782. _nd_image.distance_transform_bf(tmp1, metric, sampling, dt, ft)
  1783. if return_indices:
  1784. if isinstance(indices, numpy.ndarray):
  1785. if indices.dtype.type != numpy.int32:
  1786. raise RuntimeError('indices array must be int32')
  1787. if indices.shape != (tmp1.ndim,) + tmp1.shape:
  1788. raise RuntimeError('indices array has wrong shape')
  1789. tmp2 = indices
  1790. else:
  1791. tmp2 = numpy.indices(tmp1.shape, dtype=numpy.int32)
  1792. ft = numpy.ravel(ft)
  1793. for ii in range(tmp2.shape[0]):
  1794. rtmp = numpy.ravel(tmp2[ii, ...])[ft]
  1795. rtmp.shape = tmp1.shape
  1796. tmp2[ii, ...] = rtmp
  1797. ft = tmp2
  1798. # construct and return the result
  1799. result = []
  1800. if return_distances and not dt_inplace:
  1801. result.append(dt)
  1802. if return_indices and not ft_inplace:
  1803. result.append(ft)
  1804. if len(result) == 2:
  1805. return tuple(result)
  1806. elif len(result) == 1:
  1807. return result[0]
  1808. else:
  1809. return None
  1810. def distance_transform_cdt(input, metric='chessboard', return_distances=True,
  1811. return_indices=False, distances=None, indices=None):
  1812. """
  1813. Distance transform for chamfer type of transforms.
  1814. In addition to the distance transform, the feature transform can
  1815. be calculated. In this case the index of the closest background
  1816. element to each foreground element is returned in a separate array.
  1817. Parameters
  1818. ----------
  1819. input : array_like
  1820. Input
  1821. metric : {'chessboard', 'taxicab'} or array_like, optional
  1822. The `metric` determines the type of chamfering that is done. If the
  1823. `metric` is equal to 'taxicab' a structure is generated using
  1824. generate_binary_structure with a squared distance equal to 1. If
  1825. the `metric` is equal to 'chessboard', a `metric` is generated
  1826. using generate_binary_structure with a squared distance equal to
  1827. the dimensionality of the array. These choices correspond to the
  1828. common interpretations of the 'taxicab' and the 'chessboard'
  1829. distance metrics in two dimensions.
  1830. A custom metric may be provided, in the form of a matrix where
  1831. each dimension has a length of three.
  1832. 'cityblock' and 'manhattan' are also valid, and map to 'taxicab'.
  1833. The default is 'chessboard'.
  1834. return_distances : bool, optional
  1835. Whether to calculate the distance transform.
  1836. Default is True.
  1837. return_indices : bool, optional
  1838. Whether to calculate the feature transform.
  1839. Default is False.
  1840. distances : int32 ndarray, optional
  1841. An output array to store the calculated distance transform, instead of
  1842. returning it.
  1843. `return_distances` must be True.
  1844. It must be the same shape as `input`.
  1845. indices : int32 ndarray, optional
  1846. An output array to store the calculated feature transform, instead of
  1847. returning it.
  1848. `return_indicies` must be True.
  1849. Its shape must be `(input.ndim,) + input.shape`.
  1850. Returns
  1851. -------
  1852. distances : int32 ndarray, optional
  1853. The calculated distance transform. Returned only when
  1854. `return_distances` is True, and `distances` is not supplied.
  1855. It will have the same shape as the input array.
  1856. indices : int32 ndarray, optional
  1857. The calculated feature transform. It has an input-shaped array for each
  1858. dimension of the input. See distance_transform_edt documentation for an
  1859. example.
  1860. Returned only when `return_indices` is True, and `indices` is not
  1861. supplied.
  1862. """
  1863. ft_inplace = isinstance(indices, numpy.ndarray)
  1864. dt_inplace = isinstance(distances, numpy.ndarray)
  1865. _distance_tranform_arg_check(
  1866. dt_inplace, ft_inplace, return_distances, return_indices
  1867. )
  1868. input = numpy.asarray(input)
  1869. if metric in ['taxicab', 'cityblock', 'manhattan']:
  1870. rank = input.ndim
  1871. metric = generate_binary_structure(rank, 1)
  1872. elif metric == 'chessboard':
  1873. rank = input.ndim
  1874. metric = generate_binary_structure(rank, rank)
  1875. else:
  1876. try:
  1877. metric = numpy.asarray(metric)
  1878. except Exception as e:
  1879. raise RuntimeError('invalid metric provided') from e
  1880. for s in metric.shape:
  1881. if s != 3:
  1882. raise RuntimeError('metric sizes must be equal to 3')
  1883. if not metric.flags.contiguous:
  1884. metric = metric.copy()
  1885. if dt_inplace:
  1886. if distances.dtype.type != numpy.int32:
  1887. raise RuntimeError('distances must be of int32 type')
  1888. if distances.shape != input.shape:
  1889. raise RuntimeError('distances has wrong shape')
  1890. dt = distances
  1891. dt[...] = numpy.where(input, -1, 0).astype(numpy.int32)
  1892. else:
  1893. dt = numpy.where(input, -1, 0).astype(numpy.int32)
  1894. rank = dt.ndim
  1895. if return_indices:
  1896. sz = numpy.prod(dt.shape, axis=0)
  1897. ft = numpy.arange(sz, dtype=numpy.int32)
  1898. ft.shape = dt.shape
  1899. else:
  1900. ft = None
  1901. _nd_image.distance_transform_op(metric, dt, ft)
  1902. dt = dt[tuple([slice(None, None, -1)] * rank)]
  1903. if return_indices:
  1904. ft = ft[tuple([slice(None, None, -1)] * rank)]
  1905. _nd_image.distance_transform_op(metric, dt, ft)
  1906. dt = dt[tuple([slice(None, None, -1)] * rank)]
  1907. if return_indices:
  1908. ft = ft[tuple([slice(None, None, -1)] * rank)]
  1909. ft = numpy.ravel(ft)
  1910. if ft_inplace:
  1911. if indices.dtype.type != numpy.int32:
  1912. raise RuntimeError('indices array must be int32')
  1913. if indices.shape != (dt.ndim,) + dt.shape:
  1914. raise RuntimeError('indices array has wrong shape')
  1915. tmp = indices
  1916. else:
  1917. tmp = numpy.indices(dt.shape, dtype=numpy.int32)
  1918. for ii in range(tmp.shape[0]):
  1919. rtmp = numpy.ravel(tmp[ii, ...])[ft]
  1920. rtmp.shape = dt.shape
  1921. tmp[ii, ...] = rtmp
  1922. ft = tmp
  1923. # construct and return the result
  1924. result = []
  1925. if return_distances and not dt_inplace:
  1926. result.append(dt)
  1927. if return_indices and not ft_inplace:
  1928. result.append(ft)
  1929. if len(result) == 2:
  1930. return tuple(result)
  1931. elif len(result) == 1:
  1932. return result[0]
  1933. else:
  1934. return None
  1935. def distance_transform_edt(input, sampling=None, return_distances=True,
  1936. return_indices=False, distances=None, indices=None):
  1937. """
  1938. Exact Euclidean distance transform.
  1939. In addition to the distance transform, the feature transform can
  1940. be calculated. In this case the index of the closest background
  1941. element to each foreground element is returned in a separate array.
  1942. Parameters
  1943. ----------
  1944. input : array_like
  1945. Input data to transform. Can be any type but will be converted
  1946. into binary: 1 wherever input equates to True, 0 elsewhere.
  1947. sampling : float, or sequence of float, optional
  1948. Spacing of elements along each dimension. If a sequence, must be of
  1949. length equal to the input rank; if a single number, this is used for
  1950. all axes. If not specified, a grid spacing of unity is implied.
  1951. return_distances : bool, optional
  1952. Whether to calculate the distance transform.
  1953. Default is True.
  1954. return_indices : bool, optional
  1955. Whether to calculate the feature transform.
  1956. Default is False.
  1957. distances : float64 ndarray, optional
  1958. An output array to store the calculated distance transform, instead of
  1959. returning it.
  1960. `return_distances` must be True.
  1961. It must be the same shape as `input`.
  1962. indices : int32 ndarray, optional
  1963. An output array to store the calculated feature transform, instead of
  1964. returning it.
  1965. `return_indicies` must be True.
  1966. Its shape must be `(input.ndim,) + input.shape`.
  1967. Returns
  1968. -------
  1969. distances : float64 ndarray, optional
  1970. The calculated distance transform. Returned only when
  1971. `return_distances` is True and `distances` is not supplied.
  1972. It will have the same shape as the input array.
  1973. indices : int32 ndarray, optional
  1974. The calculated feature transform. It has an input-shaped array for each
  1975. dimension of the input. See example below.
  1976. Returned only when `return_indices` is True and `indices` is not
  1977. supplied.
  1978. Notes
  1979. -----
  1980. The Euclidean distance transform gives values of the Euclidean
  1981. distance::
  1982. n
  1983. y_i = sqrt(sum (x[i]-b[i])**2)
  1984. i
  1985. where b[i] is the background point (value 0) with the smallest
  1986. Euclidean distance to input points x[i], and n is the
  1987. number of dimensions.
  1988. Examples
  1989. --------
  1990. >>> from scipy import ndimage
  1991. >>> import numpy as np
  1992. >>> a = np.array(([0,1,1,1,1],
  1993. ... [0,0,1,1,1],
  1994. ... [0,1,1,1,1],
  1995. ... [0,1,1,1,0],
  1996. ... [0,1,1,0,0]))
  1997. >>> ndimage.distance_transform_edt(a)
  1998. array([[ 0. , 1. , 1.4142, 2.2361, 3. ],
  1999. [ 0. , 0. , 1. , 2. , 2. ],
  2000. [ 0. , 1. , 1.4142, 1.4142, 1. ],
  2001. [ 0. , 1. , 1.4142, 1. , 0. ],
  2002. [ 0. , 1. , 1. , 0. , 0. ]])
  2003. With a sampling of 2 units along x, 1 along y:
  2004. >>> ndimage.distance_transform_edt(a, sampling=[2,1])
  2005. array([[ 0. , 1. , 2. , 2.8284, 3.6056],
  2006. [ 0. , 0. , 1. , 2. , 3. ],
  2007. [ 0. , 1. , 2. , 2.2361, 2. ],
  2008. [ 0. , 1. , 2. , 1. , 0. ],
  2009. [ 0. , 1. , 1. , 0. , 0. ]])
  2010. Asking for indices as well:
  2011. >>> edt, inds = ndimage.distance_transform_edt(a, return_indices=True)
  2012. >>> inds
  2013. array([[[0, 0, 1, 1, 3],
  2014. [1, 1, 1, 1, 3],
  2015. [2, 2, 1, 3, 3],
  2016. [3, 3, 4, 4, 3],
  2017. [4, 4, 4, 4, 4]],
  2018. [[0, 0, 1, 1, 4],
  2019. [0, 1, 1, 1, 4],
  2020. [0, 0, 1, 4, 4],
  2021. [0, 0, 3, 3, 4],
  2022. [0, 0, 3, 3, 4]]])
  2023. With arrays provided for inplace outputs:
  2024. >>> indices = np.zeros(((np.ndim(a),) + a.shape), dtype=np.int32)
  2025. >>> ndimage.distance_transform_edt(a, return_indices=True, indices=indices)
  2026. array([[ 0. , 1. , 1.4142, 2.2361, 3. ],
  2027. [ 0. , 0. , 1. , 2. , 2. ],
  2028. [ 0. , 1. , 1.4142, 1.4142, 1. ],
  2029. [ 0. , 1. , 1.4142, 1. , 0. ],
  2030. [ 0. , 1. , 1. , 0. , 0. ]])
  2031. >>> indices
  2032. array([[[0, 0, 1, 1, 3],
  2033. [1, 1, 1, 1, 3],
  2034. [2, 2, 1, 3, 3],
  2035. [3, 3, 4, 4, 3],
  2036. [4, 4, 4, 4, 4]],
  2037. [[0, 0, 1, 1, 4],
  2038. [0, 1, 1, 1, 4],
  2039. [0, 0, 1, 4, 4],
  2040. [0, 0, 3, 3, 4],
  2041. [0, 0, 3, 3, 4]]])
  2042. """
  2043. ft_inplace = isinstance(indices, numpy.ndarray)
  2044. dt_inplace = isinstance(distances, numpy.ndarray)
  2045. _distance_tranform_arg_check(
  2046. dt_inplace, ft_inplace, return_distances, return_indices
  2047. )
  2048. # calculate the feature transform
  2049. input = numpy.atleast_1d(numpy.where(input, 1, 0).astype(numpy.int8))
  2050. if sampling is not None:
  2051. sampling = _ni_support._normalize_sequence(sampling, input.ndim)
  2052. sampling = numpy.asarray(sampling, dtype=numpy.float64)
  2053. if not sampling.flags.contiguous:
  2054. sampling = sampling.copy()
  2055. if ft_inplace:
  2056. ft = indices
  2057. if ft.shape != (input.ndim,) + input.shape:
  2058. raise RuntimeError('indices array has wrong shape')
  2059. if ft.dtype.type != numpy.int32:
  2060. raise RuntimeError('indices array must be int32')
  2061. else:
  2062. ft = numpy.zeros((input.ndim,) + input.shape, dtype=numpy.int32)
  2063. _nd_image.euclidean_feature_transform(input, sampling, ft)
  2064. # if requested, calculate the distance transform
  2065. if return_distances:
  2066. dt = ft - numpy.indices(input.shape, dtype=ft.dtype)
  2067. dt = dt.astype(numpy.float64)
  2068. if sampling is not None:
  2069. for ii in range(len(sampling)):
  2070. dt[ii, ...] *= sampling[ii]
  2071. numpy.multiply(dt, dt, dt)
  2072. if dt_inplace:
  2073. dt = numpy.add.reduce(dt, axis=0)
  2074. if distances.shape != dt.shape:
  2075. raise RuntimeError('distances array has wrong shape')
  2076. if distances.dtype.type != numpy.float64:
  2077. raise RuntimeError('distances array must be float64')
  2078. numpy.sqrt(dt, distances)
  2079. else:
  2080. dt = numpy.add.reduce(dt, axis=0)
  2081. dt = numpy.sqrt(dt)
  2082. # construct and return the result
  2083. result = []
  2084. if return_distances and not dt_inplace:
  2085. result.append(dt)
  2086. if return_indices and not ft_inplace:
  2087. result.append(ft)
  2088. if len(result) == 2:
  2089. return tuple(result)
  2090. elif len(result) == 1:
  2091. return result[0]
  2092. else:
  2093. return None
  2094. def _distance_tranform_arg_check(distances_out, indices_out,
  2095. return_distances, return_indices):
  2096. """Raise a RuntimeError if the arguments are invalid"""
  2097. error_msgs = []
  2098. if (not return_distances) and (not return_indices):
  2099. error_msgs.append(
  2100. 'at least one of return_distances/return_indices must be True')
  2101. if distances_out and not return_distances:
  2102. error_msgs.append(
  2103. 'return_distances must be True if distances is supplied'
  2104. )
  2105. if indices_out and not return_indices:
  2106. error_msgs.append('return_indices must be True if indices is supplied')
  2107. if error_msgs:
  2108. raise RuntimeError(', '.join(error_msgs))