Skip to content

Commit 45044a2

Browse files
committed
working on numpy indexing for images (setitem)
1 parent 8d9c56a commit 45044a2

File tree

1 file changed

+191
-5
lines changed

1 file changed

+191
-5
lines changed

modules/python/bindings/include/core/images.hpp

+191-5
Original file line numberDiff line numberDiff line change
@@ -92,18 +92,200 @@ void define_get_item_2d_image(py::class_<vpImage<T>, std::shared_ptr<vpImage<T>>
9292
return (py::cast(self).template cast<np_array_cf<NpRep> >())[tuple].template cast<py::array_t<NpRep>>();
9393
}, py::keep_alive<0, 1>());
9494
}
95+
96+
std::tuple<int, int, int, int> solveSliceIndices(py::slice slice, unsigned int size)
97+
{
98+
99+
py::handle start = slice.attr("start"), end = slice.attr("stop"), step = slice.attr("step");
100+
101+
int startI = 0, endI = size, stepI = 1;
102+
if (!start.is(py::none())) {
103+
startI = py::cast<int>(start);
104+
if (startI < 0) {
105+
startI = size + startI;
106+
}
107+
108+
if (startI >= size) {
109+
throw std::runtime_error("Invalid slice indexing out of array");
110+
}
111+
}
112+
113+
if (!end.is(py::none())) {
114+
endI = py::cast<int>(end);
115+
if (endI < 0) {
116+
endI = size + endI;
117+
}
118+
119+
if (endI >= size) {
120+
throw std::runtime_error("Invalid slice indexing out of array");
121+
}
122+
}
123+
124+
if (!step.is(py::none())) {
125+
stepI = py::cast<int>(step);
126+
if (stepI <= 0) {
127+
throw std::runtime_error("Slice indexing: negative or zero step not supported!");
128+
}
129+
}
130+
131+
if (endI < startI) {
132+
throw std::runtime_error("Slice indexing: end index is lower than start index");
133+
}
134+
135+
int count;
136+
137+
if (stepI > endI - startI) {
138+
count = 1;
139+
}
140+
else {
141+
count = (endI - startI) / stepI;
142+
int endS = startI + t * stepI;
143+
count = endS <= endI ? t : t - 1;
144+
}
145+
146+
return std::make_tuple(startI, endI, stepI, count);
147+
}
148+
95149
/*
96150
* Image 2D indexing
97151
*/
98152
template<typename T, typename NpRep>
99-
void define_set_item_2d_image(py::class_<vpImage<T>, std::shared_ptr<vpImage<T>>> &pyClass)
153+
void define_set_item_2d_image(py::class_<vpImage<T>, std::shared_ptr<vpImage<T>>> &pyClass, unsigned int componentsPerPixel)
100154
{
155+
pyClass.def("__setitem__", [](vpImage<T> &self, std::pair<int, int> pair, const T &value) {
156+
int i = pair.first, j = pair.second;
157+
const int rows = (int)self.getRows(), cols = (int)self.getCols();
158+
if (i >= rows || j >= cols || i < -rows || j < -cols) {
159+
std::stringstream ss;
160+
ss << "Invalid indexing into a 2D image: got indices " << shape_to_string({ i, j })
161+
<< " but image has dimensions " << shape_to_string({ rows, cols });
162+
throw std::runtime_error(ss.str());
163+
}
164+
if (i < 0) {
165+
i = rows + i;
166+
}
167+
if (j < 0) {
168+
j = cols + j;
169+
}
170+
self[i][j] = value;
171+
});
172+
pyClass.def("__setitem__", [](vpImage<T> &self, int i, const T &value) {
173+
const int rows = (int)self.getRows();
174+
if (i >= rows || i < -rows) {
175+
std::stringstream ss;
176+
ss << "Invalid indexing into a 2D image: got indices (" << i << ", :)"
177+
<< " but image has dimensions " << shape_to_string({ rows, self.getCols() });
178+
throw std::runtime_error(ss.str());
179+
}
180+
if (i < 0) {
181+
i = rows + i;
182+
}
183+
T *row = self[i];
184+
for (int j = 0; j < self.getCols(); ++j) {
185+
row[j] = value;
186+
}
187+
});
188+
pyClass.def("__setitem__", [](vpImage<T> &self, py::slice slice, const T &value) {
189+
int rowStart, rowEnd, rowStep;
190+
std::tie(rowStart, rowEnd, rowStep) = solveSliceIndices(slice, self.getRows());
191+
for (int i = rowStart; i < rowEnd; i += rowStep) {
192+
T *row = self[i];
193+
for (int j = 0; j < self.getCols(); ++j) {
194+
row[j] = value;
195+
}
196+
}
197+
});
198+
pyClass.def("__setitem__", [](vpImage<T> &self, std::tuple<py::slice, py::slice> slices, const T &value) {
199+
py::slice sliceRows, sliceCols;
200+
int rowStart, rowEnd, rowStep;
201+
int colStart, colEnd, colStep;
202+
std::tie(sliceRows, sliceCols) = slices;
203+
std::tie(rowStart, rowEnd, rowStep) = solveSliceIndices(sliceRows, self.getRows());
204+
std::tie(colStart, colEnd, colStep) = solveSliceIndices(sliceCols, self.getCols());
205+
206+
for (int i = rowStart; i < rowEnd; i += rowStep) {
207+
T *row = self[i];
208+
for (unsigned int j = colStart; j < colEnd; j += colStep) {
209+
row[j] = value;
210+
}
211+
}
212+
});
213+
101214

215+
if (componentsPerPixel == 1) {
216+
pyClass.def("__setitem__", [](vpImage<T> &self, py::slice sliceRows, py::array<NpRep, py::array::c_style> &values) {
217+
int rowStart, rowEnd, rowStep;
218+
std::tie(sliceRows, sliceCols) = slices;
219+
std::tie(rowStart, rowEnd, rowStep) = solveSliceIndices(sliceRows, self.getRows());
220+
221+
py::buffer_info values_info = values.request();
222+
223+
// Copy the array into each row (same values in each row)
224+
if (values_info.ndim == 1) {
225+
if (values_info.shape[0] != self.getCols()) {
226+
throw std::runtime_error("Number of image columns and NumPy array dimension do not match");
227+
}
228+
229+
const NpRep *value_ptr = static_cast<NpRep *>(valuesInfo.ptr);
230+
231+
for (int i = rowStart; i < rowEnd; i += rowStep) {
232+
T *row = self[i];
233+
unsigned int k = 0;
234+
for (unsigned int j = colStart; j < colEnd; j += colStep) {
235+
row[j] = value_ptr[k++];
236+
}
237+
}
238+
}
239+
// 2D array to 2D array
240+
else if (values_info.ndim == 2) {
241+
unsigned int numAssignedRows = 0;
242+
if (values_info.shape[0] != numAssignedRows || values_info.shape[1] != self.getCols()) {
243+
throw std::runtime_error("Indexing into 2D image: NumPy array has wrong size");
244+
}
245+
const NpRep *value_ptr = static_cast<NpRep *>(valuesInfo.ptr);
246+
247+
unsigned int k = 0;
248+
for (int i = rowStart; i < rowEnd; i += rowStep) {
249+
T *row = self[i];
250+
for (unsigned int j = colStart; j < colEnd; j += colStep) {
251+
row[j] = value_ptr[k++];
252+
}
253+
}
254+
255+
}
256+
else {
257+
throw std::runtime_error("Cannot write into 2D raw type image with multidimensional NumPy array that has more than 2 dimensions");
258+
}
259+
});
260+
261+
}
262+
263+
// Handle vprgba/vprgbf
264+
if (componentsPerPixel > 1) {
265+
// pyClass.def("__setitem__", [](vpImage<T> &self, std::tuple<py::slice, py::slice> slices, const T &value) {
266+
// py::slice sliceRows, sliceCols;
267+
// int rowStart, rowEnd, rowStep;
268+
// int colStart, colEnd, colStep;
269+
// std::tie(sliceRows, sliceCols) = slices;
270+
// std::tie(rowStart, rowEnd, rowStep) = solveSliceIndices(sliceRows, self.getRows());
271+
// std::tie(colStart, colEnd, colStep) = solveSliceIndices(sliceCols, self.getCols());
272+
273+
// for (int i = rowStart; i < rowEnd; i += rowStep) {
274+
// T *row = self[i];
275+
// for (unsigned int j = colStart; j < colEnd; j += colStep) {
276+
// row[j] = value;
277+
// }
278+
// }
279+
// });
280+
281+
282+
}
102283
}
103284

