Skip to content

Commit

Permalink
Improve performance on windows
Browse files Browse the repository at this point in the history
Change opencv backend API to DSHOW, and changes related to this, which allows for higher resolution and native camera options window. OpenCV camera support on windows is apparently atrocious, but this had the best performance on Win 10, python 3.10, and OpenCV 4.x.

Also, initial code for automatic camera checking to try to find the proper index. WIP.
  • Loading branch information
nickjrotundo authored Jan 10, 2023
1 parent 9f1afe6 commit 43b1563
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
4 changes: 4 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
# -*- coding: utf-8 -*-
import dearpygui.dearpygui as dpg


from pyCameraController import CameraController
from pyCameraWindow import CameraWindow
from pyCameraCheck import find_cameras

if __name__ == "__main__":
#If not seeing any camera output, change the 1 below to 0, 2, 3, etc.
#This could probably be automated or a user-select added.
# Uncomment the next line to try to see what could be a valid index.
find_cameras()
cc = CameraController(1)
dpg.create_context()
dpg.create_viewport(title='PyReflectorCollimator', width=1800, height=800)
Expand Down
47 changes: 47 additions & 0 deletions pyCameraCheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# import os
# os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2 as cv
#import pymf

import site
import sys


def find_cameras():
# site.addsitedir('') # Always appends to end
# print(sys.path)
# # MSMF version using pymf
# device_list = pymf.get_MF_devices()
# for i, device_name in enumerate(device_list):
# print(f"opencv_index: {i}, device_name: {device_name}")
#list APIs
backends = cv.videoio_registry.getCameraBackends()
for i in backends:
print(cv.videoio_registry.getBackendName(i))
# Test the ports and returns a tuple with the available ports and the ones that are working.
camera_port = 0
non_working_ports = []
working_ports = []
available_ports = []

while len(non_working_ports) < 3: # if there are more than 5 non working ports stop the testing.
camera = cv.VideoCapture(camera_port, cv.CAP_DSHOW)
if not camera.isOpened():
non_working_ports.append(camera_port)
print("Port %s is not working." %camera_port)
else:
is_reading, img = camera.read()
w = camera.get(cv.CAP_PROP_FRAME_WIDTH)
h = camera.get(cv.CAP_PROP_FRAME_HEIGHT)
f = camera.get(cv.CAP_PROP_FPS)
if is_reading:
print("Port %s is working and reads images (%s x %s) at %s fps" %(camera_port,h,w,f))
working_ports.append(camera_port)
else:
print("Port %s for camera ( %s x %s) is present but does not reads." %(camera_port,h,w))
available_ports.append(camera_port)
camera_port +=1
# per the docs, the camera will be deinitialized automatically in VideoCapture destructor or on subsequent call
return available_ports,working_ports,non_working_ports
33 changes: 20 additions & 13 deletions pyCameraController.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,38 @@ def __init__(self, video_idx):
self.gamma = 100
self.contrast = 100


self.vid = cv.VideoCapture(video_idx)
# Original is self.vid = cv.VideoCapture(video_idx)
# On Windows, the above will somewhat work, but adding cv.CAP_DSHOW seemed to allow UVC props to be read correctly.
# Revert back to above for non-windows. cv.CAP_ANY can potentially be used however
# it tried to default to CAP_MSMF which did not pull the properties correctly.
self.vid = cv.VideoCapture(video_idx, cv.CAP_DSHOW)
#self.vid = cv.VideoCapture(-1)
cap = self.vid
#This check may or may not work since sometimes a camera is "opened" just not the right one and it will not display
if not cap.isOpened():
print("Cannot open camera. Check cc = CameraController(3) in main.py and change 3 to maybe 0, 1, etc.")
print("Cannot open camera. Check cc = CameraController(%s) in main.py and change 1 to maybe 0, 2, 3, etc." %video_idx)

oldfourcc = self.decode_fourcc(self.vid.get(cv.CAP_PROP_FOURCC))
print("current codec {}".format(oldfourcc))
# On Windows, decoding not working. Display undecoded output just to see what it says.
print("without decode {}".format(self.vid.get(cv.CAP_PROP_FOURCC)))
# MP4V also works if desired. codec = cv.VideoWriter_fourcc(*"MP4V")

codec = cv.VideoWriter_fourcc(*"MJPG")
res=self.vid.set(cv.CAP_PROP_FOURCC,codec)
if res:
print("codec is ",self.decode_fourcc(self.vid.get(cv.CAP_PROP_FOURCC)))
else:
print("error, codec is ",self.decode_fourcc(self.vid.get(cv.CAP_PROP_FOURCC)))

w=1280
h=720
fps=30
res1=cap.set(cv.CAP_PROP_FRAME_WIDTH,w)
res2=cap.set(cv.CAP_PROP_FRAME_HEIGHT,h)
res3=cap.set(cv.CAP_PROP_FPS,fps)
w=1920
h=1080
fps=15
cap.set(cv.CAP_PROP_FPS, 30.0)
cap.set(cv.CAP_PROP_FOURCC, cv.VideoWriter.fourcc('m','j','p','g'))
cap.set(cv.CAP_PROP_FOURCC, cv.VideoWriter.fourcc('M','J','P','G'))
cap.set(cv.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv.CAP_PROP_FRAME_HEIGHT, 1080)
# res1=cap.set(cv.CAP_PROP_FRAME_WIDTH,w)
# res2=cap.set(cv.CAP_PROP_FRAME_HEIGHT,h)
# res3=cap.set(cv.CAP_PROP_FPS,fps)
res4=cap.set(cv.CAP_PROP_SETTINGS,1)

self.fps = self.vid.get(cv.CAP_PROP_FPS)
ret, frame = self.vid.read()
Expand Down

0 comments on commit 43b1563

Please sign in to comment.