From 66e8d91b527b9df4d4a7f8a64195308bdd345db9 Mon Sep 17 00:00:00 2001 From: RanAS Date: Fri, 21 Nov 2025 13:54:07 -0300 Subject: [PATCH] feat: add fourcc / device format support on Linux, using V4L2 (Video4Linux2) --- facetracker.py | 4 ++++ input_reader.py | 27 ++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/facetracker.py b/facetracker.py index bb6906c..0a81423 100644 --- a/facetracker.py +++ b/facetracker.py @@ -18,6 +18,8 @@ if os.name == 'nt': else: parser.add_argument("-W", "--width", type=int, help="Set raw RGB width", default=640) parser.add_argument("-H", "--height", type=int, help="Set raw RGB height", default=360) +if sys.platform == 'linux': + parser.add_argument("--dformat", type=str, help="Set which device format to use", default=None) parser.add_argument("-F", "--fps", type=int, help="Set camera frames per second", default=24) parser.add_argument("-c", "--capture", help="Set camera ID (0, 1...) or video file", default="0") parser.add_argument("-M", "--mirror-input", action="store_true", help="Process a mirror image of the input video") @@ -162,6 +164,8 @@ if os.name == 'nt': input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, use_dshowcapture=use_dshowcapture_flag, dcap=dcap) if args.dcap == -1 and type(input_reader) == DShowCaptureReader: fps = min(fps, input_reader.device.get_fps()) +elif sys.platform == 'linux' and args.dformat: + input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, dcap=args.dformat) else: input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps) if type(input_reader.reader) == VideoReader: diff --git a/input_reader.py b/input_reader.py index f2a2c91..49b7a5b 100644 --- a/input_reader.py +++ b/input_reader.py @@ -13,7 +13,7 @@ class VideoReader(): def __init__(self, capture, camera=False): if os.name == 'nt' and camera: self.cap = cv2.VideoCapture(capture, cv2.CAP_DSHOW) - else: + elif self.cap is None: self.cap = cv2.VideoCapture(capture) if self.cap is None: print("The video source cannot be opened") @@ -121,6 +121,23 @@ class OpenCVReader(VideoReader): def close(self): super(OpenCVReader, self).close() +class V4L2Reader(OpenCVReader): + def __init__(self, capture, width, height, fps, dformat): + self.cap = cv2.VideoCapture(capture, cv2.CAP_V4L2) + if dformat: + fourcc = cv2.VideoWriter_fourcc(*dformat) + self.cap.set(6, fourcc) + super(V4L2Reader, self).__init__(capture, width, height, fps) + self.cap.set(5, fps) + def is_open(self): + return super(V4L2Reader, self).is_open() + def is_ready(self): + return super(V4L2Reader, self).is_ready() + def read(self): + return super(V4L2Reader, self).read() + def close(self): + super(V4L2Reader, self).close() + class RawReader: def __init__(self, width, height): self.width = int(width) @@ -232,6 +249,14 @@ class InputReader(): print(f"Escapi failed. Falling back to OpenCV. If this fails, please change your camera settings.", file=sys.stderr) self.reader = OpenCVReader(int(capture), width, height, fps) self.name = self.reader.name + elif sys.platform == 'linux': + try: + self.reader = V4L2Reader(int(capture), width, height, fps, dcap) + except: + print("V4L2 exception: ") + traceback.print_exc() + print(f"V4L2 failed. Falling back to OpenCV. If this fails, please change your camera settings.", file=sys.stderr) + self.reader = OpenCVReader(int(capture), width, height, fps) else: self.reader = OpenCVReader(int(capture), width, height, fps) except Exception as e: