the changes I made don't use these anymore

This commit is contained in:
Cowwan 2024-02-05 16:54:13 -05:00
parent 1e622bbc92
commit 33007ca428
7 changed files with 0 additions and 2632 deletions

View file

@ -1,341 +0,0 @@
import os
import platform
import sys
import numpy as np
from ctypes import *
from PIL import Image
import cv2
import json
def resolve(name):
f = os.path.join(os.path.dirname(__file__), name)
return f
lib = None
bm_lib = None
bm_options = None
bm_enabled = False
def set_bm_enabled(v):
global bm_enabled
bm_enabled = v
def set_options(str):
global bm_options
bm_options = str
def create_frame_buffer(width, height, factor):
buffer = bytearray(width * height * 4 * factor)
char_array = c_char * len(buffer)
return char_array.from_buffer(buffer)
class DShowCapture():
def __init__(self):
global lib
global bm_lib
global bm_options
global bm_enabled
if lib is None or bm_lib is None:
if platform.architecture()[0] == '32bit':
dll_path = resolve(os.path.join("dshowcapture", "dshowcapture_x86.dll"))
lib = cdll.LoadLibrary(dll_path)
if bm_enabled:
dll_path = resolve(os.path.join("dshowcapture", "libminibmcapture32.dll"))
bm_lib = cdll.LoadLibrary(dll_path)
else:
dll_path = resolve(os.path.join("dshowcapture", "dshowcapture_x64.dll"))
lib = cdll.LoadLibrary(dll_path)
if bm_enabled:
dll_path = resolve(os.path.join("dshowcapture", "libminibmcapture64.dll"))
bm_lib = cdll.LoadLibrary(dll_path)
# DirectShow
lib.create_capture.restype = c_void_p
lib.get_devices.argtypes = [c_void_p]
lib.get_device.argtypes = [c_void_p, c_int, c_char_p, c_int]
lib.capture_device.argtypes = [c_void_p, c_int, c_int, c_int, c_int]
lib.capture_device_by_dcap.argtypes = [c_void_p, c_int, c_int, c_int, c_int, c_longlong]
lib.capture_device_default.argtypes = [c_void_p, c_int]
lib.get_width.argtypes = [c_void_p]
lib.get_height.argtypes = [c_void_p]
lib.get_fps.argtypes = [c_void_p]
lib.get_flipped.argtypes = [c_void_p]
lib.get_colorspace.argtypes = [c_void_p]
lib.get_colorspace_internal.argtypes = [c_void_p]
lib.capturing.argtypes = [c_void_p]
lib.get_frame.argtypes = [c_void_p, c_int, c_char_p, c_int]
lib.get_size.argtypes = [c_void_p]
lib.stop_capture.argtypes = [c_void_p]
lib.get_json_length.argtypes = [c_void_p]
lib.get_json.argtypes = [c_void_p, c_char_p, c_int]
lib.destroy_capture.argtypes = [c_void_p]
# Blackmagic
if bm_enabled:
bm_lib.start_capture_single.argtypes = [c_int, c_int, c_void_p]
bm_lib.read_frame_bgra32_blocking.argtypes = [c_char_p, c_int]
bm_lib.stop_capture_single.argtypes = []
bm_lib.get_json_length.argtypes = []
bm_lib.get_json.argtypes = [c_char_p, c_int]
if bm_options is not None:
bm_lib.set_options.argtypes = [c_char_p]
bm_lib.set_options(bm_options.encode())
self.lib = lib
if bm_enabled:
self.bm_lib = bm_lib
self.cap = lib.create_capture()
self.name_buffer = create_string_buffer(255);
self.buffer = None
self.have_devices = False
self.size = None
self.real_size = None
self.info = None
self.type = None
def __del__(self):
if self.buffer is not None:
del self.buffer
del self.name_buffer
self.destroy_capture()
def get_devices(self):
self.have_devices = True
return self.lib.get_devices(self.cap)
def get_device(self, device_number):
self.lib.get_device(self.cap, device_number, self.name_buffer, 255)
name_str = str(self.name_buffer.value.decode('utf8', 'surrogateescape'))
return name_str
def get_info(self):
global bm_enabled
# DirectShow
json_length = self.lib.get_json_length(self.cap);
json_buffer = create_string_buffer(json_length)
self.lib.get_json(self.cap, json_buffer, json_length);
json_text = str(json_buffer.value.decode('utf8', 'surrogateescape'))
self.info = json.loads(json_text)
for cam in self.info:
cam["type"] = "DirectShow"
cam["index"] = cam["id"]
# Blackmagic
if bm_enabled:
json_length = self.bm_lib.get_json_length();
json_buffer = create_string_buffer(json_length)
self.bm_lib.get_json(json_buffer, json_length);
json_text = str(json_buffer.value.decode('utf8', 'surrogateescape'))
bm_info = json.loads(json_text)
dshow_len = len(self.info)
for cam in bm_info:
cam["type"] = "Blackmagic"
cam["index"] = cam["id"] + dshow_len
self.info.extend(bm_info)
return self.info
def capture_device(self, cam, width, height, fps):
if not self.have_devices:
self.get_devices()
ret = self.lib.capture_device(self.cap, cam, width, height, fps) == 1
if ret:
self.type = "DirectShow"
self.width = self.get_width()
self.height = self.get_height()
self.fps = self.get_fps()
self.flipped = self.get_flipped()
self.colorspace = self.get_colorspace()
self.colorspace_internal = self.get_colorspace_internal()
self.size = self.width * self.height * 4
self.buffer = create_frame_buffer(self.width, self.height, 4)
else:
self.size = None
self.real_size = None
return ret;
def capture_device_by_dcap(self, cam, dcap, width, height, fps):
global bm_enabled
if not self.have_devices:
self.get_devices()
if self.info is None:
self.get_info()
fps = int(10000000 / fps)
ret = False
if self.info[cam]['type'] == "DirectShow":
ret = self.lib.capture_device_by_dcap(self.cap, cam, dcap, width, height, fps) == 1
elif bm_enabled and self.info[cam]['type'] == "Blackmagic":
ret = self.bm_lib.start_capture_single(self.info[cam]['id'], self.info[cam]['caps'][dcap]['bmModecode'], None) == 1
if ret:
self.type = self.info[cam]['type']
if self.type == "DirectShow":
self.width = self.get_width()
self.height = self.get_height()
self.fps = self.get_fps()
self.flipped = self.get_flipped()
self.colorspace = self.get_colorspace()
self.colorspace_internal = self.get_colorspace_internal()
self.size = self.width * self.height * 4 * 4
self.buffer = create_frame_buffer(self.width, self.height, 4)
elif bm_enabled and self.type == "Blackmagic":
self.width = self.info[cam]['caps'][dcap]['minCX']
self.height = self.info[cam]['caps'][dcap]['minCY']
self.fps = int(10000000 / self.info[cam]['caps'][dcap]['minInterval'])
self.flipped = True
self.colorspace = 101
self.colorspace_internal = self.colorspace
self.size = self.width * self.height * 4
self.buffer = create_frame_buffer(self.width, self.height, 1)
self.real_size = self.size
else:
self.size = None
self.real_size = None
return ret;
def capture_device_default(self, cam):
if not self.have_devices:
self.get_devices()
ret = self.lib.capture_device_default(self.cap, cam) == 1
if ret:
self.type = "DirectShow"
self.width = self.get_width()
self.height = self.get_height()
self.fps = self.get_fps()
self.flipped = self.get_flipped()
self.colorspace = self.get_colorspace()
self.colorspace_internal = self.get_colorspace_internal()
self.size = self.width * self.height * 4 * 4
self.buffer = create_frame_buffer(self.width, self.height, 4)
else:
self.size = None
self.real_size = None
return ret;
def get_width(self):
return self.lib.get_width(self.cap)
def get_height(self):
return self.lib.get_height(self.cap)
def get_fps(self):
return self.lib.get_fps(self.cap)
def get_flipped(self):
return self.lib.get_flipped(self.cap) != 0
def get_colorspace(self):
colorspace = self.lib.get_colorspace(self.cap)
if colorspace == 0:
colorspace = self.lib.get_colorspace_internal(self.cap)
return colorspace
def get_colorspace_internal(self):
return self.lib.get_colorspace_internal(self.cap)
def capturing(self):
if self.type == "Blackmagic":
return 1
return self.lib.capturing(self.cap) == 1
def get_frame(self, timeout):
global bm_enabled
if self.size is None:
return None
if self.type == "DirectShow":
self.real_size = self.lib.get_frame(self.cap, timeout, self.buffer, self.size)
elif bm_enabled and self.type == "Blackmagic":
self.bm_lib.read_frame_bgra32_blocking(self.buffer, self.size)
else:
return None
img = np.frombuffer(self.buffer, dtype=np.uint8)[0:self.real_size]
if self.colorspace in [100, 101]:
if self.real_size == self.height * self.width * 4:
img = cv2.cvtColor(img.reshape((self.height,self.width,4)), cv2.COLOR_BGRA2BGR)
elif self.real_size == self.height * self.width * 3:
img = img.reshape((self.height,self.width,3))
else:
return None
if not self.flipped:
img = cv2.flip(img, 0)
return img
elif self.colorspace == 200 and self.real_size == (3 * self.height // 2) * self.width:
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_I420)
elif self.colorspace == 201 and self.real_size == (3 * self.height // 2) * self.width:
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_NV12)
elif self.colorspace == 202 and self.real_size == (3 * self.height // 2) * self.width:
img = cv2.cvtColor(img.reshape((3 * self.height // 2,self.width,1)), cv2.COLOR_YUV2BGR_YV12)
elif self.colorspace == 203 and self.real_size == self.height * self.width:
img = cv2.cvtColor(img.reshape((self.height,self.width,1)), cv2.COLOR_GRAY2BGR)
elif self.colorspace == 300 and self.real_size == self.height * self.width * 2:
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YVYU)
elif self.colorspace == 301 and self.real_size == self.height * self.width * 2:
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_YUY2)
elif self.colorspace == 302 and self.real_size == self.height * self.width * 2:
img = cv2.cvtColor(img.reshape((self.height,self.width,2)), cv2.COLOR_YUV2BGR_UYVY)
elif self.colorspace == 303:
return None
elif self.colorspace == 400 and self.real_size > 4:
img = cv2.imdecode(img, cv2.IMREAD_COLOR);
else:
return None
if self.flipped:
img = cv2.flip(img, 0)
return img
def stop_capture(self):
global bm_enabled
self.size = None
self.real_size = None
if self.type == "DirectShow":
return self.lib.stop_capture(self.cap)
elif bm_enabled and self.type == "Blackmagic":
return self.bm_lib.stop_capture_single() != 0
return True
def destroy_capture(self):
if self.cap is None:
return 0
self.stop_capture()
ret = self.lib.destroy_capture(self.cap)
self.cap = None
self.size = None
self.real_size = None
return ret
if __name__ == "__main__":
cam = 0
width = 1280
height = 720
fps = 30
if len(sys.argv) > 1:
cam = int(sys.argv[1])
if len(sys.argv) > 2:
width = int(sys.argv[2])
if len(sys.argv) > 3:
height = int(sys.argv[3])
if len(sys.argv) > 4:
fps = int(sys.argv[4])
cap = DShowCapture()
devices = cap.get_devices()
print("Devices: ", devices)
for i in range(devices):
print(f"{i} {cap.get_device(i)}")
print(f"Capturing: {cap.capture_device(cam, width, height, fps)}")
width = cap.get_width()
height = cap.get_height()
flipped = cap.get_flipped()
print(f"Width: {width} Height: {height} FPS: {cap.get_fps()} Flipped: {flipped} Colorspace: {cap.get_colorspace()} Internal: {cap.get_colorspace_internal()}")
while True:
img = cap.get_frame(1000)
if img is not None:
cv2.imshow("DShowCapture", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
sys.exit(0)
else:
print("Lost frame")
cap.stop_capture()
cap.destroy_capture()
del cap

125
escapi.py
View file

@ -1,125 +0,0 @@
"""
A simple python wrapper around escapi
Usage:
from camera import Device
device = Deveice.connect(0, 500, 500)
image = device.get_image()
"""
import os
import platform
import sys
from ctypes import *
from PIL import Image
import numpy as np
import cv2
def resolve(name):
f = os.path.join(os.path.dirname(__file__), name)
return f
class CAPTURE_PROPETIES:
CAPTURE_BRIGHTNESS = 1,
CAPTURE_CONTRAST = 2,
CAPTURE_HUE = 3,
CAPTURE_SATURATION = 4,
CAPTURE_SHARPNESS = 5,
CAPTURE_GAMMA = 6,
CAPTURE_COLORENABLE = 7,
CAPTURE_WHITEBALANCE = 8,
CAPTURE_BACKLIGHTCOMPENSATION = 9,
CAPTURE_GAIN = 10,
CAPTURE_PAN = 11,
CAPTURE_TILT = 12,
CAPTURE_ROLL = 13,
CAPTURE_ZOOM = 14,
CAPTURE_EXPOSURE = 15,
CAPTURE_IRIS = 16,
CAPTURE_FOCUS = 17,
CAPTURE_PROP_MAX = 18,
class SimpleCapParms(Structure):
_fields_ = [
("buffer", POINTER(c_int)),
("width", c_int),
("height", c_int),
("fps", c_int),
]
lib = None
def init():
global lib
if platform.architecture()[0] == '32bit':
dll_path = resolve(os.path.join("escapi", "escapi_x86.dll"))
lib = cdll.LoadLibrary(dll_path)
else:
dll_path = resolve(os.path.join("escapi", "escapi_x64.dll"))
lib = cdll.LoadLibrary(dll_path)
if lib is None or lib.ESCAPIVersion() != 0xfff001:
print("Invalid ESCAPI DLL found.")
sys.exit(1)
lib.initCapture.argtypes = [c_int, POINTER(SimpleCapParms)]
lib.initCapture.restype = c_int
lib.initCOM()
def count_capture_devices():
return lib.countCaptureDevices()
def device_name(device):
"""
Get the device name for the given device
:param device: The number of the device
:return: The name of the device
"""
namearry = (c_char * 256)()
lib.getCaptureDeviceName(device, namearry, 256)
camearaname = namearry.value
return camearaname
def init_camera(device, width, height, fps):
global devices
array = (width * height * c_int)()
options = SimpleCapParms()
options.width = width
options.height = height
options.fps = fps
options.buffer = array
lib.initCapture(device, byref(options))
return array
def do_capture(device):
lib.doCapture(device)
def is_capture_done(device):
return lib.isCaptureDone(device)
def read(device, width, height, array):
if is_capture_done(device):
img = Image.frombuffer('RGBA', (width, height), array, 'raw', 'BGRA', 0, 1)
img = np.array(img)
img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR);
return img
else:
return None
def get_image(device, width, height, array):
lib.doCapture(device)
while lib.isCaptureDone(device) == 0:
pass
img = Image.frombuffer('RGBA', (width, height), array, 'raw', 'BGRA', 0, 0)
img = np.array(img)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR);
return img
def deinit_camera(device):
lib.deinitCapture(device)

View file

@ -1,250 +0,0 @@
import re
import sys
import os
import cv2
import numpy as np
import escapi
import dshowcapture
import time
import traceback
import gc
class VideoReader():
def __init__(self, capture, camera=False):
if os.name == 'nt' and camera:
self.cap = cv2.VideoCapture(capture, cv2.CAP_DSHOW)
else:
self.cap = cv2.VideoCapture(capture)
if self.cap is None:
print("The video source cannot be opened")
sys.exit(0)
self.name = str(capture)
def is_open(self):
return self.cap.isOpened()
def is_ready(self):
return True
def read(self):
return self.cap.read()
def close(self):
self.cap.release()
class EscapiReader(VideoReader):
def __init__(self, capture, width, height, fps):
self.device = None
self.width = width
self.height = height
self.fps = fps
self.device = capture
escapi.count_capture_devices()
self.name = str(escapi.device_name(self.device).decode('utf8', 'surrogateescape'))
self.buffer = escapi.init_camera(self.device, self.width, self.height, self.fps)
escapi.do_capture(self.device)
def is_open(self):
return True
def is_ready(self):
return escapi.is_capture_done(self.device)
def read(self):
if escapi.is_capture_done(self.device):
image = escapi.read(self.device, self.width, self.height, self.buffer)
escapi.do_capture(self.device)
return True, image
else:
return False, None
def close(self):
escapi.deinit_camera(self.device)
class DShowCaptureReader(VideoReader):
def __init__(self, capture, width, height, fps, use_dshowcapture=True, dcap=None):
self.device = None
self.width = width
self.height = height
self.fps = fps
self.dcap = dcap;
self.device = dshowcapture.DShowCapture()
self.device.get_devices()
info = self.device.get_info()
self.name = info[capture]['name']
if info[capture]['type'] == "Blackmagic":
self.name = "Blackmagic: " + self.name
if dcap is None or dcap < 0:
dcap = 0
ret = False
if dcap is None:
ret = self.device.capture_device(capture, self.width, self.height, self.fps)
else:
if dcap < 0:
ret = self.device.capture_device_default(capture)
else:
ret = self.device.capture_device_by_dcap(capture, dcap, self.width, self.height, self.fps)
if not ret:
raise Exception("Failed to start capture.")
self.width = self.device.width
self.height = self.device.height
self.fps = self.device.fps
print(f"Camera: \"{self.name}\" Capability ID: {dcap} Resolution: {self.device.width}x{self.device.height} Frame rate: {self.device.fps} Colorspace: {self.device.colorspace} Internal: {self.device.colorspace_internal} Flipped: {self.device.flipped}")
self.timeout = 1000
def is_open(self):
return self.device.capturing()
def is_ready(self):
return self.device.capturing()
def read(self):
img = None
try:
img = self.device.get_frame(self.timeout)
except:
gc.collect()
img = self.device.get_frame(self.timeout)
if img is None:
return False, None
else:
return True, img
def close(self):
self.device.destroy_capture()
class OpenCVReader(VideoReader):
def __init__(self, capture, width, height, fps):
self.device = None
self.width = width
self.height = height
self.fps = fps
self.name = str(capture)
super(OpenCVReader, self).__init__(capture, camera=True)
self.cap.set(3, width)
self.cap.set(4, height)
self.cap.set(38, 1)
def is_open(self):
return super(OpenCVReader, self).is_open()
def is_ready(self):
return super(OpenCVReader, self).is_ready()
def read(self):
return super(OpenCVReader, self).read()
def close(self):
super(OpenCVReader, self).close()
class RawReader:
def __init__(self, width, height):
self.width = int(width)
self.height = int(height)
if self.width < 1 or self.height < 1:
print("No acceptable size was given for reading raw RGB frames.")
sys.exit(0)
self.len = self.width * self.height * 3
self.open = True
def is_open(self):
return self.open
def is_ready(self):
return True
def read(self):
frame = bytearray()
read_bytes = 0
while read_bytes < self.len:
bytes = sys.stdin.buffer.read(self.len)
read_bytes += len(bytes)
frame.extend(bytes)
return True, np.frombuffer(frame, dtype=np.uint8).reshape((self.height, self.width, 3))
def close(self):
self.open = False
def try_int(s):
try:
return int(s)
except:
return None
def test_reader(reader):
got_any = 0
try:
for i in range(30):
if not reader.is_ready():
time.sleep(0.02)
ret, frame = reader.read()
if not ret:
time.sleep(0.02)
print("No frame")
else:
print("Got frame")
got_any += 1
if got_any > 10:
break
if reader.is_open():
return got_any > 0
print("Fail")
return False
except:
traceback.print_exc()
print("Except")
return False
class InputReader():
def __init__(self, capture, raw_rgb, width, height, fps, use_dshowcapture=False, dcap=None):
self.reader = None
self.name = str(capture)
try:
if raw_rgb > 0:
self.reader = RawReader(width, height)
elif os.path.exists(capture):
self.reader = VideoReader(capture)
elif capture == str(try_int(capture)):
if os.name == 'nt':
# Try with DShowCapture
good = True
name = ""
try:
if use_dshowcapture:
self.reader = DShowCaptureReader(int(capture), width, height, fps, dcap=dcap)
name = self.reader.name
good = test_reader(self.reader)
self.name = name
else:
good = False
except:
print("DShowCapture exception: ")
traceback.print_exc()
good = False
if good:
return
# Try with Escapi
good = True
try:
print(f"DShowCapture failed. Falling back to escapi for device {name}.", file=sys.stderr)
escapi.init()
devices = escapi.count_capture_devices()
found = None
for i in range(devices):
escapi_name = str(escapi.device_name(i).decode('utf8', 'surrogateescape'))
if name == escapi_name:
found = i
if found is None:
good = False
else:
print(f"Found device {name} as {i}.", file=sys.stderr)
self.reader = EscapiReader(found, width, height, fps)
good = test_reader(self.reader)
except:
print("Escapi exception: ")
traceback.print_exc()
good = False
if good:
return
# Try with OpenCV
print(f"Escapi failed. Falling back to OpenCV. If this fails, please change your camera settings.", file=sys.stderr)
self.reader = OpenCVReader(int(capture), width, height, fps)
self.name = self.reader.name
else:
self.reader = OpenCVReader(int(capture), width, height, fps)
except Exception as e:
print("Error: " + str(e))
if self.reader is None or not self.reader.is_open():
print("There was no valid input.")
sys.exit(0)
def is_open(self):
return self.reader.is_open()
def is_ready(self):
return self.reader.is_ready()
def read(self):
return self.reader.read()
def close(self):
self.reader.close()

314
model.py
View file

@ -1,314 +0,0 @@
# This file is not used by the tracking application and currently outdated
import torch
import torch.nn as nn
import geffnet.mobilenetv3 # geffnet.mobilenetv3._gen_mobilenet_v3 needs to be patched to return the parameters instead of instantiating the network, based on commit c450c12ae6ffb1757f62dde3c2765da3c10f6def
from geffnet.efficientnet_builder import round_channels
class DSConv2d(nn.Module):
def __init__(self, in_planes, out_planes, kernels_per_layer=4, groups=1, old=0):
super(DSConv2d, self).__init__()
if old == 2:
self.conv = nn.Sequential(
nn.Conv2d(in_planes, in_planes * kernels_per_layer, kernel_size=3, padding=1, groups=in_planes),
nn.Conv2d(in_planes * kernels_per_layer, out_planes, kernel_size=1, groups=groups)
)
elif old == 1:
self.conv = nn.Sequential(
nn.Conv2d(in_planes, in_planes * kernels_per_layer, kernel_size=3, padding=1, groups=in_planes, bias=False),
nn.BatchNorm2d(in_planes * kernels_per_layer),
nn.Conv2d(in_planes * kernels_per_layer, out_planes, kernel_size=1, groups=groups, bias=False),
nn.BatchNorm2d(out_planes),
nn.ReLU6(inplace=True)
)
else:
self.conv = nn.Sequential(
nn.Conv2d(in_planes, in_planes * kernels_per_layer, kernel_size=3, padding=1, groups=in_planes, bias=False),
nn.BatchNorm2d(in_planes * kernels_per_layer),
nn.ReLU6(inplace=True),
nn.Conv2d(in_planes * kernels_per_layer, out_planes, kernel_size=1, groups=groups, bias=False),
nn.BatchNorm2d(out_planes),
nn.ReLU6(inplace=True)
)
def forward(self, x):
x = self.conv(x)
return x
class UNetUp(nn.Module):
def __init__(self, in_channels, residual_in_channels, out_channels, size, old=0):
super(UNetUp, self).__init__()
self.up = nn.Upsample(size=size, mode='bilinear', align_corners=True)
self.conv = DSConv2d(in_channels + residual_in_channels, out_channels, 1, 1, old=old)
def forward(self, x1, x2):
x1 = self.up(x1)
x = torch.cat([x2, x1], dim=1)
x = self.conv(x)
return x
# This is the gaze tracking model
class OpenSeeFaceGaze(geffnet.mobilenetv3.MobileNetV3):
def __init__(self):
kwargs = geffnet.mobilenetv3._gen_mobilenet_v3(['small'])
super(OpenSeeFaceGaze, self).__init__(**kwargs)
self.up1 = UNetUp(576, 48, 64, (2,2), old=2)
self.up2 = UNetUp(64, 24, 32, (4,4), old=2)
self.up3 = UNetUp(32, 16, 15, (8,8), old=2)
self.group = DSConv2d(15, 3, kernels_per_layer=4, groups=3, old=2)
def _forward_impl(self, x):
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
r1 = None
r2 = None
r3 = None
for i, feature in enumerate(self.blocks):
x = feature(x)
if i == 3:
r3 = x
if i == 1:
r2 = x
if i == 0:
r1 = x
x = self.up1(x, r3)
x = self.up2(x, r2)
x = self.up3(x, r1)
x = self.group(x)
return x
def forward(self, x):
return self._forward_impl(x)
# This is the face detection model. Because the landmark model is very robust, it gets away with predicting very rough bounding boxes. It is fully convolutional and can be made to run on different resolutions. It was trained on 224x224 crops and the most reasonable results can be found in the range of 224x224 to 640x640.
class OpenSeeFaceDetect(geffnet.mobilenetv3.MobileNetV3):
def __init__(self, size="large", channel_multiplier=0.1):
kwargs = geffnet.mobilenetv3._gen_mobilenet_v3([size], channel_multiplier=channel_multiplier)
super(OpenSeeFaceDetect, self).__init__(**kwargs)
if size == "large":
self.up1 = UNetUp(round_channels(960, channel_multiplier), round_channels(112, channel_multiplier), 256, (14,14), old=1)
self.up2 = UNetUp(256, round_channels(40, channel_multiplier), 128, (28,28), old=1)
self.up3 = UNetUp(128, round_channels(24, channel_multiplier), 64, (56,56), old=1)
self.group = DSConv2d(64, 2, kernels_per_layer=4, groups=2, old=1)
self.r1_i = 1
self.r2_i = 2
self.r3_i = 4
elif size == "small":
self.up1 = UNetUp(round_channels(576, channel_multiplier), round_channels(40, channel_multiplier), 256, (14,14), old=1)
self.up2 = UNetUp(256, round_channels(24, channel_multiplier), 128, (28,28), old=1)
self.up3 = UNetUp(128, round_channels(16, channel_multiplier), 64, (56,56), old=1)
self.group = DSConv2d(64, 2, kernels_per_layer=4, groups=2, old=1)
self.r1_i = 0
self.r2_i = 1
self.r3_i = 2
self.maxpool = nn.MaxPool2d(kernel_size=3, dilation=1, stride=1, padding=1)
def _forward_impl(self, x):
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
r2 = None
r3 = None
for i, feature in enumerate(self.blocks):
x = feature(x)
if i == self.r3_i:
r3 = x
if i == self.r2_i:
r2 = x
if i == self.r1_i:
r1 = x
x = self.up1(x, r3)
x = self.up2(x, r2)
x = self.up3(x, r1)
x = self.group(x)
x2 = self.maxpool(x)
return x, x2
def forward(self, x):
return self._forward_impl(x)
def logit_arr(p, factor=16.0):
p = p.clamp(0.0000001, 0.9999999)
return torch.log(p / (1 - p)) / factor
# Landmark detection model
# Models:
# 0: "small", 0.5
# 1: "small", 1.0
# 2: "large", 0.75
# 3: "large", 1.0
class OpenSeeFaceLandmarks(geffnet.mobilenetv3.MobileNetV3):
def __init__(self, size="large", channel_multiplier=1.0, inference=False):
kwargs = geffnet.mobilenetv3._gen_mobilenet_v3([size], channel_multiplier=channel_multiplier)
super(OpenSeeFaceLandmarks, self).__init__(**kwargs)
if size == "large":
self.up1 = UNetUp(round_channels(960, channel_multiplier), round_channels(112, channel_multiplier), 256, (14,14))
self.up2 = UNetUp(256, round_channels(40, channel_multiplier), 198 * 1, (28,28))
self.group = DSConv2d(198 * 1, 198, kernels_per_layer=4, groups=3)
self.r2_i = 2
self.r3_i = 4
elif size == "small":
self.up1 = UNetUp(round_channels(576, channel_multiplier), round_channels(40, channel_multiplier), 256, (14,14))
self.up2 = UNetUp(256, round_channels(24, channel_multiplier), 198 * 1, (28,28))
self.group = DSConv2d(198 * 1, 198, kernels_per_layer=4, groups=3)
self.r2_i = 1
self.r3_i = 2
self.inference = inference
def _forward_impl(self, x):
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
r2 = None
r3 = None
for i, feature in enumerate(self.blocks):
x = feature(x)
if i == self.r3_i:
r3 = x
if i == self.r2_i:
r2 = x
x = self.up1(x, r3)
x = self.up2(x, r2)
x = self.group(x)
if self.inference:
t_main = x[:, 0:66].reshape((-1, 66, 28*28))
t_m = t_main.argmax(dim=2)
indices = t_m.unsqueeze(2)
t_conf = t_main.gather(2, indices).squeeze(2)
t_off_x = x[:, 66:132].reshape((-1, 66, 28*28)).gather(2, indices).squeeze(2)
t_off_y = x[:, 132:198].reshape((-1, 66, 28*28)).gather(2, indices).squeeze(2)
t_off_x = (223. * logit_arr(t_off_x) + 0.5).floor()
t_off_y = (223. * logit_arr(t_off_y) + 0.5).floor()
t_x = 223. * (t_m / 28.).floor() / 27. + t_off_x
t_y = 223. * t_m.remainder(28.).float() / 27. + t_off_y
x = (t_conf.mean(1), torch.stack([t_x, t_y, t_conf], 2))
return x
def forward(self, x):
return self._forward_impl(x)
# lm_modelT for 56x56 30 point inference
class OpenSeeFaceLandmarks30Pt(geffnet.mobilenetv3.MobileNetV3):
def __init__(self, size="large", channel_multiplier=1.0, inference=False):
kwargs = geffnet.mobilenetv3._gen_mobilenet_v3([size], channel_multiplier=channel_multiplier)
super(OpenSeeFaceLandmarks30Pt, self).__init__(**kwargs)
self.up1 = UNetUp(960, 112, 256, (4,4))
self.up2 = UNetUp(256, 40, 180, (7,7))
self.group = DSConv2d(180, 90, kernels_per_layer=4, groups=3)
self.inference = inference
def _forward_impl(self, x):
x = self.conv_stem(x)
x = self.bn1(x)
x = self.act1(x)
r2 = None
r3 = None
for i, feature in enumerate(self.blocks):
x = feature(x)
if i == 4:
r3 = x
if i == 2:
r2 = x
x = self.up1(x, r3)
x = self.up2(x, r2)
x = self.group(x)
if self.inference:
t_main = x[:, 0:30].reshape((-1, 30, 7*7))
t_m = t_main.argmax(dim=2)
indices = t_m.unsqueeze(2)
t_conf = t_main.gather(2, indices).squeeze(2)
t_off_x = x[:, 30:60].reshape((-1, 30, 7*7)).gather(2, indices).squeeze(2)
t_off_y = x[:, 60:90].reshape((-1, 30, 7*7)).gather(2, indices).squeeze(2)
t_off_x = 55. * logit_arr(t_off_x, factor=8.0)
t_off_y = 55. * logit_arr(t_off_y, factor=8.0)
t_x = 55. * (t_m / 7.).floor() / 6. + t_off_x
t_y = 55. * t_m.remainder(7.).float() / 6. + t_off_y
x = (t_conf.mean(1), torch.stack([t_x, t_y, t_conf], 2))
return x
def forward(self, x):
return self._forward_impl(x)
# Adaptive Wing Loss with offset layers and emphasis for eyes and eyebrows with 66 landmark points
def AdapWingLoss(pre_hm, gt_hm):
# pre_hm = pre_hm.to('cpu')
# gt_hm = gt_hm.to('cpu')
theta = 0.5
alpha = 2.1
w = 14
e = 1
A = w * (1 / (1 + torch.pow(theta / e, alpha - gt_hm))) * (alpha - gt_hm) * torch.pow(theta / e, alpha - gt_hm - 1) * (1 / e)
C = (theta * A - w * torch.log(1 + torch.pow(theta / e, alpha - gt_hm)))
batch_size = gt_hm.size()[0]
hm_num = gt_hm.size()[1]
mask = torch.zeros_like(gt_hm)
# W = 10
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
first = True
first_mask = None
for i in range(batch_size):
img_list = []
for j in range(hm_num // 3):
hm = np.round(gt_hm[i][j].cpu().numpy() * 255)
img_list.append(hm)
img_merge = cv2.merge(img_list)
img_dilate = cv2.morphologyEx(img_merge, cv2.MORPH_DILATE, kernel)
img_dilate[img_dilate < 51] = 1 # 0*W+1
img_dilate[img_dilate >= 51] = 11 # 1*W+1
img_dilate = np.array(img_dilate, dtype=int)
img_dilate = img_dilate.transpose(2, 0, 1)
dilated = torch.from_numpy(img_dilate).float()
if first:
first_mask = dilated
first = False
# These weights varied between training runs and model sizes
dilated[17:27][dilated[17:27] > 1] *= 1.4
dilated[17, dilated[17] > 1] *= 1.6
dilated[18, dilated[18] > 1] *= 1.8
dilated[25, dilated[25] > 1] *= 1.8
dilated[26, dilated[26] > 1] *= 1.6
dilated[36:48][dilated[36:48] > 1] *= 2.8
# Used for a very small model
#dilated[[37,38,40,41,43,44,46,47]][dilated[[37,38,40,41,43,44,46,47]] > 1] *= 20.8
mask[i] = torch.cat([dilated, dilated, dilated], 0)
diff_hm = torch.abs(gt_hm - pre_hm)
AWingLoss = A * diff_hm - C
idx = diff_hm < theta
AWingLoss[idx] = w * torch.log(1 + torch.pow(diff_hm / e, alpha - gt_hm))[idx]
AWingLoss *= mask
sum_loss = torch.sum(AWingLoss)
all_pixel = torch.sum(mask)
mean_loss = sum_loss / all_pixel
return first_mask.detach(), mean_loss
# Checkpoint test
if __name__== "__main__":
print("Checking gaze model")
m=OpenSeeFaceGaze()
ckpt = torch.load("gaze.pth")
m.load_state_dict(ckpt)
print("Checking detection model")
m=OpenSeeFaceDetect()
ckpt = torch.load("detection.pth")
m.load_state_dict(ckpt)
print("Checking lm_model0 model")
m=OpenSeeFaceLandmarks("small", 0.5)
ckpt = torch.load("lm_model0.pth")
m.load_state_dict(ckpt)
print("Checking lm_model1 model")
m=OpenSeeFaceLandmarks("small", 1.0)
ckpt = torch.load("lm_model1.pth")
m.load_state_dict(ckpt)
print("Checking lm_model2 model")
m=OpenSeeFaceLandmarks("large", 0.75)
ckpt = torch.load("lm_model2.pth")
m.load_state_dict(ckpt)
print("Checking lm_model3 model")
m=OpenSeeFaceLandmarks("large", 1.0)
ckpt = torch.load("lm_model3.pth")
m.load_state_dict(ckpt)
print("Checking lm_modelT model")
m=OpenSeeFaceLandmarks("large", 1.0)
ckpt = torch.load("lm_modelT.pth")
m.load_state_dict(ckpt)

View file

@ -1,82 +0,0 @@
"""
Watch over a stream of numbers, incrementally learning their median.
Implemented via nested lists. New numbers are added to `lst[i]` and
when it fills up, it posts its median to `lst[i+1]`. Wen `lst[i+1]`
fills up, it posts the medians of its medians to `lst[i+2]`. Etc.
When a remedian is queried for the current median, it returns the
median of the last list with any numbers.
This approach is quite space efficient . E.g. four nested lists,
each with 64 items, require memory for 4*64 items yet can hold the
median of the median of the median of the median of over 17 million
numbers.
Example usage:
z=remedian()
for i in range(1000):
z + i
if not i % 100:
print(i, z.median())
Based on [The Remedian:A Robust Averaging Method for Large Data
Sets](http://web.ipac.caltech.edu/staff/fmasci/home/astro_refs/Remedian.pdf).
by Peter J. Rousseeuw and Gilbert W. Bassett Jr. Journal of the
American Statistical Association March 1990, Vol. 85, No. 409,
Theory and Methods
The code [remedianeg.py](remedianeg.py) compares this rig to just
using Python's built-in sort then reporing the middle number.
Assuming lists of length 64 and use of pypy3:
- Remedian is getting nearly as fast (within 20%) as raw sort after 500 items;
- While at the same time, avoids having to store all the numbers in RAM;
- Further, remedian's computed median is within 1% (or less) of the medians found via Python's sort.
_____
## Programmer's Guide
"""
# If `ordered` is `False`, do not sort `lst`
def median(lst,ordered=False):
assert lst,"median needs a non-empty list"
n = len(lst)
p = q = n//2
if n < 3:
p,q = 0, n-1
else:
lst = lst if ordered else sorted(lst)
if not n % 2: # for even-length lists, use mean of mid 2 nums
q = p -1
return lst[p] if p==q else (lst[p]+lst[q])/2
class remedian:
# Initialization
def __init__(i,inits=[], k=64, # after some experimentation, 64 works ok
about = None):
i.all,i.k = [],k
i.more,i._median=None,None
[i + x for x in inits]
# When full, push the median of current values to next list, then reset.
def __add__(i,x):
i._median = None
i.all.append(x)
if len(i.all) == i.k:
i.more = i.more or remedian(k=i.k)
i.more + i._medianPrim(i.all)
i.all = [] # reset
# If there is a next list, ask its median. Else, work it out locally.
def median(i):
return i.more.median() if i.more else i._medianPrim(i.all)
# Only recompute median if we do not know it already.
def _medianPrim(i,all):
if i._median == None:
i._median = median(all,ordered=False)
return i._median

View file

@ -1,150 +0,0 @@
import sys
import numpy as np
import cv2
import onnxruntime
import time
import queue
import threading
import json
import copy
def py_cpu_nms(dets, thresh):
""" Pure Python NMS baseline.
Copyright (c) 2015 Microsoft
Licensed under The MIT License
Written by Ross Girshick
"""
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1[i], x1[order[1:]])
yy1 = np.maximum(y1[i], y1[order[1:]])
xx2 = np.minimum(x2[i], x2[order[1:]])
yy2 = np.minimum(y2[i], y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas[i] + areas[order[1:]] - inter)
inds = np.where(ovr <= thresh)[0]
order = order[inds + 1]
return keep
def decode(loc, priors, variances):
data = (
priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
priors[:, 2:] * np.exp(loc[:, 2:] * variances[1])
)
boxes = np.concatenate(data, 1)
boxes[:, :2] -= boxes[:, 2:] / 2
boxes[:, 2:] += boxes[:, :2]
return boxes
def worker_thread(rfd, frame):
results = rfd.detect_retina(frame, is_background=True)
rfd.results.put(results, False)
rfd.finished = True
rfd.running = False
class RetinaFaceDetector():
def __init__(self, model_path="models/retinaface_640x640_opt.onnx", json_path="models/priorbox_640x640.json", threads=4, min_conf=0.4, nms_threshold=0.4, top_k=1, res=(640, 640)):
options = onnxruntime.SessionOptions()
options.inter_op_num_threads = 1
options.intra_op_num_threads = threads
options.execution_mode = onnxruntime.ExecutionMode.ORT_SEQUENTIAL
options.graph_optimization_level = onnxruntime.GraphOptimizationLevel.ORT_ENABLE_ALL
options.log_severity_level = 3
providersList = onnxruntime.capi._pybind_state.get_available_providers()
self.session = onnxruntime.InferenceSession(model_path, sess_options=options, providers=providersList)
self.res_w, self.res_h = res
with open(json_path, "r") as prior_file:
self.priorbox = np.array(json.loads(prior_file.read()))
self.min_conf = min_conf
self.nms_threshold = nms_threshold
self.top_k = top_k
self.finished = False
self.running = False
self.results = queue.Queue()
def detect_retina(self, frame, is_background=False):
h, w, _ = frame.shape
im = None
im = cv2.resize(frame, (self.res_w, self.res_h), interpolation=cv2.INTER_LINEAR)
resize_w = w / self.res_w
resize_w = 1 / resize_w
resize_h = h / self.res_h
resize_h = 1 / resize_h
im = np.float32(im)
scale = np.array((self.res_w / resize_w, self.res_h / resize_h, self.res_w / resize_w, self.res_h / resize_h))
im -= (104, 117, 123)
im = im.transpose(2, 0, 1)
im = np.expand_dims(im, 0)
output = self.session.run([], {"input0": im})
loc, conf = output[0][0], output[1][0]
boxes = decode(loc, self.priorbox, [0.1, 0.2])
boxes = boxes * scale
scores = conf[:, 1]
inds = np.where(scores > self.min_conf)[0]
boxes = boxes[inds]
scores = scores[inds]
dets = np.hstack((boxes, scores[:, np.newaxis])).astype(np.float32, copy=False)
keep = py_cpu_nms(dets, self.nms_threshold)
dets = dets[keep, :]
dets = dets[:self.top_k, 0:4]
dets[:, 2:4] = dets[:, 2:4] - dets[:, 0:2]
if True:#is_background:
upsize = dets[:, 2:4] * np.array([[0.15, 0.2]])
dets[:, 0:2] -= upsize
dets[:, 2:4] += upsize * 2
return list(map(tuple, dets))
def background_detect(self, frame):
if self.running or self.finished:
return
self.running = True
im = copy.copy(frame)
thread = threading.Thread(target=worker_thread, args=(self, im))
thread.start()
def get_results(self):
if self.finished:
results = []
try:
while True:
detection = self.results.get(False)
results.append(detection)
except:
"No error"
self.finished = False
return list(*results)
else:
return []
if __name__== "__main__":
retina = RetinaFaceDetector(top_k=40, min_conf=0.2)
im = cv2.imread(sys.argv[1], cv2.IMREAD_COLOR)
start = time.perf_counter()
faces = retina.detect_retina(im)
end = 1000 * (time.perf_counter() - start)
print(f"Runtime: {end:.3f}ms")
for (x,y,w,h) in faces:
im = cv2.rectangle(im, (int(x),int(y)), (int(x+w),int(y+w)), (0,0,255), 1)
cv2.imshow("Frame", im)
while cv2.waitKey(0) & 0xff != ord('q'):
""

File diff suppressed because it is too large Load diff