Compare commits

...

12 Commits

Author SHA1 Message Date
Ruben van de Ven 4c519e46a6 explanation for running in Virtualbox 2021-01-27 11:04:28 +01:00
Ruben van de Ven 75db761507 Requirements.txt for Windows installer 2021-01-04 15:13:14 +01:00
Ruben van de Ven e9c48317f8 Comment in Readme 2020-12-21 15:03:01 +01:00
Ruben van de Ven df5d72ca83 remove unneccesary import 2020-12-21 13:14:38 +01:00
Ruben van de Ven 12ce9e1751 Changes after feedback + better Windows installer explanation in Readme 2020-12-21 12:04:38 +01:00
Ruben van de Ven b3b407a99e Better use of absolute paths for portability 2020-10-12 16:23:27 +02:00
Ruben van de Ven b3a0e4035b Exe build command documented 2020-10-12 15:59:25 +02:00
Ruben van de Ven cf88f3a8ee Windows pyinstaller compatibilities 2020-10-12 15:27:43 +02:00
Ruben van de Ven 9d1b523c56 Fix image saving for windows + error message on failure 2020-10-02 17:32:30 +02:00
Ruben van de Ven 2af58fc170 Styling and saves now flip back 2020-10-02 16:52:34 +02:00
Ruben van de Ven 357d481b97 update visualhaar 2020-10-02 14:59:54 +02:00
Ruben van de Ven 50e05c631e latest visualhaar 2020-10-02 14:11:02 +02:00
7 changed files with 235 additions and 80 deletions

5
.vscode/launch.json vendored
View File

@ -17,8 +17,9 @@
"request": "launch",
"program": "mirror.py",
"args": [
// "--fullscreen",
"--camera", "2",
"--windowed",
"--output", "/tmp/face_saves",
"--camera", "0",
],
"console": "integratedTerminal"
}

View File

