fastremap 1.15.1__tar.gz → 1.16.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {fastremap-1.15.1 → fastremap-1.16.0}/.github/workflows/build_wheel.yml +7 -3
  2. {fastremap-1.15.1 → fastremap-1.16.0}/.github/workflows/test.yml +2 -2
  3. {fastremap-1.15.1 → fastremap-1.16.0}/ChangeLog +25 -0
  4. fastremap-1.16.0/PKG-INFO +238 -0
  5. {fastremap-1.15.1 → fastremap-1.16.0}/README.md +9 -0
  6. {fastremap-1.15.1 → fastremap-1.16.0}/automated_test.py +73 -4
  7. fastremap-1.16.0/fastremap.egg-info/PKG-INFO +238 -0
  8. fastremap-1.16.0/fastremap.egg-info/pbr.json +1 -0
  9. {fastremap-1.15.1 → fastremap-1.16.0}/setup.py +1 -1
  10. {fastremap-1.15.1 → fastremap-1.16.0}/src/fastremap.pyx +134 -19
  11. fastremap-1.15.1/PKG-INFO +0 -218
  12. fastremap-1.15.1/fastremap.egg-info/PKG-INFO +0 -218
  13. fastremap-1.15.1/fastremap.egg-info/pbr.json +0 -1
  14. {fastremap-1.15.1 → fastremap-1.16.0}/.dockerignore +0 -0
  15. {fastremap-1.15.1 → fastremap-1.16.0}/AUTHORS +0 -0
  16. {fastremap-1.15.1 → fastremap-1.16.0}/LICENSE +0 -0
  17. {fastremap-1.15.1 → fastremap-1.16.0}/MANIFEST.in +0 -0
  18. {fastremap-1.15.1 → fastremap-1.16.0}/build_linux.sh +0 -0
  19. {fastremap-1.15.1 → fastremap-1.16.0}/fastremap.egg-info/SOURCES.txt +0 -0
  20. {fastremap-1.15.1 → fastremap-1.16.0}/fastremap.egg-info/dependency_links.txt +0 -0
  21. {fastremap-1.15.1 → fastremap-1.16.0}/fastremap.egg-info/not-zip-safe +0 -0
  22. {fastremap-1.15.1 → fastremap-1.16.0}/fastremap.egg-info/requires.txt +0 -0
  23. {fastremap-1.15.1 → fastremap-1.16.0}/fastremap.egg-info/top_level.txt +0 -0
  24. {fastremap-1.15.1 → fastremap-1.16.0}/manylinux1.Dockerfile +0 -0
  25. {fastremap-1.15.1 → fastremap-1.16.0}/manylinux2010.Dockerfile +0 -0
  26. {fastremap-1.15.1 → fastremap-1.16.0}/manylinux2014.Dockerfile +0 -0
  27. {fastremap-1.15.1 → fastremap-1.16.0}/pyproject.toml +0 -0
  28. {fastremap-1.15.1 → fastremap-1.16.0}/requirements.txt +0 -0
  29. {fastremap-1.15.1 → fastremap-1.16.0}/requirements_dev.txt +0 -0
  30. {fastremap-1.15.1 → fastremap-1.16.0}/setup.cfg +0 -0
  31. {fastremap-1.15.1 → fastremap-1.16.0}/src/fastremap.pxd +0 -0
  32. {fastremap-1.15.1 → fastremap-1.16.0}/src/ipt.hpp +0 -0
  33. {fastremap-1.15.1 → fastremap-1.16.0}/src/ska_flat_hash_map.hpp +0 -0
  34. {fastremap-1.15.1 → fastremap-1.16.0}/test.py +0 -0
  35. {fastremap-1.15.1 → fastremap-1.16.0}/tox.ini +0 -0
@@ -6,7 +6,7 @@ on:
6
6
  tags:
7
7
  - '*'
8
8
  env:
9
- CIBW_SKIP: cp27-* cp33-* cp34-* cp35-* cp36-* cp37-* pp* *-musllinux* cp312-manylinux_i686
9
+ CIBW_SKIP: cp27-* cp33-* cp34-* cp35-* cp36-* cp37-* cp38* pp* *-musllinux* cp312-manylinux_i686
10
10
 
11
11
  jobs:
12
12
  build_wheels:
@@ -35,6 +35,10 @@ jobs:
35
35
  CIBW_BEFORE_BUILD: pip install numpy setuptools wheel cython
36
36
  CIBW_ARCHS_MACOS: "x86_64 arm64"
37
37
 
38
- - uses: actions/upload-artifact@v3
38
+
39
+ - name: Upload built wheels
40
+ uses: actions/upload-artifact@v4
39
41
  with:
