From b2324c539b0bbf682e9656dd959b3757514bc44a Mon Sep 17 00:00:00 2001 From: Cowwan Date: Sat, 10 Feb 2024 11:46:07 -0500 Subject: [PATCH] removed multi-processing added low-latnecy mode and an argument for it added target brightness argument --- facetracker.py | 64 ++++++++++++++++++++++++++------------------------ webcam.py | 3 +-- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/facetracker.py b/facetracker.py index 223569c..75cc5ea 100644 --- a/facetracker.py +++ b/facetracker.py @@ -26,8 +26,12 @@ parser.add_argument("--model", type=int, help="This can be used to select the tr parser.add_argument("--preview", type=int, help="Preview the frames sent to the tracker", default=0) parser.add_argument("--feature-type", type=int, help="Sets which version of feature extraction is used. 0 is my new version that works well for me and allows for some customization, 1 is EmilianaVT's version aka, normal OpenSeeFace operation", default=0, choices=[0, 1]) parser.add_argument("--numpy-threads", type=int, help="Numer of threads Numpy can use, doesn't seem to effect much", default=1) -parser.add_argument("-T","--threads", type=int, help="Numer of threads used for landmark detection. Default is 1 (~15ms per frame on my computer), 2 gets slightly faster frames (~10ms on my computer), more than 2 doesn't seem to help much", default=2) +parser.add_argument("-T","--threads", type=int, help="Numer of threads used for landmark detection. Default is 1 (~15ms per frame on my computer), 2 gets slightly faster frames (~10ms on my computer), more than 2 doesn't seem to help much", default=1) parser.add_argument("-v", "--visualize", type=int, help="Set this to 1 to visualize the tracking", default=0) +parser.add_argument("--low-latency", type=int, help="Low latency mode. Lowers latency, at the cost of inconsistent timings", default = 0) +parser.add_argument("--target-brightness", type=float, help="range 0.25-0.75, Target brightness of the brightness adjustment. Defaults to 0.55", default = 0.55) + + args = parser.parse_args() @@ -42,20 +46,25 @@ previewFlag = (args.preview == 1) mirror_input = args.mirror_input featureType = args.feature_type visualizeFlag = (args.visualize == 1) +lowLatency = (args.low_latency == 1) threads = args.threads - -target_duration = 1. / fps -targetBrightness = 0.55 +targetBrightness = min(max(args.target_brightness, 0.25), 0.75) silent = (args.silent == 1) -#creating queues -if height > 480: + +if lowLatency: + target_duration = 0.75 / fps + frameQueueSize = 1 +elif height > 480: + target_duration = 1 / fps frameQueueSize = 2 else: - frameQueueSize = 2 + frameQueueSize = 1 + target_duration = 1 / fps -frameQueue = multiprocessing.Queue(maxsize=frameQueueSize) -faceQueue = multiprocessing.Queue(maxsize=1) + +frameQueue = queue.Queue(maxsize=frameQueueSize) +faceQueue = queue.Queue(maxsize=1) packetQueue = queue.Queue() #I want to decouple the requests so they don't block anything else if they're slow @@ -64,18 +73,16 @@ packetSenderThread = threading.Thread(target = VTS.packetSender, args = [],) packetSenderThread.daemon = True packetSenderThread.start() -#creating processes -webcamProcess = multiprocessing.Process(target=webcam.startProcess,args = (frameQueue, faceQueue, fps, targetBrightness, width, height, mirror_input, )) -webcamProcess.daemon = True -webcamProcess.start() +webcamThread = threading.Thread(target=webcam.startProcess,args = (frameQueue, faceQueue, fps, targetBrightness, width, height, mirror_input, )) +webcamThread.daemon = True +webcamThread.start() -#This is it's own process because it's super weird performance wise if previewFlag or visualizeFlag: - previewFrameQueue = multiprocessing.Queue(maxsize=1) - previewProcess = multiprocessing.Process(target=preview.startProcess, args = (previewFrameQueue, target_duration,)) - previewProcess.daemon = True - previewProcess.start() + previewFrameQueue = queue.Queue(maxsize=1) + previewThread = threading.Thread(target=preview.startProcess, args = (previewFrameQueue, target_duration,)) + previewThread.daemon = True + previewThread.start() frame_count = 0 peak_frame_time=0.0 @@ -94,9 +101,9 @@ totalTotalLatency = 0.0 tracker = Tracker(width, height, featureType, threads, threshold=args.threshold, silent=silent, model_type=args.model, detection_threshold=args.detection_threshold) #don't start until the webcam is ready, then give it a little more time to fill it's buffer -while frameQueue.qsize() < 1: - time.sleep(0.1) +frameQueue.get() time.sleep(0.1) + face = None totalFrameLatency = 0 try: @@ -126,14 +133,12 @@ try: if visualizeFlag: preview.visualize(frame, faceInfo, previewFrameQueue, face_center, face_radius) - duration = time.perf_counter() - frame_get - total_active_time += duration - peak_frame_time = max(peak_frame_time, duration) - if duration < target_duration: - time.sleep(target_duration - duration) - else: - #all caps because if this is consistently happening it's an issue - messageQueue.put("CANNOT MAINTAIN FRAMERATE!") + frameTime = time.perf_counter() - frame_get + total_active_time += frameTime + peak_frame_time = max(peak_frame_time, frameTime) + + duration = time.perf_counter() - frame_start + time.sleep(max(0, target_duration - duration)) peak_time_between = max(peak_time_between, time.perf_counter() -frame_start) total_run_time += time.perf_counter() -frame_start @@ -171,7 +176,4 @@ print(f"Peak frame time: {(peak_frame_time*1000):.3f}ms") print(f"Average frame time: { ((total_active_time)*1000/(frame_count)):.3f}ms") print(f"Run time (seconds): {total_run_time:.2f} s\nFrames: {frame_count}") -webcamProcess.terminate() -if previewFlag: - previewProcess.terminate() os._exit(0) diff --git a/webcam.py b/webcam.py index 538284b..7036746 100644 --- a/webcam.py +++ b/webcam.py @@ -42,7 +42,7 @@ class Webcam(): self.cap.set(3, width) self.cap.set(4, height) self.cap.set(cv2.CAP_PROP_FPS, fps) - self.targetFrameTime = 1. / fps + self.targetFrameTime = 1. / (fps - 0.001) self.gamma = 0.7 self.mirror = mirrorInput self.frameQueue = frameQueue #outgoing frames from the webcam @@ -101,7 +101,6 @@ class Webcam(): self.frame = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2RGB) - #calculate the ideal gamma based on the brightness of the user's face #kind of winging it with the math, but it's a lot better than calculating based on the whole frame def updateGamma(self):