@ -11,21 +11,55 @@ A `mirror` which shows which faces are detected through three different facial d
The installation in Windows can be done, though it is quite elaborate:
* Install rustup-init
* Install VS C++
* Install python3
* Install python3.8
* Install VS C++ build tools
* Install Cmake (needed for python dlib)
+ make sure to add it to path
* Install git
+ including ssh deploy key
* `git clone https://git.rubenvandeven.com/r/face_detector`
* `git clone https://git.rubenvandeven.com/r/face_recognition`
* `cd face_recognition`
* `git submodules init`
* `git submodules update`
* `pip install virtualenv`
* `virtualenv.exe venv`
+ Might be that you need to run: `C:\Users\DP Medialab\AppData\Roaming\Python\Python39\Scripts\virtualenv.exe` (see pip output)
* `.\venv\Scripts\activate`
+ Might be that you need to first run `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
* `cd .\dnn\face_detector`
* `python.exe .\download_weights.py`
* `cd ..\..`
* `pip.exe install -r requirements.txt`
* `cd .\visualhaar`
* `cargo build --lib --release`
* Either one of:
+ Compile rust library
* Install rustup-init
* `git submodules init`
* `git submodules update`
* `cargo build --lib --release`
+ Download dll from https://git.rubenvandeven.com/r/visualhaar/releases
+ Fetch `SourceSansPro-Regular.ttf` from the internet
+ Make the installer:
* Either one of:
* `& 'C:\Users\DP Medialab\AppData\Roaming\Python\Python38\Scripts\pyinstaller.exe' .\mirror.py --add-binary '.\visualhaar\target\release\visual_haarcascades_lib.dll;.' --add-data '.\haarcascade_frontalface_alt2.xml;.' --add-data '.\SourceSansPro-Regular.ttf;.' --add-data 'dnn;dnn'`
* `& '.\venv\Scripts\pyinstaller.exe' .\mirror.py --add-binary '.\visualhaar\target\release\visual_haarcascades_lib.dll;.' --add-data '.\haarcascade_frontalface_alt2.xml;.' --add-data '.\SourceSansPro-Regular.ttf;.' --add-data 'dnn;dnn' --hidden-import 'scipy.spatial.transform._rotation_groups' --hidden-import 'skimage.filters.rank.core_cy_3d'`
* `Compress-Archive -LiteralPath .\dist\mirror -DestinationPath .\dist\mirror.zip`
+ We could also [use wine for cross compilation](https://www.andreafortuna.org/2017/12/27/how-to-cross-compile-a-python-script-into-a-windows-executable-on-linux/) from Linux
- make sure wine is set to pose as Windows 10 (`winecfg`)
- `wine ~/Downloads/python-3.9.0-amd64.exe` (or whichever version you use)
- Install for all users
-
### On windows in VirtualBox
See [this](https://askubuntu.com/questions/4875/how-can-i-use-my-webcam-with-ubuntu-running-in-virtualbox/1237808#1237808) on getting the webcam working in the VM:
1. Install extension pack: `sudo apt install virtualbox-ext-pack`
2. `VBoxManage list webcams`
3. `VBoxManage controlvm "WIn10" webcam attach .3`
## Instructor help
If screen stays black: is the camera on?
Enable camera through keyboard (MSI laptops: fn+F6). Then go to Settings/Instellingen -> Privacy instellingen voor camera -> Grant apps access to camera.

View File

@ -9,11 +9,12 @@ import math
import datetime
from PIL import ImageFont, ImageDraw, Image
import os
import sys
draw_colors = {
'hog': (255,255,255), #(198,65,124),
'hog': (198,65,124),
'haar': (255,255,255),
'dnn': (255,255,255) #(251,212,36),
'dnn': (251,212,36),
}
titles = {
@ -22,7 +23,10 @@ titles = {
'dnn' : "Neural network",
}
fontfile = "SourceSansPro-Regular.ttf"
project_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)),'..')
fontfile = os.path.join(project_dir, "SourceSansPro-Regular.ttf")
font = ImageFont.truetype(fontfile, 30)
font_s = ImageFont.truetype(fontfile, 20)
@ -45,42 +49,50 @@ class Result():
})
return self
def draw_detections(self, include_title = False):
def draw_detections(self, include_title = False, coloured=False):
cv2_im_rgb = cv2.cvtColor(self.visualisation,cv2.COLOR_BGR2RGB)
# Pass the image to PIL
pil_im = Image.fromarray(cv2_im_rgb)
draw = ImageDraw.Draw(pil_im, 'RGBA')
self.draw_detections_on(draw)
self.draw_detections_on(draw, coloured)
if include_title:
draw.text((10,10), titles[self.algorithm], fill=draw_colors[self.algorithm], font=font)
color = draw_colors[self.algorithm] if coloured else (255,255,255)
draw.text((10,10), titles[self.algorithm], fill=color, font=font, stroke_width=1, stroke_fill=(0,0,0,100))
return cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
def draw_detections_on(self, draw: ImageDraw):
def draw_detections_on(self, draw: ImageDraw, coloured=False, onlyIfConfident=False):
'''
Draw on a specified canvas
'''
color = draw_colors[self.algorithm]
color = draw_colors[self.algorithm] if coloured else (255,255,255)
for detection in self.detections:
self.draw_detection(draw, detection, color)
self.draw_detection(draw, detection, color, onlyIfConfident)
def draw_detection(self, draw: ImageDraw, detection: dict, color: tuple):
width = 2
def draw_detection(self, draw: ImageDraw, detection: dict, color: tuple, onlyIfConfident: bool = False):
if detection['confidence'] > self.confidence_threshold:
width = 8
# draw the bounding box of the face along with the associated
# probability
text = "{:.2f}%".format(detection['confidence'] * 100)
text = "{:.0f}%".format(detection['confidence'] * 100)
y = detection['startY'] - 40 if detection['startY'] - 40 > 10 else detection['startY'] + 10
draw.text((detection['startX'], y), text, font=font, fill=color)
draw.text((detection['startX'], y), text, font=font, fill=color, stroke_fill=(0,0,0,100), stroke_width=1)
# cv2.putText(self.visualisation, text, (detection['startX'], y),
# cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2, lineType = cv2.LINE_AA)
alpha = 1
draw.rectangle((detection['startX']-1, detection['startY']-1, detection['endX']+1, detection['endY']+1), outline=(0,0,0,100), width=1)
draw.rectangle((detection['startX']+width, detection['startY']+width, detection['endX']-width, detection['endY']-width), outline=(0,0,0,100), width=1)
elif onlyIfConfident:
# Only draw if above threshold, so this should be ignored.
return
else:
width = int(detection['confidence'] * 10 * 8)
# At least 10% opacity
alpha = max(.2, detection['confidence'])
@ -90,23 +102,35 @@ class Result():
draw.rectangle((detection['startX'], detection['startY'], detection['endX'], detection['endY']), outline=color, width=width)
def resize(self, width, height):
def resize(self, width, height, flip=False):
# TODO resize to new target incl all detections
img = self.visualisation
factor_x = width / self.visualisation.shape[1]
factor_y = height / self.visualisation.shape[0]
inter = cv2.INTER_NEAREST if self.algorithm in ['dnn', 'haar'] else cv2.INTER_CUBIC
img = cv2.resize(img, (width, height), interpolation=inter)
if flip:
img = cv2.flip(img, 1)
result = Result(self.algorithm, img, self.confidence_threshold)
for d in self.detections:
result.add_detection(
int(d['startX'] * factor_x),
int(d['startY'] * factor_y),
int(d['endX'] * factor_x),
int(d['endY'] * factor_y),
d['confidence']
)
if flip:
result.add_detection(
int(width - d['endX'] * factor_x),
int(d['startY'] * factor_y),
int(width - d['startX'] * factor_x),
int(d['endY'] * factor_y),
d['confidence']
)
else:
result.add_detection(
int(d['startX'] * factor_x),
int(d['startY'] * factor_y),
int(d['endX'] * factor_x),
int(d['endY'] * factor_y),
d['confidence']
)
return result
def count_detections(self):
@ -128,7 +152,7 @@ def record(device_id, q1,q2, q3, q4, resolution, rotate):
ret, image = capture.read()
if image is None:
logging.critical("Error with camera?")
exit()
sys.exit()
if rotate is not None:
@ -199,9 +223,9 @@ def draw_detection(image, startX, startY, endX, endY, confidence, color=(0,0,255
def process1_hog(in_q, out_q):
from skimage.feature import hog as hog_orig
# from skimage.feature import hog as hog_orig
from .hog import hog # use modified version for viz
from skimage import data, exposure
from skimage import exposure
import matplotlib.pyplot as plt
import dlib
import matplotlib.pyplot as plt
@ -212,7 +236,7 @@ def process1_hog(in_q, out_q):
face_detector = dlib.get_frontal_face_detector()
visualisation_factor = 1
detection_factor = .4
detection_factor = .3
process_this_frame = True
@ -222,7 +246,7 @@ def process1_hog(in_q, out_q):
frame = in_q.get()
frame = cv2.cvtColor(src=frame, code=cv2.COLOR_BGR2GRAY)
viz_frame = cv2.resize(frame, (0, 0), fx=visualisation_factor, fy=visualisation_factor)
# viz_frame = cv2.resize(frame, (0, 0), fx=visualisation_factor, fy=visualisation_factor)
det_frame = cv2.resize(frame, (0, 0), fx=detection_factor, fy=detection_factor)
start = time.time()
@ -330,7 +354,7 @@ def process2_dnn(in_q, out_q):
out_q.put(result)
def process3_haar(in_q, out_q, cascade_file):
def process3_haar(in_q, out_q, cascade_file, library_filename = None):
from cffi import FFI
from PIL import Image
import cv2
@ -347,19 +371,27 @@ def process3_haar(in_q, out_q, cascade_file):
void scan_image(haarclassifier, size_t width,size_t height, char *input, char *buffer, size_t length, size_t min_face_factor, bool debug);
""")
dir_path = os.path.dirname(os.path.realpath(__file__))
lib_path = os.path.join(dir_path, "..", "visualhaar", "target", "release")
so_path = os.path.join(lib_path, "libvisual_haarcascades_lib.so")
dll_path = os.path.join(lib_path, "visual_haarcascades_lib.dll")
if os.path.exists(so_path):
C = ffi.dlopen(so_path)
elif os.path.exists(dll_path):
C = ffi.dlopen(dll_path)
if library_filename is not None:
C = ffi.dlopen(library_filename)
else:
raise RuntimeException("Visual haarcascades library is not found")
lib_path = os.path.join(project_dir, "visualhaar", "target", "release")
possible_paths = [
os.path.join(lib_path, "libvisual_haarcascades_lib.so"),
os.path.join(lib_path, "visual_haarcascades_lib.dll"),
os.path.join(project_dir, "visual_haarcascades_lib.dll"),
]
existing_paths = [p for p in possible_paths if os.path.exists(p)]
if not len(existing_paths):
raise RuntimeError("Visual haarcascades library is not found")
logger.debug(f"Using library: {existing_paths[0]}")
C = ffi.dlopen(existing_paths[0])
# print(C.test(9))
# i = Image.open("Marjo.jpg")
# width = i.size[0]
@ -406,7 +438,7 @@ def process3_haar(in_q, out_q, cascade_file):
start = time.time()
C.scan_image(haar, width, height, buffer2, buffer, buffer_len, 5, False)
logger.info(f"Visualised scan into buffer: {buffer}")
print(f"duration: {time.time() - start}s")
# print(f"duration: {time.time() - start}s")
img = Image.frombuffer(pixel_format, (width, height), ffi.buffer(buffer),
"raw", pixel_format, 0, 1)
@ -434,10 +466,13 @@ def process3_haar(in_q, out_q, cascade_file):
# print(img)
out_q.put(result)
def draw_stats(image, results, padding):
def draw_stats(image, results, padding, coloured=False, drawDetections=False):
pil_im = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_im, 'RGBA')
draw_stats_on_canvas(draw, results, padding, coloured, drawDetections)
return cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
def draw_stats_on_canvas(draw, results, padding, coloured=False, drawDetections=False):
for i, result in enumerate(results):
if result is None:
continue
@ -446,9 +481,11 @@ def draw_stats(image, results, padding):
txt = "face" if c == 1 else "faces"
txt = f"{result.algorithm.ljust(5)} {c} {txt}"
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))
colour = draw_colors[result.algorithm] if coloured else (255,255,255)
draw.text((padding, draw.im.size[1] - (i+1)*height - padding), txt, fill=colour, font=font, stroke_width=2, stroke_fill=(0,0,0))
return cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
if drawDetections:
result.draw_detections_on(draw, coloured, onlyIfConfident=True)
def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
@ -535,7 +572,9 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
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)
# for the plain webcam image (no viz), draw all detected faces.
drawDetections = (selectPreview.imageIdx == 0)
grid_img = draw_stats(grid_img, results, padding, coloured=True, drawDetections=drawDetections)
pil_im = Image.fromarray(cv2.cvtColor(grid_img, cv2.COLOR_BGR2RGB))
draw = ImageDraw.Draw(pil_im, 'RGBA')
@ -543,7 +582,7 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
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)
draw.text(((grid_img.shape[1]-w)/2,(grid_img.shape[0]-h)/2), f"{duration}", fill="white", stroke="black", font=countdown_font, stroke_width=1, stroke_fill=(0,0,0,100))
grid_img = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
@ -554,41 +593,63 @@ def display(image_res, q1, q2, q3, q4, fullscreen, output_dir):
# Hit 'q' on the keyboard to quit!
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
if key == ord('q') or key == 27: # key 27: escape
break
if key == ord(' ') and not override_image:
# TODO: the truth value of an array with ore than one element is ambiguous, use a.any or a.all() (OF DUS override_image is None)
if key == ord(' ') and override_image is None:
countdown_until = time.time() + 3 # seconds of countdown
# SNAP! SAVE FRAMES
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...
# 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 = Image.fromarray(cv2.cvtColor(cv2.flip(images[0],1), cv2.COLOR_BGR2RGB))
pil_im = pil_im.resize(output_res)
# base name for all images
name = datetime.datetime.now().isoformat(timespec='seconds').replace(':','-')
# filename of clean frame
filename = os.path.join(output_dir, f'{name}-frame.jpg')
pil_im.save(filename)
# now draw all results to the main image
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)
result.resize(output_res[0], output_res[1], flip=True).draw_detections_on(draw, coloured=True)
draw_stats_on_canvas(draw, results, padding, coloured=True)
override_image = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
override_until = time.time() + 5
logger.info("Show frame until %f", override_until)
# save images:
name = datetime.datetime.now().isoformat(timespec='seconds')
cv2.imwrite(os.path.join(output_dir, f'{name}.png'),override_image)
filename = os.path.join(output_dir, f'{name}-all.png')
print(f"Save to {filename}")
r=cv2.imwrite(filename, override_image)
if not r:
raise RuntimeError(f"Could not save image {filename}")
# finally, store each visualisation with the results
for result in results:
cv2.imwrite(os.path.join(output_dir, f'{name}-{result.algorithm}.png'),result.visualisation)
result_img =result.draw_detections(include_title = True)
filename = os.path.join(output_dir, f'{name}-{result.algorithm}.png')
r = cv2.imwrite(filename, result_img)
if not r:
raise RuntimeError(f"Could not save image {filename}")
def main(camera_id, rotate, fullscreen, cascade_file, output_dir):
def main(camera_id, rotate, fullscreen, cascade_file, output_dir, visualhaar_lib = None):
image_size = (1920, 1080) #(int(1920/2), int(1080/2))
if not os.path.exists(cascade_file):
@ -616,7 +677,7 @@ def main(camera_id, rotate, fullscreen, cascade_file, output_dir):
p2 = Process(target=display, args=(image_size, q_webcam1, q_process1, q_process2, q_process3, fullscreen, output_dir ))
p3 = Process(target=process1_hog, args=(q_webcam2, q_process1,))
p4 = Process(target=process2_dnn, args=(q_webcam3, q_process2,))
p5 = Process(target=process3_haar, args=(q_webcam4, q_process3,cascade_file))
p5 = Process(target=process3_haar, args=(q_webcam4, q_process3,cascade_file, visualhaar_lib))
p1.start()
p2.start()

32
face_recognition/tools.py Normal file
View File

@ -0,0 +1,32 @@
from PIL import ImageFont, ImageDraw, Image
import cv2
import numpy as np
text_to_show = "The quick brown fox jumps over the lazy dog"
# Load image in OpenCV
image = cv2.imread("Me.jpg")
# Convert the image to RGB (OpenCV uses BGR)
cv2_im_rgb = cv2.cvtColor(image,cv2.COLOR_BGR2RGB)
# Pass the image to PIL
pil_im = Image.fromarray(cv2_im_rgb)
draw = ImageDraw.Draw(pil_im)
# Draw the text
draw.text((10, 700), text_to_show, font=font)
# Get back the image to OpenCV
cv2_im_processed = cv2.cvtColor(np.array(pil_im), cv2.COLOR_RGB2BGR)
cv2.imshow('Fonts', cv2_im_processed)
cv2.waitKey(0)
cv2.destroyAllWindows()
def get_font(filename, size):
return ImageFont.truetype(filename, size)
def draw_text(img, ):

View File

@ -1,22 +1,27 @@
import argparse
import face_recognition.comparison
import cv2
from multiprocessing import freeze_support
import os
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Visualise face recognition algorithms.')
freeze_support() # support pyinstaller on Windows
parser = argparse.ArgumentParser(description='Visualise face recognition algorithms.', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--camera', '-c', type=int, default=0,
help='Numeric id of the camera')
parser.add_argument('--fullscreen', '-f', action='store_true',
help='Display output full screen')
parser.add_argument('--windowed', '-w', action='store_true',
help='Display output windowed instead of fullscreen')
parser.add_argument('--clockwise', action='store_true',
help='Rotate clockwise')
parser.add_argument('--counter-clockwise', action='store_true',
help='Rotate counter clockwise')
parser.add_argument('--cascade', default='haarcascade_frontalface_alt2.xml',
parser.add_argument('--cascade', default=os.path.join(os.path.dirname(os.path.realpath(__file__)),'haarcascade_frontalface_alt2.xml'),
help='Cascade XML file to use (opencv format)')
parser.add_argument('--output', default='saves',
parser.add_argument('--output', metavar="DIRECTORY", default=os.path.expanduser("~/Desktop/faces"),
help='Directory to store images (after pressing spacebar)')
parser.add_argument('--visualhaar-lib', metavar="LIBRARY", default=None,
help='path/filename for visualhaar library (.so on linux, .dll on windows)\nSee: https://git.rubenvandeven.com/r/visualhaar/releases')
args = parser.parse_args()
@ -26,4 +31,8 @@ if __name__ == '__main__':
if args.counter_clockwise:
rotate = cv2.ROTATE_90_COUNTERCLOCKWISE
face_recognition.comparison.main(args.camera, rotate, args.fullscreen, args.cascade, args.output)
if not os.path.exists(args.output):
print("Making directory:", args.output)
os.mkdir(args.output)
face_recognition.comparison.main(args.camera, rotate, not args.windowed, args.cascade, args.output, args.visualhaar_lib)

View File

@ -1,7 +1,25 @@
scipy
numpy
dlib
Pillow
opencv-python
cffi
scikit-image
altgraph==0.17
cffi==1.14.4
cycler==0.10.0
decorator==4.4.2
dlib==19.21.1
future==0.18.2
imageio==2.9.0
kiwisolver==1.3.1
matplotlib==3.3.3
networkx==2.5
numpy==1.19.3
opencv-python==4.5.1.48
pefile==2019.4.18
Pillow==8.1.0
pycparser==2.20
pyinstaller==4.1
pyinstaller-hooks-contrib==2020.11
pyparsing==2.4.7
python-dateutil==2.8.1
PyWavelets==1.1.1
pywin32-ctypes==0.2.0
scikit-image==0.18.1
scipy==1.6.0
six==1.15.0
tifffile==2020.12.8

@ -1 +1 @@
Subproject commit ac1aea1d68f346be194e64a3275629a177327f2e
Subproject commit 1319e644b1f59debe46be866d18209d2a6089e1b