From 2b7899377e9bd5c1e33e4e28bfaee8b8d4044ee9 Mon Sep 17 00:00:00 2001 From: SimLeek Date: Sun, 8 Apr 2018 14:12:02 -0700 Subject: [PATCH] Updated major version due to breaking changes. Reformatted files and imports. Removed unused ctrl commands. Made all pubsub channels camel case. changed sub_win_loop to class. Removed implementation specific code. Removed unreliable 'x to quit' code. Made windows quit from pubsub. Added WinCtrl class for window specific commands. Added threaded interactive key press test. Removed testt file. --- cv_pubsubs/{webcam_pub => }/listen_default.py | 0 cv_pubsubs/webcam_pub/__init__.py | 5 +- cv_pubsubs/webcam_pub/camctrl.py | 12 +-- cv_pubsubs/webcam_pub/frame_handler.py | 16 +-- cv_pubsubs/webcam_pub/pub_cam.py | 28 +++--- cv_pubsubs/window_sub/__init__.py | 2 +- cv_pubsubs/window_sub/cv_window_sub.py | 98 ++++++++++--------- cv_pubsubs/window_sub/winctrl.py | 12 +++ setup.py | 7 +- tests_interactive/__init__.py | 0 tests_interactive/test_sub_win.py | 60 +++++++++--- testt.py | 8 -- 12 files changed, 143 insertions(+), 105 deletions(-) rename cv_pubsubs/{webcam_pub => }/listen_default.py (100%) create mode 100644 cv_pubsubs/window_sub/winctrl.py delete mode 100644 tests_interactive/__init__.py delete mode 100644 testt.py diff --git a/cv_pubsubs/webcam_pub/listen_default.py b/cv_pubsubs/listen_default.py similarity index 100% rename from cv_pubsubs/webcam_pub/listen_default.py rename to cv_pubsubs/listen_default.py diff --git a/cv_pubsubs/webcam_pub/__init__.py b/cv_pubsubs/webcam_pub/__init__.py index 8e41c0e..6fff6cb 100644 --- a/cv_pubsubs/webcam_pub/__init__.py +++ b/cv_pubsubs/webcam_pub/__init__.py @@ -1,5 +1,4 @@ -from .listen_default import listen_default +from .camctrl import CamCtrl +from .frame_handler import frame_handler_thread from .get_cam_ids import get_cam_ids from .pub_cam import pub_cam_thread -from .frame_handler import frame_handler_thread -from .camctrl import CamCtrl diff --git a/cv_pubsubs/webcam_pub/camctrl.py b/cv_pubsubs/webcam_pub/camctrl.py index 541ac5c..04efd32 100644 --- a/cv_pubsubs/webcam_pub/camctrl.py +++ b/cv_pubsubs/webcam_pub/camctrl.py @@ -9,14 +9,4 @@ class CamCtrl: @staticmethod def stop_cam(cam_id # type: Union[int, str] ): - pubsub.publish("cvcamhandlers." + str(cam_id) + ".cmd", 'q') - - @staticmethod - def reset_vid(cam_id # type: Union[int, str] - ): - pubsub.publish("cvcamhandlers." + str(cam_id) + ".cmd", 'r') - - @staticmethod - def key_stroke(key_entered): - pubsub.publish("cvKeyStroke.", key_entered) - + pubsub.publish("CVCamHandlers." + str(cam_id) + ".Cmd", 'quit') diff --git a/cv_pubsubs/webcam_pub/frame_handler.py b/cv_pubsubs/webcam_pub/frame_handler.py index 8af1f2c..a0a3cbb 100644 --- a/cv_pubsubs/webcam_pub/frame_handler.py +++ b/cv_pubsubs/webcam_pub/frame_handler.py @@ -1,7 +1,9 @@ -import pubsub -import numpy as np import threading -from .listen_default import listen_default + +import numpy as np +import pubsub + +from cv_pubsubs.listen_default import listen_default from .pub_cam import pub_cam_thread if False: @@ -15,16 +17,16 @@ def frame_handler_loop(cam_id, # type: Union[int, str] fps_limit=240 # type: float ): t = pub_cam_thread(cam_id, request_size, high_speed, fps_limit) - sub_cam = pubsub.subscribe("cvcams." + str(cam_id) + ".vid") - sub_owner = pubsub.subscribe("cvcamhandlers." + str(cam_id) + ".cmd") + sub_cam = pubsub.subscribe("CVCams." + str(cam_id) + ".Vid") + sub_owner = pubsub.subscribe("CVCamHandlers." + str(cam_id) + ".Cmd") msg_owner = '' - while msg_owner != 'q': + while msg_owner != 'quit': frame = listen_default(sub_cam, timeout=.1) # type: np.ndarray if frame is not None: frame = frame[0] frame_handler(frame, cam_id) msg_owner = listen_default(sub_owner, block=False, empty='') - pubsub.publish("cvcams." + str(cam_id) + ".cmd", 'q') + pubsub.publish("CVCams." + str(cam_id) + ".Cmd", 'quit') t.join() diff --git a/cv_pubsubs/webcam_pub/pub_cam.py b/cv_pubsubs/webcam_pub/pub_cam.py index 81a53f5..91ce302 100644 --- a/cv_pubsubs/webcam_pub/pub_cam.py +++ b/cv_pubsubs/webcam_pub/pub_cam.py @@ -1,9 +1,11 @@ -import pubsub +import threading +import time + import cv2 import numpy as np -import time -import threading -from .listen_default import listen_default +import pubsub + +from cv_pubsubs.listen_default import listen_default if False: from typing import Union, Tuple @@ -14,9 +16,9 @@ def pub_cam_loop(cam_id, # type: Union[int, str] high_speed=False, # type: bool fps_limit=240 # type: float ): # type: (...)->bool - """Publishes whichever camera you select to cvcams..vid - You can send a quit command 'q' to cvcams..cmd - Status information, such as failure to open, will be posted to cvcams..status + """Publishes whichever camera you select to CVCams..Vid + You can send a quit command 'quit' to CVCams..Cmd + Status information, such as failure to open, will be posted to CVCams..Status :param high_speed: Selects mjpeg transferring, which most cameras seem to support, so speed isn't limited @@ -25,7 +27,7 @@ def pub_cam_loop(cam_id, # type: Union[int, str] :param request_size: A tuple with width, then height, to request the video size. :return: True if loop ended normally, False if it failed somehow. """ - sub = pubsub.subscribe("cvcams." + str(cam_id) + ".cmd") + sub = pubsub.subscribe("CVCams." + str(cam_id) + ".Cmd") msg = '' cam = cv2.VideoCapture(cam_id) # cam.set(cv2.CAP_PROP_CONVERT_RGB, 0) @@ -38,23 +40,23 @@ def pub_cam_loop(cam_id, # type: Union[int, str] cam.set(cv2.CAP_PROP_FRAME_HEIGHT, request_size[1]) if not cam.isOpened(): - pubsub.publish("cvcams." + str(cam_id) + ".status", "failed") + pubsub.publish("CVCams." + str(cam_id) + ".Status", "failed") return False now = time.time() - while msg != 'q': + while msg != 'quit': time.sleep(1. / (fps_limit - (time.time() - now))) now = time.time() (ret, frame) = cam.read() # type: Tuple[bool, np.ndarray ] if ret is False or not isinstance(frame, np.ndarray): cam.release() - pubsub.publish("cvcams." + str(cam_id) + ".status", "failed") + pubsub.publish("CVCams." + str(cam_id) + ".Status", "failed") return False if cam.get(cv2.CAP_PROP_FRAME_COUNT) > 0: - frame_counter+=1 + frame_counter += 1 if frame_counter >= cam.get(cv2.CAP_PROP_FRAME_COUNT): frame_counter = 0 cam = cv2.VideoCapture(cam_id) - pubsub.publish("cvcams." + str(cam_id) + ".vid", (frame,)) + pubsub.publish("CVCams." + str(cam_id) + ".Vid", (frame,)) msg = listen_default(sub, block=False, empty='') cam.release() diff --git a/cv_pubsubs/window_sub/__init__.py b/cv_pubsubs/window_sub/__init__.py index 0cab85a..d7d6227 100644 --- a/cv_pubsubs/window_sub/__init__.py +++ b/cv_pubsubs/window_sub/__init__.py @@ -1 +1 @@ -from .cv_window_sub import sub_win_loop, frame_dict \ No newline at end of file +from .cv_window_sub import SubscriberWindows diff --git a/cv_pubsubs/window_sub/cv_window_sub.py b/cv_pubsubs/window_sub/cv_window_sub.py index ec3b054..725b15c 100644 --- a/cv_pubsubs/window_sub/cv_window_sub.py +++ b/cv_pubsubs/window_sub/cv_window_sub.py @@ -1,64 +1,66 @@ -import cv2 -from ..webcam_pub.camctrl import CamCtrl +import warnings +import cv2 +import pubsub + +from .winctrl import WinCtrl +from ..listen_default import listen_default +from ..webcam_pub.camctrl import CamCtrl if False: from typing import List -frame_dict = {} -def triangle_seen(): - print("a triangle was seen") -def square_seen(): - print("a square was seen") -def nothing_seen(): - print("nothing was seen") +class SubscriberWindows(object): + frame_dict = {} -command_dict = { - "t": triangle_seen, - "s": square_seen, - " ": nothing_seen -} + esc_key_codes = [27] # ESC key on most keyboards -# todo: figure out how to get the red x button to work. Try: https://stackoverflow.com/a/37881722/782170 -def sub_win_loop( - names, # type: List[str] + def __init__(self, + window_names, # type: List[str] input_vid_global_names, # type: List[str] callbacks=(None,), input_cams=(0,) ): - global frame_dict + self.window_names = window_names + self.input_vid_global_names = input_vid_global_names + self.callbacks = callbacks + self.input_cams = input_cams - while True: - for i in range(len(input_vid_global_names)): - if input_vid_global_names[i] in frame_dict and frame_dict[input_vid_global_names[i]] is not None: - if callbacks[i % len(callbacks)] is not None: - frames = callbacks[i % len(callbacks)](frame_dict[input_vid_global_names[i]]) + def handle_keys(self, + key_input, # type: int + ): + if key_input in self.esc_key_codes: + for name in self.window_names: + cv2.destroyWindow(name + " (press ESC to quit)") + for c in self.input_cams: + CamCtrl.stop_cam(c) + WinCtrl.quit() + elif key_input not in [-1, 0]: + try: + WinCtrl.key_stroke(chr(key_input)) + except ValueError: + warnings.warn( + RuntimeWarning("Unknown key code: [{}]. Please report to cv_pubsubs issue page.".format(key_input)) + ) + + def update_window_frames(self): + for i in range(len(self.input_vid_global_names)): + if self.input_vid_global_names[i] in self.frame_dict and self.frame_dict[ + self.input_vid_global_names[i]] is not None: + if self.callbacks[i % len(self.callbacks)] is not None: + frames = self.callbacks[i % len(self.callbacks)](self.frame_dict[self.input_vid_global_names[i]]) else: - frames = frame_dict[input_vid_global_names[i]] + frames = self.frame_dict[self.input_vid_global_names[i]] for f in range(len(frames)): - cv2.imshow(names[f % len(names)]+" (press q to quit)", frames[f]) - if cv2.getWindowProperty(names[f % len(names)]+" (press q to quit)", 0) != 0: - print("X was pressed") - for name in names: - cv2.destroyWindow(name) - for c in input_cams: - CamCtrl.stop_cam(c) - - - key_criteria = cv2.waitKey(1) & 0xFF - - if key_criteria == ord("q"): - for name in names: - cv2.destroyWindow(name) - for c in input_cams: - CamCtrl.stop_cam(c) - return - - if chr(key_criteria) in command_dict: - command_dict[chr(key_criteria)]() - CamCtrl.key_stroke(chr(key_criteria)) - elif chr(key_criteria) != "ΓΏ": - print(chr(key_criteria)) - + cv2.imshow(self.window_names[f % len(self.window_names)] + " (press ESC to quit)", frames[f]) + # todo: figure out how to get the red x button to work. Try: https://stackoverflow.com/a/37881722/782170 + def loop(self): + sub_cmd = pubsub.subscribe("CVWinCmd") + msg_cmd = '' + while msg_cmd != 'quit': + self.update_window_frames() + self.handle_keys(cv2.waitKey(1)) + msg_cmd = listen_default(sub_cmd, block=False, empty='') + pubsub.publish("CVWinCmd", 'quit') diff --git a/cv_pubsubs/window_sub/winctrl.py b/cv_pubsubs/window_sub/winctrl.py new file mode 100644 index 0000000..d971664 --- /dev/null +++ b/cv_pubsubs/window_sub/winctrl.py @@ -0,0 +1,12 @@ +import pubsub + + +class WinCtrl: + + @staticmethod + def key_stroke(key_entered): + pubsub.publish("CVKeyStroke", key_entered) + + @staticmethod + def quit(): + pubsub.publish("CVWinCmd", "quit") diff --git a/setup.py b/setup.py index 2217471..5cd3bfe 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,10 @@ from distutils.core import setup +from setuptools import find_packages setup( name= 'cv_pubsubs', - packages = ['cv_pubsubs', 'cv_pubsubs.webcam_pub', 'cv_pubsubs.window_sub'], - version='0.1', + version='1.0.0', + packages = find_packages(), description='Pubsub interface for Python OpenCV', author='Josh Miklos', author_email='simulatorleek@gmail.com', @@ -16,5 +17,7 @@ setup( 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', ] ) \ No newline at end of file diff --git a/tests_interactive/__init__.py b/tests_interactive/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests_interactive/test_sub_win.py b/tests_interactive/test_sub_win.py index e3d4960..089e1ad 100644 --- a/tests_interactive/test_sub_win.py +++ b/tests_interactive/test_sub_win.py @@ -1,22 +1,36 @@ +import threading import unittest as ut -import cv_pubsubs.webcam_pub as w -from cv_pubsubs.window_sub import frame_dict, sub_win_loop -def subscribe_to_key_command(cam_id, # type: Union[int, str] - frame_handler, # type: Callable[[int, np.ndarray], Any] - request_size=(1280, 720), # type: Tuple[int, int] - high_speed=False, # type: bool - fps_limit=240 # type: float - ): # type: (...) -> threading.Thread - t = threading.Thread(target=frame_handler_loop, args=(cam_id, frame_handler, request_size, high_speed, fps_limit)) +import pubsub + +import cv_pubsubs.webcam_pub as w +from cv_pubsubs.listen_default import listen_default +from cv_pubsubs.window_sub import SubscriberWindows + + +def print_keys_thread(): + sub_key = pubsub.subscribe("CVKeyStroke") + sub_cmd = pubsub.subscribe("CVWinCmd") + msg_cmd = '' + while msg_cmd != 'quit': + key_chr = listen_default(sub_key, timeout=.1) # type: np.ndarray + if key_chr is not None: + print("key pressed: " + str(key_chr)) + msg_cmd = listen_default(sub_cmd, block=False, empty='') + pubsub.publish("CVWinCmd", 'quit') + + +def start_print_keys_thread(): # type: (...) -> threading.Thread + t = threading.Thread(target=print_keys_thread, args=()) t.start() return t + class TestSubWin(ut.TestCase): def test_sub(self): def cam_handler(frame, cam_id): - frame_dict[str(cam_id) + "Frame"] = (frame, frame) + SubscriberWindows.frame_dict[str(cam_id) + "Frame"] = (frame, frame) t = w.frame_handler_thread(0, cam_handler, request_size=(1280, 720), @@ -24,9 +38,31 @@ class TestSubWin(ut.TestCase): fps_limit=240 ) - sub_win_loop(names=['cammy', 'cammy2'], - input_vid_global_names=[str(0) + "Frame"]) + SubscriberWindows(window_names=['cammy', 'cammy2'], + input_vid_global_names=[str(0) + "Frame"] + ).loop() w.CamCtrl.stop_cam(0) t.join() + + def test_key_sub(self): + def cam_handler(frame, cam_id): + SubscriberWindows.frame_dict[str(cam_id) + "Frame"] = (frame, frame) + + t = w.frame_handler_thread(0, cam_handler, + request_size=(1280, 720), + high_speed=True, + fps_limit=240 + ) + + kt = start_print_keys_thread() + + SubscriberWindows(window_names=['cammy', 'cammy2'], + input_vid_global_names=[str(0) + "Frame"] + ).loop() + + w.CamCtrl.stop_cam(0) + + t.join() + kt.join() diff --git a/testt.py b/testt.py deleted file mode 100644 index eb39d42..0000000 --- a/testt.py +++ /dev/null @@ -1,8 +0,0 @@ -def hi(): - print("hi") - -if __name__ == '__main__': - dic = {} - dic['a'] = hi - - dic['a']() \ No newline at end of file