Skip to content

Commit 125af7d

Browse files
Issue 2075 : Apply RGB on transpose matrices. (#2206)
* adding unit test for applyRGB buffer, check in the applyRGB if the buffer is C order or not, for now throw error Signed-off-by: Afsaneh Sheikhmiri <[email protected]> * Adsk Contrib - Detect C/F order, if not C order : RuntimeError shown, test is still not passing Signed-off-by: Afsaneh Sheikhmiri <[email protected]> * remove 1d arrays from the test, since even after Transpose, 1d arrays are still contiguous Signed-off-by: Afsaneh Sheikhmiri <[email protected]> --------- Signed-off-by: Afsaneh Sheikhmiri <[email protected]> Co-authored-by: Doug Walker <[email protected]>
1 parent a9ca7e5 commit 125af7d

File tree

4 files changed

+99
-2
lines changed

4 files changed

+99
-2
lines changed

src/bindings/python/PyCPUProcessor.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,10 @@ written to the dstImgDesc image, leaving srcImgDesc unchanged.
9696
py::buffer_info info = data.request();
9797
checkBufferDivisible(info, 3);
9898

99-
// Interpret as single row of RGB pixels
99+
// --- detect C-contiguous ---
100+
checkCContiguousArray(info);
101+
102+
// --- proceed normally ---
100103
BitDepth bitDepth = getBufferBitDepth(info);
101104

102105
py::gil_scoped_release release;
@@ -115,6 +118,7 @@ written to the dstImgDesc image, leaving srcImgDesc unchanged.
115118
chanStrideBytes,
116119
xStrideBytes,
117120
yStrideBytes);
121+
118122
self->apply(img);
119123
},
120124
"data"_a,
@@ -171,6 +175,8 @@ float values is returned, leaving the input list unchanged.
171175
py::buffer_info info = data.request();
172176
checkBufferDivisible(info, 4);
173177

178+
// --- detect C-contiguous ---
179+
checkCContiguousArray(info);
174180
// Interpret as single row of RGBA pixels
175181
BitDepth bitDepth = getBufferBitDepth(info);
176182

src/bindings/python/PyUtils.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,27 @@ void checkBufferType(const py::buffer_info & info, BitDepth bitDepth)
179179
checkBufferType(info, bitDepthToDtype(bitDepth));
180180
}
181181

182+
void checkCContiguousArray(const py::buffer_info & info)
183+
{
184+
bool isC = true;
185+
ptrdiff_t itemsize = info.itemsize;
186+
auto shape = info.shape;
187+
auto strides = info.strides;
188+
py::ssize_t ndim = info.ndim;
189+
190+
ptrdiff_t expected = itemsize;
191+
for (py::ssize_t i = ndim - 1; i >= 0; --i)
192+
{
193+
if (strides[i] != expected) { isC = false; break; }
194+
expected *= shape[i];
195+
}
196+
197+
if (!isC)
198+
{
199+
throw std::runtime_error("function only supports C-contiguous (row-major) arrays");
200+
}
201+
}
202+
182203
void checkBufferDivisible(const py::buffer_info & info, py::ssize_t numChannels)
183204
{
184205
if (info.size % numChannels != 0)

src/bindings/python/PyUtils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ unsigned long getBufferLut3DGridSize(const py::buffer_info & info);
8989
// Throw if vector size is not divisible by channel count
9090
void checkVectorDivisible(const std::vector<float> & pixel, size_t numChannels);
9191

92+
// Throw if array is not C-contiguous
93+
void checkCContiguousArray(const py::buffer_info & info);
94+
9295
} // namespace OCIO_NAMESPACE
9396

9497
#endif // INCLUDED_OCIO_PYUTILS_H

tests/python/CPUProcessorTest.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020

2121
class CPUProcessorTest(unittest.TestCase):
22-
FLOAT_DELTA = 1e+5
22+
FLOAT_DELTA = 1e-5
2323
UINT_DELTA = 1
2424

2525
@classmethod
@@ -385,6 +385,28 @@ def test_apply_rgb_list(self):
385385
delta=self.FLOAT_DELTA
386386
)
387387

388+
def test_apply_rgb_buffer_column_major(self):
389+
if not np:
390+
logger.warning("NumPy not found. Skipping test!")
391+
return
392+
393+
for arr, cpu_proc_fwd in [
394+
(self.float_rgb_2d, self.default_cpu_proc_fwd),
395+
(self.float_rgb_3d, self.default_cpu_proc_fwd),
396+
(self.half_rgb_2d, self.half_cpu_proc_fwd),
397+
(self.half_rgb_3d, self.half_cpu_proc_fwd),
398+
(self.uint16_rgb_2d, self.uint16_cpu_proc_fwd),
399+
(self.uint16_rgb_3d, self.uint16_cpu_proc_fwd),
400+
(self.uint8_rgb_2d, self.uint8_cpu_proc_fwd),
401+
(self.uint8_rgb_3d, self.uint8_cpu_proc_fwd),
402+
]:
403+
# Transpose to F-order (column-major)
404+
arr_copy = arr.copy().T
405+
406+
# Expect runtime error for non-C-contiguous array
407+
with self.assertRaises(RuntimeError):
408+
cpu_proc_fwd.applyRGB(arr_copy)
409+
388410
def test_apply_rgb_buffer(self):
389411
if not np:
390412
logger.warning("NumPy not found. Skipping test!")
@@ -627,3 +649,48 @@ def test_apply_rgba_buffer(self):
627649
arr.flat[i],
628650
delta=self.UINT_DELTA
629651
)
652+
653+
def test_apply_rgba_buffer_column_major(self):
654+
if not np:
655+
logger.warning("NumPy not found. Skipping test!")
656+
return
657+
658+
for arr, cpu_proc_fwd in [
659+
(
660+
self.float_rgba_2d,
661+
self.default_cpu_proc_fwd
662+
),
663+
(
664+
self.float_rgba_3d,
665+
self.default_cpu_proc_fwd
666+
),
667+
(
668+
self.half_rgba_2d,
669+
self.half_cpu_proc_fwd
670+
),
671+
(
672+
self.half_rgba_3d,
673+
self.half_cpu_proc_fwd
674+
),
675+
(
676+
self.uint16_rgba_2d,
677+
self.uint16_cpu_proc_fwd
678+
),
679+
(
680+
self.uint16_rgba_3d,
681+
self.uint16_cpu_proc_fwd
682+
),
683+
(
684+
self.uint8_rgba_2d,
685+
self.uint8_cpu_proc_fwd
686+
),
687+
(
688+
self.uint8_rgba_3d,
689+
self.uint8_cpu_proc_fwd,
690+
),
691+
]:
692+
# Transpose to F-order (column-major)
693+
arr_copy = arr.copy().T
694+
# Expect runtime error for non-C-contiguous array
695+
with self.assertRaises(RuntimeError):
696+
cpu_proc_fwd.applyRGBA(arr_copy)

0 commit comments

Comments
 (0)