_measurements.py 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  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 numpy
  31. import numpy as np
  32. from . import _ni_support
  33. from . import _ni_label
  34. from . import _nd_image
  35. from . import _morphology
  36. __all__ = ['label', 'find_objects', 'labeled_comprehension', 'sum', 'mean',
  37. 'variance', 'standard_deviation', 'minimum', 'maximum', 'median',
  38. 'minimum_position', 'maximum_position', 'extrema', 'center_of_mass',
  39. 'histogram', 'watershed_ift', 'sum_labels', 'value_indices']
  40. def label(input, structure=None, output=None):
  41. """
  42. Label features in an array.
  43. Parameters
  44. ----------
  45. input : array_like
  46. An array-like object to be labeled. Any non-zero values in `input` are
  47. counted as features and zero values are considered the background.
  48. structure : array_like, optional
  49. A structuring element that defines feature connections.
  50. `structure` must be centrosymmetric
  51. (see Notes).
  52. If no structuring element is provided,
  53. one is automatically generated with a squared connectivity equal to
  54. one. That is, for a 2-D `input` array, the default structuring element
  55. is::
  56. [[0,1,0],
  57. [1,1,1],
  58. [0,1,0]]
  59. output : (None, data-type, array_like), optional
  60. If `output` is a data type, it specifies the type of the resulting
  61. labeled feature array.
  62. If `output` is an array-like object, then `output` will be updated
  63. with the labeled features from this function. This function can
  64. operate in-place, by passing output=input.
  65. Note that the output must be able to store the largest label, or this
  66. function will raise an Exception.
  67. Returns
  68. -------
  69. label : ndarray or int
  70. An integer ndarray where each unique feature in `input` has a unique
  71. label in the returned array.
  72. num_features : int
  73. How many objects were found.
  74. If `output` is None, this function returns a tuple of
  75. (`labeled_array`, `num_features`).
  76. If `output` is a ndarray, then it will be updated with values in
  77. `labeled_array` and only `num_features` will be returned by this
  78. function.
  79. See Also
  80. --------
  81. find_objects : generate a list of slices for the labeled features (or
  82. objects); useful for finding features' position or
  83. dimensions
  84. Notes
  85. -----
  86. A centrosymmetric matrix is a matrix that is symmetric about the center.
  87. See [1]_ for more information.
  88. The `structure` matrix must be centrosymmetric to ensure
  89. two-way connections.
  90. For instance, if the `structure` matrix is not centrosymmetric
  91. and is defined as::
  92. [[0,1,0],
  93. [1,1,0],
  94. [0,0,0]]
  95. and the `input` is::
  96. [[1,2],
  97. [0,3]]
  98. then the structure matrix would indicate the
  99. entry 2 in the input is connected to 1,
  100. but 1 is not connected to 2.
  101. Examples
  102. --------
  103. Create an image with some features, then label it using the default
  104. (cross-shaped) structuring element:
  105. >>> from scipy.ndimage import label, generate_binary_structure
  106. >>> import numpy as np
  107. >>> a = np.array([[0,0,1,1,0,0],
  108. ... [0,0,0,1,0,0],
  109. ... [1,1,0,0,1,0],
  110. ... [0,0,0,1,0,0]])
  111. >>> labeled_array, num_features = label(a)
  112. Each of the 4 features are labeled with a different integer:
  113. >>> num_features
  114. 4
  115. >>> labeled_array
  116. array([[0, 0, 1, 1, 0, 0],
  117. [0, 0, 0, 1, 0, 0],
  118. [2, 2, 0, 0, 3, 0],
  119. [0, 0, 0, 4, 0, 0]])
  120. Generate a structuring element that will consider features connected even
  121. if they touch diagonally:
  122. >>> s = generate_binary_structure(2,2)
  123. or,
  124. >>> s = [[1,1,1],
  125. ... [1,1,1],
  126. ... [1,1,1]]
  127. Label the image using the new structuring element:
  128. >>> labeled_array, num_features = label(a, structure=s)
  129. Show the 2 labeled features (note that features 1, 3, and 4 from above are
  130. now considered a single feature):
  131. >>> num_features
  132. 2
  133. >>> labeled_array
  134. array([[0, 0, 1, 1, 0, 0],
  135. [0, 0, 0, 1, 0, 0],
  136. [2, 2, 0, 0, 1, 0],
  137. [0, 0, 0, 1, 0, 0]])
  138. References
  139. ----------
  140. .. [1] James R. Weaver, "Centrosymmetric (cross-symmetric)
  141. matrices, their basic properties, eigenvalues, and
  142. eigenvectors." The American Mathematical Monthly 92.10
  143. (1985): 711-717.
  144. """
  145. input = numpy.asarray(input)
  146. if numpy.iscomplexobj(input):
  147. raise TypeError('Complex type not supported')
  148. if structure is None:
  149. structure = _morphology.generate_binary_structure(input.ndim, 1)
  150. structure = numpy.asarray(structure, dtype=bool)
  151. if structure.ndim != input.ndim:
  152. raise RuntimeError('structure and input must have equal rank')
  153. for ii in structure.shape:
  154. if ii != 3:
  155. raise ValueError('structure dimensions must be equal to 3')
  156. # Use 32 bits if it's large enough for this image.
  157. # _ni_label.label() needs two entries for background and
  158. # foreground tracking
  159. need_64bits = input.size >= (2**31 - 2)
  160. if isinstance(output, numpy.ndarray):
  161. if output.shape != input.shape:
  162. raise ValueError("output shape not correct")
  163. caller_provided_output = True
  164. else:
  165. caller_provided_output = False
  166. if output is None:
  167. output = np.empty(input.shape, np.intp if need_64bits else np.int32)
  168. else:
  169. output = np.empty(input.shape, output)
  170. # handle scalars, 0-D arrays
  171. if input.ndim == 0 or input.size == 0:
  172. if input.ndim == 0:
  173. # scalar
  174. maxlabel = 1 if (input != 0) else 0
  175. output[...] = maxlabel
  176. else:
  177. # 0-D
  178. maxlabel = 0
  179. if caller_provided_output:
  180. return maxlabel
  181. else:
  182. return output, maxlabel
  183. try:
  184. max_label = _ni_label._label(input, structure, output)
  185. except _ni_label.NeedMoreBits as e:
  186. # Make another attempt with enough bits, then try to cast to the
  187. # new type.
  188. tmp_output = np.empty(input.shape, np.intp if need_64bits else np.int32)
  189. max_label = _ni_label._label(input, structure, tmp_output)
  190. output[...] = tmp_output[...]
  191. if not np.all(output == tmp_output):
  192. # refuse to return bad results
  193. raise RuntimeError(
  194. "insufficient bit-depth in requested output type"
  195. ) from e
  196. if caller_provided_output:
  197. # result was written in-place
  198. return max_label
  199. else:
  200. return output, max_label
  201. def find_objects(input, max_label=0):
  202. """
  203. Find objects in a labeled array.
  204. Parameters
  205. ----------
  206. input : ndarray of ints
  207. Array containing objects defined by different labels. Labels with
  208. value 0 are ignored.
  209. max_label : int, optional
  210. Maximum label to be searched for in `input`. If max_label is not
  211. given, the positions of all objects are returned.
  212. Returns
  213. -------
  214. object_slices : list of tuples
  215. A list of tuples, with each tuple containing N slices (with N the
  216. dimension of the input array). Slices correspond to the minimal
  217. parallelepiped that contains the object. If a number is missing,
  218. None is returned instead of a slice.
  219. See Also
  220. --------
  221. label, center_of_mass
  222. Notes
  223. -----
  224. This function is very useful for isolating a volume of interest inside
  225. a 3-D array, that cannot be "seen through".
  226. Examples
  227. --------
  228. >>> from scipy import ndimage
  229. >>> import numpy as np
  230. >>> a = np.zeros((6,6), dtype=int)
  231. >>> a[2:4, 2:4] = 1
  232. >>> a[4, 4] = 1
  233. >>> a[:2, :3] = 2
  234. >>> a[0, 5] = 3
  235. >>> a
  236. array([[2, 2, 2, 0, 0, 3],
  237. [2, 2, 2, 0, 0, 0],
  238. [0, 0, 1, 1, 0, 0],
  239. [0, 0, 1, 1, 0, 0],
  240. [0, 0, 0, 0, 1, 0],
  241. [0, 0, 0, 0, 0, 0]])
  242. >>> ndimage.find_objects(a)
  243. [(slice(2, 5, None), slice(2, 5, None)), (slice(0, 2, None), slice(0, 3, None)), (slice(0, 1, None), slice(5, 6, None))]
  244. >>> ndimage.find_objects(a, max_label=2)
  245. [(slice(2, 5, None), slice(2, 5, None)), (slice(0, 2, None), slice(0, 3, None))]
  246. >>> ndimage.find_objects(a == 1, max_label=2)
  247. [(slice(2, 5, None), slice(2, 5, None)), None]
  248. >>> loc = ndimage.find_objects(a)[0]
  249. >>> a[loc]
  250. array([[1, 1, 0],
  251. [1, 1, 0],
  252. [0, 0, 1]])
  253. """
  254. input = numpy.asarray(input)
  255. if numpy.iscomplexobj(input):
  256. raise TypeError('Complex type not supported')
  257. if max_label < 1:
  258. max_label = input.max()
  259. return _nd_image.find_objects(input, max_label)
  260. def value_indices(arr, *, ignore_value=None):
  261. """
  262. Find indices of each distinct value in given array.
  263. Parameters
  264. ----------
  265. arr : ndarray of ints
  266. Array containing integer values.
  267. ignore_value : int, optional
  268. This value will be ignored in searching the `arr` array. If not
  269. given, all values found will be included in output. Default
  270. is None.
  271. Returns
  272. -------
  273. indices : dictionary
  274. A Python dictionary of array indices for each distinct value. The
  275. dictionary is keyed by the distinct values, the entries are array
  276. index tuples covering all occurrences of the value within the
  277. array.
  278. This dictionary can occupy significant memory, usually several times
  279. the size of the input array.
  280. Notes
  281. -----
  282. For a small array with few distinct values, one might use
  283. `numpy.unique()` to find all possible values, and ``(arr == val)`` to
  284. locate each value within that array. However, for large arrays,
  285. with many distinct values, this can become extremely inefficient,
  286. as locating each value would require a new search through the entire
  287. array. Using this function, there is essentially one search, with
  288. the indices saved for all distinct values.
  289. This is useful when matching a categorical image (e.g. a segmentation
  290. or classification) to an associated image of other data, allowing
  291. any per-class statistic(s) to then be calculated. Provides a
  292. more flexible alternative to functions like ``scipy.ndimage.mean()``
  293. and ``scipy.ndimage.variance()``.
  294. Some other closely related functionality, with different strengths and
  295. weaknesses, can also be found in ``scipy.stats.binned_statistic()`` and
  296. the `scikit-image <https://scikit-image.org/>`_ function
  297. ``skimage.measure.regionprops()``.
  298. Note for IDL users: this provides functionality equivalent to IDL's
  299. REVERSE_INDICES option (as per the IDL documentation for the
  300. `HISTOGRAM <https://www.l3harrisgeospatial.com/docs/histogram.html>`_
  301. function).
  302. .. versionadded:: 1.10.0
  303. See Also
  304. --------
  305. label, maximum, median, minimum_position, extrema, sum, mean, variance,
  306. standard_deviation, numpy.where, numpy.unique
  307. Examples
  308. --------
  309. >>> import numpy as np
  310. >>> from scipy import ndimage
  311. >>> a = np.zeros((6, 6), dtype=int)
  312. >>> a[2:4, 2:4] = 1
  313. >>> a[4, 4] = 1
  314. >>> a[:2, :3] = 2
  315. >>> a[0, 5] = 3
  316. >>> a
  317. array([[2, 2, 2, 0, 0, 3],
  318. [2, 2, 2, 0, 0, 0],
  319. [0, 0, 1, 1, 0, 0],
  320. [0, 0, 1, 1, 0, 0],
  321. [0, 0, 0, 0, 1, 0],
  322. [0, 0, 0, 0, 0, 0]])
  323. >>> val_indices = ndimage.value_indices(a)
  324. The dictionary `val_indices` will have an entry for each distinct
  325. value in the input array.
  326. >>> val_indices.keys()
  327. dict_keys([0, 1, 2, 3])
  328. The entry for each value is an index tuple, locating the elements
  329. with that value.
  330. >>> ndx1 = val_indices[1]
  331. >>> ndx1
  332. (array([2, 2, 3, 3, 4]), array([2, 3, 2, 3, 4]))
  333. This can be used to index into the original array, or any other
  334. array with the same shape.
  335. >>> a[ndx1]
  336. array([1, 1, 1, 1, 1])
  337. If the zeros were to be ignored, then the resulting dictionary
  338. would no longer have an entry for zero.
  339. >>> val_indices = ndimage.value_indices(a, ignore_value=0)
  340. >>> val_indices.keys()
  341. dict_keys([1, 2, 3])
  342. """
  343. # Cope with ignore_value being None, without too much extra complexity
  344. # in the C code. If not None, the value is passed in as a numpy array
  345. # with the same dtype as arr.
  346. ignore_value_arr = numpy.zeros((1,), dtype=arr.dtype)
  347. ignoreIsNone = (ignore_value is None)
  348. if not ignoreIsNone:
  349. ignore_value_arr[0] = ignore_value_arr.dtype.type(ignore_value)
  350. val_indices = _nd_image.value_indices(arr, ignoreIsNone, ignore_value_arr)
  351. return val_indices
  352. def labeled_comprehension(input, labels, index, func, out_dtype, default, pass_positions=False):
  353. """
  354. Roughly equivalent to [func(input[labels == i]) for i in index].
  355. Sequentially applies an arbitrary function (that works on array_like input)
  356. to subsets of an N-D image array specified by `labels` and `index`.
  357. The option exists to provide the function with positional parameters as the
  358. second argument.
  359. Parameters
  360. ----------
  361. input : array_like
  362. Data from which to select `labels` to process.
  363. labels : array_like or None
  364. Labels to objects in `input`.
  365. If not None, array must be same shape as `input`.
  366. If None, `func` is applied to raveled `input`.
  367. index : int, sequence of ints or None
  368. Subset of `labels` to which to apply `func`.
  369. If a scalar, a single value is returned.
  370. If None, `func` is applied to all non-zero values of `labels`.
  371. func : callable
  372. Python function to apply to `labels` from `input`.
  373. out_dtype : dtype
  374. Dtype to use for `result`.
  375. default : int, float or None
  376. Default return value when a element of `index` does not exist
  377. in `labels`.
  378. pass_positions : bool, optional
  379. If True, pass linear indices to `func` as a second argument.
  380. Default is False.
  381. Returns
  382. -------
  383. result : ndarray
  384. Result of applying `func` to each of `labels` to `input` in `index`.
  385. Examples
  386. --------
  387. >>> import numpy as np
  388. >>> a = np.array([[1, 2, 0, 0],
  389. ... [5, 3, 0, 4],
  390. ... [0, 0, 0, 7],
  391. ... [9, 3, 0, 0]])
  392. >>> from scipy import ndimage
  393. >>> lbl, nlbl = ndimage.label(a)
  394. >>> lbls = np.arange(1, nlbl+1)
  395. >>> ndimage.labeled_comprehension(a, lbl, lbls, np.mean, float, 0)
  396. array([ 2.75, 5.5 , 6. ])
  397. Falling back to `default`:
  398. >>> lbls = np.arange(1, nlbl+2)
  399. >>> ndimage.labeled_comprehension(a, lbl, lbls, np.mean, float, -1)
  400. array([ 2.75, 5.5 , 6. , -1. ])
  401. Passing positions:
  402. >>> def fn(val, pos):
  403. ... print("fn says: %s : %s" % (val, pos))
  404. ... return (val.sum()) if (pos.sum() % 2 == 0) else (-val.sum())
  405. ...
  406. >>> ndimage.labeled_comprehension(a, lbl, lbls, fn, float, 0, True)
  407. fn says: [1 2 5 3] : [0 1 4 5]
  408. fn says: [4 7] : [ 7 11]
  409. fn says: [9 3] : [12 13]
  410. array([ 11., 11., -12., 0.])
  411. """
  412. as_scalar = numpy.isscalar(index)
  413. input = numpy.asarray(input)
  414. if pass_positions:
  415. positions = numpy.arange(input.size).reshape(input.shape)
  416. if labels is None:
  417. if index is not None:
  418. raise ValueError("index without defined labels")
  419. if not pass_positions:
  420. return func(input.ravel())
  421. else:
  422. return func(input.ravel(), positions.ravel())
  423. try:
  424. input, labels = numpy.broadcast_arrays(input, labels)
  425. except ValueError as e:
  426. raise ValueError("input and labels must have the same shape "
  427. "(excepting dimensions with width 1)") from e
  428. if index is None:
  429. if not pass_positions:
  430. return func(input[labels > 0])
  431. else:
  432. return func(input[labels > 0], positions[labels > 0])
  433. index = numpy.atleast_1d(index)
  434. if np.any(index.astype(labels.dtype).astype(index.dtype) != index):
  435. raise ValueError("Cannot convert index values from <%s> to <%s> "
  436. "(labels' type) without loss of precision" %
  437. (index.dtype, labels.dtype))
  438. index = index.astype(labels.dtype)
  439. # optimization: find min/max in index, and select those parts of labels, input, and positions
  440. lo = index.min()
  441. hi = index.max()
  442. mask = (labels >= lo) & (labels <= hi)
  443. # this also ravels the arrays
  444. labels = labels[mask]
  445. input = input[mask]
  446. if pass_positions:
  447. positions = positions[mask]
  448. # sort everything by labels
  449. label_order = labels.argsort()
  450. labels = labels[label_order]
  451. input = input[label_order]
  452. if pass_positions:
  453. positions = positions[label_order]
  454. index_order = index.argsort()
  455. sorted_index = index[index_order]
  456. def do_map(inputs, output):
  457. """labels must be sorted"""
  458. nidx = sorted_index.size
  459. # Find boundaries for each stretch of constant labels
  460. # This could be faster, but we already paid N log N to sort labels.
  461. lo = numpy.searchsorted(labels, sorted_index, side='left')
  462. hi = numpy.searchsorted(labels, sorted_index, side='right')
  463. for i, l, h in zip(range(nidx), lo, hi):
  464. if l == h:
  465. continue
  466. output[i] = func(*[inp[l:h] for inp in inputs])
  467. temp = numpy.empty(index.shape, out_dtype)
  468. temp[:] = default
  469. if not pass_positions:
  470. do_map([input], temp)
  471. else:
  472. do_map([input, positions], temp)
  473. output = numpy.zeros(index.shape, out_dtype)
  474. output[index_order] = temp
  475. if as_scalar:
  476. output = output[0]
  477. return output
  478. def _safely_castable_to_int(dt):
  479. """Test whether the NumPy data type `dt` can be safely cast to an int."""
  480. int_size = np.dtype(int).itemsize
  481. safe = ((np.issubdtype(dt, np.signedinteger) and dt.itemsize <= int_size) or
  482. (np.issubdtype(dt, np.unsignedinteger) and dt.itemsize < int_size))
  483. return safe
  484. def _stats(input, labels=None, index=None, centered=False):
  485. """Count, sum, and optionally compute (sum - centre)^2 of input by label
  486. Parameters
  487. ----------
  488. input : array_like, N-D
  489. The input data to be analyzed.
  490. labels : array_like (N-D), optional
  491. The labels of the data in `input`. This array must be broadcast
  492. compatible with `input`; typically, it is the same shape as `input`.
  493. If `labels` is None, all nonzero values in `input` are treated as
  494. the single labeled group.
  495. index : label or sequence of labels, optional
  496. These are the labels of the groups for which the stats are computed.
  497. If `index` is None, the stats are computed for the single group where
  498. `labels` is greater than 0.
  499. centered : bool, optional
  500. If True, the centered sum of squares for each labeled group is
  501. also returned. Default is False.
  502. Returns
  503. -------
  504. counts : int or ndarray of ints
  505. The number of elements in each labeled group.
  506. sums : scalar or ndarray of scalars
  507. The sums of the values in each labeled group.
  508. sums_c : scalar or ndarray of scalars, optional
  509. The sums of mean-centered squares of the values in each labeled group.
  510. This is only returned if `centered` is True.
  511. """
  512. def single_group(vals):
  513. if centered:
  514. vals_c = vals - vals.mean()
  515. return vals.size, vals.sum(), (vals_c * vals_c.conjugate()).sum()
  516. else:
  517. return vals.size, vals.sum()
  518. if labels is None:
  519. return single_group(input)
  520. # ensure input and labels match sizes
  521. input, labels = numpy.broadcast_arrays(input, labels)
  522. if index is None:
  523. return single_group(input[labels > 0])
  524. if numpy.isscalar(index):
  525. return single_group(input[labels == index])
  526. def _sum_centered(labels):
  527. # `labels` is expected to be an ndarray with the same shape as `input`.
  528. # It must contain the label indices (which are not necessarily the labels
  529. # themselves).
  530. means = sums / counts
  531. centered_input = input - means[labels]
  532. # bincount expects 1-D inputs, so we ravel the arguments.
  533. bc = numpy.bincount(labels.ravel(),
  534. weights=(centered_input *
  535. centered_input.conjugate()).ravel())
  536. return bc
  537. # Remap labels to unique integers if necessary, or if the largest
  538. # label is larger than the number of values.
  539. if (not _safely_castable_to_int(labels.dtype) or
  540. labels.min() < 0 or labels.max() > labels.size):
  541. # Use numpy.unique to generate the label indices. `new_labels` will
  542. # be 1-D, but it should be interpreted as the flattened N-D array of
  543. # label indices.
  544. unique_labels, new_labels = numpy.unique(labels, return_inverse=True)
  545. counts = numpy.bincount(new_labels)
  546. sums = numpy.bincount(new_labels, weights=input.ravel())
  547. if centered:
  548. # Compute the sum of the mean-centered squares.
  549. # We must reshape new_labels to the N-D shape of `input` before
  550. # passing it _sum_centered.
  551. sums_c = _sum_centered(new_labels.reshape(labels.shape))
  552. idxs = numpy.searchsorted(unique_labels, index)
  553. # make all of idxs valid
  554. idxs[idxs >= unique_labels.size] = 0
  555. found = (unique_labels[idxs] == index)
  556. else:
  557. # labels are an integer type allowed by bincount, and there aren't too
  558. # many, so call bincount directly.
  559. counts = numpy.bincount(labels.ravel())
  560. sums = numpy.bincount(labels.ravel(), weights=input.ravel())
  561. if centered:
  562. sums_c = _sum_centered(labels)
  563. # make sure all index values are valid
  564. idxs = numpy.asanyarray(index, numpy.int_).copy()
  565. found = (idxs >= 0) & (idxs < counts.size)
  566. idxs[~found] = 0
  567. counts = counts[idxs]
  568. counts[~found] = 0
  569. sums = sums[idxs]
  570. sums[~found] = 0
  571. if not centered:
  572. return (counts, sums)
  573. else:
  574. sums_c = sums_c[idxs]
  575. sums_c[~found] = 0
  576. return (counts, sums, sums_c)
  577. def sum(input, labels=None, index=None):
  578. """
  579. Calculate the sum of the values of the array.
  580. Notes
  581. -----
  582. This is an alias for `ndimage.sum_labels` kept for backwards compatibility
  583. reasons, for new code please prefer `sum_labels`. See the `sum_labels`
  584. docstring for more details.
  585. """
  586. return sum_labels(input, labels, index)
  587. def sum_labels(input, labels=None, index=None):
  588. """
  589. Calculate the sum of the values of the array.
  590. Parameters
  591. ----------
  592. input : array_like
  593. Values of `input` inside the regions defined by `labels`
  594. are summed together.
  595. labels : array_like of ints, optional
  596. Assign labels to the values of the array. Has to have the same shape as
  597. `input`.
  598. index : array_like, optional
  599. A single label number or a sequence of label numbers of
  600. the objects to be measured.
  601. Returns
  602. -------
  603. sum : ndarray or scalar
  604. An array of the sums of values of `input` inside the regions defined
  605. by `labels` with the same shape as `index`. If 'index' is None or scalar,
  606. a scalar is returned.
  607. See Also
  608. --------
  609. mean, median
  610. Examples
  611. --------
  612. >>> from scipy import ndimage
  613. >>> input = [0,1,2,3]
  614. >>> labels = [1,1,2,2]
  615. >>> ndimage.sum_labels(input, labels, index=[1,2])
  616. [1.0, 5.0]
  617. >>> ndimage.sum_labels(input, labels, index=1)
  618. 1
  619. >>> ndimage.sum_labels(input, labels)
  620. 6
  621. """
  622. count, sum = _stats(input, labels, index)
  623. return sum
  624. def mean(input, labels=None, index=None):
  625. """
  626. Calculate the mean of the values of an array at labels.
  627. Parameters
  628. ----------
  629. input : array_like
  630. Array on which to compute the mean of elements over distinct
  631. regions.
  632. labels : array_like, optional
  633. Array of labels of same shape, or broadcastable to the same shape as
  634. `input`. All elements sharing the same label form one region over
  635. which the mean of the elements is computed.
  636. index : int or sequence of ints, optional
  637. Labels of the objects over which the mean is to be computed.
  638. Default is None, in which case the mean for all values where label is
  639. greater than 0 is calculated.
  640. Returns
  641. -------
  642. out : list
  643. Sequence of same length as `index`, with the mean of the different
  644. regions labeled by the labels in `index`.
  645. See Also
  646. --------
  647. variance, standard_deviation, minimum, maximum, sum, label
  648. Examples
  649. --------
  650. >>> from scipy import ndimage
  651. >>> import numpy as np
  652. >>> a = np.arange(25).reshape((5,5))
  653. >>> labels = np.zeros_like(a)
  654. >>> labels[3:5,3:5] = 1
  655. >>> index = np.unique(labels)
  656. >>> labels
  657. array([[0, 0, 0, 0, 0],
  658. [0, 0, 0, 0, 0],
  659. [0, 0, 0, 0, 0],
  660. [0, 0, 0, 1, 1],
  661. [0, 0, 0, 1, 1]])
  662. >>> index
  663. array([0, 1])
  664. >>> ndimage.mean(a, labels=labels, index=index)
  665. [10.285714285714286, 21.0]
  666. """
  667. count, sum = _stats(input, labels, index)
  668. return sum / numpy.asanyarray(count).astype(numpy.float64)
  669. def variance(input, labels=None, index=None):
  670. """
  671. Calculate the variance of the values of an N-D image array, optionally at
  672. specified sub-regions.
  673. Parameters
  674. ----------
  675. input : array_like
  676. Nd-image data to process.
  677. labels : array_like, optional
  678. Labels defining sub-regions in `input`.
  679. If not None, must be same shape as `input`.
  680. index : int or sequence of ints, optional
  681. `labels` to include in output. If None (default), all values where
  682. `labels` is non-zero are used.
  683. Returns
  684. -------
  685. variance : float or ndarray
  686. Values of variance, for each sub-region if `labels` and `index` are
  687. specified.
  688. See Also
  689. --------
  690. label, standard_deviation, maximum, minimum, extrema
  691. Examples
  692. --------
  693. >>> import numpy as np
  694. >>> a = np.array([[1, 2, 0, 0],
  695. ... [5, 3, 0, 4],
  696. ... [0, 0, 0, 7],
  697. ... [9, 3, 0, 0]])
  698. >>> from scipy import ndimage
  699. >>> ndimage.variance(a)
  700. 7.609375
  701. Features to process can be specified using `labels` and `index`:
  702. >>> lbl, nlbl = ndimage.label(a)
  703. >>> ndimage.variance(a, lbl, index=np.arange(1, nlbl+1))
  704. array([ 2.1875, 2.25 , 9. ])
  705. If no index is given, all non-zero `labels` are processed:
  706. >>> ndimage.variance(a, lbl)
  707. 6.1875
  708. """
  709. count, sum, sum_c_sq = _stats(input, labels, index, centered=True)
  710. return sum_c_sq / np.asanyarray(count).astype(float)
  711. def standard_deviation(input, labels=None, index=None):
  712. """
  713. Calculate the standard deviation of the values of an N-D image array,
  714. optionally at specified sub-regions.
  715. Parameters
  716. ----------
  717. input : array_like
  718. N-D image data to process.
  719. labels : array_like, optional
  720. Labels to identify sub-regions in `input`.
  721. If not None, must be same shape as `input`.
  722. index : int or sequence of ints, optional
  723. `labels` to include in output. If None (default), all values where
  724. `labels` is non-zero are used.
  725. Returns
  726. -------
  727. standard_deviation : float or ndarray
  728. Values of standard deviation, for each sub-region if `labels` and
  729. `index` are specified.
  730. See Also
  731. --------
  732. label, variance, maximum, minimum, extrema
  733. Examples
  734. --------
  735. >>> import numpy as np
  736. >>> a = np.array([[1, 2, 0, 0],
  737. ... [5, 3, 0, 4],
  738. ... [0, 0, 0, 7],
  739. ... [9, 3, 0, 0]])
  740. >>> from scipy import ndimage
  741. >>> ndimage.standard_deviation(a)
  742. 2.7585095613392387
  743. Features to process can be specified using `labels` and `index`:
  744. >>> lbl, nlbl = ndimage.label(a)
  745. >>> ndimage.standard_deviation(a, lbl, index=np.arange(1, nlbl+1))
  746. array([ 1.479, 1.5 , 3. ])
  747. If no index is given, non-zero `labels` are processed:
  748. >>> ndimage.standard_deviation(a, lbl)
  749. 2.4874685927665499
  750. """
  751. return numpy.sqrt(variance(input, labels, index))
  752. def _select(input, labels=None, index=None, find_min=False, find_max=False,
  753. find_min_positions=False, find_max_positions=False,
  754. find_median=False):
  755. """Returns min, max, or both, plus their positions (if requested), and
  756. median."""
  757. input = numpy.asanyarray(input)
  758. find_positions = find_min_positions or find_max_positions
  759. positions = None
  760. if find_positions:
  761. positions = numpy.arange(input.size).reshape(input.shape)
  762. def single_group(vals, positions):
  763. result = []
  764. if find_min:
  765. result += [vals.min()]
  766. if find_min_positions:
  767. result += [positions[vals == vals.min()][0]]
  768. if find_max:
  769. result += [vals.max()]
  770. if find_max_positions:
  771. result += [positions[vals == vals.max()][0]]
  772. if find_median:
  773. result += [numpy.median(vals)]
  774. return result
  775. if labels is None:
  776. return single_group(input, positions)
  777. # ensure input and labels match sizes
  778. input, labels = numpy.broadcast_arrays(input, labels)
  779. if index is None:
  780. mask = (labels > 0)
  781. masked_positions = None
  782. if find_positions:
  783. masked_positions = positions[mask]
  784. return single_group(input[mask], masked_positions)
  785. if numpy.isscalar(index):
  786. mask = (labels == index)
  787. masked_positions = None
  788. if find_positions:
  789. masked_positions = positions[mask]
  790. return single_group(input[mask], masked_positions)
  791. # remap labels to unique integers if necessary, or if the largest
  792. # label is larger than the number of values.
  793. if (not _safely_castable_to_int(labels.dtype) or
  794. labels.min() < 0 or labels.max() > labels.size):
  795. # remap labels, and indexes
  796. unique_labels, labels = numpy.unique(labels, return_inverse=True)
  797. idxs = numpy.searchsorted(unique_labels, index)
  798. # make all of idxs valid
  799. idxs[idxs >= unique_labels.size] = 0
  800. found = (unique_labels[idxs] == index)
  801. else:
  802. # labels are an integer type, and there aren't too many
  803. idxs = numpy.asanyarray(index, numpy.int_).copy()
  804. found = (idxs >= 0) & (idxs <= labels.max())
  805. idxs[~ found] = labels.max() + 1
  806. if find_median:
  807. order = numpy.lexsort((input.ravel(), labels.ravel()))
  808. else:
  809. order = input.ravel().argsort()
  810. input = input.ravel()[order]
  811. labels = labels.ravel()[order]
  812. if find_positions:
  813. positions = positions.ravel()[order]
  814. result = []
  815. if find_min:
  816. mins = numpy.zeros(labels.max() + 2, input.dtype)
  817. mins[labels[::-1]] = input[::-1]
  818. result += [mins[idxs]]
  819. if find_min_positions:
  820. minpos = numpy.zeros(labels.max() + 2, int)
  821. minpos[labels[::-1]] = positions[::-1]
  822. result += [minpos[idxs]]
  823. if find_max:
  824. maxs = numpy.zeros(labels.max() + 2, input.dtype)
  825. maxs[labels] = input
  826. result += [maxs[idxs]]
  827. if find_max_positions:
  828. maxpos = numpy.zeros(labels.max() + 2, int)
  829. maxpos[labels] = positions
  830. result += [maxpos[idxs]]
  831. if find_median:
  832. locs = numpy.arange(len(labels))
  833. lo = numpy.zeros(labels.max() + 2, numpy.int_)
  834. lo[labels[::-1]] = locs[::-1]
  835. hi = numpy.zeros(labels.max() + 2, numpy.int_)
  836. hi[labels] = locs
  837. lo = lo[idxs]
  838. hi = hi[idxs]
  839. # lo is an index to the lowest value in input for each label,
  840. # hi is an index to the largest value.
  841. # move them to be either the same ((hi - lo) % 2 == 0) or next
  842. # to each other ((hi - lo) % 2 == 1), then average.
  843. step = (hi - lo) // 2
  844. lo += step
  845. hi -= step
  846. if (np.issubdtype(input.dtype, np.integer)
  847. or np.issubdtype(input.dtype, np.bool_)):
  848. # avoid integer overflow or boolean addition (gh-12836)
  849. result += [(input[lo].astype('d') + input[hi].astype('d')) / 2.0]
  850. else:
  851. result += [(input[lo] + input[hi]) / 2.0]
  852. return result
  853. def minimum(input, labels=None, index=None):
  854. """
  855. Calculate the minimum of the values of an array over labeled regions.
  856. Parameters
  857. ----------
  858. input : array_like
  859. Array_like of values. For each region specified by `labels`, the
  860. minimal values of `input` over the region is computed.
  861. labels : array_like, optional
  862. An array_like of integers marking different regions over which the
  863. minimum value of `input` is to be computed. `labels` must have the
  864. same shape as `input`. If `labels` is not specified, the minimum
  865. over the whole array is returned.
  866. index : array_like, optional
  867. A list of region labels that are taken into account for computing the
  868. minima. If index is None, the minimum over all elements where `labels`
  869. is non-zero is returned.
  870. Returns
  871. -------
  872. minimum : float or list of floats
  873. List of minima of `input` over the regions determined by `labels` and
  874. whose index is in `index`. If `index` or `labels` are not specified, a
  875. float is returned: the minimal value of `input` if `labels` is None,
  876. and the minimal value of elements where `labels` is greater than zero
  877. if `index` is None.
  878. See Also
  879. --------
  880. label, maximum, median, minimum_position, extrema, sum, mean, variance,
  881. standard_deviation
  882. Notes
  883. -----
  884. The function returns a Python list and not a NumPy array, use
  885. `np.array` to convert the list to an array.
  886. Examples
  887. --------
  888. >>> from scipy import ndimage
  889. >>> import numpy as np
  890. >>> a = np.array([[1, 2, 0, 0],
  891. ... [5, 3, 0, 4],
  892. ... [0, 0, 0, 7],
  893. ... [9, 3, 0, 0]])
  894. >>> labels, labels_nb = ndimage.label(a)
  895. >>> labels
  896. array([[1, 1, 0, 0],
  897. [1, 1, 0, 2],
  898. [0, 0, 0, 2],
  899. [3, 3, 0, 0]])
  900. >>> ndimage.minimum(a, labels=labels, index=np.arange(1, labels_nb + 1))
  901. [1.0, 4.0, 3.0]
  902. >>> ndimage.minimum(a)
  903. 0.0
  904. >>> ndimage.minimum(a, labels=labels)
  905. 1.0
  906. """
  907. return _select(input, labels, index, find_min=True)[0]
  908. def maximum(input, labels=None, index=None):
  909. """
  910. Calculate the maximum of the values of an array over labeled regions.
  911. Parameters
  912. ----------
  913. input : array_like
  914. Array_like of values. For each region specified by `labels`, the
  915. maximal values of `input` over the region is computed.
  916. labels : array_like, optional
  917. An array of integers marking different regions over which the
  918. maximum value of `input` is to be computed. `labels` must have the
  919. same shape as `input`. If `labels` is not specified, the maximum
  920. over the whole array is returned.
  921. index : array_like, optional
  922. A list of region labels that are taken into account for computing the
  923. maxima. If index is None, the maximum over all elements where `labels`
  924. is non-zero is returned.
  925. Returns
  926. -------
  927. output : float or list of floats
  928. List of maxima of `input` over the regions determined by `labels` and
  929. whose index is in `index`. If `index` or `labels` are not specified, a
  930. float is returned: the maximal value of `input` if `labels` is None,
  931. and the maximal value of elements where `labels` is greater than zero
  932. if `index` is None.
  933. See Also
  934. --------
  935. label, minimum, median, maximum_position, extrema, sum, mean, variance,
  936. standard_deviation
  937. Notes
  938. -----
  939. The function returns a Python list and not a NumPy array, use
  940. `np.array` to convert the list to an array.
  941. Examples
  942. --------
  943. >>> import numpy as np
  944. >>> a = np.arange(16).reshape((4,4))
  945. >>> a
  946. array([[ 0, 1, 2, 3],
  947. [ 4, 5, 6, 7],
  948. [ 8, 9, 10, 11],
  949. [12, 13, 14, 15]])
  950. >>> labels = np.zeros_like(a)
  951. >>> labels[:2,:2] = 1
  952. >>> labels[2:, 1:3] = 2
  953. >>> labels
  954. array([[1, 1, 0, 0],
  955. [1, 1, 0, 0],
  956. [0, 2, 2, 0],
  957. [0, 2, 2, 0]])
  958. >>> from scipy import ndimage
  959. >>> ndimage.maximum(a)
  960. 15.0
  961. >>> ndimage.maximum(a, labels=labels, index=[1,2])
  962. [5.0, 14.0]
  963. >>> ndimage.maximum(a, labels=labels)
  964. 14.0
  965. >>> b = np.array([[1, 2, 0, 0],
  966. ... [5, 3, 0, 4],
  967. ... [0, 0, 0, 7],
  968. ... [9, 3, 0, 0]])
  969. >>> labels, labels_nb = ndimage.label(b)
  970. >>> labels
  971. array([[1, 1, 0, 0],
  972. [1, 1, 0, 2],
  973. [0, 0, 0, 2],
  974. [3, 3, 0, 0]])
  975. >>> ndimage.maximum(b, labels=labels, index=np.arange(1, labels_nb + 1))
  976. [5.0, 7.0, 9.0]
  977. """
  978. return _select(input, labels, index, find_max=True)[0]
  979. def median(input, labels=None, index=None):
  980. """
  981. Calculate the median of the values of an array over labeled regions.
  982. Parameters
  983. ----------
  984. input : array_like
  985. Array_like of values. For each region specified by `labels`, the
  986. median value of `input` over the region is computed.
  987. labels : array_like, optional
  988. An array_like of integers marking different regions over which the
  989. median value of `input` is to be computed. `labels` must have the
  990. same shape as `input`. If `labels` is not specified, the median
  991. over the whole array is returned.
  992. index : array_like, optional
  993. A list of region labels that are taken into account for computing the
  994. medians. If index is None, the median over all elements where `labels`
  995. is non-zero is returned.
  996. Returns
  997. -------
  998. median : float or list of floats
  999. List of medians of `input` over the regions determined by `labels` and
  1000. whose index is in `index`. If `index` or `labels` are not specified, a
  1001. float is returned: the median value of `input` if `labels` is None,
  1002. and the median value of elements where `labels` is greater than zero
  1003. if `index` is None.
  1004. See Also
  1005. --------
  1006. label, minimum, maximum, extrema, sum, mean, variance, standard_deviation
  1007. Notes
  1008. -----
  1009. The function returns a Python list and not a NumPy array, use
  1010. `np.array` to convert the list to an array.
  1011. Examples
  1012. --------
  1013. >>> from scipy import ndimage
  1014. >>> import numpy as np
  1015. >>> a = np.array([[1, 2, 0, 1],
  1016. ... [5, 3, 0, 4],
  1017. ... [0, 0, 0, 7],
  1018. ... [9, 3, 0, 0]])
  1019. >>> labels, labels_nb = ndimage.label(a)
  1020. >>> labels
  1021. array([[1, 1, 0, 2],
  1022. [1, 1, 0, 2],
  1023. [0, 0, 0, 2],
  1024. [3, 3, 0, 0]])
  1025. >>> ndimage.median(a, labels=labels, index=np.arange(1, labels_nb + 1))
  1026. [2.5, 4.0, 6.0]
  1027. >>> ndimage.median(a)
  1028. 1.0
  1029. >>> ndimage.median(a, labels=labels)
  1030. 3.0
  1031. """
  1032. return _select(input, labels, index, find_median=True)[0]
  1033. def minimum_position(input, labels=None, index=None):
  1034. """
  1035. Find the positions of the minimums of the values of an array at labels.
  1036. Parameters
  1037. ----------
  1038. input : array_like
  1039. Array_like of values.
  1040. labels : array_like, optional
  1041. An array of integers marking different regions over which the
  1042. position of the minimum value of `input` is to be computed.
  1043. `labels` must have the same shape as `input`. If `labels` is not
  1044. specified, the location of the first minimum over the whole
  1045. array is returned.
  1046. The `labels` argument only works when `index` is specified.
  1047. index : array_like, optional
  1048. A list of region labels that are taken into account for finding the
  1049. location of the minima. If `index` is None, the ``first`` minimum
  1050. over all elements where `labels` is non-zero is returned.
  1051. The `index` argument only works when `labels` is specified.
  1052. Returns
  1053. -------
  1054. output : list of tuples of ints
  1055. Tuple of ints or list of tuples of ints that specify the location
  1056. of minima of `input` over the regions determined by `labels` and
  1057. whose index is in `index`.
  1058. If `index` or `labels` are not specified, a tuple of ints is
  1059. returned specifying the location of the first minimal value of `input`.
  1060. See Also
  1061. --------
  1062. label, minimum, median, maximum_position, extrema, sum, mean, variance,
  1063. standard_deviation
  1064. Examples
  1065. --------
  1066. >>> import numpy as np
  1067. >>> a = np.array([[10, 20, 30],
  1068. ... [40, 80, 100],
  1069. ... [1, 100, 200]])
  1070. >>> b = np.array([[1, 2, 0, 1],
  1071. ... [5, 3, 0, 4],
  1072. ... [0, 0, 0, 7],
  1073. ... [9, 3, 0, 0]])
  1074. >>> from scipy import ndimage
  1075. >>> ndimage.minimum_position(a)
  1076. (2, 0)
  1077. >>> ndimage.minimum_position(b)
  1078. (0, 2)
  1079. Features to process can be specified using `labels` and `index`:
  1080. >>> label, pos = ndimage.label(a)
  1081. >>> ndimage.minimum_position(a, label, index=np.arange(1, pos+1))
  1082. [(2, 0)]
  1083. >>> label, pos = ndimage.label(b)
  1084. >>> ndimage.minimum_position(b, label, index=np.arange(1, pos+1))
  1085. [(0, 0), (0, 3), (3, 1)]
  1086. """
  1087. dims = numpy.array(numpy.asarray(input).shape)
  1088. # see numpy.unravel_index to understand this line.
  1089. dim_prod = numpy.cumprod([1] + list(dims[:0:-1]))[::-1]
  1090. result = _select(input, labels, index, find_min_positions=True)[0]
  1091. if numpy.isscalar(result):
  1092. return tuple((result // dim_prod) % dims)
  1093. return [tuple(v) for v in (result.reshape(-1, 1) // dim_prod) % dims]
  1094. def maximum_position(input, labels=None, index=None):
  1095. """
  1096. Find the positions of the maximums of the values of an array at labels.
  1097. For each region specified by `labels`, the position of the maximum
  1098. value of `input` within the region is returned.
  1099. Parameters
  1100. ----------
  1101. input : array_like
  1102. Array_like of values.
  1103. labels : array_like, optional
  1104. An array of integers marking different regions over which the
  1105. position of the maximum value of `input` is to be computed.
  1106. `labels` must have the same shape as `input`. If `labels` is not
  1107. specified, the location of the first maximum over the whole
  1108. array is returned.
  1109. The `labels` argument only works when `index` is specified.
  1110. index : array_like, optional
  1111. A list of region labels that are taken into account for finding the
  1112. location of the maxima. If `index` is None, the first maximum
  1113. over all elements where `labels` is non-zero is returned.
  1114. The `index` argument only works when `labels` is specified.
  1115. Returns
  1116. -------
  1117. output : list of tuples of ints
  1118. List of tuples of ints that specify the location of maxima of
  1119. `input` over the regions determined by `labels` and whose index
  1120. is in `index`.
  1121. If `index` or `labels` are not specified, a tuple of ints is
  1122. returned specifying the location of the ``first`` maximal value
  1123. of `input`.
  1124. See also
  1125. --------
  1126. label, minimum, median, maximum_position, extrema, sum, mean, variance,
  1127. standard_deviation
  1128. Examples
  1129. --------
  1130. >>> from scipy import ndimage
  1131. >>> import numpy as np
  1132. >>> a = np.array([[1, 2, 0, 0],
  1133. ... [5, 3, 0, 4],
  1134. ... [0, 0, 0, 7],
  1135. ... [9, 3, 0, 0]])
  1136. >>> ndimage.maximum_position(a)
  1137. (3, 0)
  1138. Features to process can be specified using `labels` and `index`:
  1139. >>> lbl = np.array([[0, 1, 2, 3],
  1140. ... [0, 1, 2, 3],
  1141. ... [0, 1, 2, 3],
  1142. ... [0, 1, 2, 3]])
  1143. >>> ndimage.maximum_position(a, lbl, 1)
  1144. (1, 1)
  1145. If no index is given, non-zero `labels` are processed:
  1146. >>> ndimage.maximum_position(a, lbl)
  1147. (2, 3)
  1148. If there are no maxima, the position of the first element is returned:
  1149. >>> ndimage.maximum_position(a, lbl, 2)
  1150. (0, 2)
  1151. """
  1152. dims = numpy.array(numpy.asarray(input).shape)
  1153. # see numpy.unravel_index to understand this line.
  1154. dim_prod = numpy.cumprod([1] + list(dims[:0:-1]))[::-1]
  1155. result = _select(input, labels, index, find_max_positions=True)[0]
  1156. if numpy.isscalar(result):
  1157. return tuple((result // dim_prod) % dims)
  1158. return [tuple(v) for v in (result.reshape(-1, 1) // dim_prod) % dims]
  1159. def extrema(input, labels=None, index=None):
  1160. """
  1161. Calculate the minimums and maximums of the values of an array
  1162. at labels, along with their positions.
  1163. Parameters
  1164. ----------
  1165. input : ndarray
  1166. N-D image data to process.
  1167. labels : ndarray, optional
  1168. Labels of features in input.
  1169. If not None, must be same shape as `input`.
  1170. index : int or sequence of ints, optional
  1171. Labels to include in output. If None (default), all values where
  1172. non-zero `labels` are used.
  1173. Returns
  1174. -------
  1175. minimums, maximums : int or ndarray
  1176. Values of minimums and maximums in each feature.
  1177. min_positions, max_positions : tuple or list of tuples
  1178. Each tuple gives the N-D coordinates of the corresponding minimum
  1179. or maximum.
  1180. See Also
  1181. --------
  1182. maximum, minimum, maximum_position, minimum_position, center_of_mass
  1183. Examples
  1184. --------
  1185. >>> import numpy as np
  1186. >>> a = np.array([[1, 2, 0, 0],
  1187. ... [5, 3, 0, 4],
  1188. ... [0, 0, 0, 7],
  1189. ... [9, 3, 0, 0]])
  1190. >>> from scipy import ndimage
  1191. >>> ndimage.extrema(a)
  1192. (0, 9, (0, 2), (3, 0))
  1193. Features to process can be specified using `labels` and `index`:
  1194. >>> lbl, nlbl = ndimage.label(a)
  1195. >>> ndimage.extrema(a, lbl, index=np.arange(1, nlbl+1))
  1196. (array([1, 4, 3]),
  1197. array([5, 7, 9]),
  1198. [(0, 0), (1, 3), (3, 1)],
  1199. [(1, 0), (2, 3), (3, 0)])
  1200. If no index is given, non-zero `labels` are processed:
  1201. >>> ndimage.extrema(a, lbl)
  1202. (1, 9, (0, 0), (3, 0))
  1203. """
  1204. dims = numpy.array(numpy.asarray(input).shape)
  1205. # see numpy.unravel_index to understand this line.
  1206. dim_prod = numpy.cumprod([1] + list(dims[:0:-1]))[::-1]
  1207. minimums, min_positions, maximums, max_positions = _select(input, labels,
  1208. index,
  1209. find_min=True,
  1210. find_max=True,
  1211. find_min_positions=True,
  1212. find_max_positions=True)
  1213. if numpy.isscalar(minimums):
  1214. return (minimums, maximums, tuple((min_positions // dim_prod) % dims),
  1215. tuple((max_positions // dim_prod) % dims))
  1216. min_positions = [tuple(v) for v in (min_positions.reshape(-1, 1) // dim_prod) % dims]
  1217. max_positions = [tuple(v) for v in (max_positions.reshape(-1, 1) // dim_prod) % dims]
  1218. return minimums, maximums, min_positions, max_positions
  1219. def center_of_mass(input, labels=None, index=None):
  1220. """
  1221. Calculate the center of mass of the values of an array at labels.
  1222. Parameters
  1223. ----------
  1224. input : ndarray
  1225. Data from which to calculate center-of-mass. The masses can either
  1226. be positive or negative.
  1227. labels : ndarray, optional
  1228. Labels for objects in `input`, as generated by `ndimage.label`.
  1229. Only used with `index`. Dimensions must be the same as `input`.
  1230. index : int or sequence of ints, optional
  1231. Labels for which to calculate centers-of-mass. If not specified,
  1232. the combined center of mass of all labels greater than zero
  1233. will be calculated. Only used with `labels`.
  1234. Returns
  1235. -------
  1236. center_of_mass : tuple, or list of tuples
  1237. Coordinates of centers-of-mass.
  1238. Examples
  1239. --------
  1240. >>> import numpy as np
  1241. >>> a = np.array(([0,0,0,0],
  1242. ... [0,1,1,0],
  1243. ... [0,1,1,0],
  1244. ... [0,1,1,0]))
  1245. >>> from scipy import ndimage
  1246. >>> ndimage.center_of_mass(a)
  1247. (2.0, 1.5)
  1248. Calculation of multiple objects in an image
  1249. >>> b = np.array(([0,1,1,0],
  1250. ... [0,1,0,0],
  1251. ... [0,0,0,0],
  1252. ... [0,0,1,1],
  1253. ... [0,0,1,1]))
  1254. >>> lbl = ndimage.label(b)[0]
  1255. >>> ndimage.center_of_mass(b, lbl, [1,2])
  1256. [(0.33333333333333331, 1.3333333333333333), (3.5, 2.5)]
  1257. Negative masses are also accepted, which can occur for example when
  1258. bias is removed from measured data due to random noise.
  1259. >>> c = np.array(([-1,0,0,0],
  1260. ... [0,-1,-1,0],
  1261. ... [0,1,-1,0],
  1262. ... [0,1,1,0]))
  1263. >>> ndimage.center_of_mass(c)
  1264. (-4.0, 1.0)
  1265. If there are division by zero issues, the function does not raise an
  1266. error but rather issues a RuntimeWarning before returning inf and/or NaN.
  1267. >>> d = np.array([-1, 1])
  1268. >>> ndimage.center_of_mass(d)
  1269. (inf,)
  1270. """
  1271. normalizer = sum(input, labels, index)
  1272. grids = numpy.ogrid[[slice(0, i) for i in input.shape]]
  1273. results = [sum(input * grids[dir].astype(float), labels, index) / normalizer
  1274. for dir in range(input.ndim)]
  1275. if numpy.isscalar(results[0]):
  1276. return tuple(results)
  1277. return [tuple(v) for v in numpy.array(results).T]
  1278. def histogram(input, min, max, bins, labels=None, index=None):
  1279. """
  1280. Calculate the histogram of the values of an array, optionally at labels.
  1281. Histogram calculates the frequency of values in an array within bins
  1282. determined by `min`, `max`, and `bins`. The `labels` and `index`
  1283. keywords can limit the scope of the histogram to specified sub-regions
  1284. within the array.
  1285. Parameters
  1286. ----------
  1287. input : array_like
  1288. Data for which to calculate histogram.
  1289. min, max : int
  1290. Minimum and maximum values of range of histogram bins.
  1291. bins : int
  1292. Number of bins.
  1293. labels : array_like, optional
  1294. Labels for objects in `input`.
  1295. If not None, must be same shape as `input`.
  1296. index : int or sequence of ints, optional
  1297. Label or labels for which to calculate histogram. If None, all values
  1298. where label is greater than zero are used
  1299. Returns
  1300. -------
  1301. hist : ndarray
  1302. Histogram counts.
  1303. Examples
  1304. --------
  1305. >>> import numpy as np
  1306. >>> a = np.array([[ 0. , 0.2146, 0.5962, 0. ],
  1307. ... [ 0. , 0.7778, 0. , 0. ],
  1308. ... [ 0. , 0. , 0. , 0. ],
  1309. ... [ 0. , 0. , 0.7181, 0.2787],
  1310. ... [ 0. , 0. , 0.6573, 0.3094]])
  1311. >>> from scipy import ndimage
  1312. >>> ndimage.histogram(a, 0, 1, 10)
  1313. array([13, 0, 2, 1, 0, 1, 1, 2, 0, 0])
  1314. With labels and no indices, non-zero elements are counted:
  1315. >>> lbl, nlbl = ndimage.label(a)
  1316. >>> ndimage.histogram(a, 0, 1, 10, lbl)
  1317. array([0, 0, 2, 1, 0, 1, 1, 2, 0, 0])
  1318. Indices can be used to count only certain objects:
  1319. >>> ndimage.histogram(a, 0, 1, 10, lbl, 2)
  1320. array([0, 0, 1, 1, 0, 0, 1, 1, 0, 0])
  1321. """
  1322. _bins = numpy.linspace(min, max, bins + 1)
  1323. def _hist(vals):
  1324. return numpy.histogram(vals, _bins)[0]
  1325. return labeled_comprehension(input, labels, index, _hist, object, None,
  1326. pass_positions=False)
  1327. def watershed_ift(input, markers, structure=None, output=None):
  1328. """
  1329. Apply watershed from markers using image foresting transform algorithm.
  1330. Parameters
  1331. ----------
  1332. input : array_like
  1333. Input.
  1334. markers : array_like
  1335. Markers are points within each watershed that form the beginning
  1336. of the process. Negative markers are considered background markers
  1337. which are processed after the other markers.
  1338. structure : structure element, optional
  1339. A structuring element defining the connectivity of the object can be
  1340. provided. If None, an element is generated with a squared
  1341. connectivity equal to one.
  1342. output : ndarray, optional
  1343. An output array can optionally be provided. The same shape as input.
  1344. Returns
  1345. -------
  1346. watershed_ift : ndarray
  1347. Output. Same shape as `input`.
  1348. References
  1349. ----------
  1350. .. [1] A.X. Falcao, J. Stolfi and R. de Alencar Lotufo, "The image
  1351. foresting transform: theory, algorithms, and applications",
  1352. Pattern Analysis and Machine Intelligence, vol. 26, pp. 19-29, 2004.
  1353. """
  1354. input = numpy.asarray(input)
  1355. if input.dtype.type not in [numpy.uint8, numpy.uint16]:
  1356. raise TypeError('only 8 and 16 unsigned inputs are supported')
  1357. if structure is None:
  1358. structure = _morphology.generate_binary_structure(input.ndim, 1)
  1359. structure = numpy.asarray(structure, dtype=bool)
  1360. if structure.ndim != input.ndim:
  1361. raise RuntimeError('structure and input must have equal rank')
  1362. for ii in structure.shape:
  1363. if ii != 3:
  1364. raise RuntimeError('structure dimensions must be equal to 3')
  1365. if not structure.flags.contiguous:
  1366. structure = structure.copy()
  1367. markers = numpy.asarray(markers)
  1368. if input.shape != markers.shape:
  1369. raise RuntimeError('input and markers must have equal shape')
  1370. integral_types = [numpy.int8,
  1371. numpy.int16,
  1372. numpy.int32,
  1373. numpy.int_,
  1374. numpy.int64,
  1375. numpy.intc,
  1376. numpy.intp]
  1377. if markers.dtype.type not in integral_types:
  1378. raise RuntimeError('marker should be of integer type')
  1379. if isinstance(output, numpy.ndarray):
  1380. if output.dtype.type not in integral_types:
  1381. raise RuntimeError('output should be of integer type')
  1382. else:
  1383. output = markers.dtype
  1384. output = _ni_support._get_output(output, input)
  1385. _nd_image.watershed_ift(input, markers, structure, output)
  1386. return output