2020-01-01 13:55:01 +01:00
import os
2024-02-06 13:27:20 -05:00
os . environ [ " OMP_NUM_THREADS " ] = str ( 1 )
2020-01-01 13:55:01 +01:00
import argparse
2020-08-11 13:09:49 +02:00
import traceback
2024-02-05 16:54:50 -05:00
import threading
import queue
import time
from tracker import Tracker
import multiprocessing
import webcam
import vts
import preview
2020-01-01 13:55:01 +01:00
parser = argparse . ArgumentParser ( formatter_class = argparse . ArgumentDefaultsHelpFormatter )
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 )
2024-02-05 16:54:50 -05:00
parser . add_argument ( " -W " , " --width " , type = int , help = " Set raw RGB width " , default = 640 )
2024-02-07 10:32:03 -05:00
parser . add_argument ( " -H " , " --height " , type = int , help = " Set raw RGB height " , default = 480 )
2021-10-01 02:35:00 +10:00
parser . add_argument ( " -F " , " --fps " , type = int , help = " Set camera frames per second " , default = 24 )
2024-02-05 16:54:50 -05:00
parser . add_argument ( " -c " , " --capture " , help = " Set camera ID (0, 1...) or video file " , default = 0 )
2021-05-29 00:33:06 +01:00
parser . add_argument ( " -M " , " --mirror-input " , action = " store_true " , help = " Process a mirror image of the input video " )
2024-02-05 16:54:50 -05:00
parser . add_argument ( " -t " , " --threshold " , type = float , help = " Set minimum confidence threshold for face tracking " , default = 0.6 )
2020-09-02 18:42:25 +02:00
parser . add_argument ( " -d " , " --detection-threshold " , type = float , help = " Set minimum confidence threshold for face detection " , default = 0.6 )
2020-01-01 13:55:01 +01:00
parser . add_argument ( " -s " , " --silent " , type = int , help = " Set this to 1 to prevent text output on the console " , default = 0 )
2024-02-05 16:54:50 -05:00
parser . add_argument ( " --model " , type = int , help = " This can be used to select the tracking model. Higher numbers are models with better tracking quality, but slower speed, except for model 4, which is wink optimized. " , default = 3 , choices = [ 0 , 1 , 2 , 3 , 4 ] )
parser . add_argument ( " --preview " , type = int , help = " Preview the frames sent to the tracker " , default = 0 )
2024-02-06 09:53:08 -05:00
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 ] )
2024-02-06 10:04:19 -05:00
parser . add_argument ( " --numpy-threads " , type = int , help = " Numer of threads Numpy can use, doesn ' t seem to effect much " , default = 1 )
2024-02-10 11:46:07 -05:00
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 )
2024-02-06 11:07:35 -05:00
parser . add_argument ( " -v " , " --visualize " , type = int , help = " Set this to 1 to visualize the tracking " , default = 0 )
2024-02-10 11:46:07 -05:00
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 )
2020-01-01 13:55:01 +01:00
2020-09-26 23:49:54 +02:00
2024-02-06 10:04:19 -05:00
args = parser . parse_args ( )
2024-02-05 16:54:50 -05:00
#processing args
2020-01-01 13:55:01 +01:00
target_ip = args . ip
target_port = args . port
2021-10-01 02:35:00 +10:00
fps = args . fps
2024-02-05 16:54:50 -05:00
height = args . height
width = args . width
previewFlag = ( args . preview == 1 )
mirror_input = args . mirror_input
2024-02-06 09:53:08 -05:00
featureType = args . feature_type
2024-02-06 11:07:35 -05:00
visualizeFlag = ( args . visualize == 1 )
2024-02-10 11:46:07 -05:00
lowLatency = ( args . low_latency == 1 )
2024-02-07 18:41:16 -05:00
threads = args . threads
2024-02-10 11:46:07 -05:00
targetBrightness = min ( max ( args . target_brightness , 0.25 ) , 0.75 )
2024-02-05 16:54:50 -05:00
silent = ( args . silent == 1 )
2024-02-10 11:46:07 -05:00
if lowLatency :
target_duration = 0.75 / fps
frameQueueSize = 1
elif height > 480 :
target_duration = 1 / fps
2024-02-07 10:32:03 -05:00
frameQueueSize = 2
else :
2024-02-10 11:46:07 -05:00
frameQueueSize = 1
target_duration = 1 / fps
2024-02-09 20:39:51 -05:00
2024-02-10 11:46:07 -05:00
frameQueue = queue . Queue ( maxsize = frameQueueSize )
faceQueue = queue . Queue ( maxsize = 1 )
2024-02-05 16:54:50 -05:00
packetQueue = queue . Queue ( )
#I want to decouple the requests so they don't block anything else if they're slow
2024-02-06 23:37:57 -05:00
VTS = vts . VTS ( target_ip , target_port , silent , height , width , packetQueue )
2024-02-05 16:54:50 -05:00
packetSenderThread = threading . Thread ( target = VTS . packetSender , args = [ ] , )
packetSenderThread . daemon = True
packetSenderThread . start ( )
2024-02-09 20:39:51 -05:00
2024-02-10 11:46:07 -05:00
webcamThread = threading . Thread ( target = webcam . startProcess , args = ( frameQueue , faceQueue , fps , targetBrightness , width , height , mirror_input , ) )
webcamThread . daemon = True
webcamThread . start ( )
2024-02-05 16:54:50 -05:00
2024-02-06 11:07:35 -05:00
if previewFlag or visualizeFlag :
2024-02-10 11:46:07 -05:00
previewFrameQueue = queue . Queue ( maxsize = 1 )
previewThread = threading . Thread ( target = preview . startProcess , args = ( previewFrameQueue , target_duration , ) )
previewThread . daemon = True
previewThread . start ( )
2020-01-01 13:55:01 +01:00
frame_count = 0
2024-02-05 16:54:50 -05:00
peak_frame_time = 0.0
total_active_time = 0.0
total_run_time = 0.0
frame_start = 0.0
peak_time_between = 0.0
peak_camera_latency = 0.0
total_camera_latency = 0.0
2024-02-07 13:04:20 -05:00
sleepTimer = 0.0
2024-02-05 16:54:50 -05:00
failures = 0
2024-02-07 18:41:16 -05:00
peakTotalLatency = 0.0
totalTotalLatency = 0.0
2024-02-05 16:54:50 -05:00
2024-02-07 18:41:16 -05:00
tracker = Tracker ( width , height , featureType , threads , threshold = args . threshold , silent = silent , model_type = args . model , detection_threshold = args . detection_threshold )
2024-02-05 16:54:50 -05:00
2024-02-07 13:04:20 -05:00
#don't start until the webcam is ready, then give it a little more time to fill it's buffer
2024-02-10 11:46:07 -05:00
frameQueue . get ( )
2024-02-07 13:04:20 -05:00
time . sleep ( 0.1 )
2024-02-10 11:46:07 -05:00
2024-02-07 10:32:03 -05:00
face = None
2024-02-07 13:04:20 -05:00
totalFrameLatency = 0
2020-01-01 13:55:01 +01:00
try :
2024-02-05 16:54:50 -05:00
while True :
#clearing these so there's no stale data to confuse the checks
packet = None
frame_start = time . perf_counter ( )
2020-01-01 13:55:01 +01:00
frame_count + = 1
2024-02-07 18:41:16 -05:00
frame , camera_latency , totalFrameLatency = frameQueue . get ( )
frame_get = time . perf_counter ( )
#If I don't wait a few frames to start tracking I get wild peak frame times, like 500ms
if frame_count > 5 :
peak_camera_latency = max ( camera_latency , peak_camera_latency )
total_camera_latency + = camera_latency
2020-03-03 18:48:52 +01:00
2024-02-07 18:41:16 -05:00
if previewFlag :
#there's no reason to ever wait on this process or give it much of a queue
if previewFrameQueue . qsize ( ) < 1 :
previewFrameQueue . put ( frame )
2024-02-09 20:39:51 -05:00
faceInfo , face = tracker . predict ( frame )
2024-02-07 10:32:03 -05:00
2024-02-09 20:39:51 -05:00
if faceInfo is not None :
packet = VTS . preparePacket ( faceInfo )
2024-02-07 10:32:03 -05:00
if faceQueue . qsize ( ) < 1 :
2024-02-09 20:39:51 -05:00
faceQueue . put ( face )
2024-02-07 10:32:03 -05:00
if visualizeFlag :
2024-02-09 20:39:51 -05:00
preview . visualize ( frame , faceInfo , previewFrameQueue , face_center , face_radius )
2024-02-05 16:54:50 -05:00
2024-02-10 11:46:07 -05:00
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 ) )
2024-02-05 16:54:50 -05:00
peak_time_between = max ( peak_time_between , time . perf_counter ( ) - frame_start )
total_run_time + = time . perf_counter ( ) - frame_start
#If we don't have something to send to Vtube Studio we don't
if ( packet is not None ) :
packetQueue . put ( packet )
else :
2024-02-06 23:37:57 -05:00
print ( " No data sent to VTS " )
2024-02-07 18:41:16 -05:00
if frame_count > 5 :
peakTotalLatency = max ( peakTotalLatency , ( time . perf_counter ( ) - totalFrameLatency ) )
totalTotalLatency + = time . perf_counter ( ) - totalFrameLatency
2020-08-21 22:47:02 +02:00
2020-01-01 13:55:01 +01:00
except KeyboardInterrupt :
2024-02-05 16:54:50 -05:00
if not silent :
2020-01-01 13:55:01 +01:00
print ( " Quitting " )
2024-02-05 16:54:50 -05:00
#printing statistics on close
#it makes identifying problems easier
2024-02-07 18:41:16 -05:00
print ( f " Peak latency: { ( peakTotalLatency * 1000 ) : .3f } ms " )
print ( f " Average latency: { ( totalTotalLatency * 1000 / ( frame_count - 5 ) ) : .3f } ms " )
2024-02-05 16:54:50 -05:00
#480p generally results in camera latencies between 0.5ms and 2.0ms
print ( f " Peak camera latency: { ( peak_camera_latency * 1000 ) : .3f } ms " )
print ( f " Average camera latency: { ( total_camera_latency * 1000 / ( frame_count - 5 ) ) : .3f } ms " )
#The longest time a full cycle of the main loop takes, including sleep
#indicates issues when higher than average time between frames by a significant amount
print ( f " Peak time between frames: { ( peak_time_between * 1000 ) : .3f } ms " )
#at 30fps this should be a hair above 33.333ms
print ( f " Average time between frames: { ( total_run_time * 1000 / ( frame_count ) ) : .3f } ms " )
#These measure how long the main loop takes *not including sleep*
#tends to be slightly longer than the average time given by the tracking status by 1ms to 2ms
#because it includes detection frames and all the other minor operations that need to happen every frame
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 \n Frames: { frame_count } " )
os . _exit ( 0 )