From 076f76d36159933d129681d19258b7baf3c86533 Mon Sep 17 00:00:00 2001 From: z-khan Date: Thu, 24 Apr 2025 12:14:13 +1000 Subject: [PATCH 01/20] extend `from_bytes` to non-planar formats extend `VideoFrame.from_bytes` to all non-planar formats. Support for planar formats may be implemented in future. change default format to `rgb24` for consistency with other helper functions for frame creation. --- av/video/frame.pyx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 04a14fd8b..1d3fefab1 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -768,12 +768,14 @@ cdef class VideoFrame(Frame): return frame @staticmethod - def from_bytes(img_bytes: bytes, width: int, height: int, format="rgba", flip_horizontal=False, flip_vertical=False): + def from_bytes(img_bytes: bytes, width: int, height: int, format="rgb24", flip_horizontal=False, flip_vertical=False): frame = VideoFrame(width, height, format) - if format == "rgba": - copy_bytes_to_plane(img_bytes, frame.planes[0], 4, flip_horizontal, flip_vertical) - elif format in ("bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8","bayer_bggr16le", "bayer_rggb16le", "bayer_gbrg16le", "bayer_grbg16le","bayer_bggr16be", "bayer_rggb16be", "bayer_gbrg16be", "bayer_grbg16be"): - copy_bytes_to_plane(img_bytes, frame.planes[0], 1 if format.endswith("8") else 2, flip_horizontal, flip_vertical) + if frame.format.is_planar: + raise NotImplementedError(f"Conversion from bytes with format `{format}` is not yet supported") else: - raise NotImplementedError(f"Format '{format}' is not supported.") + bytes_per_pixel = frame.format.padded_bits_per_pixel//8 + expected_size = width * height * bytes_per_pixel + if len(img_bytes) != expected_size: + raise ValueError(f"Expected {expected_size} bytes, got {len(img_bytes)}") + copy_bytes_to_plane(img_bytes, frame.planes[0], bytes_per_pixel, flip_horizontal, flip_vertical) return frame From cdb989db756ff3e00ecd0b177dbee57c31ad513d Mon Sep 17 00:00:00 2001 From: z-khan Date: Fri, 25 Apr 2025 12:01:55 +1000 Subject: [PATCH 02/20] support 9,10,12,14 bit gray and 9-bit gbrp formats adds support for gray formats: `gray9be`, `gray9le` `gray10be`, `gray10le` `gray12be`, `gray12le` `gray14be`, `gray14le` gbrp formats: `gbrp9be`, `gbrp9le` --- av/video/frame.pyx | 56 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 1d3fefab1..f1347863e 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -17,8 +17,9 @@ supported_np_pix_fmts = { "abgr", "argb", "bayer_bggr16be", "bayer_bggr16le", "bayer_bggr8", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_gbrg8", "bayer_grbg16be", "bayer_grbg16le", "bayer_grbg8", "bayer_rggb16be", "bayer_rggb16le", "bayer_rggb8", "bgr24", "bgr48be", "bgr48le", "bgr8", "bgra", "bgra64be", "bgra64le", - "gbrapf32be", "gbrapf32le", "gbrp", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", + "gbrapf32be", "gbrapf32le", "gbrp", "gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", "gray", + "gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", "gray16be", "gray16le", "gray8", "grayf32be", "grayf32le", "nv12", "pal8", "rgb24", "rgb48be", "rgb48le", "rgb8", "rgba", "rgba64be", "rgba64le", "yuv420p", "yuv420p10le", "yuv422p10le", "yuv444p", "yuv444p16be", "yuv444p16le", "yuva444p16be", @@ -349,6 +350,8 @@ cdef class VideoFrame(Frame): "gbrapf32be": (4, "float32"), "gbrapf32le": (4, "float32"), "gbrp": (1, "uint8"), + "gbrp9be": (2, "uint16"), + "gbrp9le": (2, "uint16"), "gbrp10be": (2, "uint16"), "gbrp10le": (2, "uint16"), "gbrp12be": (2, "uint16"), @@ -360,6 +363,14 @@ cdef class VideoFrame(Frame): "gbrpf32be": (4, "float32"), "gbrpf32le": (4, "float32"), "gray": (1, "uint8"), + "gray9be": (2, "uint16"), + "gray9le": (2, "uint16"), + "gray10be": (2, "uint16"), + "gray10le": (2, "uint16"), + "gray12be": (2, "uint16"), + "gray12le": (2, "uint16"), + "gray14be": (2, "uint16"), + "gray14le": (2, "uint16"), "gray16be": (2, "uint16"), "gray16le": (2, "uint16"), "gray8": (1, "uint8"), @@ -395,7 +406,7 @@ cdef class VideoFrame(Frame): array = byteswap_array(array, frame.format.name.endswith("be")) if array.shape[2] == 1: # skip last channel for gray images return array.squeeze(2) - if frame.format.name.startswith("gbr"): # gbr -> rgb + if frame.format.name.startswith("gbrp"): # gbr -> rgb buffer = array[:, :, 0].copy() array[:, :, 0] = array[:, :, 2] array[:, :, 2] = array[:, :, 1] @@ -461,12 +472,16 @@ cdef class VideoFrame(Frame): return frame @staticmethod - def from_numpy_buffer(array, format="rgb24", width=0): - # Usually the width of the array is the same as the width of the image. But sometimes - # this is not possible, for example with yuv420p images that have padding. These are - # awkward because the UV rows at the bottom have padding bytes in the middle of the - # row as well as at the end. To cope with these, callers need to be able to pass the - # actual width to us. + def from_numpy_buffer(array, format="rgb24", width:int=0): + """ + Construct a frame from a numpy buffer. + + :param int width: optional width of actual image, if different from the array width. + + .. note:: For formats where width of the array is not the same as the width of the image, + for example with yuv420p images the UV rows at the bottom have padding bytes in the middle of the + row as well as at the end. To cope with these, callers need to be able to pass the actual width. + """ height = array.shape[0] if not width: width = array.shape[1] @@ -500,7 +515,8 @@ cdef class VideoFrame(Frame): if array.strides[1] != 1: raise ValueError("provided array does not have C_CONTIGUOUS rows") linesizes = (array.strides[0], ) - elif format in {"gray16le", "gray16be", "bayer_rggb16le", "bayer_gbrg16le", "bayer_grbg16le","bayer_bggr16be", "bayer_rggb16be", "bayer_gbrg16be", "bayer_grbg16be"}: + elif format in {"gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", "gray16be", "gray16le", + "bayer_rggb16be", "bayer_rggb16le", "bayer_gbrg16be", "bayer_grbg16le", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_bggr16be", "bayer_bggr16le"}: check_ndarray(array, "uint16", 2) if array.strides[1] != 2: raise ValueError("provided array does not have C_CONTIGUOUS rows") @@ -603,10 +619,12 @@ cdef class VideoFrame(Frame): "yuv444p": (3, 1, "uint8"), "yuvj444p": (3, 1, "uint8"), "gbrp": (3, 1, "uint8"), + "gbrp9be": (3, 2, "uint16"), "gbrp10be": (3, 2, "uint16"), "gbrp12be": (3, 2, "uint16"), "gbrp14be": (3, 2, "uint16"), "gbrp16be": (3, 2, "uint16"), + "gbrp9le": (3, 2, "uint16"), "gbrp10le": (3, 2, "uint16"), "gbrp12le": (3, 2, "uint16"), "gbrp14le": (3, 2, "uint16"), @@ -617,6 +635,14 @@ cdef class VideoFrame(Frame): "gray8": (1, 1, "uint8"), "rgb8": (1, 1, "uint8"), "bgr8": (1, 1, "uint8"), + "gray9be": (1, 2, "uint16"), + "gray9le": (1, 2, "uint16"), + "gray10be": (1, 2, "uint16"), + "gray10le": (1, 2, "uint16"), + "gray12be": (1, 2, "uint16"), + "gray12le": (1, 2, "uint16"), + "gray14be": (1, 2, "uint16"), + "gray14le": (1, 2, "uint16"), "gray16be": (1, 2, "uint16"), "gray16le": (1, 2, "uint16"), "grayf32be": (1, 4, "float32"), @@ -649,7 +675,7 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == channels) array = byteswap_array(array, format.endswith("be")) frame = VideoFrame(array.shape[1], array.shape[0], format) - if frame.format.name.startswith("gbr"): # rgb -> gbr + if frame.format.name.startswith("gbrp"): # rgb -> gbr array = np.concatenate([ # not inplace to avoid bad surprises array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], ], axis=2) @@ -769,6 +795,16 @@ cdef class VideoFrame(Frame): @staticmethod def from_bytes(img_bytes: bytes, width: int, height: int, format="rgb24", flip_horizontal=False, flip_vertical=False): + """ + Construct a frame from raw bytes. + + :param img_bytes: Raw image data. + :param width: Frame width. + :param height: Frame height. + :param format: Pixel format, e.g. "rgb24". + :param flip_horizontal: If True, flip image horizontally. + :param flip_vertical: If True, flip image vertically. + """ frame = VideoFrame(width, height, format) if frame.format.is_planar: raise NotImplementedError(f"Conversion from bytes with format `{format}` is not yet supported") From c0c386d9a232f24e733826b99a760753d8e2c777 Mon Sep 17 00:00:00 2001 From: z-khan Date: Fri, 25 Apr 2025 12:16:05 +1000 Subject: [PATCH 03/20] fix indentation --- av/video/frame.pyx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index f1347863e..d4acd86c8 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -795,16 +795,16 @@ cdef class VideoFrame(Frame): @staticmethod def from_bytes(img_bytes: bytes, width: int, height: int, format="rgb24", flip_horizontal=False, flip_vertical=False): - """ - Construct a frame from raw bytes. - - :param img_bytes: Raw image data. - :param width: Frame width. - :param height: Frame height. - :param format: Pixel format, e.g. "rgb24". - :param flip_horizontal: If True, flip image horizontally. - :param flip_vertical: If True, flip image vertically. - """ + """ + Construct a frame from raw bytes. + + :param img_bytes: Raw image data. + :param width: Frame width. + :param height: Frame height. + :param format: Pixel format, e.g. "rgb24". + :param flip_horizontal: If True, flip image horizontally. + :param flip_vertical: If True, flip image vertically. + """ frame = VideoFrame(width, height, format) if frame.format.is_planar: raise NotImplementedError(f"Conversion from bytes with format `{format}` is not yet supported") From dc0a2a27cb640e69bf7ac30b59cfd70925faac33 Mon Sep 17 00:00:00 2001 From: z-khan Date: Fri, 25 Apr 2025 12:26:54 +1000 Subject: [PATCH 04/20] fix indentation(2) --- av/video/frame.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index d4acd86c8..855565afd 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -795,7 +795,7 @@ cdef class VideoFrame(Frame): @staticmethod def from_bytes(img_bytes: bytes, width: int, height: int, format="rgb24", flip_horizontal=False, flip_vertical=False): - """ + """ Construct a frame from raw bytes. :param img_bytes: Raw image data. @@ -809,9 +809,10 @@ cdef class VideoFrame(Frame): if frame.format.is_planar: raise NotImplementedError(f"Conversion from bytes with format `{format}` is not yet supported") else: - bytes_per_pixel = frame.format.padded_bits_per_pixel//8 + bytes_per_pixel = frame.format.padded_bits_per_pixel // 8 expected_size = width * height * bytes_per_pixel if len(img_bytes) != expected_size: raise ValueError(f"Expected {expected_size} bytes, got {len(img_bytes)}") - copy_bytes_to_plane(img_bytes, frame.planes[0], bytes_per_pixel, flip_horizontal, flip_vertical) + copy_bytes_to_plane(img_bytes, frame.planes[0], bytes_per_pixel, flip_horizontal, flip_vertical) return frame + From 8748b9c0e0d9fb809a12ccccba02007fe1212d95 Mon Sep 17 00:00:00 2001 From: z-khan Date: Sun, 27 Apr 2025 11:33:18 +1000 Subject: [PATCH 05/20] fix typos --- av/video/frame.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 855565afd..b9ae59038 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -516,7 +516,7 @@ cdef class VideoFrame(Frame): raise ValueError("provided array does not have C_CONTIGUOUS rows") linesizes = (array.strides[0], ) elif format in {"gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", "gray16be", "gray16le", - "bayer_rggb16be", "bayer_rggb16le", "bayer_gbrg16be", "bayer_grbg16le", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_bggr16be", "bayer_bggr16le"}: + "bayer_bggr16be", "bayer_bggr16le", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_grbg16be", "bayer_grbg16le", "bayer_rggb16be", "bayer_rggb16le"}: check_ndarray(array, "uint16", 2) if array.strides[1] != 2: raise ValueError("provided array does not have C_CONTIGUOUS rows") From 837d3e45b8d9a0b16a6c386deb90ac979771cbc5 Mon Sep 17 00:00:00 2001 From: z-khan Date: Sun, 27 Apr 2025 12:14:21 +1000 Subject: [PATCH 06/20] rearrange formats --- av/video/frame.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index b9ae59038..7ea695445 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -17,7 +17,7 @@ supported_np_pix_fmts = { "abgr", "argb", "bayer_bggr16be", "bayer_bggr16le", "bayer_bggr8", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_gbrg8", "bayer_grbg16be", "bayer_grbg16le", "bayer_grbg8", "bayer_rggb16be", "bayer_rggb16le", "bayer_rggb8", "bgr24", "bgr48be", "bgr48le", "bgr8", "bgra", "bgra64be", "bgra64le", - "gbrapf32be", "gbrapf32le", "gbrp", "gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", + "gbrapf32be", "gbrapf32le", "gbrp", "gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", "gray", "gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", "gray16be", "gray16le", "gray8", "grayf32be", "grayf32le", "nv12", "pal8", "rgb24", @@ -620,14 +620,14 @@ cdef class VideoFrame(Frame): "yuvj444p": (3, 1, "uint8"), "gbrp": (3, 1, "uint8"), "gbrp9be": (3, 2, "uint16"), - "gbrp10be": (3, 2, "uint16"), - "gbrp12be": (3, 2, "uint16"), - "gbrp14be": (3, 2, "uint16"), - "gbrp16be": (3, 2, "uint16"), "gbrp9le": (3, 2, "uint16"), + "gbrp10be": (3, 2, "uint16"), "gbrp10le": (3, 2, "uint16"), + "gbrp12be": (3, 2, "uint16"), "gbrp12le": (3, 2, "uint16"), + "gbrp14be": (3, 2, "uint16"), "gbrp14le": (3, 2, "uint16"), + "gbrp16be": (3, 2, "uint16"), "gbrp16le": (3, 2, "uint16"), "gbrpf32be": (3, 4, "float32"), "gbrpf32le": (3, 4, "float32"), From b5fb8bafc7946aa563b58e984bfc59a3776d87c1 Mon Sep 17 00:00:00 2001 From: z-khan Date: Sun, 27 Apr 2025 21:11:11 +1000 Subject: [PATCH 07/20] add tests `gray9`, `gray10`, `gray12`, `gray14` and `gbrp9` --- tests/test_videoframe.py | 52 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/tests/test_videoframe.py b/tests/test_videoframe.py index 26549b31b..32946f004 100644 --- a/tests/test_videoframe.py +++ b/tests/test_videoframe.py @@ -329,6 +329,24 @@ def test_ndarray_gbrp_align() -> None: assert frame.format.name == "gbrp" assertNdarraysEqual(frame.to_ndarray(), array) +def test_ndarray_gbrp9() -> None: + array = numpy.random.randint(0, 512, size=(480, 640, 3), dtype=numpy.uint16) + for format in ("gbrp9be", "gbrp9le"): + frame = VideoFrame.from_ndarray(array, format=format) + assert format in av.video.frame.supported_np_pix_fmts + assert frame.width == 640 and frame.height == 480 + assert frame.format.name == format + assertNdarraysEqual(frame.to_ndarray(), array) + + +def test_ndarray_gbrp9_align() -> None: + array = numpy.random.randint(0, 512, size=(238, 318, 3), dtype=numpy.uint16) + for format in ("gbrp9be", "gbrp9le"): + frame = VideoFrame.from_ndarray(array, format=format) + assert format in av.video.frame.supported_np_pix_fmts + assert frame.width == 318 and frame.height == 238 + assert frame.format.name == format + assertNdarraysEqual(frame.to_ndarray(), array) def test_ndarray_gbrp10() -> None: array = numpy.random.randint(0, 1024, size=(480, 640, 3), dtype=numpy.uint16) @@ -568,25 +586,27 @@ def test_ndarray_yuyv422_align() -> None: def test_ndarray_gray16be() -> None: - array = numpy.random.randint(0, 65536, size=(480, 640), dtype=numpy.uint16) - frame = VideoFrame.from_ndarray(array, format="gray16be") - assert frame.width == 640 and frame.height == 480 - assert frame.format.name == "gray16be" - assertNdarraysEqual(frame.to_ndarray(), array) - - # check endianness by examining value of first pixel - assertPixelValue16(frame.planes[0], array[0][0], "big") + for bits in (9,10,12,14,16): + array = numpy.random.randint(0, 2**bits, size=(480, 640), dtype=numpy.uint16) + frame = VideoFrame.from_ndarray(array, format=f"gray{bits}be") + assert frame.width == 640 and frame.height == 480 + assert frame.format.name == f"gray{bits}be" + assertNdarraysEqual(frame.to_ndarray(), array) + + # check endianness by examining value of first pixel + assertPixelValue16(frame.planes[0], array[0][0], "big") def test_ndarray_gray16le() -> None: - array = numpy.random.randint(0, 65536, size=(480, 640), dtype=numpy.uint16) - frame = VideoFrame.from_ndarray(array, format="gray16le") - assert frame.width == 640 and frame.height == 480 - assert frame.format.name == "gray16le" - assertNdarraysEqual(frame.to_ndarray(), array) - - # check endianness by examining value of first pixel - assertPixelValue16(frame.planes[0], array[0][0], "little") + for bits in (9,10,12,14,16): + array = numpy.random.randint(0, 2**bits, size=(480, 640), dtype=numpy.uint16) + frame = VideoFrame.from_ndarray(array, format=f"gray{bits}le") + assert frame.width == 640 and frame.height == 480 + assert frame.format.name == f"gray{bits}le" + assertNdarraysEqual(frame.to_ndarray(), array) + + # check endianness by examining value of first pixel + assertPixelValue16(frame.planes[0], array[0][0], "little") def test_ndarray_rgb48be() -> None: From d1819b83776a72e3ce1f207badc70b8dc26d702c Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 14:23:43 +1000 Subject: [PATCH 08/20] add `gbrp` to `from_numpy_buffer` --- av/video/frame.pyx | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 7ea695445..301f9ea5c 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -510,6 +510,42 @@ cdef class VideoFrame(Frame): if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") linesizes = (array.strides[0], ) + elif pix_fmt in {"gbrp"}: + check_ndarray(array, "uint8", 3) + check_ndarray_shape(array, array.shape[2] == 3) + if array.strides[1:] != (3, 1): + raise ValueError("provided array does not have C_CONTIGUOUS rows") + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + elif pix_fmt in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: + check_ndarray(array, "uint16", 3) + check_ndarray_shape(array, array.shape[2] == 3) + if array.strides[1:] != (6, 2): + raise ValueError("provided array does not have C_CONTIGUOUS rows") + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + elif pix_fmt in {"gbrpf32be", "gbrpf32le"}: + check_ndarray(array, "float32", 3) + check_ndarray_shape(array, array.shape[2] == 3) + if array.strides[1:] != (12, 4): + raise ValueError("provided array does not have C_CONTIGUOUS rows") + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + elif pix_fmt in {"gbrapf32be", "gbrapf32le"}: + check_ndarray(array, "float32", 3) + check_ndarray_shape(array, array.shape[2] == 4) + if array.strides[1:] != (16, 4): + raise ValueError("provided array does not have C_CONTIGUOUS rows") + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) if array.strides[1] != 1: From 4ae6f32d1a50438616e608c3d861df7a7c698745 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 14:29:23 +1000 Subject: [PATCH 09/20] add import numpy --- av/video/frame.pyx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 301f9ea5c..d502ad910 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -482,6 +482,8 @@ cdef class VideoFrame(Frame): for example with yuv420p images the UV rows at the bottom have padding bytes in the middle of the row as well as at the end. To cope with these, callers need to be able to pass the actual width. """ + import numpy as np + height = array.shape[0] if not width: width = array.shape[1] From 0ddddb60651991cf3ccd8a06c26fe6e80b728ce4 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 16:25:06 +1000 Subject: [PATCH 10/20] add more `gbrap` formats --- av/video/frame.pyx | 109 +++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 49 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index d502ad910..cf5dd1776 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -17,12 +17,11 @@ supported_np_pix_fmts = { "abgr", "argb", "bayer_bggr16be", "bayer_bggr16le", "bayer_bggr8", "bayer_gbrg16be", "bayer_gbrg16le", "bayer_gbrg8", "bayer_grbg16be", "bayer_grbg16le", "bayer_grbg8", "bayer_rggb16be", "bayer_rggb16le", "bayer_rggb8", "bgr24", "bgr48be", "bgr48le", "bgr8", "bgra", "bgra64be", "bgra64le", - "gbrapf32be", "gbrapf32le", "gbrp", "gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", - "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", "gray", - "gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", - "gray16be", "gray16le", "gray8", "grayf32be", "grayf32le", "nv12", "pal8", "rgb24", - "rgb48be", "rgb48le", "rgb8", "rgba", "rgba64be", "rgba64le", "yuv420p", - "yuv420p10le", "yuv422p10le", "yuv444p", "yuv444p16be", "yuv444p16le", "yuva444p16be", + "gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le","gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le", "gbrapf32be", "gbrapf32le", + "gbrp", "gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", + "gray", "gray8", "gray9be", "gray9le", "gray10be", "gray10le", "gray12be", "gray12le", "gray14be", "gray14le", "gray16be", "gray16le", "grayf32be", "grayf32le", + "nv12", "pal8", "rgb24", "rgb48be", "rgb48le", "rgb8", "rgba", "rgba64be", "rgba64le", + "yuv420p", "yuv420p10le", "yuv422p10le", "yuv444p", "yuv444p16be", "yuv444p16le", "yuva444p16be", "yuva444p16le", "yuvj420p", "yuvj444p", "yuyv422", } @@ -347,6 +346,16 @@ cdef class VideoFrame(Frame): "bgr48le": (6, "uint16"), "bgr8": (1, "uint8"), "bgra": (4, "uint8"), + "bgra64be": (8, "uint16"), + "bgra64le": (8, "uint16"), + "gbrap10be": (2, "uint16"), + "gbrap10le": (2, "uint16"), + "gbrap12be": (2, "uint16"), + "gbrap12le": (2, "uint16"), + "gbrap14be": (2, "uint16"), + "gbrap14le": (2, "uint16"), + "gbrap16be": (2, "uint16"), + "gbrap16le": (2, "uint16"), "gbrapf32be": (4, "float32"), "gbrapf32le": (4, "float32"), "gbrp": (1, "uint8"), @@ -363,6 +372,7 @@ cdef class VideoFrame(Frame): "gbrpf32be": (4, "float32"), "gbrpf32le": (4, "float32"), "gray": (1, "uint8"), + "gray8": (1, "uint8"), "gray9be": (2, "uint16"), "gray9le": (2, "uint16"), "gray10be": (2, "uint16"), @@ -373,7 +383,6 @@ cdef class VideoFrame(Frame): "gray14le": (2, "uint16"), "gray16be": (2, "uint16"), "gray16le": (2, "uint16"), - "gray8": (1, "uint8"), "grayf32be": (4, "float32"), "grayf32le": (4, "float32"), "rgb24": (3, "uint8"), @@ -383,8 +392,6 @@ cdef class VideoFrame(Frame): "rgba": (4, "uint8"), "rgba64be": (8, "uint16"), "rgba64le": (8, "uint16"), - "bgra64be": (8, "uint16"), - "bgra64le": (8, "uint16"), "yuv444p": (1, "uint8"), "yuv444p16be": (2, "uint16"), "yuv444p16le": (2, "uint16"), @@ -406,7 +413,7 @@ cdef class VideoFrame(Frame): array = byteswap_array(array, frame.format.name.endswith("be")) if array.shape[2] == 1: # skip last channel for gray images return array.squeeze(2) - if frame.format.name.startswith("gbrp"): # gbr -> rgb + if frame.format.name.startswith("gbr"): # gbr -> rgb buffer = array[:, :, 0].copy() array[:, :, 0] = array[:, :, 2] array[:, :, 2] = array[:, :, 1] @@ -512,41 +519,35 @@ cdef class VideoFrame(Frame): if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") linesizes = (array.strides[0], ) - elif pix_fmt in {"gbrp"}: + elif format in {"gbrp"}: check_ndarray(array, "uint8", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (3, 1): raise ValueError("provided array does not have C_CONTIGUOUS rows") - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) - elif pix_fmt in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: + elif format in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (6, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) - elif pix_fmt in {"gbrpf32be", "gbrpf32le"}: + elif format in {"gbrpf32be", "gbrpf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (12, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + elif format in {"gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le", "gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le"}: + check_ndarray(array, "uint16", 3) + check_ndarray_shape(array, array.shape[2] == 4) + if array.strides[1:] != (8, 2): + raise ValueError("provided array does not have C_CONTIGUOUS rows") linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) - elif pix_fmt in {"gbrapf32be", "gbrapf32le"}: + elif format in {"gbrapf32be", "gbrapf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (16, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) @@ -579,7 +580,11 @@ cdef class VideoFrame(Frame): linesizes = (array.strides[0], array.strides[0]) else: raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") - + + if frame.format.name.startswith("gbr"): # rgb -> gbr + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame @@ -654,8 +659,29 @@ cdef class VideoFrame(Frame): # case layers are concatenated channels, itemsize, dtype = { - "yuv444p": (3, 1, "uint8"), - "yuvj444p": (3, 1, "uint8"), + "bayer_bggr8": (1, 1, "uint8"), + "bayer_rggb8": (1, 1, "uint8"), + "bayer_grbg8": (1, 1, "uint8"), + "bayer_gbrg8": (1, 1, "uint8"), + "bayer_bggr16be": (1, 2, "uint16"), + "bayer_bggr16le": (1, 2, "uint16"), + "bayer_rggb16be": (1, 2, "uint16"), + "bayer_rggb16le": (1, 2, "uint16"), + "bayer_grbg16be": (1, 2, "uint16"), + "bayer_grbg16le": (1, 2, "uint16"), + "bayer_gbrg16be": (1, 2, "uint16"), + "bayer_gbrg16le": (1, 2, "uint16"), + "bgr8": (1, 1, "uint8"), + "gbrap10be": (4, 2, "uint16"), + "gbrap10le": (4, 2, "uint16"), + "gbrap12be": (4, 2, "uint16"), + "gbrap12le": (4, 2, "uint16"), + "gbrap14be": (4, 2, "uint16"), + "gbrap14le": (4, 2, "uint16"), + "gbrap16be": (4, 2, "uint16"), + "gbrap16le": (4, 2, "uint16"), + "gbrapf32be": (4, 4, "float32"), + "gbrapf32le": (4, 4, "float32"), "gbrp": (3, 1, "uint8"), "gbrp9be": (3, 2, "uint16"), "gbrp9le": (3, 2, "uint16"), @@ -667,12 +693,8 @@ cdef class VideoFrame(Frame): "gbrp14le": (3, 2, "uint16"), "gbrp16be": (3, 2, "uint16"), "gbrp16le": (3, 2, "uint16"), - "gbrpf32be": (3, 4, "float32"), - "gbrpf32le": (3, 4, "float32"), "gray": (1, 1, "uint8"), "gray8": (1, 1, "uint8"), - "rgb8": (1, 1, "uint8"), - "bgr8": (1, 1, "uint8"), "gray9be": (1, 2, "uint16"), "gray9le": (1, 2, "uint16"), "gray10be": (1, 2, "uint16"), @@ -685,24 +707,13 @@ cdef class VideoFrame(Frame): "gray16le": (1, 2, "uint16"), "grayf32be": (1, 4, "float32"), "grayf32le": (1, 4, "float32"), - "gbrapf32be": (4, 4, "float32"), - "gbrapf32le": (4, 4, "float32"), + "rgb8": (1, 1, "uint8"), + "yuv444p": (3, 1, "uint8"), + "yuvj444p": (3, 1, "uint8"), "yuv444p16be": (3, 2, "uint16"), "yuv444p16le": (3, 2, "uint16"), "yuva444p16be": (4, 2, "uint16"), - "yuva444p16le": (4, 2, "uint16"), - "bayer_bggr8": (1, 1, "uint8"), - "bayer_rggb8": (1, 1, "uint8"), - "bayer_grbg8": (1, 1, "uint8"), - "bayer_gbrg8": (1, 1, "uint8"), - "bayer_bggr16be": (1, 2, "uint16"), - "bayer_bggr16le": (1, 2, "uint16"), - "bayer_rggb16be": (1, 2, "uint16"), - "bayer_rggb16le": (1, 2, "uint16"), - "bayer_grbg16be": (1, 2, "uint16"), - "bayer_grbg16le": (1, 2, "uint16"), - "bayer_gbrg16be": (1, 2, "uint16"), - "bayer_gbrg16le": (1, 2, "uint16"), + "yuva444p16le": (4, 2, "uint16"), }.get(format, (None, None, None)) if channels is not None: if array.ndim == 2: # (height, width) -> (height, width, 1) @@ -713,7 +724,7 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == channels) array = byteswap_array(array, format.endswith("be")) frame = VideoFrame(array.shape[1], array.shape[0], format) - if frame.format.name.startswith("gbrp"): # rgb -> gbr + if frame.format.name.startswith("gbr"): # rgb -> gbr array = np.concatenate([ # not inplace to avoid bad surprises array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], ], axis=2) From 4de9dc4ce3a84e82bed44425cf1641b0e1420707 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 16:31:28 +1000 Subject: [PATCH 11/20] fix typo --- av/video/frame.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index cf5dd1776..4f7562d14 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -581,7 +581,7 @@ cdef class VideoFrame(Frame): else: raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") - if frame.format.name.startswith("gbr"): # rgb -> gbr + if format.startswith("gbr"): # rgb -> gbr array = np.concatenate([ # not inplace to avoid bad surprises array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], ], axis=2) From 0530d309ac82a9fbc297e8c7487e508bc4e3c1e8 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 17:00:40 +1000 Subject: [PATCH 12/20] fix strides --- av/video/frame.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 4f7562d14..73add760c 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -524,31 +524,31 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (3, 1): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (array.strides[0], array.strides[0], array.strides[0], ) elif format in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (6, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (array.strides[0], array.strides[0], array.strides[0], ) elif format in {"gbrpf32be", "gbrpf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (12, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (array.strides[0], array.strides[0], array.strides[0], ) elif format in {"gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le", "gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (array.strides[0], array.strides[0], array.strides[0], ) elif format in {"gbrapf32be", "gbrapf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (16, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (array.strides[0], array.strides[0], array.strides[0], array.strides[0], ) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) if array.strides[1] != 1: From 3b8e5eef8d545949d96b72b89bdab72033ec0190 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 18:21:43 +1000 Subject: [PATCH 13/20] testing --- av/video/frame.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 73add760c..fee2456b2 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -524,7 +524,7 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (3, 1): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0], array.strides[0], array.strides[0], ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 3) @@ -581,10 +581,10 @@ cdef class VideoFrame(Frame): else: raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") - if format.startswith("gbr"): # rgb -> gbr - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) + # if format.startswith("gbr"): # rgb -> gbr + # array = np.concatenate([ # not inplace to avoid bad surprises + # array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + # ], axis=2) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame From 171d04242b8fc7855de95cafcd6d45c4e0eb7e12 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 18:45:27 +1000 Subject: [PATCH 14/20] channel first for `gbr` --- av/video/frame.pyx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index fee2456b2..a55fbefb3 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -530,25 +530,25 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (6, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0], array.strides[0], array.strides[0], ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gbrpf32be", "gbrpf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (12, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0], array.strides[0], array.strides[0], ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3,) elif format in {"gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le", "gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0], array.strides[0], array.strides[0], ) + linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4,) elif format in {"gbrapf32be", "gbrapf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (16, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0], array.strides[0], array.strides[0], array.strides[0], ) + linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4,) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) if array.strides[1] != 1: @@ -581,10 +581,8 @@ cdef class VideoFrame(Frame): else: raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") - # if format.startswith("gbr"): # rgb -> gbr - # array = np.concatenate([ # not inplace to avoid bad surprises - # array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - # ], axis=2) + if format.startswith("gbr"): # rgb -> gbr + array = np.ascontiguousarray(np.moveaxis(array,(-1,0)) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame From 9bd522fdaec7676d4edb1004d4101eef6eb4b3ad Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 18:55:58 +1000 Subject: [PATCH 15/20] fix typo ) --- av/video/frame.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index a55fbefb3..3dcb6cb9f 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -582,7 +582,7 @@ cdef class VideoFrame(Frame): raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") if format.startswith("gbr"): # rgb -> gbr - array = np.ascontiguousarray(np.moveaxis(array,(-1,0)) + array = np.ascontiguousarray(np.moveaxis(array,(-1,0))) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame From 9f587c03c2601181269b3bcc64dea7eed2c83dd9 Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 19:20:26 +1000 Subject: [PATCH 16/20] fix typo --- av/video/frame.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 3dcb6cb9f..5a890d6ab 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -582,7 +582,7 @@ cdef class VideoFrame(Frame): raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") if format.startswith("gbr"): # rgb -> gbr - array = np.ascontiguousarray(np.moveaxis(array,(-1,0))) + array = np.ascontiguousarray(np.moveaxis(array,-1,0)) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame From 427b89f4d525f233a5415af9dba622fb6d694f0b Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 19:50:09 +1000 Subject: [PATCH 17/20] update linesizes for `gbrp` --- av/video/frame.pyx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 5a890d6ab..c8321cb92 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -524,31 +524,31 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (3, 1): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (width, width, width, ) elif format in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (6, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) + linesizes = (width, width, width, ) elif format in {"gbrpf32be", "gbrpf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (12, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3,) + linesizes = (width, width, width, ) elif format in {"gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le", "gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4,) + linesizes = (width, width, width, width, ) elif format in {"gbrapf32be", "gbrapf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (16, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4,) + linesizes = (width, width, width, width, ) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) if array.strides[1] != 1: @@ -582,6 +582,9 @@ cdef class VideoFrame(Frame): raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") if format.startswith("gbr"): # rgb -> gbr + array = np.concatenate([ # not inplace to avoid bad surprises + array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], + ], axis=2) array = np.ascontiguousarray(np.moveaxis(array,-1,0)) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) From 26422ce080f00f5b81866d168b50a9523dc62efb Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 20:33:06 +1000 Subject: [PATCH 18/20] fix linesizes for `gbr*p` --- av/video/frame.pyx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index c8321cb92..4423bafa8 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -524,31 +524,31 @@ cdef class VideoFrame(Frame): check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (3, 1): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (width, width, width, ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gbrp9be", "gbrp9le", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le", "gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (6, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (width, width, width, ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gbrpf32be", "gbrpf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 3) if array.strides[1:] != (12, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (width, width, width, ) + linesizes = (array.strides[0] // 3, array.strides[0] // 3, array.strides[0] // 3, ) elif format in {"gbrap10be", "gbrap10le", "gbrap12be", "gbrap12le", "gbrap14be", "gbrap14le", "gbrap16be", "gbrap16le"}: check_ndarray(array, "uint16", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (8, 2): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (width, width, width, width, ) + linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4, ) elif format in {"gbrapf32be", "gbrapf32le"}: check_ndarray(array, "float32", 3) check_ndarray_shape(array, array.shape[2] == 4) if array.strides[1:] != (16, 4): raise ValueError("provided array does not have C_CONTIGUOUS rows") - linesizes = (width, width, width, width, ) + linesizes = (array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4, array.strides[0] // 4, ) elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}: check_ndarray(array, "uint8", 2) if array.strides[1] != 1: @@ -694,6 +694,8 @@ cdef class VideoFrame(Frame): "gbrp14le": (3, 2, "uint16"), "gbrp16be": (3, 2, "uint16"), "gbrp16le": (3, 2, "uint16"), + "gbrpf32be": (3, 4, "float32"), + "gbrpf32le": (3, 4, "float32"), "gray": (1, 1, "uint8"), "gray8": (1, 1, "uint8"), "gray9be": (1, 2, "uint16"), From e279d07f8b923ae7401a0b82de9a9ab4fbde7f4e Mon Sep 17 00:00:00 2001 From: z-khan Date: Mon, 28 Apr 2025 21:47:45 +1000 Subject: [PATCH 19/20] Update frame.pyx --- av/video/frame.pyx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 4423bafa8..4d2c55f79 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -581,11 +581,10 @@ cdef class VideoFrame(Frame): else: raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported") - if format.startswith("gbr"): # rgb -> gbr - array = np.concatenate([ # not inplace to avoid bad surprises - array[:, :, 1:3], array[:, :, 0:1], array[:, :, 3:], - ], axis=2) - array = np.ascontiguousarray(np.moveaxis(array,-1,0)) + if format.startswith("gbrap"): # rgba -> gbra + array = np.ascontiguousarray(np.moveaxis(array[..., [1, 2, 0, 3]], -1, 0)) + elif format.startswith("gbrp"): # rgb -> gbr + array = np.ascontiguousarray(np.moveaxis(array[..., [1, 2, 0]], -1, 0)) frame = alloc_video_frame() frame._image_fill_pointers_numpy(array, width, height, linesizes, format) return frame From 5fa1b7f97dede94b124f5eba7b6a7db2b082ab4c Mon Sep 17 00:00:00 2001 From: z-khan Date: Thu, 1 May 2025 10:34:01 +1000 Subject: [PATCH 20/20] change default format for `from_bytes` default to `rgb24` format consistent with other functions --- av/video/frame.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/av/video/frame.pyi b/av/video/frame.pyi index bba60cc5d..8b76c4da3 100644 --- a/av/video/frame.pyi +++ b/av/video/frame.pyi @@ -79,7 +79,7 @@ class VideoFrame(Frame): data: bytes, width: int, height: int, - format: str = "rgba", + format: str = "rgb24", flip_horizontal: bool = False, flip_vertical: bool = False, ) -> VideoFrame: ...