Skip to content

Commit 6bee799

Browse files
authored
Support grayf32le and gbrapf32le in numpy convertion
1 parent d527571 commit 6bee799

3 files changed

Lines changed: 84 additions & 16 deletions

File tree

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Features
2424

2525
- Add hardware decoding by :gh-user:`matthewlai` and :gh-user:`WyattBlue` in (:pr:`1685`).
2626
- Add ``VideoFrame.rotation`` by :gh-user:`lgeiger` in (:pr:`1675`).
27+
- Support grayf32le and gbrapf32le in numpy convertion by :gh-user:`robinechuca` in (:pr:`1712`).
2728

2829

2930
v14.0.1

av/video/frame.pyx

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,8 @@ cdef class VideoFrame(Frame):
295295
.. note:: For ``pal8``, an ``(image, palette)`` tuple will be returned,
296296
with the palette being in ARGB (PyAV will swap bytes if needed).
297297
298+
.. note:: For ``gbrp`` formats, channels are flipped to RGB order.
299+
298300
"""
299301
cdef VideoFrame frame = self.reformat(**kwargs)
300302

@@ -312,29 +314,36 @@ cdef class VideoFrame(Frame):
312314
return np.hstack((
313315
useful_array(frame.planes[0]),
314316
useful_array(frame.planes[1]),
315-
useful_array(frame.planes[2])
317+
useful_array(frame.planes[2]),
316318
)).reshape(-1, frame.height, frame.width)
317319
elif frame.format.name == "yuyv422":
318320
assert frame.width % 2 == 0
319321
assert frame.height % 2 == 0
320322
return useful_array(frame.planes[0], 2).reshape(frame.height, frame.width, -1)
321323
elif frame.format.name == "gbrp":
322324
array = np.empty((frame.height, frame.width, 3), dtype="uint8")
323-
array[:, :, 0] = useful_array(frame.planes[2], 1).reshape(-1, frame.width)
324-
array[:, :, 1] = useful_array(frame.planes[0], 1).reshape(-1, frame.width)
325-
array[:, :, 2] = useful_array(frame.planes[1], 1).reshape(-1, frame.width)
325+
array[:, :, 0] = useful_array(frame.planes[2], 1).reshape(frame.height, frame.width)
326+
array[:, :, 1] = useful_array(frame.planes[0], 1).reshape(frame.height, frame.width)
327+
array[:, :, 2] = useful_array(frame.planes[1], 1).reshape(frame.height, frame.width)
326328
return array
327329
elif frame.format.name in ("gbrp10be", "gbrp12be", "gbrp14be", "gbrp16be", "gbrp10le", "gbrp12le", "gbrp14le", "gbrp16le"):
328330
array = np.empty((frame.height, frame.width, 3), dtype="uint16")
329-
array[:, :, 0] = useful_array(frame.planes[2], 2, "uint16").reshape(-1, frame.width)
330-
array[:, :, 1] = useful_array(frame.planes[0], 2, "uint16").reshape(-1, frame.width)
331-
array[:, :, 2] = useful_array(frame.planes[1], 2, "uint16").reshape(-1, frame.width)
331+
array[:, :, 0] = useful_array(frame.planes[2], 2, "uint16").reshape(frame.height, frame.width)
332+
array[:, :, 1] = useful_array(frame.planes[0], 2, "uint16").reshape(frame.height, frame.width)
333+
array[:, :, 2] = useful_array(frame.planes[1], 2, "uint16").reshape(frame.height, frame.width)
332334
return byteswap_array(array, frame.format.name.endswith("be"))
333335
elif frame.format.name in ("gbrpf32be", "gbrpf32le"):
334336
array = np.empty((frame.height, frame.width, 3), dtype="float32")
335-
array[:, :, 0] = useful_array(frame.planes[2], 4, "float32").reshape(-1, frame.width)
336-
array[:, :, 1] = useful_array(frame.planes[0], 4, "float32").reshape(-1, frame.width)
337-
array[:, :, 2] = useful_array(frame.planes[1], 4, "float32").reshape(-1, frame.width)
337+
array[:, :, 0] = useful_array(frame.planes[2], 4, "float32").reshape(frame.height, frame.width)
338+
array[:, :, 1] = useful_array(frame.planes[0], 4, "float32").reshape(frame.height, frame.width)
339+
array[:, :, 2] = useful_array(frame.planes[1], 4, "float32").reshape(frame.height, frame.width)
340+
return byteswap_array(array, frame.format.name.endswith("be"))
341+
elif frame.format.name in ("gbrapf32be", "gbrapf32le"):
342+
array = np.empty((frame.height, frame.width, 4), dtype="float32")
343+
array[:, :, 0] = useful_array(frame.planes[2], 4, "float32").reshape(frame.height, frame.width)
344+
array[:, :, 1] = useful_array(frame.planes[0], 4, "float32").reshape(frame.height, frame.width)
345+
array[:, :, 2] = useful_array(frame.planes[1], 4, "float32").reshape(frame.height, frame.width)
346+
array[:, :, 3] = useful_array(frame.planes[3], 4, "float32").reshape(frame.height, frame.width)
338347
return byteswap_array(array, frame.format.name.endswith("be"))
339348
elif frame.format.name in ("rgb24", "bgr24"):
340349
return useful_array(frame.planes[0], 3).reshape(frame.height, frame.width, -1)
@@ -345,17 +354,22 @@ cdef class VideoFrame(Frame):
345354
elif frame.format.name in ("gray16be", "gray16le"):
346355
return byteswap_array(
347356
useful_array(frame.planes[0], 2, "uint16").reshape(frame.height, frame.width),
348-
frame.format.name == "gray16be",
357+
frame.format.name.endswith("be"),
358+
)
359+
elif frame.format.name in ("grayf32be", "grayf32le"):
360+
return byteswap_array(
361+
useful_array(frame.planes[0], 4, "float32").reshape(frame.height, frame.width),
362+
frame.format.name.endswith("be"),
349363
)
350364
elif frame.format.name in ("rgb48be", "rgb48le"):
351365
return byteswap_array(
352366
useful_array(frame.planes[0], 6, "uint16").reshape(frame.height, frame.width, -1),
353-
frame.format.name == "rgb48be",
367+
frame.format.name.endswith("be"),
354368
)
355369
elif frame.format.name in ("rgba64be", "rgba64le"):
356370
return byteswap_array(
357371
useful_array(frame.planes[0], 8, "uint16").reshape(frame.height, frame.width, -1),
358-
frame.format.name == "rgba64be",
372+
frame.format.name.endswith("be"),
359373
)
360374
elif frame.format.name == "pal8":
361375
image = useful_array(frame.planes[0]).reshape(frame.height, frame.width)
@@ -491,6 +505,8 @@ cdef class VideoFrame(Frame):
491505
must be in the system's native byte order.
492506
493507
.. note:: for ``pal8``, an ``(image, palette)`` pair must be passed. `palette` must have shape (256, 4) and is given in ARGB format (PyAV will swap bytes if needed).
508+
509+
.. note:: for ``gbrp`` formats, channels are assumed to be given in RGB order.
494510
"""
495511
if format == "pal8":
496512
array, palette = array
@@ -568,19 +584,34 @@ cdef class VideoFrame(Frame):
568584
elif format in ("gray16be", "gray16le"):
569585
check_ndarray(array, "uint16", 2)
570586
frame = VideoFrame(array.shape[1], array.shape[0], format)
571-
copy_array_to_plane(byteswap_array(array, format == "gray16be"), frame.planes[0], 2)
587+
copy_array_to_plane(byteswap_array(array, format.endswith("be")), frame.planes[0], 2)
588+
return frame
589+
elif format in ("grayf32be", "grayf32le"):
590+
check_ndarray(array, "float32", 2)
591+
frame = VideoFrame(array.shape[1], array.shape[0], format)
592+
copy_array_to_plane(byteswap_array(array, format.endswith("be")), frame.planes[0], 4)
572593
return frame
573594
elif format in ("rgb48be", "rgb48le"):
574595
check_ndarray(array, "uint16", 3)
575596
check_ndarray_shape(array, array.shape[2] == 3)
576597
frame = VideoFrame(array.shape[1], array.shape[0], format)
577-
copy_array_to_plane(byteswap_array(array, format == "rgb48be"), frame.planes[0], 6)
598+
copy_array_to_plane(byteswap_array(array, format.endswith("be")), frame.planes[0], 6)
578599
return frame
579600
elif format in ("rgba64be", "rgba64le"):
580601
check_ndarray(array, "uint16", 3)
581602
check_ndarray_shape(array, array.shape[2] == 4)
582603
frame = VideoFrame(array.shape[1], array.shape[0], format)
583-
copy_array_to_plane(byteswap_array(array, format == "rgba64be"), frame.planes[0], 8)
604+
copy_array_to_plane(byteswap_array(array, format.endswith("be")), frame.planes[0], 8)
605+
return frame
606+
elif format in ("gbrapf32be", "gbrapf32le"):
607+
check_ndarray(array, "float32", 3)
608+
check_ndarray_shape(array, array.shape[2] == 4)
609+
610+
frame = VideoFrame(array.shape[1], array.shape[0], format)
611+
copy_array_to_plane(byteswap_array(array[:, :, 1], format.endswith("be")), frame.planes[0], 4)
612+
copy_array_to_plane(byteswap_array(array[:, :, 2], format.endswith("be")), frame.planes[1], 4)
613+
copy_array_to_plane(byteswap_array(array[:, :, 0], format.endswith("be")), frame.planes[2], 4)
614+
copy_array_to_plane(byteswap_array(array[:, :, 3], format.endswith("be")), frame.planes[3], 4)
584615
return frame
585616
elif format == "nv12":
586617
check_ndarray(array, "uint8", 2)

tests/test_videoframe.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,24 @@ def test_ndarray_gray_align() -> None:
223223
assertNdarraysEqual(frame.to_ndarray(), array)
224224

225225

226+
def test_ndarray_grayf32() -> None:
227+
array = numpy.random.random_sample(size=(480, 640)).astype(numpy.float32)
228+
for format in ("grayf32be", "grayf32le"):
229+
frame = VideoFrame.from_ndarray(array, format=format)
230+
assert frame.width == 640 and frame.height == 480
231+
assert frame.format.name == format
232+
assertNdarraysEqual(frame.to_ndarray(), array)
233+
234+
235+
def test_ndarray_grayf32_align() -> None:
236+
array = numpy.random.random_sample(size=(238, 318)).astype(numpy.float32)
237+
for format in ("grayf32be", "grayf32le"):
238+
frame = VideoFrame.from_ndarray(array, format=format)
239+
assert frame.width == 318 and frame.height == 238
240+
assert frame.format.name == format
241+
assertNdarraysEqual(frame.to_ndarray(), array)
242+
243+
226244
def test_ndarray_rgb() -> None:
227245
array = numpy.random.randint(0, 256, size=(480, 640, 3), dtype=numpy.uint8)
228246
for format in ("rgb24", "bgr24"):
@@ -365,6 +383,24 @@ def test_ndarray_gbrpf32_align() -> None:
365383
assertNdarraysEqual(frame.to_ndarray(), array)
366384

367385

386+
def test_ndarray_gbrapf32() -> None:
387+
array = numpy.random.random_sample(size=(480, 640, 4)).astype(numpy.float32)
388+
for format in ("gbrapf32be", "gbrapf32le"):
389+
frame = VideoFrame.from_ndarray(array, format=format)
390+
assert frame.width == 640 and frame.height == 480
391+
assert frame.format.name == format
392+
assertNdarraysEqual(frame.to_ndarray(), array)
393+
394+
395+
def test_ndarray_gbrapf32_allign() -> None:
396+
array = numpy.random.random_sample(size=(238, 318, 4)).astype(numpy.float32)
397+
for format in ("gbrapf32be", "gbrapf32le"):
398+
frame = VideoFrame.from_ndarray(array, format=format)
399+
assert frame.width == 318 and frame.height == 238
400+
assert frame.format.name == format
401+
assertNdarraysEqual(frame.to_ndarray(), array)
402+
403+
368404
def test_ndarray_yuv420p() -> None:
369405
array = numpy.random.randint(0, 256, size=(720, 640), dtype=numpy.uint8)
370406
frame = VideoFrame.from_ndarray(array, format="yuv420p")

0 commit comments

Comments
 (0)