Added support for picking directshow device capability lines.
This commit is contained in:
parent
8ff2e61e1b
commit
c9af5913fa
5 changed files with 104 additions and 33 deletions
|
|
@ -5,6 +5,7 @@ import numpy as np
|
|||
from ctypes import *
|
||||
from PIL import Image
|
||||
import cv2
|
||||
import json
|
||||
|
||||
def resolve(name):
|
||||
f = os.path.join(os.path.dirname(__file__), name)
|
||||
|
|
@ -13,7 +14,7 @@ def resolve(name):
|
|||
lib = None
|
||||
|
||||
def create_frame_buffer(width, height):
|
||||
buffer = bytearray(width * height * 4)
|
||||
buffer = bytearray(width * height * 4 * 4)
|
||||
char_array = c_char * len(buffer)
|
||||
return char_array.from_buffer(buffer)
|
||||
|
||||
|
|
@ -31,16 +32,20 @@ class DShowCapture():
|
|||
lib.get_devices.argtypes = [c_void_p]
|
||||
lib.get_device.argtypes = [c_void_p, c_int, c_char_p, c_int]
|
||||
lib.capture_device.argtypes = [c_void_p, c_int, c_int, c_int, c_int]
|
||||
lib.capture_device_by_dcap.argtypes = [c_void_p, c_int, c_int, c_int, c_int, c_longlong]
|
||||
lib.capture_device_default.argtypes = [c_void_p, c_int]
|
||||
lib.get_width.argtypes = [c_void_p]
|
||||
lib.get_height.argtypes = [c_void_p]
|
||||
lib.get_fps.argtypes = [c_void_p]
|
||||
lib.get_flipped.argtypes = [c_void_p]
|
||||
lib.get_colorspace.argtypes = [c_void_p]
|
||||
lib.get_colorspace_internal.argtypes = [c_void_p]
|
||||
lib.capturing.argtypes = [c_void_p]
|
||||
lib.get_frame.argtypes = [c_void_p, c_int, c_char_p, c_int]
|
||||
lib.get_size.argtypes = [c_void_p]
|
||||
lib.stop_capture.argtypes = [c_void_p]
|
||||
lib.get_json_length.argtypes = [c_void_p]
|
||||
lib.get_json.argtypes = [c_void_p, c_char_p, c_int]
|
||||
lib.destroy_capture.argtypes = [c_void_p]
|
||||
self.lib = lib
|
||||
self.cap = lib.create_capture()
|
||||
|
|
@ -49,6 +54,7 @@ class DShowCapture():
|
|||
self.have_devices = False
|
||||
self.size = None
|
||||
self.real_size = None
|
||||
self.info = None
|
||||
|
||||
def __del__(self):
|
||||
if not self.buffer is None:
|
||||
|
|
@ -65,6 +71,14 @@ class DShowCapture():
|
|||
name_str = str(self.name_buffer.value.decode('utf8'))
|
||||
return name_str
|
||||
|
||||
def get_info(self):
|
||||
json_length = self.lib.get_json_length(self.cap);
|
||||
json_buffer = create_string_buffer(json_length)
|
||||
self.lib.get_json(self.cap, json_buffer, json_length);
|
||||
json_text = str(json_buffer.value.decode('utf8'))
|
||||
self.info = json.loads(json_text)
|
||||
return self.info
|
||||
|
||||
def capture_device(self, cam, width, height, fps):
|
||||
if not self.have_devices:
|
||||
self.get_devices()
|
||||
|
|
@ -81,6 +95,23 @@ class DShowCapture():
|
|||
self.real_size = None
|
||||
return ret;
|
||||
|
||||
def capture_device_by_dcap(self, cam, dcap, width, height, fps):
|
||||
if not self.have_devices:
|
||||
self.get_devices()
|
||||
fps = int(10000000 / fps)
|
||||
ret = self.lib.capture_device_by_dcap(self.cap, cam, dcap, width, height, fps) == 1
|
||||
if ret:
|
||||
self.width = self.get_width()
|
||||
self.height = self.get_height()
|
||||
self.flipped = self.get_flipped()
|
||||
self.colorspace = self.get_colorspace()
|
||||
self.size = self.width * self.height * 4 * 4
|
||||
self.buffer = create_frame_buffer(self.width, self.height)
|
||||
else:
|
||||
self.size = None
|
||||
self.real_size = None
|
||||
return ret;
|
||||
|
||||
def capture_device_default(self, cam):
|
||||
if not self.have_devices:
|
||||
self.get_devices()
|
||||
|
|
@ -90,7 +121,7 @@ class DShowCapture():
|
|||
self.height = self.get_height()
|
||||
self.flipped = self.get_flipped()
|
||||
self.colorspace = self.get_colorspace()
|
||||
self.size = self.width * self.height * 4
|
||||
self.size = self.width * self.height * 4 * 4
|
||||
self.buffer = create_frame_buffer(self.width, self.height)
|
||||
else:
|
||||
self.size = None
|
||||
|
|
@ -110,7 +141,13 @@ class DShowCapture():
|
|||
return self.lib.get_flipped(self.cap) != 0
|
||||
|
||||
def get_colorspace(self):
|
||||
return self.lib.get_colorspace(self.cap)
|
||||
colorspace = self.lib.get_colorspace(self.cap)
|
||||
if colorspace == 0:
|
||||
colorspace = self.lib.get_colorspace_internal(self.cap)
|
||||
return colorspace
|
||||
|
||||
def get_colorspace_internal(self):
|
||||
return self.lib.get_colorspace_internal(self.cap)
|
||||
|
||||
def capturing(self):
|
||||
return self.lib.capturing(self.cap) == 1
|
||||
|
|
@ -118,32 +155,36 @@ class DShowCapture():
|
|||
def get_frame(self, timeout):
|
||||
if self.size is None:
|
||||
return None
|
||||
if self.lib.get_frame(self.cap, timeout, self.buffer, self.size) == 0:
|
||||
return None
|
||||
if self.real_size is None:
|
||||
self.real_size = self.lib.get_size(self.cap)
|
||||
self.real_size = self.lib.get_frame(self.cap, timeout, self.buffer, self.size)
|
||||
img = np.frombuffer(self.buffer, dtype=np.uint8)[0:self.real_size]
|
||||
if self.colorspace in [100, 101]:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,4)), cv2.COLOR_BGRA2BGR);
|
||||
if self.real_size == self.height * self.width * 4:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,4)), cv2.COLOR_BGRA2BGR)
|
||||
elif self.real_size == self.height * self.width * 3:
|
||||
img = img.reshape((self.height,self.width,3))
|
||||
else:
|
||||
return None
|
||||
if not self.flipped:
|
||||
img = cv2.flip(img, 0)
|
||||
return img
|
||||
elif self.colorspace == 200:
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_I420);
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_I420)
|
||||
elif self.colorspace == 201:
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_NV12);
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_NV12)
|
||||
elif self.colorspace == 202:
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_YV12);
|
||||
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_YV12)
|
||||
elif self.colorspace == 203:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,1)), cv2.COLOR_GRAY2BGR);
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,1)), cv2.COLOR_GRAY2BGR)
|
||||
elif self.colorspace == 300:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YVYU);
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YVYU)
|
||||
elif self.colorspace == 301:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YUY2);
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YUY2)
|
||||
elif self.colorspace == 302:
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_UYVY);
|
||||
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_UYVY)
|
||||
elif self.colorspace == 303:
|
||||
return None
|
||||
elif self.colorspace == 400:
|
||||
img = cv2.imdecode(img, cv2.IMREAD_COLOR);
|
||||
if self.flipped:
|
||||
img = cv2.flip(img, 0)
|
||||
return img
|
||||
|
|
@ -186,7 +227,7 @@ if __name__ == "__main__":
|
|||
width = cap.get_width()
|
||||
height = cap.get_height()
|
||||
flipped = cap.get_flipped()
|
||||
print(f"Width: {width} Height: {height} FPS: {cap.get_fps()} Flipped: {flipped} Colorspace: {cap.get_colorspace()}")
|
||||
print(f"Width: {width} Height: {height} FPS: {cap.get_fps()} Flipped: {flipped} Colorspace: {cap.get_colorspace()} Internal: {cap.get_colorspace_internal()}")
|
||||
|
||||
while True:
|
||||
img = cap.get_frame(1000)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -9,10 +9,11 @@ parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFo
|
|||
parser.add_argument("-i", "--ip", help="Set IP address for sending tracking data", default="127.0.0.1")
|
||||
parser.add_argument("-p", "--port", type=int, help="Set port for sending tracking data", default=11573)
|
||||
if os.name == 'nt':
|
||||
parser.add_argument("-l", "--list-cameras", type=int, help="Set this to 1 to list the available cameras and quit, set this to a number higher than 1 to output only the names", default=0)
|
||||
parser.add_argument("-l", "--list-cameras", type=int, help="Set this to 1 to list the available cameras and quit, set this to 2 to output only the names and to 3 to output camera names and device capabilities", default=0)
|
||||
parser.add_argument("-W", "--width", type=int, help="Set camera and raw RGB width", default=640)
|
||||
parser.add_argument("-H", "--height", type=int, help="Set camera and raw RGB height", default=360)
|
||||
parser.add_argument("-F", "--fps", type=int, help="Set camera frames per second", default=24)
|
||||
parser.add_argument("-D", "--dcap", type=int, help="Set which device capability line to use or -1 to use the default camera settings", default=None)
|
||||
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)
|
||||
|
|
@ -69,15 +70,30 @@ sys.stderr = OutputLog(output_logfile, sys.stderr)
|
|||
if os.name == 'nt' and args.list_cameras > 0:
|
||||
import dshowcapture
|
||||
cap = dshowcapture.DShowCapture()
|
||||
camera_count = cap.get_devices()
|
||||
if args.list_cameras == 1:
|
||||
print("Available cameras:")
|
||||
for i in range(camera_count):
|
||||
camera_name = cap.get_device(i)
|
||||
if args.list_cameras == 3:
|
||||
info = cap.get_info()
|
||||
unit = 10000000.;
|
||||
formats = {0: "Any", 1: "Unknown", 100: "ARGB", 101: "XRGB", 200: "I420", 201: "NV12", 202: "YV12", 203: "Y800", 300: "YVYU", 301: "YUY2", 302: "UYVY", 303: "HDYC (Unsupported)", 400: "MJPEG", 401: "H264" }
|
||||
for cam in info:
|
||||
print(f"{cam['id']}: {cam['name']}")
|
||||
for caps in cam['caps']:
|
||||
format = caps['format']
|
||||
if caps['format'] in formats:
|
||||
format = formats[caps['format']]
|
||||
if caps['minCX'] == caps['maxCX'] and caps['minCY'] == caps['maxCY']:
|
||||
print(f" {caps['id']}: Resolution: {caps['minCX']}x{caps['minCY']} FPS: {unit/caps['maxInterval']:.3f}-{unit/caps['minInterval']:.3f} Format: {format}")
|
||||
else:
|
||||
print(f" {caps['id']}: Resolution: {caps['minCX']}x{caps['minCY']}-{caps['maxCX']}x{caps['maxCY']} FPS: {unit/caps['maxInterval']:.3f}-{unit/caps['minInterval']:.3f} Format: {format}")
|
||||
else:
|
||||
camera_count = cap.get_devices()
|
||||
if args.list_cameras == 1:
|
||||
print(f"{i}: {camera_name}")
|
||||
else:
|
||||
print(camera_name)
|
||||
print("Available cameras:")
|
||||
for i in range(camera_count):
|
||||
camera_name = cap.get_device(i)
|
||||
if args.list_cameras == 1:
|
||||
print(f"{i}: {camera_name}")
|
||||
else:
|
||||
print(camera_name)
|
||||
cap.destroy_capture()
|
||||
sys.exit(0)
|
||||
|
||||
|
|
@ -86,7 +102,7 @@ import time
|
|||
import cv2
|
||||
import socket
|
||||
import struct
|
||||
from input_reader import InputReader, VideoReader, try_int
|
||||
from input_reader import InputReader, VideoReader, DShowCaptureReader, try_int
|
||||
from tracker import Tracker
|
||||
|
||||
target_ip = args.ip
|
||||
|
|
@ -96,11 +112,15 @@ if args.faces >= 40:
|
|||
print("Transmission of tracking data over network is not supported with 40 or more faces.")
|
||||
|
||||
fps = 0
|
||||
dcap = None
|
||||
use_dshowcapture_flag = False
|
||||
if os.name == 'nt':
|
||||
fps = args.fps
|
||||
dcap = args.dcap
|
||||
use_dshowcapture_flag = True if args.use_dshowcapture == 1 else False
|
||||
input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, use_dshowcapture=use_dshowcapture_flag)
|
||||
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())
|
||||
else:
|
||||
input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, use_dshowcapture=use_dshowcapture_flag)
|
||||
if type(input_reader.reader) == VideoReader:
|
||||
|
|
@ -146,7 +166,7 @@ try:
|
|||
source_name = input_reader.name
|
||||
while repeat or input_reader.is_open():
|
||||
if not input_reader.is_open() or need_reinit == 1:
|
||||
input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, use_dshowcapture=use_dshowcapture_flag)
|
||||
input_reader = InputReader(args.capture, args.raw_rgb, args.width, args.height, fps, use_dshowcapture=use_dshowcapture_flag, dcap=dcap)
|
||||
if input_reader.name != source_name:
|
||||
print(f"Failed to reinitialize camera and got {input_reader.name} instead of {source_name}.")
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -54,16 +54,26 @@ class EscapiReader(VideoReader):
|
|||
escapi.deinit_camera(self.device)
|
||||
|
||||
class DShowCaptureReader(VideoReader):
|
||||
def __init__(self, capture, width, height, fps, use_dshowcapture=True):
|
||||
def __init__(self, capture, width, height, fps, use_dshowcapture=True, dcap=None):
|
||||
self.device = None
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.fps = fps
|
||||
self.dcap = dcap;
|
||||
self.device = dshowcapture.DShowCapture()
|
||||
self.device.get_devices()
|
||||
self.name = self.device.get_device(capture)
|
||||
self.device.capture_device(capture, self.width, self.height, self.fps)
|
||||
print(f"Camera: \"{self.name}\" Resolution: {self.device.get_width()}x{self.device.get_height()} Frame rate: {self.device.get_fps()} Colorspace: {self.device.get_colorspace()} Flipped: {self.device.get_flipped()}")
|
||||
if dcap is None:
|
||||
print("Opening classic")
|
||||
self.device.capture_device(capture, self.width, self.height, self.fps)
|
||||
else:
|
||||
if dcap < 0:
|
||||
print("Opening default")
|
||||
self.device.capture_device_default(capture)
|
||||
else:
|
||||
print("Opening exact")
|
||||
self.device.capture_device_by_dcap(capture, dcap, self.width, self.height, self.fps)
|
||||
print(f"Camera: \"{self.name}\" Capability ID: {dcap} Resolution: {self.device.get_width()}x{self.device.get_height()} Frame rate: {self.device.get_fps()} Colorspace: {self.device.get_colorspace()} Internal: {self.device.get_colorspace_internal()} Flipped: {self.device.get_flipped()}")
|
||||
self.timeout = 1000
|
||||
def is_open(self):
|
||||
return self.device.capturing()
|
||||
|
|
@ -159,7 +169,7 @@ def test_reader(reader):
|
|||
return False
|
||||
|
||||
class InputReader():
|
||||
def __init__(self, capture, raw_rgb, width, height, fps, use_dshowcapture=False):
|
||||
def __init__(self, capture, raw_rgb, width, height, fps, use_dshowcapture=False, dcap=None):
|
||||
self.reader = None
|
||||
self.name = str(capture)
|
||||
try:
|
||||
|
|
@ -174,7 +184,7 @@ class InputReader():
|
|||
name = ""
|
||||
try:
|
||||
if use_dshowcapture:
|
||||
self.reader = DShowCaptureReader(int(capture), width, height, fps)
|
||||
self.reader = DShowCaptureReader(int(capture), width, height, fps, dcap=dcap)
|
||||
name = self.reader.name
|
||||
good = test_reader(self.reader)
|
||||
self.name = name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue