2024-02-07 07:45:54 -05:00
|
|
|
import os
|
2024-02-07 10:32:03 -05:00
|
|
|
|
|
|
|
|
os.environ["OMP_NUM_THREADS"] = str(1)
|
2024-02-07 07:45:54 -05:00
|
|
|
import dshowcapture
|
2024-02-05 16:54:50 -05:00
|
|
|
import cv2
|
2024-02-09 20:39:51 -05:00
|
|
|
cv2.setNumThreads(6)
|
2024-02-05 16:54:50 -05:00
|
|
|
import multiprocessing
|
|
|
|
|
import time
|
|
|
|
|
import numpy as np
|
|
|
|
|
import math
|
|
|
|
|
|
2024-02-09 20:39:51 -05:00
|
|
|
def clamp_to_im(pt, w, h):
|
|
|
|
|
x = pt[0]
|
|
|
|
|
y = pt[1]
|
|
|
|
|
if x < 0:
|
|
|
|
|
x = 0
|
|
|
|
|
if y < 0:
|
|
|
|
|
y = 0
|
|
|
|
|
if x >= w:
|
|
|
|
|
x = w-1
|
|
|
|
|
if y >= h:
|
|
|
|
|
y = h-1
|
|
|
|
|
return (int(x), int(y+1))
|
2024-02-05 16:54:50 -05:00
|
|
|
|
|
|
|
|
#this is just here for conveience
|
2024-02-06 23:37:57 -05:00
|
|
|
def startProcess(frameQueue, faceQueue, fps, targetBrightness, width, height, mirrorInput):
|
|
|
|
|
webcam = Webcam(frameQueue, faceQueue, fps, targetBrightness, width, height, mirrorInput)
|
2024-02-05 16:54:50 -05:00
|
|
|
webcam.start()
|
|
|
|
|
|
|
|
|
|
class Webcam():
|
2024-02-06 23:37:57 -05:00
|
|
|
def __init__(self, frameQueue, faceQueue, fps, targetBrightness, width, height, mirrorInput):
|
2024-02-07 07:45:54 -05:00
|
|
|
if os.name == 'nt':
|
|
|
|
|
self.cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
|
|
|
|
|
else:
|
|
|
|
|
self.cap = cv2.VideoCapture(0)
|
2024-02-07 13:04:20 -05:00
|
|
|
|
2024-02-07 10:32:03 -05:00
|
|
|
if height > 480:
|
2024-02-05 16:54:50 -05:00
|
|
|
self.cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc(*"MJPG"))
|
2024-02-07 10:32:03 -05:00
|
|
|
|
2024-02-07 13:04:20 -05:00
|
|
|
self.webcamBuffer = 2
|
|
|
|
|
self.cap.set(38, self.webcamBuffer)
|
2024-02-05 16:54:50 -05:00
|
|
|
self.cap.set(3, width)
|
|
|
|
|
self.cap.set(4, height)
|
|
|
|
|
self.cap.set(cv2.CAP_PROP_FPS, fps)
|
2024-02-10 11:46:07 -05:00
|
|
|
self.targetFrameTime = 1. / (fps - 0.001)
|
2024-02-05 16:54:50 -05:00
|
|
|
self.gamma = 0.7
|
|
|
|
|
self.mirror = mirrorInput
|
|
|
|
|
self.frameQueue = frameQueue #outgoing frames from the webcam
|
|
|
|
|
self.faceQueue = faceQueue #incoming face data for gamma calculation
|
|
|
|
|
self.cameraLatency = 0
|
|
|
|
|
self.brightnessFrame = None #a copy of the brightness channel for gamma calculations
|
|
|
|
|
self.targetBrightness = targetBrightness #the target average brightness of the face, used in gamma calculations
|
|
|
|
|
self.width = width #unused, but it seemed useful to have around
|
|
|
|
|
self.height = height #unused, but it seemed useful to have around
|
|
|
|
|
self.ret = 0
|
2024-02-07 18:41:16 -05:00
|
|
|
self.frame = None
|
2024-02-05 16:54:50 -05:00
|
|
|
|
|
|
|
|
def start(self):
|
2024-02-09 20:39:51 -05:00
|
|
|
|
2024-02-05 16:54:50 -05:00
|
|
|
while self.cap.isOpened():
|
|
|
|
|
frameStart= time.perf_counter()
|
2024-02-07 13:04:20 -05:00
|
|
|
totalFrameLatency = time.perf_counter()
|
2024-02-07 18:41:16 -05:00
|
|
|
self.getFrame()
|
2024-02-05 16:54:50 -05:00
|
|
|
self.cameraLatency = time.perf_counter() - frameStart
|
|
|
|
|
if self.ret:
|
2024-02-07 18:41:16 -05:00
|
|
|
self.applyGamma()
|
2024-02-05 16:54:50 -05:00
|
|
|
if self.mirror:
|
2024-02-07 18:41:16 -05:00
|
|
|
self.frame = cv2.flip(self.frame, 1)
|
|
|
|
|
self.frameQueue.put([self.frame, self.cameraLatency, totalFrameLatency])
|
2024-02-05 16:54:50 -05:00
|
|
|
if self.faceQueue.qsize() > 0:
|
|
|
|
|
self.updateGamma()
|
|
|
|
|
sleepTime = self.targetFrameTime - (time.perf_counter() - frameStart)
|
|
|
|
|
sleepTime = max(sleepTime, 0)
|
|
|
|
|
time.sleep(sleepTime)
|
|
|
|
|
|
|
|
|
|
def getFrame(self):
|
|
|
|
|
#My webcam's gain tends to be noisy
|
|
|
|
|
#and my gamma correction seems less noisy
|
|
|
|
|
#so I tell the camera not to apply gain
|
|
|
|
|
self.cap.set(cv2.CAP_PROP_GAIN, 0)
|
|
|
|
|
#keeping track of how long it takes to get frames from the webcam
|
|
|
|
|
#because that one line is the cause of 90% of late frames
|
|
|
|
|
cameraStart = time.perf_counter()
|
2024-02-07 18:41:16 -05:00
|
|
|
self.ret, self.frame = self.cap.read()
|
2024-02-05 16:54:50 -05:00
|
|
|
self.cameraLatency = time.perf_counter() - cameraStart
|
2024-02-07 18:41:16 -05:00
|
|
|
return
|
2024-02-05 16:54:50 -05:00
|
|
|
|
|
|
|
|
#Applies a gamma curve to the frame
|
|
|
|
|
#Uses a lookup table because that's way faster than actually calculating every pixel
|
|
|
|
|
#Immensely improves tracking in low light situations
|
2024-02-07 18:41:16 -05:00
|
|
|
def applyGamma(self):
|
|
|
|
|
img_yuv = cv2.cvtColor(self.frame, cv2.COLOR_BGR2YUV)
|
2024-02-05 16:54:50 -05:00
|
|
|
#saving a copy of the brightness channel so I can use it to adjust the gamma based on where the face is in that frame
|
|
|
|
|
self.brightnessFrame = img_yuv[:,:,0].copy()
|
|
|
|
|
#building a lookup table on the fly
|
|
|
|
|
lookupTable = np.array(range(256))
|
|
|
|
|
loopupTable = lookupTable/255
|
|
|
|
|
lookupTable = np.power(loopupTable, self.gamma)*255
|
|
|
|
|
img_yuv[:,:,0] = lookupTable[img_yuv[:,:,0]]
|
|
|
|
|
#I convert the image to RBG here because it was getting repeatedly converted in the face tracking
|
2024-02-07 18:41:16 -05:00
|
|
|
self.frame = cv2.cvtColor(img_yuv, cv2.COLOR_YUV2RGB)
|
|
|
|
|
|
2024-02-05 16:54:50 -05:00
|
|
|
|
|
|
|
|
#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):
|
2024-02-09 20:39:51 -05:00
|
|
|
face = self.faceQueue.get()
|
|
|
|
|
|
|
|
|
|
x,y,w,h = face
|
|
|
|
|
crop_x1 = x
|
|
|
|
|
crop_y1 = y
|
|
|
|
|
crop_x2 = x + w
|
|
|
|
|
crop_y2 = y + h
|
|
|
|
|
|
|
|
|
|
crop_x1, crop_y1 = clamp_to_im((crop_x1, crop_y1), self.width, self.height)
|
|
|
|
|
crop_x2, crop_y2 = clamp_to_im((crop_x2, crop_y2), self.width, self.height)
|
2024-02-05 16:54:50 -05:00
|
|
|
|
2024-02-09 20:39:51 -05:00
|
|
|
croppedFace = self.brightnessFrame[crop_y1:crop_y2, crop_x1:crop_x2]
|
2024-02-05 16:54:50 -05:00
|
|
|
|
2024-02-09 20:39:51 -05:00
|
|
|
averageBrightness = np.mean(croppedFace)/256
|
2024-02-05 16:54:50 -05:00
|
|
|
self.gamma = math.log(self.targetBrightness, averageBrightness)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|