104-
/*
105-
* vpImage
106-
*/
285+
286+
/*
287+
* vpImage
288+
*/
107289
template<typename T>
108290
typename std::enable_if<std::is_fundamental<T>::value, void>::type
109291
bindings_vpImage(py::class_<vpImage<T>, std::shared_ptr<vpImage<T>>> &pyImage)
@@ -129,7 +311,7 @@ Construct an image by **copying** a 2D numpy array.
129311
)doc", py::arg("np_array"));
130312

131313
define_get_item_2d_image<T, T>(pyImage);
132-
define_set_item_2d_image<T, T>(pyImage);
314+
define_set_item_2d_image<T, T>(pyImage, 1);
133315

134316
pyImage.def("__repr__", [](const vpImage<T> &self) -> std::string {
135317
std::stringstream ss;
@@ -175,6 +357,8 @@ where the 4 denotes the red, green, blue and alpha components of the image.
175357
176358
)doc", py::arg("np_array"));
177359
define_get_item_2d_image<T, NpRep>(pyImage);
360+
define_set_item_2d_image<T, NpRep>(pyImage, sizeof(T) / sizeof(NpRep));
361+
178362

179363
pyImage.def("__repr__", [](const vpImage<T> &self) -> std::string {
180364
std::stringstream ss;
@@ -219,7 +403,9 @@ where the 3 denotes the red, green and blue components of the image.
219403
:param np_array: The numpy array to copy.
220404
221405
)doc", py::arg("np_array"));
406+
222407
define_get_item_2d_image<T, NpRep>(pyImage);
408+
define_set_item_2d_image<T, NpRep>(pyImage, sizeof(T) / sizeof(NpRep));
223409

224410
pyImage.def("__repr__", [](const vpImage<T> &self) -> std::string {
225411
std::stringstream ss;

0 commit comments

Comments
 (0)