imops 0.8.8__cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

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 (58) hide show
  1. _build_utils.py +113 -0
  2. imops/__init__.py +10 -0
  3. imops/__version__.py +1 -0
  4. imops/_configs.py +29 -0
  5. imops/backend.py +95 -0
  6. imops/box.py +74 -0
  7. imops/cpp/cpp_modules.cpython-312-x86_64-linux-gnu.so +0 -0
  8. imops/cpp/interp2d/delaunator/delaunator-header-only.hpp +33 -0
  9. imops/cpp/interp2d/delaunator/delaunator.cpp +645 -0
  10. imops/cpp/interp2d/delaunator/delaunator.hpp +170 -0
  11. imops/cpp/interp2d/interpolator.h +52 -0
  12. imops/cpp/interp2d/triangulator.h +198 -0
  13. imops/cpp/interp2d/utils.h +63 -0
  14. imops/cpp/main.cpp +13 -0
  15. imops/crop.py +120 -0
  16. imops/interp1d.py +207 -0
  17. imops/interp2d.py +120 -0
  18. imops/measure.py +228 -0
  19. imops/morphology.py +525 -0
  20. imops/numeric.py +384 -0
  21. imops/pad.py +253 -0
  22. imops/py.typed +0 -0
  23. imops/radon.py +247 -0
  24. imops/src/__init__.py +0 -0
  25. imops/src/_backprojection.c +27339 -0
  26. imops/src/_backprojection.cpython-312-x86_64-linux-gnu.so +0 -0
  27. imops/src/_fast_backprojection.c +27374 -0
  28. imops/src/_fast_backprojection.cpython-312-x86_64-linux-gnu.so +0 -0
  29. imops/src/_fast_measure.c +33845 -0
  30. imops/src/_fast_measure.cpython-312-x86_64-linux-gnu.so +0 -0
  31. imops/src/_fast_morphology.c +26124 -0
  32. imops/src/_fast_morphology.cpython-312-x86_64-linux-gnu.so +0 -0
  33. imops/src/_fast_numeric.c +48686 -0
  34. imops/src/_fast_numeric.cpython-312-x86_64-linux-gnu.so +0 -0
  35. imops/src/_fast_radon.c +30749 -0
  36. imops/src/_fast_radon.cpython-312-x86_64-linux-gnu.so +0 -0
  37. imops/src/_fast_zoom.c +57238 -0
  38. imops/src/_fast_zoom.cpython-312-x86_64-linux-gnu.so +0 -0
  39. imops/src/_measure.c +33810 -0
  40. imops/src/_measure.cpython-312-x86_64-linux-gnu.so +0 -0
  41. imops/src/_morphology.c +26089 -0
  42. imops/src/_morphology.cpython-312-x86_64-linux-gnu.so +0 -0
  43. imops/src/_numba_zoom.py +503 -0
  44. imops/src/_numeric.c +48651 -0
  45. imops/src/_numeric.cpython-312-x86_64-linux-gnu.so +0 -0
  46. imops/src/_radon.c +30714 -0
  47. imops/src/_radon.cpython-312-x86_64-linux-gnu.so +0 -0
  48. imops/src/_zoom.c +57203 -0
  49. imops/src/_zoom.cpython-312-x86_64-linux-gnu.so +0 -0
  50. imops/testing.py +57 -0
  51. imops/utils.py +205 -0
  52. imops/zoom.py +297 -0
  53. imops-0.8.8.dist-info/LICENSE +21 -0
  54. imops-0.8.8.dist-info/METADATA +218 -0
  55. imops-0.8.8.dist-info/RECORD +58 -0
  56. imops-0.8.8.dist-info/WHEEL +6 -0
  57. imops-0.8.8.dist-info/top_level.txt +2 -0
  58. imops.libs/libgomp-a34b3233.so.1.0.0 +0 -0
