From 104fe807ffed3aaaa01e145c5901aea58843c504 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 31 Oct 2022 10:14:15 -0700 Subject: [PATCH 1/3] halide.imageio needs to support arbitrary bufferviews As written, the helper code assumed that everything passed in was a numpy array of some sort; this meant that passing hl.Buffer didn't work. Restructured so that we only assume that the objects passed in satisfies the Python buffer protocol, so this should now work very generically. --- python_bindings/src/halide/imageio.py | 37 +++++++++++++++------------ 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/python_bindings/src/halide/imageio.py b/python_bindings/src/halide/imageio.py index ac927af4acc0..ab6b8551c5eb 100644 --- a/python_bindings/src/halide/imageio.py +++ b/python_bindings/src/halide/imageio.py @@ -2,55 +2,58 @@ import imageio.v2 as imageio except: import imageio -import numpy +import numpy as np def is_interleaved(im): - """If the given ndarray is 3-dimensional and appears to have an interleaved + """If the given buffer is 3-dimensional and appears to have an interleaved layout, return True. Otherwise, return False.""" # Assume that 'interleaved' will only apply to channels <= 4 - return im.ndim == 3 and im.strides[2] == 1 and im.shape[2] in [1, 2, 3, 4] + mv = memoryview(im) + return mv.ndim == 3 and mv.strides[2] == 1 and mv.shape[2] in [1, 2, 3, 4] def _as_interleaved(im): - """If the given ndarray is 3-dimensional and appears to be planar layout, + """If the given buffer is 3-dimensional and appears to be planar layout, return a view that is in interleaved form, leaving the input unchanged. - Otherwise, return the image ndarray unchanged. + Otherwise, return the image buffer unchanged. Note that this call must be used with care, as the returnee may or may not be a copy.""" - if im.ndim == 3 and not is_interleaved(im): - return numpy.moveaxis(im, 0, 2) + mv = memoryview(im) + if mv.ndim == 3 and not is_interleaved(mv): + return np.moveaxis(mv, 0, 2) else: - return im + return mv def _as_planar(im): - """If the given ndarray is 3-dimensional and appears to be interleaved + """If the given buffer is 3-dimensional and appears to be interleaved layout, return a view that is in planar form, leaving the input - unchanged. Otherwise, return the image ndarray unchanged. + unchanged. Otherwise, return the image buffer unchanged. Note that this call must be used with care, as the returnee may or may not be a copy.""" - if is_interleaved(im): - return numpy.moveaxis(im, 2, 0) + mv = memoryview(im) + if is_interleaved(mv): + return np.moveaxis(mv, 2, 0) else: - return im + return mv def copy_to_interleaved(im): - """If the given ndarray is 3-dimensional and appears to be planar + """If the given buffer is 3-dimensional and appears to be planar layout, return a copy that is in interleaved form. Otherwise, return an unchanged copy of the input. Note that this call will always return a copy, leaving the input unchanged.""" - return _as_interleaved(im).copy() + return np.copy(_as_interleaved(im)) def copy_to_planar(im): - """If the given ndarray is 3-dimensional and appears to be interleaved + """If the given buffer is 3-dimensional and appears to be interleaved layout, return a copy that is in planar form. Otherwise, return an unchanged copy of the input. Note that this call will always return a copy, leaving the input unchanged.""" - return _as_planar(im).copy() + return np.copy(_as_planar(im)) def imread(uri, format=None, **kwargs): From b7088c20f5ccac0f99b30483fd26632dbcd75d4f Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 31 Oct 2022 11:51:09 -0700 Subject: [PATCH 2/3] Update imageio.py --- python_bindings/src/halide/imageio.py | 70 ++++++++++----------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/python_bindings/src/halide/imageio.py b/python_bindings/src/halide/imageio.py index ab6b8551c5eb..6c23387d2b75 100644 --- a/python_bindings/src/halide/imageio.py +++ b/python_bindings/src/halide/imageio.py @@ -7,70 +7,52 @@ def is_interleaved(im): """If the given buffer is 3-dimensional and appears to have an interleaved - layout, return True. Otherwise, return False.""" + layout, return True. Otherwise, return False.""" # Assume that 'interleaved' will only apply to channels <= 4 mv = memoryview(im) return mv.ndim == 3 and mv.strides[2] == 1 and mv.shape[2] in [1, 2, 3, 4] -def _as_interleaved(im): - """If the given buffer is 3-dimensional and appears to be planar layout, - return a view that is in interleaved form, leaving the input unchanged. - Otherwise, return the image buffer unchanged. - Note that this call must be used with care, as the returnee may or may - not be a copy.""" +def copy_to_interleaved(im): + """If the given buffer is 3-dimensional and appears to be planar + layout, return a copy that is in interleaved form. Otherwise, return + an unchanged copy of the input. Note that this call will always return + a copy, leaving the input unchanged.""" mv = memoryview(im) if mv.ndim == 3 and not is_interleaved(mv): - return np.moveaxis(mv, 0, 2) - else: - return mv + # We are presumably planar, in (c, y, x) order; we need (y, x, c) order + mv = np.moveaxis(mv, 0, 2) + mv = np.copy(mv, order="F") + + return mv -def _as_planar(im): +def copy_to_planar(im): """If the given buffer is 3-dimensional and appears to be interleaved - layout, return a view that is in planar form, leaving the input - unchanged. Otherwise, return the image buffer unchanged. - Note that this call must be used with care, as the returnee may or may - not be a copy.""" + layout, return a copy that is in planar form. Otherwise, return + an unchanged copy of the input. Note that this call will always return + a copy, leaving the input unchanged.""" mv = memoryview(im) if is_interleaved(mv): - return np.moveaxis(mv, 2, 0) - else: - return mv - - -def copy_to_interleaved(im): - """If the given buffer is 3-dimensional and appears to be planar - layout, return a copy that is in interleaved form. Otherwise, return - an unchanged copy of the input. Note that this call will always return - a copy, leaving the input unchanged.""" - return np.copy(_as_interleaved(im)) - + # Interleaved will be in (y, x, c) order; we want (c, y, x) order + # (which hl.Buffer will reverse into x, y, c order) + mv = np.moveaxis(mv, 2, 0) + mv = np.copy(mv, order="C") -def copy_to_planar(im): - """If the given buffer is 3-dimensional and appears to be interleaved - layout, return a copy that is in planar form. Otherwise, return - an unchanged copy of the input. Note that this call will always return - a copy, leaving the input unchanged.""" - return np.copy(_as_planar(im)) + return mv def imread(uri, format=None, **kwargs): """halide.imageio.imread is a thin wrapper around imagio.imread, - except that for 3-dimensional images that appear to be interleaved, - the result is converted to a planar layout before returning.""" + except that for 3-dimensional images that appear to be interleaved, + the result is converted to a planar layout before returning.""" return copy_to_planar(imageio.imread(uri, format, **kwargs)) def imwrite(uri, im, format=None, **kwargs): """halide.imageio.imwrite is a thin wrapper around imagio.imwrite, - except that for 3-dimensional images that appear to be planar, - a temporary interleaved copy of the input is made, which is used for - writing.""" - - # We can use _as_interleaved() here to save a possible copy; since the - # caller will never see the possibly-a-copy value, there should be no - # risk of possibly-different behavior between cases that need converting - # and cases that don't. - imageio.imwrite(uri, _as_interleaved(im), format, **kwargs) + except that for 3-dimensional images that appear to be planar, + a temporary interleaved copy of the input is made, which is used for + writing.""" + imageio.imwrite(uri, copy_to_interleaved(im), format, **kwargs) From e263b5cf6bd06bf63c214a7d9dbe32aca23c365a Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 31 Oct 2022 11:53:00 -0700 Subject: [PATCH 3/3] More fixes --- python_bindings/src/halide/imageio.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python_bindings/src/halide/imageio.py b/python_bindings/src/halide/imageio.py index 6c23387d2b75..e8fa7a762b0a 100644 --- a/python_bindings/src/halide/imageio.py +++ b/python_bindings/src/halide/imageio.py @@ -24,8 +24,9 @@ def copy_to_interleaved(im): # We are presumably planar, in (c, y, x) order; we need (y, x, c) order mv = np.moveaxis(mv, 0, 2) mv = np.copy(mv, order="F") - - return mv + return mv + else: + return im def copy_to_planar(im): @@ -39,8 +40,9 @@ def copy_to_planar(im): # (which hl.Buffer will reverse into x, y, c order) mv = np.moveaxis(mv, 2, 0) mv = np.copy(mv, order="C") - - return mv + return mv + else: + return im def imread(uri, format=None, **kwargs):