_measurements.py 55 KB

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