@@ -0,0 +1,170 @@
1
+ // MIT License
2
+
3
+ // Copyright (c) 2018 Volodymyr Bilonenko
4
+
5
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ // of this software and associated documentation files (the "Software"), to deal
7
+ // in the Software without restriction, including without limitation the rights
8
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ // copies of the Software, and to permit persons to whom the Software is
10
+ // furnished to do so, subject to the following conditions:
11
+
12
+ // The above copyright notice and this permission notice shall be included in all
13
+ // copies or substantial portions of the Software.
14
+
15
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ // SOFTWARE.
22
+
23
+
24
+ #pragma once
25
+
26
+ #ifdef DELAUNATOR_HEADER_ONLY
27
+ #define INLINE inline
28
+ #else
29
+ #define INLINE
30
+ #endif
31
+
32
+ #include <limits>
33
+ #include <vector>
34
+ #include <ostream>
35
+
36
+ namespace delaunator {
37
+
38
+ constexpr std::size_t INVALID_INDEX =
39
+ (std::numeric_limits<std::size_t>::max)();
40
+
41
+ class Point
42
+ {
43
+ public:
44
+ Point(double x, double y) : m_x(x), m_y(y)
45
+ {}
46
+ Point() : m_x(0), m_y(0)
47
+ {}
48
+
49
+ double x() const
50
+ { return m_x; }
51
+
52
+ double y() const
53
+ { return m_y; }
54
+
55
+ double magnitude2() const
56
+ { return m_x * m_x + m_y * m_y; }
57
+
58
+ static double determinant(const Point& p1, const Point& p2)
59
+ {
60
+ return p1.m_x * p2.m_y - p1.m_y * p2.m_x;
61
+ }
62
+
63
+ static Point vector(const Point& p1, const Point& p2)
64
+ {
65
+ return Point(p2.m_x - p1.m_x, p2.m_y - p1.m_y);
66
+ }
67
+
68
+ static double dist2(const Point& p1, const Point& p2)
69
+ {
70
+ Point vec = vector(p1, p2);
71
+ return vec.m_x * vec.m_x + vec.m_y * vec.m_y;
72
+ }
73
+
74
+ static bool equal(const Point& p1, const Point& p2, double span)
75
+ {
76
+ double dist = dist2(p1, p2) / span;
77
+
78
+ // ABELL - This number should be examined to figure how how
79
+ // it correlates with the breakdown of calculating determinants.
80
+ return dist < 1e-20;
81
+ }
82
+
83
+ private:
84
+ double m_x;
85
+ double m_y;
86
+ };
87
+
88
+ inline std::ostream& operator<<(std::ostream& out, const Point& p)
89
+ {
90
+ out << p.x() << "/" << p.y();
91
+ return out;
92
+ }
93
+
94
+
95
+ class Points
96
+ {
97
+ public:
98
+ using const_iterator = Point const *;
99
+
100
+ Points(const std::vector<double>& coords) : m_coords(coords)
101
+ {}
102
+
103
+ const Point& operator[](size_t offset)
104
+ {
105
+ return reinterpret_cast<const Point&>(
106
+ *(m_coords.data() + (offset * 2)));
107
+ };
108
+
109
+ Points::const_iterator begin() const
110
+ { return reinterpret_cast<const Point *>(m_coords.data()); }
111
+ Points::const_iterator end() const
112
+ { return reinterpret_cast<const Point *>(
113
+ m_coords.data() + m_coords.size()); }
114
+ size_t size() const
115
+ { return m_coords.size() / 2; }
116
+
117
+ private:
118
+ const std::vector<double>& m_coords;
119
+ };
120
+
121
+ class Delaunator {
122
+
123
+ public:
124
+ std::vector<double> const& coords;
125
+ Points m_points;
126
+
127
+ // 'triangles' stores the indices to the 'X's of the input
128
+ // 'coords'.
129
+ std::vector<std::size_t> triangles;
130
+
131
+ // 'halfedges' store indices into 'triangles'. If halfedges[X] = Y,
132
+ // It says that there's an edge from X to Y where a) X and Y are
133
+ // both indices into triangles and b) X and Y are indices into different
134
+ // triangles in the array. This allows you to get from a triangle to
135
+ // its adjacent triangle. If the a triangle edge has no adjacent triangle,
136
+ // its half edge will be INVALID_INDEX.
137
+ std::vector<std::size_t> halfedges;
138
+
139
+ std::vector<std::size_t> hull_prev;
140
+ std::vector<std::size_t> hull_next;
141
+
142
+ // This contains indexes into the triangles array.
143
+ std::vector<std::size_t> hull_tri;
144
+ std::size_t hull_start;
145
+
146
+ INLINE Delaunator(std::vector<double> const& in_coords);
147
+ INLINE double get_hull_area();
148
+ INLINE double get_triangle_area();
149
+
150
+ private:
151
+ std::vector<std::size_t> m_hash;
152
+ Point m_center;
153
+ std::size_t m_hash_size;
154
+ std::vector<std::size_t> m_edge_stack;
155
+
156
+ INLINE std::size_t legalize(std::size_t a);
157
+ INLINE std::size_t hash_key(double x, double y) const;
158
+ INLINE std::size_t add_triangle(
159
+ std::size_t i0,
160
+ std::size_t i1,
161
+ std::size_t i2,
162
+ std::size_t a,
163
+ std::size_t b,
164
+ std::size_t c);
165
+ INLINE void link(std::size_t a, std::size_t b);
166
+ };
167
+
168
+ } //namespace delaunator
169
+
170
+ #undef INLINE
@@ -0,0 +1,52 @@
1
+ #include "triangulator.h"
2
+
3
+ class Interpolator {
4
+
5
+ public:
6
+ using pyarr_double = py::array_t<double, py::array::c_style | py::array::forcecast>;
7
+ const Triangulator triangulation;
8
+ Interpolator(const Triangulator::pyarr_size_t& pypoints, int n_jobs,
9
+ std::optional<Triangulator::pyarr_size_t> pytriangles)
10
+ : triangulation(pypoints, n_jobs, pytriangles) {
11
+ }
12
+
13
+ pyarr_double operator()(const Triangulator::pyarr_size_t& int_points,
14
+ const pyarr_double& values, const Triangulator::pyarr_size_t& neighbors,
15
+ double fill_value = 0.0) {
16
+ if (static_cast<long>(triangulation.points.size() / 2) != values.shape(0)) {
17
+ throw std::invalid_argument("Length mismatch between known points and their values");
18
+ }
19
+ if (neighbors.shape(0) != int_points.shape(0)) {
20
+ throw std::invalid_argument("Length mismatch between int_points and their neighbors");
21
+ }
22
+
23
+ size_t n = int_points.shape(0);
24
+ std::vector<double> int_values(n);
25
+
26
+ omp_set_dynamic(0); // Explicitly disable dynamic teams
27
+ omp_set_num_threads(triangulation.n_jobs_);
28
+
29
+ #pragma parallel for
30
+ for (size_t i = 0; i < n; ++i) {
31
+ size_t x = int_points.at(i, 0);
32
+ size_t y = int_points.at(i, 1);
33
+ auto location_info = triangulation.locate_point(x, y, neighbors.at(i));
34
+ if (location_info.has_value()) {
35
+ size_t t = location_info->first;
36
+ auto& coords_info = location_info->second;
37
+ double one_over_2area = 1.0 / static_cast<double>(coords_info[3]);
38
+ double lambda_1 = static_cast<double>(coords_info[0]);
39
+ double lambda_2 = static_cast<double>(coords_info[1]);
40
+ double lambda_3 = static_cast<double>(coords_info[2]);
41
+ double f1 = values.at(triangulation.triangles.at(t));
42
+ double f2 = values.at(triangulation.triangles.at(t + 1));
43
+ double f3 = values.at(triangulation.triangles.at(t + 2));
44
+ int_values[i] = (f1 * lambda_1 + f2 * lambda_2 + f3 * lambda_3) * one_over_2area;
45
+ } else {
46
+ int_values[i] = fill_value;
47
+ }
48
+ }
49
+
50
+ return {{n}, {sizeof(double)}, int_values.data()};
51
+ }
52
+ };
@@ -0,0 +1,198 @@
1
+ #include <omp.h>
2
+ #include <memory>
3
+ #include <array>
4
+ #include <pybind11/pybind11.h>
5
+ #include <pybind11/numpy.h>
6
+ #include <pybind11/embed.h>
7
+ #include <pybind11/stl.h>
8
+ #include "delaunator/delaunator-header-only.hpp"
9
+ #include "utils.h"
10
+
11
+ namespace py = pybind11;
12
+
13
+ class Triangulator {
14
+ private:
15
+ size_t n_jobs_;
16
+ friend class Interpolator;
17
+
18
+ public:
19
+ using pyarr_size_t = py::array_t<size_t, py::array::c_style | py::array::forcecast>;
20
+ std::vector<size_t> points;
21
+ std::vector<size_t> triangles;
22
+ std::vector<std::vector<size_t>> point2tri;
23
+ std::unordered_map<uint64_t, std::array<size_t, 2>> edge2tri;
24
+
25
+ Triangulator(const pyarr_size_t& pypoints, int n_jobs,
26
+ std::optional<pyarr_size_t> pytriangles) {
27
+ if (n_jobs <= 0 and n_jobs != -1) {
28
+ throw std::invalid_argument(
29
+ "Invalid number of workers, has to be -1 or positive integer");
30
+ }
31
+ if (pytriangles.has_value()) {
32
+ if (pytriangles->shape(1) != 3 or pytriangles->shape(0) == 0 or
33
+ pytriangles->size() / 3 != pytriangles->shape(0)) {
34
+ throw std::invalid_argument("Passed triangles argument has an incorrect shape");
35
+ }
36
+ }
37
+
38
+ n_jobs_ = n_jobs == -1 ? omp_get_num_procs() : n_jobs;
39
+ size_t n = pypoints.shape(0);
40
+
41
+ points.resize(2 * n);
42
+
43
+ omp_set_dynamic(0); // Explicitly disable dynamic teams
44
+ omp_set_num_threads(n_jobs_);
45
+
46
+ if (pytriangles.has_value()) {
47
+ #pragma parallel for
48
+ for (size_t i = 0; i < n; ++i) {
49
+ size_t j = 2 * i;
50
+ points.at(j) = pypoints.at(i, 0);
51
+ points.at(j + 1) = pypoints.at(i, 1);
52
+ }
53
+
54
+ size_t m = pytriangles->shape(0);
55
+ triangles.resize(3 * m);
56
+
57
+ #pragma parallel for
58
+ for (size_t i = 0; i < m; ++i) {
59
+ size_t j = 3 * i;
60
+ triangles.at(j) = pytriangles->at(i, 0);
61
+ triangles.at(j + 1) = pytriangles->at(i, 1);
62
+ triangles.at(j + 2) = pytriangles->at(i, 2);
63
+ }
64
+ }
65
+
66
+ else {
67
+ std::vector<double> double_points(2 * n);
68
+ #pragma parallel for
69
+ for (size_t i = 0; i < n; ++i) {
70
+ double_points.at(2 * i) = static_cast<double>(pypoints.at(i, 0));
71
+ double_points.at(2 * i + 1) = static_cast<double>(pypoints.at(i, 1));
72
+ }
73
+ delaunator::Delaunator delaunated(double_points);
74
+ triangles = std::move(delaunated.triangles);
75
+
76
+ #pragma parallel for
77
+ for (size_t i = 0; i < n; ++i) {
78
+ size_t j = 2 * i;
79
+ points.at(j) = static_cast<size_t>(delaunated.coords.at(j));
80
+ points.at(j + 1) = static_cast<size_t>(delaunated.coords.at(j + 1));
81
+ }
82
+ }
83
+
84
+ point2tri.resize(n);
85
+ for (size_t i = 0; i < triangles.size() / 3; ++i) {
86
+ size_t t = 3 * i;
87
+ for (size_t j = 0; j < 3; ++j) {
88
+ size_t a = triangles.at(t + j);
89
+ size_t b = triangles.at(t + fast_mod(j + 1, 3));
90
+ point2tri.at(a).push_back(t);
91
+ uint64_t e = a < b ? elegant_pair(a, b) : elegant_pair(b, a);
92
+ auto got = edge2tri.find(e);
93
+ if (got != edge2tri.end()) {
94
+ got->second.at(1) = t;
95
+ } else {
96
+ edge2tri[e] = {t, t};
97
+ }
98
+ }
99
+ }
100
+ }
101
+
102
+ inline std::optional<std::pair<size_t, std::array<int64_t, 4>>> locate_point(
103
+ size_t x, size_t y, size_t neighbor) const {
104
+ size_t nx = points.at(2 * neighbor);
105
+ size_t ny = points.at(2 * neighbor + 1);
106
+ int64_t curr_t = -1;
107
+ size_t curr_a, curr_b;
108
+ for (size_t t : point2tri.at(neighbor)) {
109
+ auto coords_info = barycentric_coords(x, y, t);
110
+ if (point_in_triangle(coords_info)) {
111
+ return {std::make_pair(t, coords_info)};
112
+ }
113
+ for (size_t j = 0; j < 3; ++j) {
114
+ size_t a = triangles.at(t + j);
115
+ size_t b = triangles.at(t + fast_mod(j + 1, 3));
116
+ if (a == neighbor or b == neighbor) {
117
+ continue;
118
+ }
119
+ size_t ax = points.at(2 * a);
120
+ size_t ay = points.at(2 * a + 1);
121
+ size_t bx = points.at(2 * b);
122
+ size_t by = points.at(2 * b + 1);
123
+ if (segments_intersection(nx, ny, x, y, ax, ay, bx, by)) {
124
+ curr_t = static_cast<int64_t>(t);
125
+ curr_a = a;
126
+ curr_b = b;
127
+ if (curr_a > curr_b) {
128
+ std::swap(curr_a, curr_b);
129
+ }
130
+ break;
131
+ }
132
+ }
133
+ if (curr_t != -1) {
134
+ break;
135
+ }
136
+ }
137
+
138
+ if (curr_t == -1) {
139
+ return std::nullopt;
140
+ }
141
+
142
+ while (true) {
143
+ uint64_t e = elegant_pair(curr_a, curr_b); // already curr_a < curr_b
144
+ auto& adj_t = edge2tri.at(e);
145
+ if (adj_t.at(0) == adj_t.at(1)) {
146
+ return std::nullopt;
147
+ }
148
+ size_t t = (adj_t.at(0) == static_cast<size_t>(curr_t)) ? adj_t.at(1) : adj_t.at(0);
149
+ auto coords_info = barycentric_coords(x, y, t);
150
+ if (point_in_triangle(coords_info)) {
151
+ return {std::make_pair(t, coords_info)};
152
+ }
153
+ for (size_t i = 0; i < 3; ++i) {
154
+ size_t a = triangles.at(t + i);
155
+ size_t b = triangles.at(t + fast_mod(i + 1, 3));
156
+ if (a > b) {
157
+ std::swap(a, b);
158
+ }
159
+ if (a == curr_a and b == curr_b) {
160
+ continue;
161
+ }
162
+ size_t ax = points.at(2 * a);
163
+ size_t ay = points.at(2 * a + 1);
164
+ size_t bx = points.at(2 * b);
165
+ size_t by = points.at(2 * b + 1);
166
+ if (segments_intersection(nx, ny, x, y, ax, ay, bx, by)) {
167
+ curr_t = static_cast<int64_t>(t);
168
+ curr_a = a;
169
+ curr_b = b;
170
+ break;
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ inline std::array<int64_t, 4> barycentric_coords(size_t x, size_t y, size_t t) const {
177
+ size_t r1 = 2 * triangles.at(t);
178
+ auto x1 = static_cast<int64_t>(points.at(r1));
179
+ auto y1 = static_cast<int64_t>(points.at(r1 + 1));
180
+ size_t r2 = 2 * triangles.at(t + 1);
181
+ auto x2 = static_cast<int64_t>(points.at(r2));
182
+ auto y2 = static_cast<int64_t>(points.at(r2 + 1));
183
+ size_t r3 = 2 * triangles.at(t + 2);
184
+ auto x3 = static_cast<int64_t>(points.at(r3));
185
+ auto y3 = static_cast<int64_t>(points.at(r3 + 1));
186
+ auto x1y2 = x1 * y2, x1y3 = x1 * y3;
187
+ auto x2y3 = x2 * y3, x2y1 = x2 * y1;
188
+ auto x3y1 = x3 * y1, x3y2 = x3 * y2;
189
+ int64_t xx = static_cast<int64_t>(x);
190
+ int64_t yy = static_cast<int64_t>(y);
191
+ std::array<int64_t, 4> coords_info;
192
+ coords_info[0] = x2y3 - x3y2 + (y2 - y3) * xx + (x3 - x2) * yy;
193
+ coords_info[1] = x3y1 - x1y3 + (y3 - y1) * xx + (x1 - x3) * yy;
194
+ coords_info[2] = x1y2 - x2y1 + (y1 - y2) * xx + (x2 - x1) * yy;
195
+ coords_info[3] = x1y2 - x1y3 + x2y3 - x2y1 + x3y1 - x3y2;
196
+ return coords_info;
197
+ }
198
+ };
@@ -0,0 +1,63 @@
1
+ // Szudzik’s Pairing Function https://en.wikipedia.org/wiki/Pairing_function#Other_pairing_functions
2
+ inline uint64_t elegant_pair(size_t a, size_t b) {
3
+ return static_cast<uint64_t>(b) * b + a; // implicit cast
4
+ }
5
+
6
+ inline size_t fast_mod(const size_t i, const size_t c) {
7
+ return i >= c ? i % c : i;
8
+ }
9
+
10
+ inline bool on_segment(size_t Ax, size_t Ay, size_t Bx, size_t By, size_t Cx, size_t Cy) {
11
+ return Bx <= std::max(Ax, Cx) and Bx >= std::min(Ax, Cx) and By <= std::max(Ay, Cy) and
12
+ By >= std::min(Ay, Cy);
13
+ }
14
+
15
+ inline int32_t orientation(int64_t Ax, int64_t Ay, int64_t Bx, int64_t By, int64_t Cx, int64_t Cy) {
16
+ int64_t val = (By - Ay) * (Cx - Bx) - (Bx - Ax) * (Cy - By);
17
+ if (val > 0) {
18
+ return 1;
19
+ } else if (val < 0) {
20
+ return 2;
21
+ }
22
+ return 0;
23
+ }
24
+
25
+ inline bool segments_intersection(size_t Ax, size_t Ay, size_t Bx, size_t By, size_t Cx, size_t Cy,
26
+ size_t Dx, size_t Dy) {
27
+ auto o1 = orientation(Ax, Ay, Bx, By, Cx, Cy);
28
+ auto o2 = orientation(Ax, Ay, Bx, By, Dx, Dy);
29
+ auto o3 = orientation(Cx, Cy, Dx, Dy, Ax, Ay);
30
+ auto o4 = orientation(Cx, Cy, Dx, Dy, Bx, By);
31
+ if ((o1 != o2) and (o3 != o4)) {
32
+ return true;
33
+ }
34
+
35
+ if ((o1 == 0) and on_segment(Ax, Ay, Cx, Cy, Bx, By)) {
36
+ return true;
37
+ }
38
+
39
+ if ((o2 == 0) and on_segment(Ax, Ay, Dx, Dy, Bx, By)) {
40
+ return true;
41
+ }
42
+
43
+ if ((o3 == 0) and on_segment(Cx, Cy, Ax, Ay, Dx, Dy)) {
44
+ return true;
45
+ }
46
+
47
+ if ((o4 == 0) and on_segment(Cx, Cy, Bx, By, Dx, Dy)) {
48
+ return true;
49
+ }
50
+
51
+ return false;
52
+ }
53
+
54
+ inline bool point_in_triangle(const std::array<int64_t, 4>& coords_info) {
55
+ if (coords_info[3] < 0) {
56
+ return coords_info[3] <= coords_info[0] and coords_info[0] <= 0 and
57
+ coords_info[3] <= coords_info[1] and coords_info[1] <= 0 and
58
+ coords_info[3] <= coords_info[2] and coords_info[2] <= 0;
59
+ }
60
+ return coords_info[3] >= coords_info[0] and coords_info[0] >= 0 and
61
+ coords_info[3] >= coords_info[1] and coords_info[1] >= 0 and
62
+ coords_info[3] >= coords_info[2] and coords_info[2] >= 0;
63
+ }
imops/cpp/main.cpp ADDED
@@ -0,0 +1,13 @@
1
+ #include <cassert>
2
+ #include <iostream>
3
+ #include <stdexcept>
4
+ #include "interp2d/interpolator.h"
5
+ #include <Python.h>
6
+
7
+ PYBIND11_MODULE(cpp_modules, m) {
8
+ py::class_<Interpolator>(m, "Linear2DInterpolatorCpp", "Interpolator class")
9
+ .def(py::init<const Triangulator::pyarr_size_t&, int, std::optional<Triangulator::pyarr_size_t>>())
10
+ .def("__call__", &Interpolator::operator());
11
+ }
12
+
13
+ int main() {}
imops/crop.py ADDED
@@ -0,0 +1,120 @@
1
+ import numpy as np
2
+
3
+ from .backend import BackendLike
4
+ from .numeric import _NUMERIC_DEFAULT_NUM_THREADS
5
+ from .pad import pad
6
+ from .utils import AxesLike, AxesParams, assert_subdtype, broadcast_axis, fill_by_indices
7
+
8
+
9
+ def crop_to_shape(x: np.ndarray, shape: AxesLike, axis: AxesLike = None, ratio: AxesParams = 0.5) -> np.ndarray:
10
+ """
11
+ Crop `x` to match `shape` along `axis`.
12
+
13
+ Parameters
14
+ ----------
15
+ x: np.ndarray
16
+ n-dimensional array
17
+ shape: AxesLike
18
+ final shape
19
+ axis: AxesLike
20
+ axis along which `x` will be padded
21
+ ratio: AxesParams
22
+ float or sequence of floats describing what proportion of cropping to apply on the left sides of cropping axes.
23
+ Remaining ratio of cropping will be applied on the right sides
24
+
25
+ Returns
26
+ -------
27
+ cropped: np.ndarray
28
+ cropped array
29
+
30
+ Examples
31
+ --------
32
+ ```python
33
+ x # array of shape [2, 3, 4]
34
+ cropped = crop_to_shape(x, [1, 2, 3], ratio=0) # crop to shape [1, 2, 3] from the right
35
+ cropped = crop_to_shape(x, 2, axis=1, ratio=1) # crop to shape [2, 2, 4] from the left
36
+ cropped = crop_to_shape(x, [3, 4, 5]) # fail due to bigger resulting shape
37
+ ```
38
+ """
39
+ x = np.asarray(x)
40
+ shape = np.asarray(shape)
41
+ assert_subdtype(shape.dtype, np.integer, 'shape')
42
+
43
+ axis, shape, ratio = broadcast_axis(axis, x.ndim, shape, ratio)
44
+
45
+ old_shape, new_shape = np.array(x.shape), np.array(fill_by_indices(x.shape, shape, axis))
46
+ if (old_shape < new_shape).any():
47
+ raise ValueError(f'The resulting shape cannot be greater than the original one: {old_shape} vs {new_shape}.')
48
+
49
+ ndim = len(x.shape)
50
+ ratio = fill_by_indices(np.zeros(ndim), ratio, axis)
51
+ start = ((old_shape - new_shape) * ratio).astype(int)
52
+
53
+ # TODO: Create contiguous array?
54
+ return x[tuple(map(slice, start, start + new_shape))]
55
+
56
+
57
+ def crop_to_box(
58
+ x: np.ndarray,
59
+ box: np.ndarray,
60
+ axis: AxesLike = None,
61
+ padding_values: AxesParams = None,
62
+ num_threads: int = _NUMERIC_DEFAULT_NUM_THREADS,
63
+ backend: BackendLike = None,
64
+ ) -> np.ndarray:
65
+ """
66
+ Crop `x` according to `box` along `axis`.
67
+
68
+ Parameters
69
+ ----------
70
+ x: np.ndarray
71
+ n-dimensional array
72
+ box: np.ndarray
73
+ array of shape (2, x.ndim or len(axis) if axis is passed) describing crop boundaries
74
+ axis: AxesLike
75
+ axis along which `x` will be cropped
76
+ padding_values: AxesParams
77
+ values to pad with if box exceeds the input's limits
78
+ num_threads: int
79
+ the number of threads to use for computation. Default = 4. If negative value passed
80
+ cpu count + num_threads + 1 threads will be used
81
+ backend: BackendLike
82
+ which backend to use. `cython` and `scipy` are available, `cython` is used by default
83
+
84
+ Returns
85
+ -------
86
+ cropped: np.ndarray
87
+ cropped array
88
+
89
+ Examples
90
+ --------
91
+ ```python
92
+ x # array of shape [2, 3, 4]
93
+ cropped = crop_to_box(x, np.array([[0, 0, 0], [1, 1, 1]])) # crop to shape [1, 1, 1]
94
+ cropped = crop_to_box(x, np.array([[0, 0, 0], [5, 5, 5]])) # fail, box exceeds the input's limits
95
+ cropped = crop_to_box(x, np.array([[0], [5]]), axis=0, padding_values=0) # pad with 0-s to shape [5, 3, 4]
96
+ ```
97
+ """
98
+ x = np.asarray(x)
99
+ box = np.asarray(box)
100
+ assert_subdtype(box.dtype, np.integer, 'box')
101
+
102
+ start, stop = box
103
+ axis, start, stop = broadcast_axis(axis, x.ndim, start, stop)
104
+
105
+ slice_start = np.maximum(start, 0)
106
+ slice_stop = np.minimum(stop, np.array(x.shape)[list(axis)])
107
+ padding = np.array([slice_start - start, stop - slice_stop], dtype=int).T
108
+
109
+ if padding_values is None and padding.any():
110
+ raise ValueError(f"The box {box} exceeds the input's limits {x.shape}.")
111
+
112
+ slice_start = fill_by_indices(np.zeros(x.ndim, int), slice_start, axis)
113
+ slice_stop = fill_by_indices(x.shape, slice_stop, axis)
114
+ # TODO: Create contiguous array?
115
+ x = x[tuple(map(slice, slice_start, slice_stop))]
116
+
117
+ if padding_values is not None and padding.any():
118
+ x = pad(x, padding, axis, padding_values, num_threads=num_threads, backend=backend)
119
+
120
+ return x