From c67d898c2cff0c097270dd8bcbab6bb14ca4fed2 Mon Sep 17 00:00:00 2001 From: Ryan Huang Date: Fri, 21 May 2021 08:50:05 +0000 Subject: [PATCH 1/6] Support yuv444p in to_ndarray --- av/video/frame.pyx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 6a3add3e1..6a2e845c3 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -269,6 +269,12 @@ cdef class VideoFrame(Frame): useful_array(frame.planes[1]), useful_array(frame.planes[2]) )).reshape(-1, frame.width) + elif frame.format.name in ('yuv444p', 'yuvj444p'): + return np.hstack(( + useful_array(frame.planes[0]), + useful_array(frame.planes[1]), + useful_array(frame.planes[2]) + )).reshape(frame.height, frame.width, -1) elif frame.format.name == 'yuyv422': assert frame.width % 2 == 0 assert frame.height % 2 == 0 From 054d4d352db3d6ce50d35470a70a7874668afb30 Mon Sep 17 00:00:00 2001 From: Ryan Huang Date: Fri, 21 May 2021 09:21:15 +0000 Subject: [PATCH 2/6] Fix conversion to yuv444p The channel dimension has to be first, so we fix the order with moveaxis. --- av/video/frame.pyx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 6a2e845c3..115060562 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -270,11 +270,12 @@ cdef class VideoFrame(Frame): useful_array(frame.planes[2]) )).reshape(-1, frame.width) elif frame.format.name in ('yuv444p', 'yuvj444p'): - return np.hstack(( + image = np.hstack(( useful_array(frame.planes[0]), useful_array(frame.planes[1]), useful_array(frame.planes[2]) - )).reshape(frame.height, frame.width, -1) + )).reshape(-1, frame.height, frame.width) + return np.moveaxis(image, 0, -1) elif frame.format.name == 'yuyv422': assert frame.width % 2 == 0 assert frame.height % 2 == 0 From 53cc7e6caa9581d5f47173510c512c9440f69a3a Mon Sep 17 00:00:00 2001 From: Ryan Huang Date: Fri, 21 May 2021 09:56:13 +0000 Subject: [PATCH 3/6] Support yuv444p in from_ndarray --- av/video/frame.pyx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 115060562..473a45657 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -356,6 +356,16 @@ cdef class VideoFrame(Frame): copy_array_to_plane(flat[u_start:v_start], frame.planes[1], 1) copy_array_to_plane(flat[v_start:], frame.planes[2], 1) return frame + elif format in ('yuv444p', 'yuvj444p'): + assert array.dtype == 'uint8' + assert array.ndim == 3 + assert array.shape[2] == 3 + frame = VideoFrame(array.shape[1], array.shape[0], format) + array = array.reshape(-1, 3) + copy_array_to_plane(array[:, 0], frame.planes[0], 1) + copy_array_to_plane(array[:, 1], frame.planes[1], 1) + copy_array_to_plane(array[:, 2], frame.planes[2], 1) + return frame elif format == 'yuyv422': check_ndarray(array, 'uint8', 3) check_ndarray_shape(array, array.shape[0] % 2 == 0) From 7b04980722d43cb40c61ca09d2569abbdc791f50 Mon Sep 17 00:00:00 2001 From: Ryan Huang Date: Mon, 7 Feb 2022 23:13:36 -0800 Subject: [PATCH 4/6] Put channel first for yuv444p/yuvj444p Channel last makes sense for a packed format, not a planar format. --- av/video/frame.pyx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 473a45657..983531290 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -270,12 +270,11 @@ cdef class VideoFrame(Frame): useful_array(frame.planes[2]) )).reshape(-1, frame.width) elif frame.format.name in ('yuv444p', 'yuvj444p'): - image = np.hstack(( + return np.hstack(( useful_array(frame.planes[0]), useful_array(frame.planes[1]), useful_array(frame.planes[2]) )).reshape(-1, frame.height, frame.width) - return np.moveaxis(image, 0, -1) elif frame.format.name == 'yuyv422': assert frame.width % 2 == 0 assert frame.height % 2 == 0 @@ -359,12 +358,12 @@ cdef class VideoFrame(Frame): elif format in ('yuv444p', 'yuvj444p'): assert array.dtype == 'uint8' assert array.ndim == 3 - assert array.shape[2] == 3 - frame = VideoFrame(array.shape[1], array.shape[0], format) - array = array.reshape(-1, 3) - copy_array_to_plane(array[:, 0], frame.planes[0], 1) - copy_array_to_plane(array[:, 1], frame.planes[1], 1) - copy_array_to_plane(array[:, 2], frame.planes[2], 1) + assert array.shape[0] == 3 + frame = VideoFrame(array.shape[2], array.shape[1], format) + array = array.reshape(3, -1) + copy_array_to_plane(array[0], frame.planes[0], 1) + copy_array_to_plane(array[1], frame.planes[1], 1) + copy_array_to_plane(array[2], frame.planes[2], 1) return frame elif format == 'yuyv422': check_ndarray(array, 'uint8', 3) From 58f004a14c227679fba7113ab0fd06f01b75b493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Fri, 25 Feb 2022 15:24:06 +0100 Subject: [PATCH 5/6] Use check_ndarray and check_ndarray_shape --- av/video/frame.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/av/video/frame.pyx b/av/video/frame.pyx index 983531290..ec2e0814e 100644 --- a/av/video/frame.pyx +++ b/av/video/frame.pyx @@ -356,9 +356,9 @@ cdef class VideoFrame(Frame): copy_array_to_plane(flat[v_start:], frame.planes[2], 1) return frame elif format in ('yuv444p', 'yuvj444p'): - assert array.dtype == 'uint8' - assert array.ndim == 3 - assert array.shape[0] == 3 + check_ndarray(array, 'uint8', 3) + check_ndarray_shape(array, array.shape[0] == 3) + frame = VideoFrame(array.shape[2], array.shape[1], format) array = array.reshape(3, -1) copy_array_to_plane(array[0], frame.planes[0], 1) From f2b3e6b9dda01ffd28b00806e8a0aad326b7f9c6 Mon Sep 17 00:00:00 2001 From: Ryan Huang Date: Tue, 29 Mar 2022 16:35:00 -0700 Subject: [PATCH 6/6] Add ndarray tests for yuv444p/yuvj444p --- tests/test_videoframe.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_videoframe.py b/tests/test_videoframe.py index 09ab06b13..4c162a40e 100644 --- a/tests/test_videoframe.py +++ b/tests/test_videoframe.py @@ -239,6 +239,22 @@ def test_ndarray_yuyv422(self): self.assertEqual(frame.format.name, "yuyv422") self.assertNdarraysEqual(frame.to_ndarray(), array) + def test_ndarray_yuv444p(self): + array = numpy.random.randint(0, 256, size=(3, 480, 640), dtype=numpy.uint8) + frame = VideoFrame.from_ndarray(array, format="yuv444p") + self.assertEqual(frame.width, 640) + self.assertEqual(frame.height, 480) + self.assertEqual(frame.format.name, "yuv444p") + self.assertNdarraysEqual(frame.to_ndarray(), array) + + def test_ndarray_yuvj444p(self): + array = numpy.random.randint(0, 256, size=(3, 480, 640), dtype=numpy.uint8) + frame = VideoFrame.from_ndarray(array, format="yuvj444p") + self.assertEqual(frame.width, 640) + self.assertEqual(frame.height, 480) + self.assertEqual(frame.format.name, "yuvj444p") + self.assertNdarraysEqual(frame.to_ndarray(), array) + def test_ndarray_yuyv422_align(self): array = numpy.random.randint(0, 256, size=(238, 318, 2), dtype=numpy.uint8) frame = VideoFrame.from_ndarray(array, format="yuyv422")