|
|
|
@ -5,14 +5,15 @@ import logging
@@ -5,14 +5,15 @@ import logging
|
|
|
|
|
import argparse |
|
|
|
|
import numpy as np |
|
|
|
|
import time |
|
|
|
|
import math |
|
|
|
|
import datetime |
|
|
|
|
from PIL import ImageFont, ImageDraw, Image |
|
|
|
|
import os |
|
|
|
|
|
|
|
|
|
draw_colors = { |
|
|
|
|
'hog': (198,65,124), |
|
|
|
|
'hog': (255,255,255), #(198,65,124), |
|
|
|
|
'haar': (255,255,255), |
|
|
|
|
'dnn': (251,212,36), |
|
|
|
|
'dnn': (255,255,255) #(251,212,36), |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
titles = { |
|
|
|
@ -25,6 +26,7 @@ fontfile = "SourceSansPro-Regular.ttf"
@@ -25,6 +26,7 @@ fontfile = "SourceSansPro-Regular.ttf"
|
|
|
|
|
|
|
|
|
|
font = ImageFont.truetype(fontfile, 30) |
|
|
|
|
font_s = ImageFont.truetype(fontfile, 20) |
|
|
|
|
countdown_font = ImageFont.truetype(fontfile, 160) |
|
|
|
|
|
|
|
|
|
class Result(): |
|
|
|
|
def __init__(self, algorithm, image, confidence_threshold = 0.5): |
|
|
|
@ -80,7 +82,7 @@ class Result():
@@ -80,7 +82,7 @@ class Result():
|
|
|
|
|
alpha = 1 |
|
|
|
|
else: |
|
|
|
|
# At least 10% opacity |
|
|
|
|
alpha = max(.3, detection['confidence']) |
|
|
|
|
alpha = max(.2, detection['confidence']) |
|
|
|
|
|
|
|
|
|
color = list(color) |
|
|
|
|
color.append(int(alpha*255)) |
|
|
|
@ -119,6 +121,8 @@ def record(device_id, q1,q2, q3, q4, resolution, rotate):
@@ -119,6 +121,8 @@ def record(device_id, q1,q2, q3, q4, resolution, rotate):
|
|
|
|
|
|
|
|
|
|
capture.set(cv2.CAP_PROP_FRAME_WIDTH, resolution[1] if is_rotated_90 else resolution[0]) |
|
|
|
|
capture.set(cv2.CAP_PROP_FRAME_HEIGHT, resolution[0] if is_rotated_90 else resolution[1]) |
|
|
|
|
|
|
|
|
|
gave_camera_warning = False |
|
|
|
|
|
|
|
|
|
while True: |
|
|
|
|
ret, image = capture.read() |
|
|
|
@ -130,9 +134,13 @@ def record(device_id, q1,q2, q3, q4, resolution, rotate):
@@ -130,9 +134,13 @@ def record(device_id, q1,q2, q3, q4, resolution, rotate):
|
|
|
|
|
if rotate is not None: |
|
|
|
|
image = cv2.rotate(image, rotate) |
|
|
|
|
|
|
|
|
|
# Flip image to create the 'mirror' effect. |
|
|
|
|
image = cv2.flip(image, 1) |
|
|
|
|
|
|
|
|
|
# print(image.shape[:2], image.shape[1::-1]) |
|
|
|
|
if image.shape[1::-1] != resolution: |
|
|
|
|
if image.shape[1::-1] != resolution and not gave_camera_warning: |
|
|
|
|
logging.warning(f"Camera resultion seems wrong: {image.shape[:2]} instead of {resolution}") |
|
|
|
|
gave_camera_warning = True |
|
|
|
|
|
|
|
|
|
try: |
|
|
|
|
q1.put_nowait(image) |
|
|
|
@ -283,7 +291,7 @@ def process2_dnn(in_q, out_q):
@@ -283,7 +291,7 @@ def process2_dnn(in_q, out_q):
|
|
|
|
|
prototxt = "dnn/face_detector/opencv_face_detector.pbtxt" |
|
|
|
|
prototxt = "dnn/face_detector/deploy.prototxt" |
|
|
|
|
model = "dnn/face_detector/res10_300x300_ssd_iter_140000_fp16.caffemodel" |
|
|
|
|
confidence_threshold = 0.5 |
|
|
|
|
confidence_threshold = 0.7 |
|
|
|
|
|
|
|
|
|
logger.info("[INFO] loding model...") |
|
|
|
|
net = cv2.dnn.readNetFromCaffe(prototxt, model) |
|
|
|
@ -371,7 +379,7 @@ def process3_haar(in_q, out_q, cascade_file):
@@ -371,7 +379,7 @@ def process3_haar(in_q, out_q, cascade_file):
|
|
|
|
|
frame = in_q.get() |
|
|
|
|
(height_orig, width_orig) = frame.shape[:2] |
|
|
|
|
|
|
|
|
|
scale_factor = 3 |
|
|
|
|
scale_factor = 4 |
|
|
|
|
width = int(width_orig/scale_factor) |
|
|
|
|
height = int(height_orig/scale_factor) |
|
|
|
|
|
|
|
|
@ -426,7 +434,7 @@ def process3_haar(in_q, out_q, cascade_file):
@@ -426,7 +434,7 @@ def process3_haar(in_q, out_q, cascade_file):
|
|
|
|
|
# print(img) |
|
|
|
|
out_q.put(result) |
|
|
|
|
|
|
|
|
|
def draw_stats(image, results): |
|
|
|
|
def draw_stats(image, results, padding): |
|
|
|
|
pil_im = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)) |
|
|
|
|
draw = ImageDraw.Draw(pil_im, 'RGBA') |
|
|
|
|
|
|
|
|
@ -437,7 +445,8 @@ def draw_stats(image, results):
@@ -437,7 +445,8 @@ def draw_stats(image, results):
|
|
|
|
|
c = result.count_detections() |
|
|
|
|
txt = "face" if c == 1 else "faces" |
|
|
|
|
txt = f"{result.algorithm.ljust(5)} {c} {txt}" |
|
|
|
|
draw.text((10, pil_im.size[1] - i*25 - 50), txt, fill=draw_colors[result.algorithm], font=font_s, stroke_width=1, stroke_fill=(0,0,0)) |
|
|
|
|
height = padding + 25 |
|
|
|
|
draw.text((padding, pil_im.size[1] - i*height - height), txt, fill=draw_colors[result.algorithm], font=font_s, stroke_width=1, stroke_fill=(0,0,0)) |
|
|
|
|
|
|
|
|
|
return cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR) |
|
|
|
|
|
|
|
|
@ -447,19 +456,50 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
@@ -447,19 +456,50 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
|
|
|
|
|
|
|
|
|
|
empty_image = np.zeros((image_res[1],image_res[0],3), np.uint8) |
|
|
|
|
|
|
|
|
|
image_ratio = image_res[0] / image_res[1] |
|
|
|
|
|
|
|
|
|
results = [None, None, None] |
|
|
|
|
result_queues = [q2, q3, q4] |
|
|
|
|
images = [empty_image, empty_image, empty_image, empty_image] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override_image = None |
|
|
|
|
override_until = None |
|
|
|
|
countdown_until = None |
|
|
|
|
|
|
|
|
|
# imageIdx = 0 |
|
|
|
|
|
|
|
|
|
# grid in the right corner |
|
|
|
|
preview_scale = 10 |
|
|
|
|
preview_width = round(image_res[0] / preview_scale) |
|
|
|
|
preview_height = round(preview_width / image_ratio) |
|
|
|
|
padding = round(image_res[0] / 100) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if fullscreen: |
|
|
|
|
cv2.namedWindow("output", cv2.WND_PROP_FULLSCREEN) |
|
|
|
|
cv2.namedWindow("output", cv2.WINDOW_NORMAL) |
|
|
|
|
cv2.setWindowProperty("output",cv2.WND_PROP_FULLSCREEN,cv2.WINDOW_FULLSCREEN) |
|
|
|
|
else: |
|
|
|
|
cv2.namedWindow("output", cv2.WINDOW_AUTOSIZE) |
|
|
|
|
|
|
|
|
|
def selectPreview(event, x, y, flags, param): |
|
|
|
|
if event == cv2.EVENT_LBUTTONDOWN: |
|
|
|
|
if x > image_res[0] - padding or x < image_res[0] - padding - preview_width: |
|
|
|
|
return |
|
|
|
|
|
|
|
|
|
preview_images = [idx for idx,image in enumerate(images) if idx != selectPreview.imageIdx] |
|
|
|
|
for offset, image_nr in enumerate(preview_images): |
|
|
|
|
offset_y = (preview_height + padding) * offset |
|
|
|
|
# print(offset, y, image_res[0] - padding - preview_height - offset_y, image_res[0] - padding - offset_y) |
|
|
|
|
if y > image_res[1] - padding - preview_height - offset_y and y < image_res[1] - padding - offset_y: |
|
|
|
|
selectPreview.imageIdx = image_nr |
|
|
|
|
print("Select image", offset, image_nr) |
|
|
|
|
break |
|
|
|
|
|
|
|
|
|
selectPreview.imageIdx = 0 |
|
|
|
|
cv2.setMouseCallback('output', selectPreview) |
|
|
|
|
|
|
|
|
|
while True: |
|
|
|
|
logging.debug('r') |
|
|
|
|
try: |
|
|
|
|
image = q1.get_nowait() |
|
|
|
|
images[0] = cv2.resize(image, (image_res[0], image_res[1])) |
|
|
|
@ -481,30 +521,60 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
@@ -481,30 +521,60 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
|
|
|
|
|
else: |
|
|
|
|
override_image = None |
|
|
|
|
|
|
|
|
|
images[0] = draw_stats(images[0], results) |
|
|
|
|
# images[0] = draw_stats(images[0], results) |
|
|
|
|
|
|
|
|
|
# show the selected image: |
|
|
|
|
grid_img = images[selectPreview.imageIdx].copy() |
|
|
|
|
|
|
|
|
|
# previews in the right bottom corner |
|
|
|
|
preview_images = [image for idx,image in enumerate(images) if idx != selectPreview.imageIdx] |
|
|
|
|
for idx, image in enumerate(preview_images): |
|
|
|
|
offset_y = (preview_height + padding) * idx |
|
|
|
|
grid_img[ |
|
|
|
|
grid_img.shape[0] - padding - preview_height - offset_y:grid_img.shape[0] - padding - offset_y, |
|
|
|
|
grid_img.shape[1] - padding - preview_width:grid_img.shape[1] - padding] = cv2.resize(image, (preview_width, preview_height), cv2.INTER_CUBIC) |
|
|
|
|
|
|
|
|
|
# statistics |
|
|
|
|
grid_img = draw_stats(grid_img, results, padding) |
|
|
|
|
pil_im = Image.fromarray(cv2.cvtColor(grid_img, cv2.COLOR_BGR2RGB)) |
|
|
|
|
draw = ImageDraw.Draw(pil_im, 'RGBA') |
|
|
|
|
|
|
|
|
|
# Draw countdown |
|
|
|
|
if countdown_until: |
|
|
|
|
duration = math.ceil(countdown_until - time.time()) |
|
|
|
|
w, h = draw.textsize(f"{duration}", font=countdown_font) |
|
|
|
|
draw.text(((grid_img.shape[1]-w)/2,(grid_img.shape[0]-h)/2), f"{duration}", fill="white", stroke="black", font=countdown_font) |
|
|
|
|
|
|
|
|
|
img_concate_Verti1 = np.concatenate((images[0],images[1]),axis=0) |
|
|
|
|
img_concate_Verti2 = np.concatenate((images[2],images[3]),axis=0) |
|
|
|
|
grid_img = np.concatenate((img_concate_Verti1,img_concate_Verti2),axis=1) |
|
|
|
|
grid_img = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR) |
|
|
|
|
|
|
|
|
|
# img_concate_Verti1 = np.concatenate((images[0],images[1]),axis=0) |
|
|
|
|
# img_concate_Verti2 = np.concatenate((images[2],images[3]),axis=0) |
|
|
|
|
# grid_img = np.concatenate((img_concate_Verti1,img_concate_Verti2),axis=1) |
|
|
|
|
cv2.imshow("output", grid_img) |
|
|
|
|
|
|
|
|
|
# Hit 'q' on the keyboard to quit! |
|
|
|
|
key = cv2.waitKey(1) & 0xFF |
|
|
|
|
if key == ord('q'): |
|
|
|
|
break |
|
|
|
|
if key == ord(' '): |
|
|
|
|
if key == ord(' ') and not override_image: |
|
|
|
|
countdown_until = time.time() + 3 # seconds of countdown |
|
|
|
|
|
|
|
|
|
if countdown_until is not None and time.time() > countdown_until: |
|
|
|
|
countdown_until = None |
|
|
|
|
# TODO wait for frame to be processed. Eg. if I move and make a pic, it should use the last frame... |
|
|
|
|
output_res = (image_res[0] *2, image_res[1] * 2) |
|
|
|
|
# SNAP! |
|
|
|
|
# output_res = (image_res[0] *2, image_res[1] * 2) |
|
|
|
|
output_res = image_res # no scaling needed anyore |
|
|
|
|
pil_im = Image.fromarray(cv2.cvtColor(images[0], cv2.COLOR_BGR2RGB)) |
|
|
|
|
pil_im = pil_im.resize(output_res) |
|
|
|
|
draw = ImageDraw.Draw(pil_im, 'RGBA') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for result in results: |
|
|
|
|
if result is None: |
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
result.resize(output_res[0], output_res[1]).draw_detections_on(draw) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
override_image = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR) |
|
|
|
|
override_until = time.time() + 5 |
|
|
|
|
logger.info("Show frame until %f", override_until) |
|
|
|
@ -514,10 +584,12 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
@@ -514,10 +584,12 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
|
|
|
|
|
cv2.imwrite(os.path.join(output_dir, f'{name}.png'),override_image) |
|
|
|
|
for result in results: |
|
|
|
|
cv2.imwrite(os.path.join(output_dir, f'{name}-{result.algorithm}.png'),result.visualisation) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main(camera_id, rotate, fullscreen, cascade_file, output_dir): |
|
|
|
|
image_size = (int(1920/2), int(1080/2)) |
|
|
|
|
image_size = (1920, 1080) #(int(1920/2), int(1080/2)) |
|
|
|
|
|
|
|
|
|
if not os.path.exists(cascade_file): |
|
|
|
|
raise RuntimeError(f"Cannot load OpenCV haar-cascade file '{cascade_file}'") |
|
|
|
|