40
- path: ./wheelhouse/*.whl
42
+ name: built-wheels-${{ matrix.os }}-${{ matrix.arch }}
43
+ path: ./wheelhouse/*.whl
44
+ if-no-files-found: warn
@@ -14,8 +14,8 @@ jobs:
14
14
  runs-on: ${{ matrix.os }}
15
15
  strategy:
16
16
  matrix:
17
- os: [ubuntu-20.04, macos-latest, windows-2019]
18
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
17
+ os: [ubuntu-latest, macos-latest, windows-latest]
18
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
19
19
 
20
20
  steps:
21
21
  - name: Set up Python ${{ matrix.python-version }}
@@ -1,6 +1,31 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ 1.16.0
5
+ ------
6
+
7
+ * test: add a more stringent random test
8
+ * fix(unique): unique\_via\_sort returns proper data width
9
+ * fix(unique): axis=0, ndim=2 works for higher data widths
10
+ * fix(unique): edge case for uint64 range
11
+ * fix(unique): handle floating point
12
+ * test: make sure fastremap.unique matches np.unique
13
+ * test: sanity check that axis=0 works
14
+ * perf: accelerate special case of np.unique(x, axis=0) where x.ndim=2
15
+ * feat: add indices
16
+ * feat+test: add widen\_dtype and narrow\_dtype
17
+ * docs: show how to use "indices"
18
+
19
+ 1.15.2
20
+ ------
21
+
22
+ * install: restrict to py39+
23
+ * ci: exclude py38 from build
24
+ * ci: remove py38 as it is EOL
25
+ * ci: update runners
26
+ * test: add python3.13
27
+ * ci: upgrade to upload-artifact v4
28
+
4
29
  1.15.1
5
30
  ------
6
31
 
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastremap
3
+ Version: 1.16.0
4
+ Summary: Remap, mask, renumber, unique, and in-place transposition of 3D labeled images. Point cloud too.
5
+ Home-page: https://github.com/seung-lab/fastremap/
6
+ Author: William Silversmith
7
+ Author-email: ws9@princeton.edu
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.9,<4.0
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ License-File: AUTHORS
23
+ Requires-Dist: numpy
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: classifier
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: license-file
31
+ Dynamic: requires-dist
32
+ Dynamic: requires-python
33
+ Dynamic: summary
34
+
35
+ [![PyPI version](https://badge.fury.io/py/fastremap.svg)](https://badge.fury.io/py/fastremap)
36
+
37
+ # fastremap
38
+
39
+ Renumber and relabel Numpy arrays at C++ speed and physically convert rectangular Numpy arrays between C and Fortran order using an in-place transposition.
40
+
41
+ ```python
42
+ import fastremap
43
+
44
+ uniq, cts = fastremap.unique(labels, return_counts=True) # may be much faster than np.unique
45
+
46
+ idxs = fastremap.indices(labels, 1231) # important for huge arrays
47
+
48
+ labels, remapping = fastremap.renumber(labels, in_place=True) # relabel values from 1 and refit data type
49
+ ptc = fastremap.point_cloud(labels) # dict of coordinates by label
50
+
51
+ labels = fastremap.refit(labels) # resize the data type of the array to fit extrema
52
+ labels = fastremap.refit(labels, value=-35) # resize the data type to fit the value provided
53
+
54
+ wider_dtype = fastremap.widen_dtype(np.uint32) # np.uint64
55
+ narrower_dtype = fastremap.narrow_dtype(np.uint32) # np.uint16
56
+
57
+ # remap all occurances of 1 -> 2
58
+ labels = fastremap.remap(labels, { 1: 2 }, preserve_missing_labels=True, in_place=True)
59
+
60
+ labels = fastremap.mask(labels, [1,5,13]) # set all occurances of 1,5,13 to 0
61
+ labels = fastremap.mask_except(labels, [1,5,13]) # set all labels except 1,5,13 to 0
62
+
63
+ mapping = fastremap.component_map([ 1, 2, 3, 4 ], [ 5, 5, 6, 7 ]) # { 1: 5, 2: 5, 3: 6, 4: 7 }
64
+ mapping = fastremap.inverse_component_map([ 1, 2, 1, 3 ], [ 4, 4, 5, 6 ]) # { 1: [ 4, 5 ], 2: [ 4 ], 3: [ 6 ] }
65
+
66
+ fastremap.transpose(labels) # physically transpose labels in-place
67
+ fastremap.ascontiguousarray(labels) # try to perform a physical in-place transposition to C order
68
+ fastremap.asfortranarray(labels) # try to perform a physical in-place transposition to F order
69
+
70
+ minval, maxval = fastremap.minmax(labels) # faster version of (np.min(labels), np.max(labels))
71
+
72
+ # computes number of matching adjacent pixel pairs in an image
73
+ num_pairs = fastremap.pixel_pairs(labels)
74
+ n_foreground = fastremap.foreground(labels) # number of nonzero voxels
75
+
76
+ # computes the cutout.tobytes(order) of each chunk and returns
77
+ # the binaries indexed by fortran order in the order specified (C or F)
78
+ # If the input image is F contiguous and F is requested, or C and C order,
79
+ # and the image is larger than a single chunk, this will be significantly
80
+ # faster than iterating and using tobytes.
81
+ binaries = fastremap.tobytes(labels, (64,64,64), order="F")
82
+ ```
83
+
84
+ ## All Available Functions
85
+ - **unique:** Faster implementation of `np.unique`.
86
+ - **renumber:** Relabel array from 1 to N which can often use smaller datatypes.
87
+ - **indices:** Optimized search for matching values.
88
+ - **remap:** Custom relabeling of values in an array from a dictionary.
89
+ - **refit:** Resize the data type of an array to the smallest that can contain the most extreme values in it.
90
+ - **narrow_dtype:** Find the next sized up dtype. e.g. uint16 -> uint32
91
+ - **widen_dtype:** Find the next sized down dtype. e.g. uint16 -> uint8
92
+ - **mask:** Zero out labels in an array specified by a given list.
93
+ - **mask_except**: Zero out all labels except those specified in a given list.
94
+ - **component_map**: Extract an int-to-int dictionary mapping of labels from one image containing component labels to another parent labels.
95
+ - **inverse_component_map**: Extract an int-to-list-of-ints dictionary mapping from an image containing groups of components to an image containing the components.
96
+ - **remap_from_array:** Same as remap, but the map is an array where the key is the array index and the value is the value.
97
+ - **remap_from_array_kv:** Same as remap, but the map consists of two equal sized arrays, the first containing keys, the second containing values.
98
+ - **asfortranarray:** Perform an in-place matrix transposition for rectangular arrays if memory is contiguous, standard numpy otherwise.
99
+ - **ascontiguousarray:** Perform an in-place matrix transposition for rectangular arrays if memory is contiguous, standard numpy algorithm otherwise.
100
+ - **minmax:** Compute the min and max of an array in one pass.
101
+ - **pixel_pairs:** Computes the number of adjacent matching memory locations in an image. A quick heuristic for understanding if the image statistics are roughly similar to a connectomics segmentation.
102
+ - **foreground:** Count the number of non-zero voxels rapidly.
103
+ - **point_cloud:** Get the X,Y,Z locations of each foreground voxel grouped by label.
104
+ - **tobytes**: Compute the tobytes of an image divided into a grid and return the resultant binaries indexed by their gridpoint in fortran order with the binary in the order requested (C or F).
105
+
106
+ ## `pip` Installation
107
+
108
+ ```bash
109
+ pip install fastremap
110
+ ```
111
+
112
+ *If not, a C++ compiler is required.*
113
+
114
+ ```bash
115
+ pip install numpy
116
+ pip install fastremap --no-binary :all:
117
+ ```
118
+
119
+ ## Manual Installation
120
+
121
+ *A C++ compiler is required.*
122
+
123
+ ```bash
124
+ sudo apt-get install g++ python3-dev
125
+ mkvirtualenv -p python3 fastremap
126
+ pip install numpy
127
+
128
+ # Choose one:
129
+ python setup.py develop
130
+ python setup.py install
131
+ ```
132
+
133
+ ## The Problem of Remapping
134
+
135
+ Python loops are slow, so Numpy is often used to perform remapping on large arrays (hundreds of megabytes or gigabytes). In order to efficiently remap an array in Numpy you need a key-value array where the index is the key and the value is the contents of that index.
136
+
137
+ ```python
138
+ import numpy as np
139
+
140
+ original = np.array([ 1, 3, 5, 5, 10 ])
141
+ remap = np.array([ 0, -5, 0, 6, 0, 0, 2, 0, 0, 0, -100 ])
142
+ # Keys: 0 1 2 3 4 5 6 7 8 9 10
143
+
144
+ remapped = remap[ original ]
145
+ >>> [ -5, 6, 2, 2, -100 ]
146
+ ```
147
+
148
+ If there are 32 or 64 bit labels in the array, this becomes impractical as the size of the array can grow larger than RAM. Therefore, it would be helpful to be able to perform this mapping using a C speed loop. Numba can be used for this in some circumstances. However, this library provides an alternative.
149
+
150
+ ```python
151
+ import numpy as np
152
+ import fastremap
153
+
154
+ mappings = {
155
+ 1: 100,
156
+ 2: 200,
157
+ -3: 7,
158
+ }
159
+
160
+ arr = np.array([5, 1, 2, -5, -3, 10, 6])
161
+ # Custom remapping of -3, 5, and 6 leaving the rest alone
162
+ arr = fastremap.remap(arr, mappings, preserve_missing_labels=True)
163
+ # result: [ 5, 100, 200, -5, 7, 10, 6 ]
164
+ ```
165
+
166
+ ## The Problem of Renumbering
167
+
168
+ Sometimes a 64-bit array contains values that could be represented by an 8-bit array. However, similarly to the remapping problem, Python loops can be too slow to do this. Numpy doesn't provide a convenient way to do it either. Therefore this library provides an alternative solution.
169
+
170
+ ```python
171
+ import fastremap
172
+ import numpy as np
173
+
174
+ arr = np.array([ 283732875, 439238823, 283732875, 182812404, 0 ], dtype=np.int64)
175
+
176
+ arr, remapping = fastremap.renumber(arr, preserve_zero=True) # Returns uint8 array
177
+ >>> arr = [ 1, 2, 1, 3, 0 ]
178
+ >>> remapping = { 0: 0, 283732875: 1, 439238823: 2, 182812404: 3 }
179
+
180
+ arr, remapping = fastremap.renumber(arr, preserve_zero=False) # Returns uint8 array
181
+ >>> arr = [ 1, 2, 1, 3, 4 ]
182
+ >>> remapping = { 0: 4, 283732875: 1, 439238823: 2, 182812404: 3 }
183
+
184
+ arr, remapping = fastremap.renumber(arr, preserve_zero=False, in_place=True) # Mutate arr to use less memory
185
+ >>> arr = [ 1, 2, 1, 3, 4 ]
186
+ >>> remapping = { 0: 4, 283732875: 1, 439238823: 2, 182812404: 3 }
187
+ ```
188
+
189
+ ## The Problem of In-Place Transposition
190
+
191
+ When transitioning between different media, e.g. CPU to GPU, CPU to Network, CPU to disk, it's often necessary to physically transpose multi-dimensional arrays to reformat as C or Fortran order. Tranposing matrices is also a common action in linear algebra, but often you can get away with just changing the strides.
192
+
193
+ An out-of-place transposition is easy to write, and often faster, but it will spike peak memory consumption. This library grants the user the option of performing an in-place transposition which trades CPU time for peak memory usage. In the special case of square or cubic arrays, the in-place transpisition is both lower memory and faster.
194
+
195
+ - **fastremap.asfortranarray:** Same as np.asfortranarray but will perform the transposition in-place for 1, 2, 3, and 4D arrays. 2D and 3D square matrices are faster to process than with Numpy.
196
+ - **fastremap.ascontiguousarray:** Same as np.ascontiguousarray but will perform the transposition in-place for 1, 2, 3, and 4D arrays. 2D and 3D square matrices are faster to process than with Numpy.
197
+
198
+ ```python
199
+ import fastremap
200
+ import numpy as np
201
+
202
+ arr = np.ones((512,512,512), dtype=np.float32)
203
+ arr = fastremap.asfortranarray(x)
204
+
205
+ arr = np.ones((512,512,512), dtype=np.float32, order='F')
206
+ arr = fastremap.ascontiguousarray(x)
207
+ ```
208
+
209
+ ## C++ Usage
210
+
211
+ The in-place matrix transposition is implemented in ipt.hpp. If you're working in C++, you can also use it directly like so:
212
+
213
+ ```cpp
214
+ #include "ipt.hpp"
215
+
216
+ int main() {
217
+
218
+ int sx = 128;
219
+ int sy = 124;
220
+ int sz = 103;
221
+ int sw = 3;
222
+
223
+ auto* arr = ....;
224
+
225
+ // All primitive number types supported
226
+ // The array will be modified in place,
227
+ // so these functions are void type.
228
+ ipt::ipt<int>(arr, sx, sy); // 2D
229
+ ipt::ipt<float>(arr, sx, sy, sz); // 3D
230
+ ipt::ipt<double>(arr, sx, sy, sz, sw); // 4D
231
+
232
+ return 0;
233
+ }
234
+ ```
235
+
236
+ --
237
+ Made with <3
238
+
@@ -8,12 +8,18 @@ Renumber and relabel Numpy arrays at C++ speed and physically convert rectangula
8
8
  import fastremap
9
9
 
10
10
  uniq, cts = fastremap.unique(labels, return_counts=True) # may be much faster than np.unique
11
+
12
+ idxs = fastremap.indices(labels, 1231) # important for huge arrays
13
+
11
14
  labels, remapping = fastremap.renumber(labels, in_place=True) # relabel values from 1 and refit data type
12
15
  ptc = fastremap.point_cloud(labels) # dict of coordinates by label
13
16
 
14
17
  labels = fastremap.refit(labels) # resize the data type of the array to fit extrema
15
18
  labels = fastremap.refit(labels, value=-35) # resize the data type to fit the value provided
16
19
 
20
+ wider_dtype = fastremap.widen_dtype(np.uint32) # np.uint64
21
+ narrower_dtype = fastremap.narrow_dtype(np.uint32) # np.uint16
22
+
17
23
  # remap all occurances of 1 -> 2
18
24
  labels = fastremap.remap(labels, { 1: 2 }, preserve_missing_labels=True, in_place=True)
19
25
 
@@ -44,8 +50,11 @@ binaries = fastremap.tobytes(labels, (64,64,64), order="F")
44
50
  ## All Available Functions
45
51
  - **unique:** Faster implementation of `np.unique`.
46
52
  - **renumber:** Relabel array from 1 to N which can often use smaller datatypes.
53
+ - **indices:** Optimized search for matching values.
47
54
  - **remap:** Custom relabeling of values in an array from a dictionary.
48
55
  - **refit:** Resize the data type of an array to the smallest that can contain the most extreme values in it.
56
+ - **narrow_dtype:** Find the next sized up dtype. e.g. uint16 -> uint32
57
+ - **widen_dtype:** Find the next sized down dtype. e.g. uint16 -> uint8
49
58
  - **mask:** Zero out labels in an array specified by a given list.
50
59
  - **mask_except**: Zero out all labels except those specified in a given list.
51
60
  - **component_map**: Extract an int-to-int dictionary mapping of labels from one image containing component labels to another parent labels.
@@ -397,6 +397,29 @@ def test_minmax():
397
397
  assert minval == np.min(volume)
398
398
  assert maxval == np.max(volume)
399
399
 
400
+ @pytest.mark.parametrize("dtype", DTYPES)
401
+ def test_unique_axis_0(dtype):
402
+ arr = np.array([
403
+ [0,1],
404
+ [0,2],
405
+ [0,3],
406
+ [0,2],
407
+ [1,2],
408
+ [2,3],
409
+ ], dtype=dtype)
410
+
411
+ res = fastremap.unique(arr, axis=0)
412
+ ans = np.unique(arr, axis=0)
413
+
414
+ assert np.all(res == ans)
415
+
416
+ def test_unique_axis_0_random():
417
+ arr = np.random.randint(0,100000, size=[1000000,2], dtype=np.uint32)
418
+ a1 = fastremap.unique(arr, axis=0)
419
+ a2 = np.unique(arr, axis=0)
420
+ assert np.all(a1 == a2)
421
+
422
+
400
423
  @pytest.mark.parametrize("order", [ "C", "F" ])
401
424
  def test_unique(order):
402
425
  def reorder(arr):
@@ -610,9 +633,55 @@ def test_tobytes_misaligned(order):
610
633
  except ValueError:
611
634
  pass
612
635
 
613
-
614
-
615
-
616
-
636
+ def test_narrow_dtype():
637
+ assert fastremap.narrow_dtype(np.uint64) == np.uint32
638
+ assert fastremap.narrow_dtype(np.uint32) == np.uint16
639
+ assert fastremap.narrow_dtype(np.uint16) == np.uint8
640
+ assert fastremap.narrow_dtype(np.uint8) == np.uint8
641
+
642
+ assert fastremap.narrow_dtype(np.int64) == np.int32
643
+ assert fastremap.narrow_dtype(np.int32) == np.int16
644
+ assert fastremap.narrow_dtype(np.int16) == np.int8
645
+ assert fastremap.narrow_dtype(np.int8) == np.int8
646
+
647
+ assert fastremap.narrow_dtype(np.uint64, exotics=True) == np.uint32
648
+ assert fastremap.narrow_dtype(np.uint32, exotics=True) == np.uint16
649
+ assert fastremap.narrow_dtype(np.uint16, exotics=True) == np.uint8
650
+ assert fastremap.narrow_dtype(np.uint8, exotics=True) == np.uint8
651
+
652
+ assert fastremap.narrow_dtype(np.int64, exotics=True) == np.int32
653
+ assert fastremap.narrow_dtype(np.int32, exotics=True) == np.int16
654
+ assert fastremap.narrow_dtype(np.int16, exotics=True) == np.int8
655
+ assert fastremap.narrow_dtype(np.int8, exotics=True) == np.int8
656
+
657
+ assert fastremap.narrow_dtype(np.float64) == np.float32
658
+ assert fastremap.narrow_dtype(np.float32) == np.float32
659
+
660
+ assert fastremap.narrow_dtype(np.float64, exotics=True) == np.float32
661
+ assert fastremap.narrow_dtype(np.float32, exotics=True) == np.float16
662
+
663
+ assert fastremap.narrow_dtype(np.complex128) == np.complex64
664
+ assert fastremap.narrow_dtype(np.complex64) == np.complex64
665
+
666
+ def test_widen_dtype():
667
+ assert fastremap.widen_dtype(np.uint64) == np.uint64
668
+ assert fastremap.widen_dtype(np.uint32) == np.uint64
669
+ assert fastremap.widen_dtype(np.uint16) == np.uint32
670
+ assert fastremap.widen_dtype(np.uint8) == np.uint16
671
+
672
+ assert fastremap.widen_dtype(np.int64) == np.int64
673
+ assert fastremap.widen_dtype(np.int32) == np.int64
674
+ assert fastremap.widen_dtype(np.int16) == np.int32
675
+ assert fastremap.widen_dtype(np.int8) == np.int16
676
+
677
+ assert fastremap.widen_dtype(np.float64, exotics=True) == np.longdouble
678
+ assert fastremap.widen_dtype(np.float64) == np.float64
679
+ assert fastremap.widen_dtype(np.float32) == np.float64
680
+ assert fastremap.widen_dtype(np.float16) == np.float32
681
+
682
+ assert fastremap.widen_dtype(np.complex64) == np.complex64
683
+ assert fastremap.widen_dtype(np.complex64, exotics=True) == np.complex128
684
+ assert fastremap.widen_dtype(np.complex128, exotics=True) == np.clongdouble
685
+ assert fastremap.widen_dtype(np.clongdouble, exotics=True) == np.clongdouble
617
686
 
618
687
 
@@ -0,0 +1,238 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastremap
3
+ Version: 1.16.0
4
+ Summary: Remap, mask, renumber, unique, and in-place transposition of 3D labeled images. Point cloud too.
5
+ Home-page: https://github.com/seung-lab/fastremap/
6
+ Author: William Silversmith
7
+ Author-email: ws9@princeton.edu
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.7
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.9,<4.0
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ License-File: AUTHORS
23
+ Requires-Dist: numpy
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: classifier
27
+ Dynamic: description
28
+ Dynamic: description-content-type
29
+ Dynamic: home-page
30
+ Dynamic: license-file
31
+ Dynamic: requires-dist
32
+ Dynamic: requires-python
33
+ Dynamic: summary
34
+
35
+ [![PyPI version](https://badge.fury.io/py/fastremap.svg)](https://badge.fury.io/py/fastremap)
36
+
37
+ # fastremap
38
+
39
+ Renumber and relabel Numpy arrays at C++ speed and physically convert rectangular Numpy arrays between C and Fortran order using an in-place transposition.
40
+
41
+ ```python
42
+ import fastremap
43
+
44
+ uniq, cts = fastremap.unique(labels, return_counts=True) # may be much faster than np.unique
45
+
46
+ idxs = fastremap.indices(labels, 1231) # important for huge arrays
47
+
48
+ labels, remapping = fastremap.renumber(labels, in_place=True) # relabel values from 1 and refit data type
49
+ ptc = fastremap.point_cloud(labels) # dict of coordinates by label
50
+
51
+ labels = fastremap.refit(labels) # resize the data type of the array to fit extrema
52
+ labels = fastremap.refit(labels, value=-35) # resize the data type to fit the value provided
53
+
54
+ wider_dtype = fastremap.widen_dtype(np.uint32) # np.uint64
55
+ narrower_dtype = fastremap.narrow_dtype(np.uint32) # np.uint16
56
+
57
+ # remap all occurances of 1 -> 2
58
+ labels = fastremap.remap(labels, { 1: 2 }, preserve_missing_labels=True, in_place=True)
59
+
60
+ labels = fastremap.mask(labels, [1,5,13]) # set all occurances of 1,5,13 to 0
61
+ labels = fastremap.mask_except(labels, [1,5,13]) # set all labels except 1,5,13 to 0
62
+
63
+ mapping = fastremap.component_map([ 1, 2, 3, 4 ], [ 5, 5, 6, 7 ]) # { 1: 5, 2: 5, 3: 6, 4: 7 }
64
+ mapping = fastremap.inverse_component_map([ 1, 2, 1, 3 ], [ 4, 4, 5, 6 ]) # { 1: [ 4, 5 ], 2: [ 4 ], 3: [ 6 ] }
65
+
66
+ fastremap.transpose(labels) # physically transpose labels in-place
67
+ fastremap.ascontiguousarray(labels) # try to perform a physical in-place transposition to C order
68
+ fastremap.asfortranarray(labels) # try to perform a physical in-place transposition to F order
69
+
70
+ minval, maxval = fastremap.minmax(labels) # faster version of (np.min(labels), np.max(labels))
71
+
72
+ # computes number of matching adjacent pixel pairs in an image
73
+ num_pairs = fastremap.pixel_pairs(labels)
74
+ n_foreground = fastremap.foreground(labels) # number of nonzero voxels
75
+
76
+ # computes the cutout.tobytes(order) of each chunk and returns
77
+ # the binaries indexed by fortran order in the order specified (C or F)
78
+ # If the input image is F contiguous and F is requested, or C and C order,
79
+ # and the image is larger than a single chunk, this will be significantly
80
+ # faster than iterating and using tobytes.
81
+ binaries = fastremap.tobytes(labels, (64,64,64), order="F")
82
+ ```
83
+
84
+ ## All Available Functions
85
+ - **unique:** Faster implementation of `np.unique`.
86
+ - **renumber:** Relabel array from 1 to N which can often use smaller datatypes.
87
+ - **indices:** Optimized search for matching values.
88
+ - **remap:** Custom relabeling of values in an array from a dictionary.
89
+ - **refit:** Resize the data type of an array to the smallest that can contain the most extreme values in it.
90
+ - **narrow_dtype:** Find the next sized up dtype. e.g. uint16 -> uint32
91
+ - **widen_dtype:** Find the next sized down dtype. e.g. uint16 -> uint8
92
+ - **mask:** Zero out labels in an array specified by a given list.
93
+ - **mask_except**: Zero out all labels except those specified in a given list.
94
+ - **component_map**: Extract an int-to-int dictionary mapping of labels from one image containing component labels to another parent labels.
95
+ - **inverse_component_map**: Extract an int-to-list-of-ints dictionary mapping from an image containing groups of components to an image containing the components.
96
+ - **remap_from_array:** Same as remap, but the map is an array where the key is the array index and the value is the value.
97
+ - **remap_from_array_kv:** Same as remap, but the map consists of two equal sized arrays, the first containing keys, the second containing values.
98
+ - **asfortranarray:** Perform an in-place matrix transposition for rectangular arrays if memory is contiguous, standard numpy otherwise.
99
+ - **ascontiguousarray:** Perform an in-place matrix transposition for rectangular arrays if memory is contiguous, standard numpy algorithm otherwise.
100
+ - **minmax:** Compute the min and max of an array in one pass.
101
+ - **pixel_pairs:** Computes the number of adjacent matching memory locations in an image. A quick heuristic for understanding if the image statistics are roughly similar to a connectomics segmentation.
102
+ - **foreground:** Count the number of non-zero voxels rapidly.
103
+ - **point_cloud:** Get the X,Y,Z locations of each foreground voxel grouped by label.
104
+ - **tobytes**: Compute the tobytes of an image divided into a grid and return the resultant binaries indexed by their gridpoint in fortran order with the binary in the order requested (C or F).
105
+
106
+ ## `pip` Installation
107
+
108
+ ```bash
109
+ pip install fastremap
110
+ ```
111
+
112
+ *If not, a C++ compiler is required.*
113
+
114
+ ```bash
115
+ pip install numpy
116
+ pip install fastremap --no-binary :all:
117
+ ```
118
+
119
+ ## Manual Installation
120
+
121
+ *A C++ compiler is required.*
122
+
123
+ ```bash
124
+ sudo apt-get install g++ python3-dev
125
+ mkvirtualenv -p python3 fastremap
126
+ pip install numpy
127
+
128
+ # Choose one:
129
+ python setup.py develop
130
+ python setup.py install
131
+ ```
132
+
133
+ ## The Problem of Remapping
134
+
135
+ Python loops are slow, so Numpy is often used to perform remapping on large arrays (hundreds of megabytes or gigabytes). In order to efficiently remap an array in Numpy you need a key-value array where the index is the key and the value is the contents of that index.
136
+
137
+ ```python
138
+ import numpy as np
139
+
140
+ original = np.array([ 1, 3, 5, 5, 10 ])
141
+ remap = np.array([ 0, -5, 0, 6, 0, 0, 2, 0, 0, 0, -100 ])
142
+ # Keys: 0 1 2 3 4 5 6 7 8 9 10
143
+
144
+ remapped = remap[ original ]
145
+ >>> [ -5, 6, 2, 2, -100 ]
146
+ ```
147
+
148
+ If there are 32 or 64 bit labels in the array, this becomes impractical as the size of the array can grow larger than RAM. Therefore, it would be helpful to be able to perform this mapping using a C speed loop. Numba can be used for this in some circumstances. However, this library provides an alternative.
149
+
150
+ ```python
151
+ import numpy as np
152
+ import fastremap
153
+
154
+ mappings = {
155
+ 1: 100,
156
+ 2: 200,
157
+ -3: 7,
158
+ }
159
+
160
+ arr = np.array([5, 1, 2, -5, -3, 10, 6])
161
+ # Custom remapping of -3, 5, and 6 leaving the rest alone
162
+ arr = fastremap.remap(arr, mappings, preserve_missing_labels=True)
163
+ # result: [ 5, 100, 200, -5, 7, 10, 6 ]
164
+ ```
165
+
166
+ ## The Problem of Renumbering
167
+
168
+ Sometimes a 64-bit array contains values that could be represented by an 8-bit array. However, similarly to the remapping problem, Python loops can be too slow to do this. Numpy doesn't provide a convenient way to do it either. Therefore this library provides an alternative solution.
169
+
170
+ ```python
171
+ import fastremap
172
+ import numpy as np
173
+
174
+ arr = np.array([ 283732875, 439238823, 283732875, 182812404, 0 ], dtype=np.int64)
175
+
176
+ arr, remapping = fastremap.renumber(arr, preserve_zero=True) # Returns uint8 array
177
+ >>> arr = [ 1, 2, 1, 3, 0 ]
178
+ >>> remapping = { 0: 0, 283732875: 1, 439238823: 2, 182812404: 3 }
179
+
180
+ arr, remapping = fastremap.renumber(arr, preserve_zero=False) # Returns uint8 array
181
+ >>> arr = [ 1, 2, 1, 3, 4 ]
182
+ >>> remapping = { 0: 4, 283732875: 1, 439238823: 2, 182812404: 3 }
183
+
184
+ arr, remapping = fastremap.renumber(arr, preserve_zero=False, in_place=True) # Mutate arr to use less memory
185
+ >>> arr = [ 1, 2, 1, 3, 4 ]
186
+ >>> remapping = { 0: 4, 283732875: 1, 439238823: 2, 182812404: 3 }
187
+ ```
188
+
189
+ ## The Problem of In-Place Transposition
190
+
191
+ When transitioning between different media, e.g. CPU to GPU, CPU to Network, CPU to disk, it's often necessary to physically transpose multi-dimensional arrays to reformat as C or Fortran order. Tranposing matrices is also a common action in linear algebra, but often you can get away with just changing the strides.
192
+
193
+ An out-of-place transposition is easy to write, and often faster, but it will spike peak memory consumption. This library grants the user the option of performing an in-place transposition which trades CPU time for peak memory usage. In the special case of square or cubic arrays, the in-place transpisition is both lower memory and faster.
194
+
195
+ - **fastremap.asfortranarray:** Same as np.asfortranarray but will perform the transposition in-place for 1, 2, 3, and 4D arrays. 2D and 3D square matrices are faster to process than with Numpy.
196
+ - **fastremap.ascontiguousarray:** Same as np.ascontiguousarray but will perform the transposition in-place for 1, 2, 3, and 4D arrays. 2D and 3D square matrices are faster to process than with Numpy.
197
+
198
+ ```python
199
+ import fastremap
200
+ import numpy as np
201
+
202
+ arr = np.ones((512,512,512), dtype=np.float32)
203
+ arr = fastremap.asfortranarray(x)
204
+
205
+ arr = np.ones((512,512,512), dtype=np.float32, order='F')
206
+ arr = fastremap.ascontiguousarray(x)
207
+ ```
208
+
209
+ ## C++ Usage
210
+
211
+ The in-place matrix transposition is implemented in ipt.hpp. If you're working in C++, you can also use it directly like so:
212
+
213
+ ```cpp
214
+ #include "ipt.hpp"
215
+
216
+ int main() {
217
+
218
+ int sx = 128;
219
+ int sy = 124;
220
+ int sz = 103;
221
+ int sw = 3;
222
+
223
+ auto* arr = ....;
224
+
225
+ // All primitive number types supported
226
+ // The array will be modified in place,
227
+ // so these functions are void type.
228
+ ipt::ipt<int>(arr, sx, sy); // 2D
229
+ ipt::ipt<float>(arr, sx, sy, sz); // 3D
230
+ ipt::ipt<double>(arr, sx, sy, sz, sw); // 4D
231
+
232
+ return 0;
233
+ }
234
+ ```
235
+
236
+ --
237
+ Made with <3
238
+
@@ -0,0 +1 @@
1
+ {"git_version": "a7c2749", "is_release": true}
@@ -23,7 +23,7 @@ if sys.platform == 'darwin':
23
23
 
24
24
  setuptools.setup(
25
25
  setup_requires=['pbr', 'cython', 'numpy'],
26
- python_requires=">=3.8,<4.0",
26
+ python_requires=">=3.9,<4.0",
27
27
  pbr=True,
28
28
  ext_modules=[
29
29
  setuptools.Extension(