Change to CAP_DSHOW for VideoCapture
This commit is contained in:
@@ -5,6 +5,12 @@
|
||||
"left": "PointIndex",
|
||||
"right": "",
|
||||
"extensions": []
|
||||
},
|
||||
{
|
||||
"action": "Pointer",
|
||||
"left": "",
|
||||
"right": "PointIndex",
|
||||
"extensions": []
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -1,10 +1,92 @@
|
||||
import numpy as np
|
||||
import json
|
||||
import cv2
|
||||
import threading
|
||||
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import List, Optional, Tuple, Dict
|
||||
|
||||
from pygrabber.dshow_graph import FilterGraph
|
||||
|
||||
|
||||
class DShowMJPGCapture:
|
||||
"""
|
||||
cv2.VideoCapture-like wrapper (read/release/isOpened) that selects an exact
|
||||
DirectShow media type (e.g. MJPG at a given resolution) before capturing.
|
||||
|
||||
This exists because cv2.VideoCapture(..., cv2.CAP_DSHOW).set(CAP_PROP_FOURCC, ...)
|
||||
is unreliable on some webcams: OpenCV opens its own separate DirectShow filter
|
||||
graph, which renegotiates its own format independently of anything set through
|
||||
pygrabber beforehand. Doing format selection AND frame grabbing in the same
|
||||
graph (via pygrabber's sample grabber) avoids that.
|
||||
"""
|
||||
|
||||
def __init__(self, index: int, width: int, height: int,
|
||||
fourcc_substr: str = "MJPG", timeout: float = 2.0):
|
||||
self.index = index
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.selected_format = None
|
||||
self._opened = False
|
||||
self._latest_frame = None
|
||||
self._frame_event = threading.Event()
|
||||
self._timeout = timeout
|
||||
|
||||
self._graph = FilterGraph()
|
||||
self._graph.add_video_input_device(index)
|
||||
|
||||
device = self._graph.get_input_device()
|
||||
formats = device.get_formats()
|
||||
match = next(
|
||||
(f for f in formats
|
||||
if f["width"] == width and f["height"] == height
|
||||
and fourcc_substr.upper() in f["media_type_str"].upper()),
|
||||
None,
|
||||
)
|
||||
if match is None:
|
||||
raise RuntimeError(
|
||||
f"No {fourcc_substr} format at {width}x{height} available on camera {index}. "
|
||||
f"Available formats: {formats}"
|
||||
)
|
||||
device.set_format(match["index"])
|
||||
self.selected_format = match
|
||||
|
||||
self._graph.add_sample_grabber(self._on_frame)
|
||||
self._graph.add_null_render()
|
||||
self._graph.prepare_preview_graph()
|
||||
self._graph.run()
|
||||
self._opened = True
|
||||
|
||||
def _on_frame(self, frame):
|
||||
self._latest_frame = frame
|
||||
self._frame_event.set()
|
||||
|
||||
def isOpened(self) -> bool:
|
||||
return self._opened
|
||||
|
||||
def read(self):
|
||||
if not self._opened:
|
||||
return False, None
|
||||
self._frame_event.clear()
|
||||
self._graph.grab_frame()
|
||||
if not self._frame_event.wait(self._timeout):
|
||||
return False, None
|
||||
return True, self._latest_frame
|
||||
|
||||
def release(self):
|
||||
if self._opened:
|
||||
self._graph.stop()
|
||||
self._graph.remove_filters()
|
||||
self._opened = False
|
||||
|
||||
# No-ops for compatibility with code paths that still call .set()/.get()
|
||||
# on the capture object (real config happens via device.set_format above).
|
||||
def set(self, prop_id, value):
|
||||
return False
|
||||
|
||||
def get(self, prop_id):
|
||||
return 0.0
|
||||
|
||||
|
||||
# ----- Helper Functions -----
|
||||
def compute_distance(p1, p2):
|
||||
|
||||
@@ -436,9 +436,9 @@ class HomeWindow(QWidget):
|
||||
for camera in self.cameraList: # Loop all Cameras
|
||||
ok, frame = camera.capture.read() #Capture a frame
|
||||
|
||||
if not ok: # Check if captured frame is valid, if even one frame is invalide the whole capture failed
|
||||
if not ok: # A single failed grab shouldn't discard what other cameras saw
|
||||
self.logger.log(f"Could not grab frame for calibration sample, from camera {camera.index}.")
|
||||
return
|
||||
continue
|
||||
|
||||
h, w = frame.shape[:2]
|
||||
self.logger.log(f"cam{camera.index} capture size = {w}x{h}")
|
||||
@@ -451,9 +451,9 @@ class HomeWindow(QWidget):
|
||||
|
||||
#This is apparently a fast check, bevor i detect the actuall board
|
||||
corner, ids, _ = self.arcuo_detector.detectMarkers(gray) #Detect ChArCuo Markers
|
||||
if ids is None or len(ids) == 0: #Check if Markers where found, if none where found the sample is invalid
|
||||
if ids is None or len(ids) == 0: #Board not visible from this camera this time, skip it, not the whole sample
|
||||
self.logger.log(f"Not enought markers found for calibration sample, from camera {camera.index}.")
|
||||
return
|
||||
continue
|
||||
|
||||
# Detecting the Charcuo board and evaluating if its good as a sample
|
||||
result = self.charuco_detector.detectBoard(gray)
|
||||
@@ -463,11 +463,11 @@ class HomeWindow(QWidget):
|
||||
|
||||
if charuco_ids is None:
|
||||
self.logger.log(f"Not enought markers found for calibration sample, from camera {camera.index}.")
|
||||
return
|
||||
continue
|
||||
|
||||
if len(charuco_ids) < MIN_CHARUCO_CORNERS:
|
||||
self.logger.log(f"Not enought markers found for calibration sample, from camera {camera.index}.")
|
||||
return
|
||||
continue
|
||||
|
||||
# Adding the sample for the current camera to detections
|
||||
detections[camera.index] = CharucoDetection(
|
||||
@@ -477,12 +477,18 @@ class HomeWindow(QWidget):
|
||||
gray_frame = gray
|
||||
)
|
||||
|
||||
# All cameras found valide markers so the sample capture was succesfull
|
||||
# Need at least two cameras' worth of detections for this sample to be
|
||||
# useful for stereo pairing (a single-camera detection can still help
|
||||
# that camera's intrinsics, but can't contribute to any camera pair).
|
||||
if len(detections) < 2:
|
||||
self.logger.log("Not enough cameras saw the board in this sample, discarding.")
|
||||
return
|
||||
|
||||
self.calibration_samples.append(
|
||||
CalibrationSample(detections=detections)
|
||||
)
|
||||
|
||||
self.logger.log("Added calibration sample.")
|
||||
self.logger.log(f"Added calibration sample ({len(detections)}/{len(self.cameraList)} cameras saw the board).")
|
||||
|
||||
def updateCameras(self):
|
||||
self._clearLayout()
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import cv2, numpy as np
|
||||
import os
|
||||
|
||||
|
||||
from Tbd.helper import CameraObject
|
||||
from Tbd.helper import CameraObject, DShowMJPGCapture
|
||||
from UI.UILogger import UILogger
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
@@ -98,30 +97,23 @@ class SetupPage(QWidget):
|
||||
# index=None
|
||||
#)
|
||||
for index in indexToAdd:
|
||||
capture = cv2.VideoCapture(index, cv2.CAP_MSMF)
|
||||
#capture = cv2.VideoCapture(index, cv2.CAP_DSHOW)
|
||||
try:
|
||||
capture = DShowMJPGCapture(index, 2592, 1944, fourcc_substr="MJPG")
|
||||
except RuntimeError as e:
|
||||
self.logger.log(str(e))
|
||||
print(str(e))
|
||||
continue
|
||||
|
||||
capture.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*'MJPG'))
|
||||
capture.set(cv2.CAP_PROP_FPS, 30)
|
||||
capture.set(cv2.CAP_PROP_FRAME_WIDTH, 2592)
|
||||
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1944)
|
||||
self.logger.log(f"Selected format: {capture.selected_format}")
|
||||
print(f"Selected format: {capture.selected_format}")
|
||||
|
||||
#capture.set(cv2.CAP_PROP_FRAME_WIDTH, 2048.0)
|
||||
#capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 1536.0)
|
||||
width = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||
height = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||||
fps = capture.get(cv2.CAP_PROP_FPS)
|
||||
|
||||
fourcc = int(capture.get(cv2.CAP_PROP_FOURCC))
|
||||
fourcc_str = "".join([chr((fourcc >> 8*i) & 0xFF) for i in range(4)])
|
||||
|
||||
print(f"Resolution: {width}x{height}")
|
||||
print(f"FPS: {fps}")
|
||||
print(f"Format (FOURCC): {fourcc_str}")
|
||||
self.logger.log(f"Resolution: {width}x{height}")
|
||||
self.logger.log(f"FPS: {fps}")
|
||||
self.logger.log(f"Format (FOURCC): {fourcc_str}")
|
||||
#currentcapture = cv2.VideoCapture(index, cv2.CAP_MSMF)
|
||||
ret, frame = capture.read()
|
||||
if ret:
|
||||
self.logger.log(f"Actual frame shape: {frame.shape}")
|
||||
print(f"Actual frame shape: {frame.shape}")
|
||||
else:
|
||||
self.logger.log("Failed to read a frame after configuring camera")
|
||||
print("Failed to read a frame after configuring camera")
|
||||
|
||||
#self.logger.log("Initial frame:", w, "x", h)
|
||||
self.cameraList.append(CameraObject(
|
||||
@@ -185,7 +177,7 @@ class SetupPage(QWidget):
|
||||
index = int(self.cameraIndex.text())
|
||||
|
||||
self.release_camera()
|
||||
self.cameraObject.capture = cv2.VideoCapture(index)
|
||||
self.cameraObject.capture = cv2.VideoCapture(index, cv2.CAP_DSHOW)
|
||||
self.cameraObject.index = index
|
||||
|
||||
if not self.cameraObject.capture.isOpened():
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user