Skip to content

Commit fc1402c

Browse files
z-khanWyattBlue
andauthored
Add BGR48, BGRA64 pixel formats
Extend `supported_np_pix_fmts` pixel formats with `bgr48be` `bgr48le` `bgra64be` `bgra64le`. Add handling for `argb` `abgr` `grayf32le` `grayf32be` pixel formats. --------- Co-authored-by: WyattBlue <[email protected]>
1 parent f3504ba commit fc1402c

File tree

2 files changed

+137
-15
lines changed

2 files changed

+137
-15
lines changed

av/video/frame.pyx

+34-15
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ cdef object _cinit_bypass_sentinel
1616
supported_np_pix_fmts = {
1717
"abgr", "argb", "bayer_bggr16be", "bayer_bggr16le", "bayer_bggr8", "bayer_gbrg16be",
1818
"bayer_gbrg16le", "bayer_gbrg8", "bayer_grbg16be", "bayer_grbg16le", "bayer_grbg8",
19-
"bayer_rggb16be", "bayer_rggb16le", "bayer_rggb8", "bgr24", "bgr8", "bgra",
19+
"bayer_rggb16be", "bayer_rggb16le", "bayer_rggb8", "bgr24", "bgr48be", "bgr48le", "bgr8", "bgra", "bgra64be", "bgra64le",
2020
"gbrapf32be", "gbrapf32le", "gbrp", "gbrp10be", "gbrp10le", "gbrp12be", "gbrp12le",
2121
"gbrp14be", "gbrp14le", "gbrp16be", "gbrp16le", "gbrpf32be", "gbrpf32le", "gray",
2222
"gray16be", "gray16le", "gray8", "grayf32be", "grayf32le", "nv12", "pal8", "rgb24",
@@ -342,6 +342,8 @@ cdef class VideoFrame(Frame):
342342
"bayer_rggb16le": (2, "uint16"),
343343
"bayer_rggb16be": (2, "uint16"),
344344
"bgr24": (3, "uint8"),
345+
"bgr48be": (6, "uint16"),
346+
"bgr48le": (6, "uint16"),
345347
"bgr8": (1, "uint8"),
346348
"bgra": (4, "uint8"),
347349
"gbrapf32be": (4, "float32"),
@@ -370,6 +372,8 @@ cdef class VideoFrame(Frame):
370372
"rgba": (4, "uint8"),
371373
"rgba64be": (8, "uint16"),
372374
"rgba64le": (8, "uint16"),
375+
"bgra64be": (8, "uint16"),
376+
"bgra64le": (8, "uint16"),
373377
"yuv444p": (1, "uint8"),
374378
"yuv444p16be": (2, "uint16"),
375379
"yuv444p16le": (2, "uint16"),
@@ -467,43 +471,58 @@ cdef class VideoFrame(Frame):
467471
if not width:
468472
width = array.shape[1]
469473

470-
if format in ("rgb24", "bgr24"):
474+
if format in {"rgb24", "bgr24"}:
471475
check_ndarray(array, "uint8", 3)
472476
check_ndarray_shape(array, array.shape[2] == 3)
473477
if array.strides[1:] != (3, 1):
474478
raise ValueError("provided array does not have C_CONTIGUOUS rows")
479+
linesizes = (array.strides[0], )
480+
elif format in {"rgb48le", "rgb48be", "bgr48le", "bgr48be"}:
481+
check_ndarray(array, "uint16", 3)
482+
check_ndarray_shape(array, array.shape[2] == 3)
483+
if array.strides[1:] != (6, 2):
484+
raise ValueError("provided array does not have C_CONTIGUOUS rows")
475485
linesizes = (array.strides[0], )
476-
elif format in ("rgba", "bgra"):
486+
elif format in {"rgba", "bgra", "argb", "abgr"}:
477487
check_ndarray(array, "uint8", 3)
478488
check_ndarray_shape(array, array.shape[2] == 4)
479489
if array.strides[1:] != (4, 1):
480490
raise ValueError("provided array does not have C_CONTIGUOUS rows")
481491
linesizes = (array.strides[0], )
482-
elif format in ("gray", "gray8", "rgb8", "bgr8"):
492+
elif format in {"rgba64le", "rgba64be", "bgra64le", "bgra64be"}:
493+
check_ndarray(array, "uint16", 3)
494+
check_ndarray_shape(array, array.shape[2] == 4)
495+
if array.strides[1:] != (8, 2):
496+
raise ValueError("provided array does not have C_CONTIGUOUS rows")
497+
linesizes = (array.strides[0], )
498+
elif format in {"gray", "gray8", "rgb8", "bgr8","bayer_bggr8", "bayer_rggb8", "bayer_gbrg8", "bayer_grbg8"}:
483499
check_ndarray(array, "uint8", 2)
484500
if array.strides[1] != 1:
485501
raise ValueError("provided array does not have C_CONTIGUOUS rows")
486502
linesizes = (array.strides[0], )
487-
elif format in ("yuv420p", "yuvj420p", "nv12"):
503+
elif format in {"gray16le", "gray16be", "bayer_rggb16le", "bayer_gbrg16le", "bayer_grbg16le","bayer_bggr16be", "bayer_rggb16be", "bayer_gbrg16be", "bayer_grbg16be"}:
504+
check_ndarray(array, "uint16", 2)
505+
if array.strides[1] != 2:
506+
raise ValueError("provided array does not have C_CONTIGUOUS rows")
507+
linesizes = (array.strides[0], )
508+
elif format in {"grayf32le", "grayf32be"}:
509+
check_ndarray(array, "float32", 2)
510+
if array.strides[1] != 4:
511+
raise ValueError("provided array does not have C_CONTIGUOUS rows")
512+
linesizes = (array.strides[0], )
513+
elif format in {"yuv420p", "yuvj420p", "nv12"}:
488514
check_ndarray(array, "uint8", 2)
489515
check_ndarray_shape(array, array.shape[0] % 3 == 0)
490516
check_ndarray_shape(array, array.shape[1] % 2 == 0)
491517
height = height // 6 * 4
492518
if array.strides[1] != 1:
493519
raise ValueError("provided array does not have C_CONTIGUOUS rows")
494-
if format in ("yuv420p", "yuvj420p"):
520+
if format in {"yuv420p", "yuvj420p"}:
495521
# For YUV420 planar formats, the UV plane stride is always half the Y stride.
496522
linesizes = (array.strides[0], array.strides[0] // 2, array.strides[0] // 2)
497523
else:
498524
# Planes where U and V are interleaved have the same stride as Y.
499525
linesizes = (array.strides[0], array.strides[0])
500-
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"}:
501-
check_ndarray(array, "uint8" if format.endswith("8") else "uint16", 2)
502-
503-
if array.strides[1] != (1 if format.endswith("8") else 2):
504-
raise ValueError("provided array does not have C_CONTIGUOUS rows")
505-
506-
linesizes = (array.strides[0],)
507526
else:
508527
raise ValueError(f"Conversion from numpy array with format `{format}` is not yet supported")
509528

@@ -717,13 +736,13 @@ cdef class VideoFrame(Frame):
717736
elif format in {"argb", "rgba", "abgr", "bgra"}:
718737
check_ndarray(array, "uint8", 3)
719738
check_ndarray_shape(array, array.shape[2] == 4)
720-
elif format in {"rgb48be", "rgb48le"}:
739+
elif format in {"rgb48be", "rgb48le","bgr48be", "bgr48le"}:
721740
check_ndarray(array, "uint16", 3)
722741
check_ndarray_shape(array, array.shape[2] == 3)
723742
frame = VideoFrame(array.shape[1], array.shape[0], format)
724743
copy_array_to_plane(byteswap_array(array, format.endswith("be")), frame.planes[0], 6)
725744
return frame
726-
elif format in {"rgba64be", "rgba64le"}:
745+
elif format in {"rgba64be", "rgba64le", "bgra64be", "bgra64le"}:
727746
check_ndarray(array, "uint16", 3)
728747
check_ndarray_shape(array, array.shape[2] == 4)
729748
frame = VideoFrame(array.shape[1], array.shape[0], format)

tests/test_videoframe.py

+103
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,35 @@ def test_ndarray_rgba_align() -> None:
285285
assertNdarraysEqual(frame.to_ndarray(), array)
286286

287287

288+
def test_ndarray_bayer8() -> None:
289+
array = numpy.random.randint(0, 256, size=(480, 640), dtype=numpy.uint8)
290+
for format in ("bayer_bggr8", "bayer_gbrg8", "bayer_grbg8", "bayer_rggb8"):
291+
frame = VideoFrame.from_ndarray(array, format=format)
292+
assert format in av.video.frame.supported_np_pix_fmts
293+
assert frame.width == 640 and frame.height == 480
294+
assert frame.format.name == format
295+
assertNdarraysEqual(frame.to_ndarray(), array)
296+
297+
298+
def test_ndarray_bayer16() -> None:
299+
array = numpy.random.randint(0, 65536, size=(480, 640), dtype=numpy.uint16)
300+
for format in (
301+
"bayer_bggr16be",
302+
"bayer_bggr16le",
303+
"bayer_gbrg16be",
304+
"bayer_gbrg16le",
305+
"bayer_grbg16be",
306+
"bayer_grbg16le",
307+
"bayer_rggb16be",
308+
"bayer_rggb16le",
309+
):
310+
frame = VideoFrame.from_ndarray(array, format=format)
311+
assert format in av.video.frame.supported_np_pix_fmts
312+
assert frame.width == 640 and frame.height == 480
313+
assert frame.format.name == format
314+
assertNdarraysEqual(frame.to_ndarray(), array)
315+
316+
288317
def test_ndarray_gbrp() -> None:
289318
array = numpy.random.randint(0, 256, size=(480, 640, 3), dtype=numpy.uint8)
290319
frame = VideoFrame.from_ndarray(array, format="gbrp")
@@ -571,6 +600,17 @@ def test_ndarray_rgb48be() -> None:
571600
assertPixelValue16(frame.planes[0], array[0][0][0], "big")
572601

573602

603+
def test_ndarray_bgr48be() -> None:
604+
array = numpy.random.randint(0, 65536, size=(480, 640, 3), dtype=numpy.uint16)
605+
frame = VideoFrame.from_ndarray(array, format="bgr48be")
606+
assert frame.width == 640 and frame.height == 480
607+
assert frame.format.name == "bgr48be"
608+
assertNdarraysEqual(frame.to_ndarray(), array)
609+
610+
# check endianness by examining blue value of first pixel
611+
assertPixelValue16(frame.planes[0], array[0][0][0], "big")
612+
613+
574614
def test_ndarray_rgb48le() -> None:
575615
array = numpy.random.randint(0, 65536, size=(480, 640, 3), dtype=numpy.uint16)
576616
frame = VideoFrame.from_ndarray(array, format="rgb48le")
@@ -582,6 +622,17 @@ def test_ndarray_rgb48le() -> None:
582622
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
583623

584624

625+
def test_ndarray_bgr48le() -> None:
626+
array = numpy.random.randint(0, 65536, size=(480, 640, 3), dtype=numpy.uint16)
627+
frame = VideoFrame.from_ndarray(array, format="bgr48le")
628+
assert frame.width == 640 and frame.height == 480
629+
assert frame.format.name == "bgr48le"
630+
assertNdarraysEqual(frame.to_ndarray(), array)
631+
632+
# check endianness by examining blue value of first pixel
633+
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
634+
635+
585636
def test_ndarray_rgb48le_align() -> None:
586637
array = numpy.random.randint(0, 65536, size=(238, 318, 3), dtype=numpy.uint16)
587638
frame = VideoFrame.from_ndarray(array, format="rgb48le")
@@ -593,6 +644,17 @@ def test_ndarray_rgb48le_align() -> None:
593644
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
594645

595646

647+
def test_ndarray_bgr48le_align() -> None:
648+
array = numpy.random.randint(0, 65536, size=(238, 318, 3), dtype=numpy.uint16)
649+
frame = VideoFrame.from_ndarray(array, format="bgr48le")
650+
assert frame.width == 318 and frame.height == 238
651+
assert frame.format.name == "bgr48le"
652+
assertNdarraysEqual(frame.to_ndarray(), array)
653+
654+
# check endianness by examining blue value of first pixel
655+
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
656+
657+
596658
def test_ndarray_rgba64be() -> None:
597659
array = numpy.random.randint(0, 65536, size=(480, 640, 4), dtype=numpy.uint16)
598660
frame = VideoFrame.from_ndarray(array, format="rgba64be")
@@ -604,6 +666,17 @@ def test_ndarray_rgba64be() -> None:
604666
assertPixelValue16(frame.planes[0], array[0][0][0], "big")
605667

606668

669+
def test_ndarray_bgra64be() -> None:
670+
array = numpy.random.randint(0, 65536, size=(480, 640, 4), dtype=numpy.uint16)
671+
frame = VideoFrame.from_ndarray(array, format="bgra64be")
672+
assert frame.width == 640 and frame.height == 480
673+
assert frame.format.name == "bgra64be"
674+
assertNdarraysEqual(frame.to_ndarray(), array)
675+
676+
# check endianness by examining blue value of first pixel
677+
assertPixelValue16(frame.planes[0], array[0][0][0], "big")
678+
679+
607680
def test_ndarray_rgba64le() -> None:
608681
array = numpy.random.randint(0, 65536, size=(480, 640, 4), dtype=numpy.uint16)
609682
frame = VideoFrame.from_ndarray(array, format="rgba64le")
@@ -615,6 +688,17 @@ def test_ndarray_rgba64le() -> None:
615688
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
616689

617690

691+
def test_ndarray_bgra64le() -> None:
692+
array = numpy.random.randint(0, 65536, size=(480, 640, 4), dtype=numpy.uint16)
693+
frame = VideoFrame.from_ndarray(array, format="bgra64le")
694+
assert frame.width == 640 and frame.height == 480
695+
assert frame.format.name == "bgra64le"
696+
assertNdarraysEqual(frame.to_ndarray(), array)
697+
698+
# check endianness by examining blue value of first pixel
699+
assertPixelValue16(frame.planes[0], array[0][0][0], "little")
700+
701+
618702
def test_ndarray_rgb8() -> None:
619703
array = numpy.random.randint(0, 256, size=(480, 640), dtype=numpy.uint8)
620704
frame = VideoFrame.from_ndarray(array, format="rgb8")
@@ -805,6 +889,25 @@ def test_shares_memory_rgba() -> None:
805889
assertNdarraysEqual(frame.to_ndarray(), array)
806890

807891

892+
def test_shares_memory_bayer8() -> None:
893+
for format in ("bayer_rggb8", "bayer_bggr8", "bayer_grbg8", "bayer_gbrg8"):
894+
array = numpy.random.randint(0, 256, size=(357, 318), dtype=numpy.uint8)
895+
frame = VideoFrame.from_numpy_buffer(array, format)
896+
assertNdarraysEqual(frame.to_ndarray(), array)
897+
898+
array[...] = numpy.random.randint(0, 256, size=(357, 318), dtype=numpy.uint8)
899+
assertNdarraysEqual(frame.to_ndarray(), array)
900+
901+
array = numpy.random.randint(0, 256, size=(357, 318), dtype=numpy.uint8)
902+
array = array[:, :300]
903+
assert not array.data.c_contiguous
904+
frame = VideoFrame.from_numpy_buffer(array, format)
905+
assertNdarraysEqual(frame.to_ndarray(), array)
906+
907+
array[...] = numpy.random.randint(0, 256, size=array.shape, dtype=numpy.uint8)
908+
assertNdarraysEqual(frame.to_ndarray(), array)
909+
910+
808911
def test_shares_memory_yuv420p() -> None:
809912
array = numpy.random.randint(0, 256, size=(512 * 6 // 4, 256), dtype=numpy.uint8)
810913
frame = VideoFrame.from_numpy_buffer(array, "yuv420p")

0 commit comments

Comments
 (0)