All sorts of test scripts and the first functional mirror version
This commit is contained in:
commit
cf94593382
30 changed files with 35514 additions and 0 deletions
1790
dnn/deploy.prototxt
Normal file
1790
dnn/deploy.prototxt
Normal file
File diff suppressed because it is too large
Load diff
1790
dnn/deploy_lowres.prototxt
Normal file
1790
dnn/deploy_lowres.prototxt
Normal file
File diff suppressed because it is too large
Load diff
2
dnn/face_detector/.gitignore
vendored
Normal file
2
dnn/face_detector/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*.caffemodel
|
||||||
|
*.pb
|
1790
dnn/face_detector/deploy.prototxt
Normal file
1790
dnn/face_detector/deploy.prototxt
Normal file
File diff suppressed because it is too large
Load diff
1790
dnn/face_detector/deploy_lowres.prototxt
Normal file
1790
dnn/face_detector/deploy_lowres.prototxt
Normal file
File diff suppressed because it is too large
Load diff
74
dnn/face_detector/download_weights.py
Executable file
74
dnn/face_detector/download_weights.py
Executable file
|
@ -0,0 +1,74 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import hashlib
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
if sys.version_info[0] < 3:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
else:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
|
class HashMismatchException(Exception):
|
||||||
|
def __init__(self, expected, actual):
|
||||||
|
Exception.__init__(self)
|
||||||
|
self.expected = expected
|
||||||
|
self.actual = actual
|
||||||
|
def __str__(self):
|
||||||
|
return 'Hash mismatch: {} vs {}'.format(self.expected, self.actual)
|
||||||
|
|
||||||
|
class MetalinkDownloader(object):
|
||||||
|
BUFSIZE = 10*1024*1024
|
||||||
|
NS = {'ml': 'urn:ietf:params:xml:ns:metalink'}
|
||||||
|
tick = 0
|
||||||
|
|
||||||
|
def download(self, metalink_file):
|
||||||
|
status = True
|
||||||
|
for file_elem in ET.parse(metalink_file).getroot().findall('ml:file', self.NS):
|
||||||
|
url = file_elem.find('ml:url', self.NS).text
|
||||||
|
fname = file_elem.attrib['name']
|
||||||
|
hash_sum = file_elem.find('ml:hash', self.NS).text
|
||||||
|
print('*** {}'.format(fname))
|
||||||
|
try:
|
||||||
|
self.verify(hash_sum, fname)
|
||||||
|
except Exception as ex:
|
||||||
|
print(' {}'.format(ex))
|
||||||
|
try:
|
||||||
|
print(' {}'.format(url))
|
||||||
|
with open(fname, 'wb') as file_stream:
|
||||||
|
self.buffered_read(urlopen(url), file_stream.write)
|
||||||
|
self.verify(hash_sum, fname)
|
||||||
|
except Exception as ex:
|
||||||
|
print(' {}'.format(ex))
|
||||||
|
print(' FAILURE')
|
||||||
|
status = False
|
||||||
|
continue
|
||||||
|
print(' SUCCESS')
|
||||||
|
return status
|
||||||
|
|
||||||
|
def print_progress(self, msg, timeout = 0):
|
||||||
|
if time.time() - self.tick > timeout:
|
||||||
|
print(msg, end='')
|
||||||
|
sys.stdout.flush()
|
||||||
|
self.tick = time.time()
|
||||||
|
|
||||||
|
def buffered_read(self, in_stream, processing):
|
||||||
|
self.print_progress(' >')
|
||||||
|
while True:
|
||||||
|
buf = in_stream.read(self.BUFSIZE)
|
||||||
|
if not buf:
|
||||||
|
break
|
||||||
|
processing(buf)
|
||||||
|
self.print_progress('>', 5)
|
||||||
|
print(' done')
|
||||||
|
|
||||||
|
def verify(self, hash_sum, fname):
|
||||||
|
sha = hashlib.sha1()
|
||||||
|
with open(fname, 'rb') as file_stream:
|
||||||
|
self.buffered_read(file_stream, sha.update)
|
||||||
|
if hash_sum != sha.hexdigest():
|
||||||
|
raise HashMismatchException(hash_sum, sha.hexdigest())
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(0 if MetalinkDownloader().download('weights.meta4') else 1)
|
79
dnn/face_detector/how_to_train_face_detector.txt
Normal file
79
dnn/face_detector/how_to_train_face_detector.txt
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
This is a brief description of training process which has been used to get res10_300x300_ssd_iter_140000.caffemodel.
|
||||||
|
The model was created with SSD framework using ResNet-10 like architecture as a backbone. Channels count in ResNet-10 convolution layers was significantly dropped (2x- or 4x- fewer channels).
|
||||||
|
The model was trained in Caffe framework on some huge and available online dataset.
|
||||||
|
|
||||||
|
1. Prepare training tools
|
||||||
|
You need to use "ssd" branch from this repository https://github.com/weiliu89/caffe/tree/ssd . Checkout this branch and built it (see instructions in repo's README)
|
||||||
|
|
||||||
|
2. Prepare training data.
|
||||||
|
The data preparation pipeline can be represented as:
|
||||||
|
|
||||||
|
(a)Download original face detection dataset -> (b)Convert annotation to the PASCAL VOC format -> (c)Create LMDB database with images + annotations for training
|
||||||
|
|
||||||
|
a) Find some datasets with face bounding boxes annotation. For some reasons I can't provide links here, but you easily find them on your own. Also study the data. It may contain small or low quality faces which can spoil training process. Often there are special flags about object quality in annotation. Remove such faces from annotation (smaller when 16 along at least one side, or blurred, of highly-occluded, or something else).
|
||||||
|
|
||||||
|
b) The downloaded dataset will have some format of annotation. It may be one single file for all images, or separate file for each image or something else. But to train SSD in Caffe you need to convert annotation to PASCAL VOC format.
|
||||||
|
PASCAL VOC annotation consist of .xml file for each image. In this xml file all face bounding boxes should be listed as:
|
||||||
|
|
||||||
|
<annotation>
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>300</height>
|
||||||
|
</size>
|
||||||
|
<object>
|
||||||
|
<name>face</name>
|
||||||
|
<difficult>0</difficult>
|
||||||
|
<bndbox>
|
||||||
|
<xmin>100</xmin>
|
||||||
|
<ymin>100</ymin>
|
||||||
|
<xmax>200</xmax>
|
||||||
|
<ymax>200</ymax>
|
||||||
|
</bndbox>
|
||||||
|
</object>
|
||||||
|
<object>
|
||||||
|
<name>face</name>
|
||||||
|
<difficult>0</difficult>
|
||||||
|
<bndbox>
|
||||||
|
<xmin>0</xmin>
|
||||||
|
<ymin>0</ymin>
|
||||||
|
<xmax>100</xmax>
|
||||||
|
<ymax>100</ymax>
|
||||||
|
</bndbox>
|
||||||
|
</object>
|
||||||
|
</annotation>
|
||||||
|
|
||||||
|
So, convert your dataset's annotation to the format above.
|
||||||
|
Also, you should create labelmap.prototxt file with the following content:
|
||||||
|
item {
|
||||||
|
name: "none_of_the_above"
|
||||||
|
label: 0
|
||||||
|
display_name: "background"
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
name: "face"
|
||||||
|
label: 1
|
||||||
|
display_name: "face"
|
||||||
|
}
|
||||||
|
|
||||||
|
You need this file to establish correspondence between name of class and digital label of class.
|
||||||
|
|
||||||
|
For next step we also need file there all our image-annotation file names pairs are listed. This file should contain similar lines:
|
||||||
|
images_val/0.jpg annotations_val/0.jpg.xml
|
||||||
|
|
||||||
|
c) To create LMDB you need to use create_data.sh tool from caffe/data/VOC0712 Caffe's source code directory.
|
||||||
|
This script calls create_annoset.py inside, so check out what you need to pass as script's arguments
|
||||||
|
|
||||||
|
You need to prepare 2 LMDB databases: one for training images, one for validation images.
|
||||||
|
|
||||||
|
3. Train your detector
|
||||||
|
For training you need to have 3 files: train.prototxt, test.prototxt and solver.prototxt. You can find these files in the same directory as for this readme.
|
||||||
|
Also you need to edit train.prototxt and test.prototxt to replace paths for your LMDB databases to actual databases you've created in step 2.
|
||||||
|
|
||||||
|
Now all is done for launch training process.
|
||||||
|
Execute next lines in Terminal:
|
||||||
|
mkdir -p snapshot
|
||||||
|
mkdir -p log
|
||||||
|
/path_for_caffe_build_dir/tools/caffe train -solver="solver.prototxt" -gpu 0 2>&1 | tee -a log/log.log
|
||||||
|
|
||||||
|
And wait. It will take about 8 hours to finish the process.
|
||||||
|
After it you can use your .caffemodel from snapshot/ subdirectory in resnet_face_ssd_python.py sample.
|
2368
dnn/face_detector/opencv_face_detector.pbtxt
Normal file
2368
dnn/face_detector/opencv_face_detector.pbtxt
Normal file
File diff suppressed because it is too large
Load diff
28
dnn/face_detector/solver.prototxt
Normal file
28
dnn/face_detector/solver.prototxt
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
train_net: "train.prototxt"
|
||||||
|
test_net: "test.prototxt"
|
||||||
|
|
||||||
|
test_iter: 2312
|
||||||
|
test_interval: 5000
|
||||||
|
test_initialization: true
|
||||||
|
|
||||||
|
base_lr: 0.01
|
||||||
|
display: 10
|
||||||
|
lr_policy: "multistep"
|
||||||
|
max_iter: 140000
|
||||||
|
stepvalue: 80000
|
||||||
|
stepvalue: 120000
|
||||||
|
gamma: 0.1
|
||||||
|
momentum: 0.9
|
||||||
|
weight_decay: 0.0005
|
||||||
|
average_loss: 500
|
||||||
|
iter_size: 1
|
||||||
|
type: "SGD"
|
||||||
|
|
||||||
|
solver_mode: GPU
|
||||||
|
random_seed: 0
|
||||||
|
debug_info: false
|
||||||
|
snapshot: 1000
|
||||||
|
snapshot_prefix: "snapshot/res10_300x300_ssd"
|
||||||
|
|
||||||
|
eval_type: "detection"
|
||||||
|
ap_version: "11point"
|
1831
dnn/face_detector/test.prototxt
Normal file
1831
dnn/face_detector/test.prototxt
Normal file
File diff suppressed because it is too large
Load diff
1898
dnn/face_detector/train.prototxt
Normal file
1898
dnn/face_detector/train.prototxt
Normal file
File diff suppressed because it is too large
Load diff
13
dnn/face_detector/weights.meta4
Normal file
13
dnn/face_detector/weights.meta4
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<metalink xmlns="urn:ietf:params:xml:ns:metalink">
|
||||||
|
<file name="res10_300x300_ssd_iter_140000_fp16.caffemodel">
|
||||||
|
<identity>OpenCV face detector FP16 weights</identity>
|
||||||
|
<hash type="sha-1">31fc22bfdd907567a04bb45b7cfad29966caddc1</hash>
|
||||||
|
<url>https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180205_fp16/res10_300x300_ssd_iter_140000_fp16.caffemodel</url>
|
||||||
|
</file>
|
||||||
|
<file name="opencv_face_detector_uint8.pb">
|
||||||
|
<identity>OpenCV face detector UINT8 weights</identity>
|
||||||
|
<hash type="sha-1">4f2fdf6f231d759d7bbdb94353c5a68690f3d2ae</hash>
|
||||||
|
<url>https://raw.githubusercontent.com/opencv/opencv_3rdparty/dnn_samples_face_detector_20180220_uint8/opencv_face_detector_uint8.pb</url>
|
||||||
|
</file>
|
||||||
|
</metalink>
|
79
dnn_test.py
Normal file
79
dnn_test.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# import face_recognition
|
||||||
|
import cv2
|
||||||
|
from skimage.feature import hog
|
||||||
|
from skimage import data, exposure
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import dlib
|
||||||
|
import time
|
||||||
|
|
||||||
|
imagefile = "Marjo.jpg"
|
||||||
|
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
|
||||||
|
|
||||||
|
image = cv2.imread(imagefile)
|
||||||
|
# rows = open(args["labels"]).read().strip().split("\n")
|
||||||
|
# classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]
|
||||||
|
|
||||||
|
(h, w) = image.shape[:2]
|
||||||
|
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,
|
||||||
|
(300, 300), (104.0, 177.0, 123.0))
|
||||||
|
|
||||||
|
print("[INFO] loding model...")
|
||||||
|
net = cv2.dnn.readNetFromCaffe(prototxt, model)
|
||||||
|
print("Loaded")
|
||||||
|
net.setInput(blob)
|
||||||
|
start = time.time()
|
||||||
|
detections = net.forward()
|
||||||
|
end = time.time()
|
||||||
|
print("[INFO] classification took {:.5} seconds".format(end-start))
|
||||||
|
idxs = np.argsort(detections[0])[::-1][:5]
|
||||||
|
|
||||||
|
for i in range(0, detections.shape[2]):
|
||||||
|
# extract the confidence (i.e., probability) associated with the
|
||||||
|
# prediction
|
||||||
|
confidence = detections[0, 0, i, 2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# compute the (x, y)-coordinates of the bounding box for the
|
||||||
|
# object
|
||||||
|
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
||||||
|
(startX, startY, endX, endY) = box.astype("int")
|
||||||
|
|
||||||
|
# we always draw
|
||||||
|
|
||||||
|
# First we crop the sub-rect from the image
|
||||||
|
sub_img = image[startY:endY, startX:endX]
|
||||||
|
rect_img = sub_img.copy()
|
||||||
|
width = 2
|
||||||
|
cv2.rectangle(rect_img, (0, 0), (sub_img.shape[1]-int(width/2), sub_img.shape[0]-int(width/2)),
|
||||||
|
(0, 0, 255), width)
|
||||||
|
# white_rect = np.ones(sub_img.shape, dtype=np.uint8) * 255
|
||||||
|
|
||||||
|
# At least 10% opacity
|
||||||
|
alpha = max(.1, confidence)
|
||||||
|
|
||||||
|
res = cv2.addWeighted(sub_img, 1-alpha, rect_img, alpha, 1.0)
|
||||||
|
|
||||||
|
# Putting the image back to its position
|
||||||
|
image[startY:endY, startX:endX] = res
|
||||||
|
|
||||||
|
# filter out weak detections by ensuring the `confidence` is
|
||||||
|
# greater than the minimum confidence
|
||||||
|
if confidence > confidence_threshold:
|
||||||
|
|
||||||
|
# draw the bounding box of the face along with the associated
|
||||||
|
# probability
|
||||||
|
text = "{:.2f}%".format(confidence * 100)
|
||||||
|
y = startY - 10 if startY - 10 > 10 else startY + 10
|
||||||
|
# cv2.rectangle(image, (startX, startY), (endX, endY),
|
||||||
|
# (0, 0, 255), 2)
|
||||||
|
cv2.putText(image, text, (startX, y),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
|
||||||
|
|
||||||
|
# show the output image
|
||||||
|
cv2.imshow("Output", image)
|
||||||
|
cv2.waitKey(0)
|
0
face_recognition/__init__.py
Normal file
0
face_recognition/__init__.py
Normal file
BIN
face_recognition/__pycache__/__init__.cpython-38.pyc
Normal file
BIN
face_recognition/__pycache__/__init__.cpython-38.pyc
Normal file
Binary file not shown.
BIN
face_recognition/__pycache__/comparison.cpython-38.pyc
Normal file
BIN
face_recognition/__pycache__/comparison.cpython-38.pyc
Normal file
Binary file not shown.
BIN
face_recognition/__pycache__/hog.cpython-38.pyc
Normal file
BIN
face_recognition/__pycache__/hog.cpython-38.pyc
Normal file
Binary file not shown.
447
face_recognition/comparison.py
Normal file
447
face_recognition/comparison.py
Normal file
|
@ -0,0 +1,447 @@
|
||||||
|
from multiprocessing import Process, Queue
|
||||||
|
from queue import Empty, Full
|
||||||
|
import cv2
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
import time
|
||||||
|
|
||||||
|
draw_colors = {
|
||||||
|
'dnn': (255,0,0),
|
||||||
|
'haar': (0,255,0),
|
||||||
|
'hog': (0,0,255),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ft = cv2.freetype.createFreeType2()
|
||||||
|
ft.loadFontData(fontFileName='Ubuntu-R.ttf',
|
||||||
|
id=0)
|
||||||
|
|
||||||
|
class Result():
|
||||||
|
def __init__(self, algorithm, image, confidence_threshold = 0.5):
|
||||||
|
self.algorithm = algorithm
|
||||||
|
self.visualisation = image
|
||||||
|
self.detections = []
|
||||||
|
self.confidence_threshold = confidence_threshold
|
||||||
|
|
||||||
|
def add_detection(self, startX, startY, endX, endY, confidence):
|
||||||
|
self.detections.append({
|
||||||
|
'startX': startX,
|
||||||
|
'startY': startY,
|
||||||
|
'endX': endX,
|
||||||
|
'endY': endY,
|
||||||
|
'confidence': confidence
|
||||||
|
})
|
||||||
|
return self
|
||||||
|
|
||||||
|
def draw_detections(self):
|
||||||
|
color = draw_colors[self.algorithm]
|
||||||
|
for detection in self.detections:
|
||||||
|
self.draw_detection(detection, color)
|
||||||
|
|
||||||
|
def draw_detection(self, detection, color=(0,0,255)):
|
||||||
|
|
||||||
|
# First we crop the sub-rect from the image
|
||||||
|
sub_img = self.visualisation[detection['startY']:detection['endY'], detection['startX']:detection['endX']]
|
||||||
|
rect_img = sub_img.copy()
|
||||||
|
width = 2
|
||||||
|
cv2.rectangle(rect_img, (0, 0),
|
||||||
|
(sub_img.shape[1]-int(width/2), sub_img.shape[0]-int(width/2)),
|
||||||
|
color, width)
|
||||||
|
# white_rect = np.ones(sub_img.shape, dtype=np.uint8) * 255
|
||||||
|
|
||||||
|
|
||||||
|
# filter out weak detections by ensuring the `confidence` is
|
||||||
|
# greater than the minimum confidence
|
||||||
|
if detection['confidence'] > self.confidence_threshold:
|
||||||
|
# draw the bounding box of the face along with the associated
|
||||||
|
# probability
|
||||||
|
text = "{:.2f}%".format(detection['confidence'] * 100)
|
||||||
|
y = detection['startY'] - 10 if detection['startY'] - 10 > 10 else detection['startY'] + 10
|
||||||
|
# cv2.rectangle(image, (startX, startY), (endX, endY),
|
||||||
|
# color, 2)
|
||||||
|
cv2.putText(self.visualisation, text, (detection['startX'], y),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2, lineType = cv2.LINE_AA)
|
||||||
|
|
||||||
|
alpha = 1
|
||||||
|
else:
|
||||||
|
# At least 10% opacity
|
||||||
|
alpha = max(.3, detection['confidence'])
|
||||||
|
|
||||||
|
res = cv2.addWeighted(sub_img, 1-alpha, rect_img, alpha, 1.0)
|
||||||
|
|
||||||
|
# Putting the image back to its position
|
||||||
|
self.visualisation[detection['startY']:detection['endY'], detection['startX']:detection['endX']] = res
|
||||||
|
|
||||||
|
def resize(self, width, height):
|
||||||
|
# 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)
|
||||||
|
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']
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def record(device_id, q1,q2, q3, q4):
|
||||||
|
capture = cv2.VideoCapture(device_id)
|
||||||
|
while True:
|
||||||
|
ret, image = capture.read()
|
||||||
|
logging.debug('r')
|
||||||
|
try:
|
||||||
|
q1.put_nowait(image)
|
||||||
|
except Full as e:
|
||||||
|
# ignore if processing doesn't keep up
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
q2.put_nowait(image)
|
||||||
|
except Full as e:
|
||||||
|
# ignore if processing doesn't keep up
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
q3.put_nowait(image)
|
||||||
|
except Full as e:
|
||||||
|
# ignore if processing doesn't keep up
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
q4.put_nowait(image)
|
||||||
|
except Full as e:
|
||||||
|
# ignore if processing doesn't keep up
|
||||||
|
pass
|
||||||
|
|
||||||
|
def draw_detection(image, startX, startY, endX, endY, confidence, color=(0,0,255), confidence_threshold = .5):
|
||||||
|
|
||||||
|
# First we crop the sub-rect from the image
|
||||||
|
sub_img = image[startY:endY, startX:endX]
|
||||||
|
rect_img = sub_img.copy()
|
||||||
|
width = 2
|
||||||
|
cv2.rectangle(rect_img, (0, 0),
|
||||||
|
(sub_img.shape[1]-int(width/2), sub_img.shape[0]-int(width/2)),
|
||||||
|
color, width)
|
||||||
|
# white_rect = np.ones(sub_img.shape, dtype=np.uint8) * 255
|
||||||
|
|
||||||
|
|
||||||
|
# filter out weak detections by ensuring the `confidence` is
|
||||||
|
# greater than the minimum confidence
|
||||||
|
if confidence > confidence_threshold:
|
||||||
|
# draw the bounding box of the face along with the associated
|
||||||
|
# probability
|
||||||
|
text = "{:.2f}%".format(confidence * 100)
|
||||||
|
y = startY - 10 if startY - 10 > 10 else startY + 10
|
||||||
|
# cv2.rectangle(image, (startX, startY), (endX, endY),
|
||||||
|
# color, 2)
|
||||||
|
cv2.putText(image, text, (startX, y),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 0.45, color, 2)
|
||||||
|
|
||||||
|
alpha = 1
|
||||||
|
else:
|
||||||
|
# At least 10% opacity
|
||||||
|
alpha = max(.3, confidence)
|
||||||
|
|
||||||
|
res = cv2.addWeighted(sub_img, 1-alpha, rect_img, alpha, 1.0)
|
||||||
|
|
||||||
|
# Putting the image back to its position
|
||||||
|
image[startY:endY, startX:endX] = res
|
||||||
|
|
||||||
|
|
||||||
|
def process1_hog(in_q, out_q):
|
||||||
|
from skimage.feature import hog as hog_orig
|
||||||
|
from .hog import hog # use modified version for viz
|
||||||
|
from skimage import data, exposure
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import dlib
|
||||||
|
|
||||||
|
face_detector = dlib.get_frontal_face_detector()
|
||||||
|
|
||||||
|
visualisation_factor = 1
|
||||||
|
detection_factor = .4
|
||||||
|
|
||||||
|
|
||||||
|
process_this_frame = True
|
||||||
|
while True:
|
||||||
|
if process_this_frame:
|
||||||
|
# Grab a single frame of video
|
||||||
|
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)
|
||||||
|
det_frame = cv2.resize(frame, (0, 0), fx=detection_factor, fy=detection_factor)
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
fd, hog_image = hog(det_frame, orientations=6, pixels_per_cell=(8, 8),
|
||||||
|
cells_per_block=(1, 1), visualize=True, multichannel=False, visualize_factor=visualisation_factor/detection_factor)
|
||||||
|
logging.debug(f"Duration of hog viz: {time.time() - start}")
|
||||||
|
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))
|
||||||
|
# hog_image_rescaled = viz_frame
|
||||||
|
|
||||||
|
# Resize frame of video to 1/4 size for faster face recognition processing
|
||||||
|
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
|
||||||
|
# rgb_small_frame = det_frame[:, :, ::-1]
|
||||||
|
# dets, scores, idxs = face_detector.run(rgb_small_frame, 1, -2)
|
||||||
|
dets, scores, idxs = face_detector.run(det_frame, 1, -2)
|
||||||
|
# print(dets, scores, idxs)
|
||||||
|
|
||||||
|
hog_image_rescaled = (hog_image_rescaled.astype('float32') * 255).astype('uint8')
|
||||||
|
hog_image_rescaled = cv2.cvtColor(hog_image_rescaled, cv2.COLOR_GRAY2BGR)
|
||||||
|
|
||||||
|
result = Result('hog', hog_image_rescaled, 0)
|
||||||
|
|
||||||
|
# Display the results
|
||||||
|
for i, rectangle in enumerate(dets):
|
||||||
|
probability = scores[i]
|
||||||
|
# print(rectangle)
|
||||||
|
|
||||||
|
|
||||||
|
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
|
||||||
|
top = int(rectangle.top() * (visualisation_factor / detection_factor))
|
||||||
|
right = int(rectangle.right() * (visualisation_factor / detection_factor))
|
||||||
|
bottom = int(rectangle.bottom() * (visualisation_factor / detection_factor))
|
||||||
|
left = int(rectangle.left() * (visualisation_factor / detection_factor))
|
||||||
|
|
||||||
|
result.add_detection(left, top, right, bottom,probability)
|
||||||
|
|
||||||
|
# draw_detection(hog_image_rescaled, left, top, right, bottom, probability, draw_colors['hog'], 0)
|
||||||
|
|
||||||
|
# brightness = int(min(255, (probability + 1)*255))
|
||||||
|
|
||||||
|
# # Draw a box around the face
|
||||||
|
# cv2.rectangle(hog_image_rescaled, (left, top), (right, bottom), (0,0,brightness), 2)
|
||||||
|
|
||||||
|
# # Draw a label with a name below the face
|
||||||
|
# cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Display the resulting image
|
||||||
|
out_q.put(result)
|
||||||
|
# print(cgray.shape)
|
||||||
|
|
||||||
|
process_this_frame = not process_this_frame
|
||||||
|
|
||||||
|
|
||||||
|
def process2_dnn(in_q, out_q):
|
||||||
|
logger = logging.getLogger('dnn')
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
logger.info("[INFO] loding model...")
|
||||||
|
net = cv2.dnn.readNetFromCaffe(prototxt, model)
|
||||||
|
logger.info("Loaded")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
image = in_q.get()
|
||||||
|
(h, w) = image.shape[:2]
|
||||||
|
|
||||||
|
image_small = cv2.resize(image, (300, 300))
|
||||||
|
(hs, ws) = image_small.shape[:2]
|
||||||
|
blob = cv2.dnn.blobFromImage(image_small, 1.0,
|
||||||
|
(300, 300), (104.0, 177.0, 123.0))
|
||||||
|
image = cv2.cvtColor(cv2.cvtColor(image_small, cv2.COLOR_BGR2GRAY), cv2.COLOR_GRAY2BGR)
|
||||||
|
|
||||||
|
net.setInput(blob)
|
||||||
|
detections = net.forward()
|
||||||
|
# idxs = np.argsort(detections[0])[::-1][:5]
|
||||||
|
|
||||||
|
result = Result('dnn', image)
|
||||||
|
|
||||||
|
for i in range(0, detections.shape[2]):
|
||||||
|
# extract the confidence (i.e., probability) associated with the
|
||||||
|
# prediction
|
||||||
|
confidence = detections[0, 0, i, 2]
|
||||||
|
|
||||||
|
# compute the (x, y)-coordinates of the bounding box for the
|
||||||
|
# object
|
||||||
|
# box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
||||||
|
box = detections[0, 0, i, 3:7] * np.array([ws, hs, ws, hs])
|
||||||
|
(startX, startY, endX, endY) = box.astype("int")
|
||||||
|
|
||||||
|
result.add_detection(startX, startY, endX, endY, confidence)
|
||||||
|
|
||||||
|
# draw_detection(image, startX, startY, endX, endY, confidence, draw_colors['dnn'])
|
||||||
|
|
||||||
|
out_q.put(result)
|
||||||
|
|
||||||
|
def process3_haar(in_q, out_q):
|
||||||
|
from cffi import FFI
|
||||||
|
from PIL import Image
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
logger = logging.getLogger('haar')
|
||||||
|
|
||||||
|
ffi = FFI()
|
||||||
|
ffi.cdef("""
|
||||||
|
int test(int);
|
||||||
|
|
||||||
|
typedef void* haarclassifier;
|
||||||
|
haarclassifier classifier_new();
|
||||||
|
void scan_image(haarclassifier, size_t width,size_t height, char *input, char *buffer, size_t length, bool debug);
|
||||||
|
""")
|
||||||
|
|
||||||
|
C = ffi.dlopen("/home/ruben/Documents/Projecten/2020/rust/testproject/target/debug/libvisual_haarcascades_lib.so")
|
||||||
|
|
||||||
|
# print(C.test(9))
|
||||||
|
# i = Image.open("Marjo.jpg")
|
||||||
|
# width = i.size[0]
|
||||||
|
# height = i.size[0]
|
||||||
|
|
||||||
|
# use the rust lib to draw the visualisation
|
||||||
|
haar = C.classifier_new()
|
||||||
|
logger.info("Initialised haar classifier")
|
||||||
|
|
||||||
|
# opencv for the actual detections
|
||||||
|
faceCascade = cv2.CascadeClassifier('./haarcascade_frontalface_alt2.xml')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
frame = in_q.get()
|
||||||
|
(height_orig, width_orig) = frame.shape[:2]
|
||||||
|
|
||||||
|
scale_factor = 3
|
||||||
|
width = int(width_orig/scale_factor)
|
||||||
|
height = int(height_orig/scale_factor)
|
||||||
|
|
||||||
|
frame = cv2.resize(frame, (width, height))
|
||||||
|
|
||||||
|
# Run the B&W version through opencv haar to detect faces
|
||||||
|
# for some reason the variable 'frame' is modified after
|
||||||
|
# running the visualisation, so we do this before
|
||||||
|
f = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
||||||
|
faces = faceCascade.detectMultiScale(f)
|
||||||
|
|
||||||
|
|
||||||
|
pixel_format = "RGB" #The raytracer only supports one format
|
||||||
|
bytes_per_pixel = 3
|
||||||
|
buffer_len = width * height * bytes_per_pixel
|
||||||
|
buffer = ffi.new("char[]", buffer_len)
|
||||||
|
buffer2 = ffi.from_buffer("char[]", frame.tobytes())
|
||||||
|
|
||||||
|
# i = Image.open("/home/ruben/Documents/Projecten/(2020/rust/lena_orig.png")
|
||||||
|
# data = i.tobytes("raw", "RGB")
|
||||||
|
|
||||||
|
logger.info("Start haar scan")
|
||||||
|
|
||||||
|
start = time.time()
|
||||||
|
C.scan_image(haar, width, height, buffer2, buffer, buffer_len, False)
|
||||||
|
logger.info(f"Visualised scan into buffer: {buffer}")
|
||||||
|
print(f"duration: {time.time() - start}s")
|
||||||
|
|
||||||
|
img = Image.frombuffer(pixel_format, (width, height), ffi.buffer(buffer),
|
||||||
|
"raw", pixel_format, 0, 1)
|
||||||
|
img= np.array(img)
|
||||||
|
# a= np.frombuffer(ffi.buffer(buffer))
|
||||||
|
# a.reshape((height, width, bytes_per_pixel))
|
||||||
|
|
||||||
|
# flip RGB back to BGR
|
||||||
|
# img = img[:, :, ::-1]
|
||||||
|
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
||||||
|
img = cv2.resize(img, (width_orig, height_orig))
|
||||||
|
|
||||||
|
result = Result('haar', img)
|
||||||
|
|
||||||
|
for face in faces:
|
||||||
|
x1, y1, w, h = face
|
||||||
|
x2 = x1 + w
|
||||||
|
y2 = y1 + h
|
||||||
|
# print(img.shape)
|
||||||
|
# TODO: is scale factor ok here?
|
||||||
|
# draw_detection(img, x1 * scale_factor, y1 * scale_factor, x2 * scale_factor, y2 * scale_factor, 1, draw_colors['haar'],)
|
||||||
|
result.add_detection(x1 * scale_factor, y1 * scale_factor, x2 * scale_factor, y2 * scale_factor, 1)
|
||||||
|
|
||||||
|
|
||||||
|
# print(img)
|
||||||
|
out_q.put(result)
|
||||||
|
|
||||||
|
def display(image_res, q1, q2, q3, q4):
|
||||||
|
prev_image1 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image2 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image3 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image4 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
logging.debug('r')
|
||||||
|
try:
|
||||||
|
image1 = q1.get_nowait()
|
||||||
|
image1 = cv2.resize(image1, (image_res[0], image_res[1]))
|
||||||
|
prev_image1 = image1
|
||||||
|
except Empty as e:
|
||||||
|
image1 = prev_image1
|
||||||
|
try:
|
||||||
|
result2 = q2.get_nowait()
|
||||||
|
result2 = result2.resize(image_res[0], image_res[1])
|
||||||
|
result2.draw_detections()
|
||||||
|
image2 = result2.visualisation
|
||||||
|
# image2 = cv2.resize(image2, (image_res[0], image_res[1]))
|
||||||
|
prev_image2 = image2
|
||||||
|
except Empty as e:
|
||||||
|
image2 = prev_image2
|
||||||
|
try:
|
||||||
|
result3 = q3.get_nowait()
|
||||||
|
result3 = result3.resize(image_res[0], image_res[1])
|
||||||
|
result3.draw_detections()
|
||||||
|
image3 = result3.visualisation
|
||||||
|
# image3 = cv2.resize(image3, (image_res[0], image_res[1]))
|
||||||
|
prev_image3 = image3
|
||||||
|
except Empty as e:
|
||||||
|
image3 = prev_image3
|
||||||
|
try:
|
||||||
|
result4 = q4.get_nowait()
|
||||||
|
result4 = result4.resize(image_res[0], image_res[1])
|
||||||
|
result4.draw_detections()
|
||||||
|
image4 = result4.visualisation
|
||||||
|
# image4 = cv2.resize(image4, (image_res[0], image_res[1]))
|
||||||
|
prev_image4 = image4
|
||||||
|
except Empty as e:
|
||||||
|
image4 = prev_image4
|
||||||
|
|
||||||
|
img_concate_Verti1 = np.concatenate((image1,image2),axis=0)
|
||||||
|
img_concate_Verti2 = np.concatenate((image3,image4),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!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
def main(camera_id):
|
||||||
|
image_size = (int(1920/2), int(1080/2))
|
||||||
|
# TODO should we use queues here at all?
|
||||||
|
# https://docs.python.org/3/library/multiprocessing.html#programming-guidelines
|
||||||
|
# TODO: queue maxsize, or prefrabily some sort of throttled queue (like zmq hight water mark)
|
||||||
|
q_webcam1 = Queue(maxsize=1)
|
||||||
|
q_webcam2 = Queue(maxsize=1)
|
||||||
|
q_webcam3 = Queue(maxsize=1)
|
||||||
|
q_webcam4 = Queue(maxsize=1)
|
||||||
|
q_process1 = Queue(maxsize=1)
|
||||||
|
q_process2 = Queue(maxsize=1)
|
||||||
|
q_process3 = Queue(maxsize=1)
|
||||||
|
|
||||||
|
p1 = Process(target=record, args=(camera_id, q_webcam1, q_webcam2,q_webcam3,q_webcam4))
|
||||||
|
p2 = Process(target=display, args=(image_size, q_webcam1, q_process1, q_process2, q_process3 ))
|
||||||
|
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,))
|
||||||
|
|
||||||
|
p1.start()
|
||||||
|
p2.start()
|
||||||
|
p3.start()
|
||||||
|
p4.start()
|
||||||
|
p5.start()
|
||||||
|
|
||||||
|
p2.join() # process with the display interface
|
||||||
|
|
||||||
|
p1.kill()
|
||||||
|
p3.kill()
|
||||||
|
p4.kill()
|
||||||
|
p5.kill()
|
256
face_recognition/hog.py
Normal file
256
face_recognition/hog.py
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
import numpy as np
|
||||||
|
from skimage.feature import _hoghistogram
|
||||||
|
from skimage.feature._hog import _hog_normalize_block, _hog_channel_gradient
|
||||||
|
|
||||||
|
def hog(image, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3),
|
||||||
|
block_norm='L2-Hys', visualize=False, transform_sqrt=False,
|
||||||
|
feature_vector=True, multichannel=None, visualize_factor=1.):
|
||||||
|
"""Extract Histogram of Oriented Gradients (HOG) for a given image.
|
||||||
|
|
||||||
|
Compute a Histogram of Oriented Gradients (HOG) by
|
||||||
|
|
||||||
|
1. (optional) global image normalization
|
||||||
|
2. computing the gradient image in `row` and `col`
|
||||||
|
3. computing gradient histograms
|
||||||
|
4. normalizing across blocks
|
||||||
|
5. flattening into a feature vector
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
image : (M, N[, C]) ndarray
|
||||||
|
Input image.
|
||||||
|
orientations : int, optional
|
||||||
|
Number of orientation bins.
|
||||||
|
pixels_per_cell : 2-tuple (int, int), optional
|
||||||
|
Size (in pixels) of a cell.
|
||||||
|
cells_per_block : 2-tuple (int, int), optional
|
||||||
|
Number of cells in each block.
|
||||||
|
block_norm : str {'L1', 'L1-sqrt', 'L2', 'L2-Hys'}, optional
|
||||||
|
Block normalization method:
|
||||||
|
|
||||||
|
``L1``
|
||||||
|
Normalization using L1-norm.
|
||||||
|
``L1-sqrt``
|
||||||
|
Normalization using L1-norm, followed by square root.
|
||||||
|
``L2``
|
||||||
|
Normalization using L2-norm.
|
||||||
|
``L2-Hys``
|
||||||
|
Normalization using L2-norm, followed by limiting the
|
||||||
|
maximum values to 0.2 (`Hys` stands for `hysteresis`) and
|
||||||
|
renormalization using L2-norm. (default)
|
||||||
|
For details, see [3]_, [4]_.
|
||||||
|
|
||||||
|
visualize : bool, optional
|
||||||
|
Also return an image of the HOG. For each cell and orientation bin,
|
||||||
|
the image contains a line segment that is centered at the cell center,
|
||||||
|
is perpendicular to the midpoint of the range of angles spanned by the
|
||||||
|
orientation bin, and has intensity proportional to the corresponding
|
||||||
|
histogram value.
|
||||||
|
transform_sqrt : bool, optional
|
||||||
|
Apply power law compression to normalize the image before
|
||||||
|
processing. DO NOT use this if the image contains negative
|
||||||
|
values. Also see `notes` section below.
|
||||||
|
feature_vector : bool, optional
|
||||||
|
Return the data as a feature vector by calling .ravel() on the result
|
||||||
|
just before returning.
|
||||||
|
multichannel : boolean, optional
|
||||||
|
If True, the last `image` dimension is considered as a color channel,
|
||||||
|
otherwise as spatial.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
out : (n_blocks_row, n_blocks_col, n_cells_row, n_cells_col, n_orient) ndarray
|
||||||
|
HOG descriptor for the image. If `feature_vector` is True, a 1D
|
||||||
|
(flattened) array is returned.
|
||||||
|
hog_image : (M, N) ndarray, optional
|
||||||
|
A visualisation of the HOG image. Only provided if `visualize` is True.
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
.. [1] https://en.wikipedia.org/wiki/Histogram_of_oriented_gradients
|
||||||
|
|
||||||
|
.. [2] Dalal, N and Triggs, B, Histograms of Oriented Gradients for
|
||||||
|
Human Detection, IEEE Computer Society Conference on Computer
|
||||||
|
Vision and Pattern Recognition 2005 San Diego, CA, USA,
|
||||||
|
https://lear.inrialpes.fr/people/triggs/pubs/Dalal-cvpr05.pdf,
|
||||||
|
:DOI:`10.1109/CVPR.2005.177`
|
||||||
|
|
||||||
|
.. [3] Lowe, D.G., Distinctive image features from scale-invatiant
|
||||||
|
keypoints, International Journal of Computer Vision (2004) 60: 91,
|
||||||
|
http://www.cs.ubc.ca/~lowe/papers/ijcv04.pdf,
|
||||||
|
:DOI:`10.1023/B:VISI.0000029664.99615.94`
|
||||||
|
|
||||||
|
.. [4] Dalal, N, Finding People in Images and Videos,
|
||||||
|
Human-Computer Interaction [cs.HC], Institut National Polytechnique
|
||||||
|
de Grenoble - INPG, 2006,
|
||||||
|
https://tel.archives-ouvertes.fr/tel-00390303/file/NavneetDalalThesis.pdf
|
||||||
|
|
||||||
|
Notes
|
||||||
|
-----
|
||||||
|
The presented code implements the HOG extraction method from [2]_ with
|
||||||
|
the following changes: (I) blocks of (3, 3) cells are used ((2, 2) in the
|
||||||
|
paper); (II) no smoothing within cells (Gaussian spatial window with sigma=8pix
|
||||||
|
in the paper); (III) L1 block normalization is used (L2-Hys in the paper).
|
||||||
|
|
||||||
|
Power law compression, also known as Gamma correction, is used to reduce
|
||||||
|
the effects of shadowing and illumination variations. The compression makes
|
||||||
|
the dark regions lighter. When the kwarg `transform_sqrt` is set to
|
||||||
|
``True``, the function computes the square root of each color channel
|
||||||
|
and then applies the hog algorithm to the image.
|
||||||
|
"""
|
||||||
|
image = np.atleast_2d(image)
|
||||||
|
|
||||||
|
if multichannel is None:
|
||||||
|
multichannel = (image.ndim == 3)
|
||||||
|
|
||||||
|
ndim_spatial = image.ndim - 1 if multichannel else image.ndim
|
||||||
|
if ndim_spatial != 2:
|
||||||
|
raise ValueError('Only images with 2 spatial dimensions are '
|
||||||
|
'supported. If using with color/multichannel '
|
||||||
|
'images, specify `multichannel=True`.')
|
||||||
|
|
||||||
|
"""
|
||||||
|
The first stage applies an optional global image normalization
|
||||||
|
equalisation that is designed to reduce the influence of illumination
|
||||||
|
effects. In practice we use gamma (power law) compression, either
|
||||||
|
computing the square root or the log of each color channel.
|
||||||
|
Image texture strength is typically proportional to the local surface
|
||||||
|
illumination so this compression helps to reduce the effects of local
|
||||||
|
shadowing and illumination variations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if transform_sqrt:
|
||||||
|
image = np.sqrt(image)
|
||||||
|
|
||||||
|
"""
|
||||||
|
The second stage computes first order image gradients. These capture
|
||||||
|
contour, silhouette and some texture information, while providing
|
||||||
|
further resistance to illumination variations. The locally dominant
|
||||||
|
color channel is used, which provides color invariance to a large
|
||||||
|
extent. Variant methods may also include second order image derivatives,
|
||||||
|
which act as primitive bar detectors - a useful feature for capturing,
|
||||||
|
e.g. bar like structures in bicycles and limbs in humans.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if image.dtype.kind == 'u':
|
||||||
|
# convert uint image to float
|
||||||
|
# to avoid problems with subtracting unsigned numbers
|
||||||
|
image = image.astype('float')
|
||||||
|
|
||||||
|
if multichannel:
|
||||||
|
g_row_by_ch = np.empty_like(image, dtype=np.double)
|
||||||
|
g_col_by_ch = np.empty_like(image, dtype=np.double)
|
||||||
|
g_magn = np.empty_like(image, dtype=np.double)
|
||||||
|
|
||||||
|
for idx_ch in range(image.shape[2]):
|
||||||
|
g_row_by_ch[:, :, idx_ch], g_col_by_ch[:, :, idx_ch] = \
|
||||||
|
_hog_channel_gradient(image[:, :, idx_ch])
|
||||||
|
g_magn[:, :, idx_ch] = np.hypot(g_row_by_ch[:, :, idx_ch],
|
||||||
|
g_col_by_ch[:, :, idx_ch])
|
||||||
|
|
||||||
|
# For each pixel select the channel with the highest gradient magnitude
|
||||||
|
idcs_max = g_magn.argmax(axis=2)
|
||||||
|
rr, cc = np.meshgrid(np.arange(image.shape[0]),
|
||||||
|
np.arange(image.shape[1]),
|
||||||
|
indexing='ij',
|
||||||
|
sparse=True)
|
||||||
|
g_row = g_row_by_ch[rr, cc, idcs_max]
|
||||||
|
g_col = g_col_by_ch[rr, cc, idcs_max]
|
||||||
|
else:
|
||||||
|
g_row, g_col = _hog_channel_gradient(image)
|
||||||
|
|
||||||
|
"""
|
||||||
|
The third stage aims to produce an encoding that is sensitive to
|
||||||
|
local image content while remaining resistant to small changes in
|
||||||
|
pose or appearance. The adopted method pools gradient orientation
|
||||||
|
information locally in the same way as the SIFT [Lowe 2004]
|
||||||
|
feature. The image window is divided into small spatial regions,
|
||||||
|
called "cells". For each cell we accumulate a local 1-D histogram
|
||||||
|
of gradient or edge orientations over all the pixels in the
|
||||||
|
cell. This combined cell-level 1-D histogram forms the basic
|
||||||
|
"orientation histogram" representation. Each orientation histogram
|
||||||
|
divides the gradient angle range into a fixed number of
|
||||||
|
predetermined bins. The gradient magnitudes of the pixels in the
|
||||||
|
cell are used to vote into the orientation histogram.
|
||||||
|
"""
|
||||||
|
|
||||||
|
s_row, s_col = image.shape[:2]
|
||||||
|
c_row, c_col = pixels_per_cell
|
||||||
|
b_row, b_col = cells_per_block
|
||||||
|
|
||||||
|
n_cells_row = int(s_row // c_row) # number of cells along row-axis
|
||||||
|
n_cells_col = int(s_col // c_col) # number of cells along col-axis
|
||||||
|
|
||||||
|
# compute orientations integral images
|
||||||
|
orientation_histogram = np.zeros((n_cells_row, n_cells_col, orientations))
|
||||||
|
|
||||||
|
_hoghistogram.hog_histograms(g_col, g_row, c_col, c_row, s_col, s_row,
|
||||||
|
n_cells_col, n_cells_row,
|
||||||
|
orientations, orientation_histogram)
|
||||||
|
|
||||||
|
# now compute the histogram for each cell
|
||||||
|
hog_image = None
|
||||||
|
|
||||||
|
if visualize:
|
||||||
|
from skimage import draw
|
||||||
|
|
||||||
|
radius = min(c_row * visualize_factor, c_col * visualize_factor) // 2 - 1
|
||||||
|
orientations_arr = np.arange(orientations)
|
||||||
|
# set dr_arr, dc_arr to correspond to midpoints of orientation bins
|
||||||
|
orientation_bin_midpoints = (
|
||||||
|
np.pi * (orientations_arr + .5) / orientations)
|
||||||
|
dr_arr = radius * np.sin(orientation_bin_midpoints)
|
||||||
|
dc_arr = radius * np.cos(orientation_bin_midpoints)
|
||||||
|
hog_image = np.zeros((
|
||||||
|
int(s_row * visualize_factor), int(s_col * visualize_factor)
|
||||||
|
), dtype=float)
|
||||||
|
for r in range(n_cells_row):
|
||||||
|
for c in range(n_cells_col):
|
||||||
|
for o, dr, dc in zip(orientations_arr, dr_arr, dc_arr):
|
||||||
|
centre = tuple([r * c_row * visualize_factor + c_row * visualize_factor // 2,
|
||||||
|
c * c_col * visualize_factor + c_col * visualize_factor // 2])
|
||||||
|
rr, cc = draw.line(int(centre[0] - dc),
|
||||||
|
int(centre[1] + dr),
|
||||||
|
int(centre[0] + dc),
|
||||||
|
int(centre[1] - dr))
|
||||||
|
hog_image[rr, cc] += orientation_histogram[r, c, o]
|
||||||
|
|
||||||
|
"""
|
||||||
|
The fourth stage computes normalization, which takes local groups of
|
||||||
|
cells and contrast normalizes their overall responses before passing
|
||||||
|
to next stage. Normalization introduces better invariance to illumination,
|
||||||
|
shadowing, and edge contrast. It is performed by accumulating a measure
|
||||||
|
of local histogram "energy" over local groups of cells that we call
|
||||||
|
"blocks". The result is used to normalize each cell in the block.
|
||||||
|
Typically each individual cell is shared between several blocks, but
|
||||||
|
its normalizations are block dependent and thus different. The cell
|
||||||
|
thus appears several times in the final output vector with different
|
||||||
|
normalizations. This may seem redundant but it improves the performance.
|
||||||
|
We refer to the normalized block descriptors as Histogram of Oriented
|
||||||
|
Gradient (HOG) descriptors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
n_blocks_row = (n_cells_row - b_row) + 1
|
||||||
|
n_blocks_col = (n_cells_col - b_col) + 1
|
||||||
|
normalized_blocks = np.zeros((n_blocks_row, n_blocks_col,
|
||||||
|
b_row, b_col, orientations))
|
||||||
|
|
||||||
|
for r in range(n_blocks_row):
|
||||||
|
for c in range(n_blocks_col):
|
||||||
|
block = orientation_histogram[r:r + b_row, c:c + b_col, :]
|
||||||
|
normalized_blocks[r, c, :] = \
|
||||||
|
_hog_normalize_block(block, method=block_norm)
|
||||||
|
|
||||||
|
"""
|
||||||
|
The final step collects the HOG descriptors from all blocks of a dense
|
||||||
|
overlapping grid of blocks covering the detection window into a combined
|
||||||
|
feature vector for use in the window classifier.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if feature_vector:
|
||||||
|
normalized_blocks = normalized_blocks.ravel()
|
||||||
|
|
||||||
|
if visualize:
|
||||||
|
return normalized_blocks, hog_image
|
||||||
|
else:
|
||||||
|
return normalized_blocks
|
20719
haarcascade_frontalface_alt2.xml
Normal file
20719
haarcascade_frontalface_alt2.xml
Normal file
File diff suppressed because it is too large
Load diff
55
hog_test.py
Normal file
55
hog_test.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import time
|
||||||
|
from skimage.feature import hog
|
||||||
|
from skimage import data, exposure
|
||||||
|
import face_recognition
|
||||||
|
import cv2
|
||||||
|
|
||||||
|
|
||||||
|
image = data.astronaut()
|
||||||
|
image = face_recognition.load_image_file("testimage.png")
|
||||||
|
|
||||||
|
face_location = face_recognition.face_locations(image)
|
||||||
|
# print(image)
|
||||||
|
|
||||||
|
fd, hog_image = hog(image, orientations=8, pixels_per_cell=(16, 16),
|
||||||
|
cells_per_block=(1, 1), visualize=True, multichannel=True)
|
||||||
|
|
||||||
|
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True)
|
||||||
|
|
||||||
|
ax1.axis('off')
|
||||||
|
ax1.imshow(image, cmap=plt.cm.gray)
|
||||||
|
ax1.set_title('Input image')
|
||||||
|
|
||||||
|
# Rescale histogram for better display
|
||||||
|
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 10))
|
||||||
|
|
||||||
|
ax2.axis('off')
|
||||||
|
ax2.imshow(hog_image_rescaled, cmap=plt.cm.gray)
|
||||||
|
ax2.set_title('Histogram of Oriented Gradients')
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
print('done')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# # Display the results
|
||||||
|
# (top, right, bottom, left) = face_location[0]
|
||||||
|
# # Scale back up face locations since the frame we detected in was scaled to 1/4 size
|
||||||
|
# top *= 4
|
||||||
|
# right *= 4
|
||||||
|
# bottom *= 4
|
||||||
|
# left *= 4
|
||||||
|
|
||||||
|
# # Draw a box around the face
|
||||||
|
# cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)
|
||||||
|
|
||||||
|
# # Draw a label with a name below the face
|
||||||
|
# cv2.rectangle(image, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
|
||||||
|
# font = cv2.FONT_HERSHEY_DUPLEX
|
||||||
|
# cv2.putText(image, '...', (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
|
||||||
|
|
||||||
|
# # Display the resulting image
|
||||||
|
# cv2.imshow('Video', image)
|
||||||
|
|
||||||
|
# time.sleep(10)
|
99
live_dnn.py
Normal file
99
live_dnn.py
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# import face_recognition
|
||||||
|
import cv2
|
||||||
|
from skimage.feature import hog
|
||||||
|
from skimage import data, exposure
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import dlib
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
imagefile = "Marjo.jpg"
|
||||||
|
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
|
||||||
|
|
||||||
|
logger = logging.getLogger('dnn')
|
||||||
|
|
||||||
|
image = cv2.imread(imagefile)
|
||||||
|
# rows = open(args["labels"]).read().strip().split("\n")
|
||||||
|
# classes = [r[r.find(" ") + 1:].split(",")[0] for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
logger.info("[INFO] loding model...")
|
||||||
|
net = cv2.dnn.readNetFromCaffe(prototxt, model)
|
||||||
|
logger.info("Loaded")
|
||||||
|
|
||||||
|
video_capture = cv2.VideoCapture(2)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Grab a single frame of video
|
||||||
|
ret, image = video_capture.read()
|
||||||
|
|
||||||
|
(h, w) = image.shape[:2]
|
||||||
|
blob = cv2.dnn.blobFromImage(cv2.resize(image, (300, 300)), 1.0,
|
||||||
|
(300, 300), (104.0, 177.0, 123.0))
|
||||||
|
|
||||||
|
net.setInput(blob)
|
||||||
|
start = time.time()
|
||||||
|
detections = net.forward()
|
||||||
|
end = time.time()
|
||||||
|
logger.debug(f"classification took {end-start:.5} seconds")
|
||||||
|
# idxs = np.argsort(detections[0])[::-1][:5]
|
||||||
|
|
||||||
|
for i in range(0, detections.shape[2]):
|
||||||
|
# extract the confidence (i.e., probability) associated with the
|
||||||
|
# prediction
|
||||||
|
confidence = detections[0, 0, i, 2]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# compute the (x, y)-coordinates of the bounding box for the
|
||||||
|
# object
|
||||||
|
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
|
||||||
|
(startX, startY, endX, endY) = box.astype("int")
|
||||||
|
|
||||||
|
# we always draw
|
||||||
|
|
||||||
|
# First we crop the sub-rect from the image
|
||||||
|
sub_img = image[startY:endY, startX:endX]
|
||||||
|
rect_img = sub_img.copy()
|
||||||
|
width = 2
|
||||||
|
cv2.rectangle(rect_img, (0, 0),
|
||||||
|
(sub_img.shape[1]-int(width/2), sub_img.shape[0]-int(width/2)),
|
||||||
|
(0, 0, 255), width)
|
||||||
|
# white_rect = np.ones(sub_img.shape, dtype=np.uint8) * 255
|
||||||
|
|
||||||
|
# At least 10% opacity
|
||||||
|
alpha = max(.1, confidence)
|
||||||
|
|
||||||
|
res = cv2.addWeighted(sub_img, 1-alpha, rect_img, alpha, 1.0)
|
||||||
|
|
||||||
|
# Putting the image back to its position
|
||||||
|
image[startY:endY, startX:endX] = res
|
||||||
|
|
||||||
|
# filter out weak detections by ensuring the `confidence` is
|
||||||
|
# greater than the minimum confidence
|
||||||
|
if confidence > confidence_threshold:
|
||||||
|
|
||||||
|
# draw the bounding box of the face along with the associated
|
||||||
|
# probability
|
||||||
|
text = "{:.2f}%".format(confidence * 100)
|
||||||
|
y = startY - 10 if startY - 10 > 10 else startY + 10
|
||||||
|
# cv2.rectangle(image, (startX, startY), (endX, endY),
|
||||||
|
# (0, 0, 255), 2)
|
||||||
|
cv2.putText(image, text, (startX, y),
|
||||||
|
cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2)
|
||||||
|
|
||||||
|
# show the output image
|
||||||
|
cv2.imshow("Output", image)
|
||||||
|
|
||||||
|
# Hit 'q' on the keyboard to quit!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
# Release handle to the webcam
|
||||||
|
video_capture.release()
|
||||||
|
cv2.destroyAllWindows()
|
71
live_hog.py
Normal file
71
live_hog.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# import face_recognition
|
||||||
|
import cv2
|
||||||
|
from skimage.feature import hog
|
||||||
|
from skimage import data, exposure
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import dlib
|
||||||
|
# This is a demo of running face recognition on live video from your webcam. It's a little more complicated than the
|
||||||
|
# other example, but it includes some basic performance tweaks to make things run a lot faster:
|
||||||
|
# 1. Process each video frame at 1/4 resolution (though still display it at full resolution)
|
||||||
|
# 2. Only detect faces in every other frame of video.
|
||||||
|
|
||||||
|
# PLEASE NOTE: This example requires OpenCV (the `cv2` library) to be installed only to read from your webcam.
|
||||||
|
# OpenCV is *not* required to use the face_recognition library. It's only required if you want to run this
|
||||||
|
# specific demo. If you have trouble installing it, try any of the other demos that don't require it instead.
|
||||||
|
|
||||||
|
# Get a reference to webcam #0 (the default one)
|
||||||
|
video_capture = cv2.VideoCapture(2)
|
||||||
|
face_detector = dlib.get_frontal_face_detector()
|
||||||
|
|
||||||
|
process_this_frame = True
|
||||||
|
|
||||||
|
while True:
|
||||||
|
if process_this_frame:
|
||||||
|
# Grab a single frame of video
|
||||||
|
ret, frame = video_capture.read()
|
||||||
|
|
||||||
|
small_frame = cv2.resize(frame, (0, 0), fx=0.7, fy=0.7)
|
||||||
|
|
||||||
|
fd, hog_image = hog(small_frame, orientations=8, pixels_per_cell=(16, 16),
|
||||||
|
cells_per_block=(1, 1), visualize=True, multichannel=True)
|
||||||
|
hog_image_rescaled = exposure.rescale_intensity(hog_image, in_range=(0, 15))
|
||||||
|
|
||||||
|
# Resize frame of video to 1/4 size for faster face recognition processing
|
||||||
|
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
|
||||||
|
rgb_small_frame = small_frame[:, :, ::-1]
|
||||||
|
dets, scores, idxs = face_detector.run(rgb_small_frame, 1, -1)
|
||||||
|
print(dets, scores, idxs)
|
||||||
|
|
||||||
|
# Display the results
|
||||||
|
for i, rectangle in enumerate(dets):
|
||||||
|
probability = scores[i]
|
||||||
|
print(rectangle)
|
||||||
|
|
||||||
|
|
||||||
|
# Scale back up face locations since the frame we detected in was scaled to 1/4 size
|
||||||
|
top = rectangle.top() #* 4
|
||||||
|
right = rectangle.right() #* 4
|
||||||
|
bottom = rectangle.bottom() #* 4
|
||||||
|
left = rectangle.left() #* 4
|
||||||
|
|
||||||
|
brightness = (probability + 1)/3
|
||||||
|
|
||||||
|
# Draw a box around the face
|
||||||
|
cv2.rectangle(hog_image_rescaled, (left, top), (right, bottom), brightness, 2)
|
||||||
|
|
||||||
|
# # Draw a label with a name below the face
|
||||||
|
# cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
|
||||||
|
|
||||||
|
# Display the resulting image
|
||||||
|
cv2.imshow('Video', hog_image_rescaled)
|
||||||
|
|
||||||
|
process_this_frame = not process_this_frame
|
||||||
|
|
||||||
|
# Hit 'q' on the keyboard to quit!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
# Release handle to the webcam
|
||||||
|
video_capture.release()
|
||||||
|
cv2.destroyAllWindows()
|
10
mirror.py
Normal file
10
mirror.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import argparse
|
||||||
|
import face_recognition.comparison
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description='Visualise face recognition algorithms.')
|
||||||
|
parser.add_argument('--camera', '-c', type=int, default=0,
|
||||||
|
help='Numeric id of the camera')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
face_recognition.comparison.main(args.camera)
|
20
recognition_test.py
Normal file
20
recognition_test.py
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
import face_recognition
|
||||||
|
|
||||||
|
image = face_recognition.load_image_file("testimage.png")
|
||||||
|
face_locations = face_recognition.face_locations(image)
|
||||||
|
|
||||||
|
|
||||||
|
print("I found {} face(s) in this photograph.".format(len(face_locations)))
|
||||||
|
|
||||||
|
for face_location in face_locations:
|
||||||
|
|
||||||
|
# Print the location of each face in this image
|
||||||
|
top, right, bottom, left = face_location
|
||||||
|
print("A face is located at pixel location Top: {}, Left: {}, Bottom: {}, Right: {}".format(top, left, bottom, right))
|
||||||
|
|
||||||
|
# You can access the actual face itself like this:
|
||||||
|
face_image = image[top:bottom, left:right]
|
||||||
|
pil_image = Image.fromarray(face_image)
|
||||||
|
pil_image.show()
|
4
requirements.txt
Normal file
4
requirements.txt
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
scipy
|
||||||
|
numpy
|
||||||
|
dlib
|
||||||
|
Pillow
|
79
test_rec.py
Normal file
79
test_rec.py
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
import face_recognition
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# This is a super simple (but slow) example of running face recognition on live video from your webcam.
|
||||||
|
# There's a second example that's a little more complicated but runs faster.
|
||||||
|
|
||||||
|
# PLEASE NOTE: This example requires OpenCV (the `cv2` library) to be installed only to read from your webcam.
|
||||||
|
# OpenCV is *not* required to use the face_recognition library. It's only required if you want to run this
|
||||||
|
# specific demo. If you have trouble installing it, try any of the other demos that don't require it instead.
|
||||||
|
|
||||||
|
# Get a reference to webcam #0 (the default one)
|
||||||
|
video_capture = cv2.VideoCapture(2)
|
||||||
|
|
||||||
|
# Load a sample picture and learn how to recognize it.
|
||||||
|
Ruben_image = face_recognition.load_image_file("Ruben.jpg")
|
||||||
|
Ruben_face_encoding = face_recognition.face_encodings(Ruben_image)[0]
|
||||||
|
|
||||||
|
# Load a second samp=le picture and learn how to recognize it.
|
||||||
|
Marjo_image = face_recognition.load_image_file("Marjo2.jpg")
|
||||||
|
Marjo_face_encoding = face_recognition.face_encodings(Marjo_image)[0]
|
||||||
|
|
||||||
|
# Create arrays of known face encodings and their names
|
||||||
|
known_face_encodings = [
|
||||||
|
Ruben_face_encoding,
|
||||||
|
Marjo_face_encoding
|
||||||
|
]
|
||||||
|
known_face_names = [
|
||||||
|
"Ruben",
|
||||||
|
"Marjo"
|
||||||
|
]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Grab a single frame of video
|
||||||
|
ret, frame = video_capture.read()
|
||||||
|
|
||||||
|
# Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
|
||||||
|
rgb_frame = frame[:, :, ::-1]
|
||||||
|
|
||||||
|
# Find all the faces and face enqcodings in the frame of video
|
||||||
|
face_locations = face_recognition.face_locations(rgb_frame)
|
||||||
|
face_encodings = face_recognition.face_encodings(rgb_frame, face_locations)
|
||||||
|
|
||||||
|
# Loop through each face in this frame of video
|
||||||
|
for (top, right, bottom, left), face_encoding in zip(face_locations, face_encodings):
|
||||||
|
# See if the face is a match for the known face(s)
|
||||||
|
matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
|
||||||
|
|
||||||
|
name = "Unknown"
|
||||||
|
|
||||||
|
# If a match was found in known_face_encodings, just use the first one.
|
||||||
|
# if True in matches:
|
||||||
|
# first_match_index = matches.index(True)
|
||||||
|
# name = known_face_names[first_match_index]
|
||||||
|
|
||||||
|
# Or instead, use the known face with the smallest distance to the new face
|
||||||
|
face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
|
||||||
|
best_match_index = np.argmin(face_distances)
|
||||||
|
if matches[best_match_index]:
|
||||||
|
name = known_face_names[best_match_index]
|
||||||
|
|
||||||
|
# Draw a box around the face
|
||||||
|
cv2.rectangle(frame, (left, top), (right, bottom), (0, 0, 255), 2)
|
||||||
|
|
||||||
|
# Draw a label with a name below the face
|
||||||
|
cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (0, 0, 255), cv2.FILLED)
|
||||||
|
font = cv2.FONT_HERSHEY_DUPLEX
|
||||||
|
cv2.putText(frame, name, (left + 6, bottom - 6), font, 1.0, (255, 255, 255), 1)
|
||||||
|
|
||||||
|
# Display the resulting image
|
||||||
|
cv2.imshow('Video', frame)
|
||||||
|
|
||||||
|
# Hit 'q' on the keyboard to quit!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
# Release handle to the webcam
|
||||||
|
video_capture.release()
|
||||||
|
cv2.destroyAllWindows()
|
89
test_rust.py
Normal file
89
test_rust.py
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
from cffi import FFI
|
||||||
|
from PIL import Image
|
||||||
|
import numpy as np
|
||||||
|
import cv2, time
|
||||||
|
|
||||||
|
ffi = FFI()
|
||||||
|
ffi.cdef("""
|
||||||
|
int test(int);
|
||||||
|
|
||||||
|
typedef void* haarclassifier;
|
||||||
|
haarclassifier classifier_new();
|
||||||
|
void scan_image(haarclassifier, size_t width,size_t height, char *input, char *buffer, size_t length, bool debug);
|
||||||
|
""")
|
||||||
|
|
||||||
|
C = ffi.dlopen("/home/ruben/Documents/Projecten/2020/rust/testproject/target/debug/libvisual_haarcascades_lib.so")
|
||||||
|
|
||||||
|
print(C.test(9))
|
||||||
|
# i = Image.open("/home/ruben/Documents/Projecten/2020/rust/lena_orig.png")
|
||||||
|
# a= np.array(i)
|
||||||
|
a= cv2.imread("/home/ruben/Documents/Projecten/2020/rust/lena_orig.png")
|
||||||
|
|
||||||
|
capture = cv2.VideoCapture(2)
|
||||||
|
|
||||||
|
print("Buffer", capture.get(cv2.CAP_PROP_BUFFERSIZE))
|
||||||
|
capture.set(cv2.CAP_PROP_BUFFERSIZE, 1)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
ret, a = capture.read()
|
||||||
|
print(a.shape)
|
||||||
|
|
||||||
|
# i = Image.open("Marjo.jpg")
|
||||||
|
width = int(a.shape[1]/4)
|
||||||
|
height = int(a.shape[0]/4)
|
||||||
|
|
||||||
|
image = cv2.resize(a, (width,height))
|
||||||
|
|
||||||
|
pixel_format = "RGB" #The raytracer only supports one format
|
||||||
|
bytes_per_pixel = 3
|
||||||
|
buffer_len = width * height * bytes_per_pixel
|
||||||
|
buffer = ffi.new("char[]", buffer_len)
|
||||||
|
# buffer2 = ffi.from_buffer("char[]", (i.tobytes("raw","RGB")))
|
||||||
|
buffer2 = ffi.from_buffer("char[]", image.tobytes())
|
||||||
|
|
||||||
|
haar = C.classifier_new()
|
||||||
|
# i = Image.open("/home/ruben/Documents/Projecten/2020/rust/lena_orig.png")
|
||||||
|
# data = i.tobytes("raw", "RGB")
|
||||||
|
|
||||||
|
print('scan!')
|
||||||
|
start=time.time()
|
||||||
|
C.scan_image(haar, width, height, buffer2, buffer, buffer_len, True)
|
||||||
|
|
||||||
|
|
||||||
|
print(f"scanned in {time.time() - start}s", buffer)
|
||||||
|
|
||||||
|
# img = Image.frombuffer(pixel_format, (width, height), ffi.buffer(buffer),
|
||||||
|
# "raw", pixel_format, 0, 1)
|
||||||
|
# img = Image.frombuffer(pixel_format, (width, height), ffi.buffer(buffer),
|
||||||
|
# "raw", pixel_format, 0, 1)
|
||||||
|
img = Image.frombuffer(pixel_format, (width, height), ffi.buffer(buffer),
|
||||||
|
"raw", pixel_format, 0, 1)
|
||||||
|
a= np.array(img)
|
||||||
|
# a= np.frombuffer(ffi.buffer(buffer))
|
||||||
|
# a.reshape((height, width, bytes_per_pixel))
|
||||||
|
a = a[:, :, ::-1]
|
||||||
|
img_concate_Verti1 = np.concatenate((image,a),axis=0)
|
||||||
|
cv2.imshow("image",img_concate_Verti1)
|
||||||
|
|
||||||
|
# Hit 'q' on the keyboard to quit!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
# img.show()
|
||||||
|
# class Scene(object):
|
||||||
|
# def __init__(self, width, height, fov, shadow_bias, max_recursion_depth):
|
||||||
|
# self.__width = width
|
||||||
|
# self.__height = height
|
||||||
|
# self.__obj = C.scene_new(width, height, fov, shadow_bias, max_recursion_depth)
|
||||||
|
|
||||||
|
# def render(self):
|
||||||
|
# pixel_format = "RGBA" #The raytracer only supports one format
|
||||||
|
# bytes_per_pixel = 4
|
||||||
|
|
||||||
|
# buffer_len = self.__width * self.__height * bytes_per_pixel
|
||||||
|
# buffer = ffi.new("char[]", buffer_len)
|
||||||
|
# C.scene_render(self.__obj, buffer, buffer_len)
|
||||||
|
# return Image.frombuffer(pixel_format, (self.__width, self.__height), ffi.buffer(buffer),
|
||||||
|
# "raw", pixel_format, 0, 1)
|
||||||
|
|
||||||
|
capture.release()
|
97
video_multiprocess.py
Normal file
97
video_multiprocess.py
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
from multiprocessing import Process, Queue
|
||||||
|
from queue import Empty
|
||||||
|
import cv2
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
def record(device_id, q1,q2, q3):
|
||||||
|
capture = cv2.VideoCapture(device_id)
|
||||||
|
while True:
|
||||||
|
ret, image = capture.read()
|
||||||
|
logging.debug('r')
|
||||||
|
q1.put(image)
|
||||||
|
q2.put(image)
|
||||||
|
q3.put(image)
|
||||||
|
|
||||||
|
|
||||||
|
def process1(in_q, out_q):
|
||||||
|
while True:
|
||||||
|
image = in_q.get()
|
||||||
|
logging.debug('r')
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
cgray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
|
||||||
|
out_q.put(cgray)
|
||||||
|
|
||||||
|
def process2(in_q, out_q):
|
||||||
|
while True:
|
||||||
|
image = in_q.get()
|
||||||
|
logging.debug('r')
|
||||||
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
||||||
|
cgray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
|
||||||
|
out_q.put(cgray)
|
||||||
|
|
||||||
|
def display(image_res, q1, q2, q3):
|
||||||
|
prev_image1 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image2 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image3 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
prev_image4 = np.zeros((image_res[1],image_res[0],3), np.uint8)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
logging.debug('r')
|
||||||
|
try:
|
||||||
|
image1 = q1.get_nowait()
|
||||||
|
image1 = cv2.resize(image1, (image_res[0], image_res[1]))
|
||||||
|
prev_image1 = image1
|
||||||
|
except Empty as e:
|
||||||
|
image1 = prev_image1
|
||||||
|
try:
|
||||||
|
image2 = q2.get_nowait()
|
||||||
|
image2 = cv2.resize(image2, (image_res[0], image_res[1]))
|
||||||
|
prev_image2 = image2
|
||||||
|
except Empty as e:
|
||||||
|
image2 = prev_image2
|
||||||
|
try:
|
||||||
|
image3 = q3.get_nowait()
|
||||||
|
image3 = cv2.resize(image3, (image_res[0], image_res[1]))
|
||||||
|
prev_image3 = image3
|
||||||
|
except Empty as e:
|
||||||
|
image3 = prev_image3
|
||||||
|
|
||||||
|
image4 = prev_image4
|
||||||
|
|
||||||
|
img_concate_Verti1 = np.concatenate((image1,image2),axis=0)
|
||||||
|
img_concate_Verti2 = np.concatenate((image3,image4),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!
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Visualise face recognition algorithms.')
|
||||||
|
parser.add_argument('--camera', '-c', type=int, default=0,
|
||||||
|
help='Numeric id of the camera')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
image_size = (int(1920/2), int(1080/2))
|
||||||
|
# TODO should we use queues here at all?
|
||||||
|
# https://docs.python.org/3/library/multiprocessing.html#programming-guidelines
|
||||||
|
# TODO: queue maxsize, or prefrabily some sort of throttled queue (like zmq hight water mark)
|
||||||
|
q_webcam1 = Queue()
|
||||||
|
q_webcam2 = Queue()
|
||||||
|
q_webcam3 = Queue()
|
||||||
|
q_process1 = Queue()
|
||||||
|
q_process2 = Queue()
|
||||||
|
p1 = Process(target=record, args=(args.camera, q_webcam1, q_webcam2,q_webcam3))
|
||||||
|
p2 = Process(target=display, args=(image_size, q_webcam1, q_process1, q_process2 ))
|
||||||
|
p3 = Process(target=process1, args=(q_webcam2, q_process1,))
|
||||||
|
p4 = Process(target=process2, args=(q_webcam3, q_process2,))
|
||||||
|
p1.start()
|
||||||
|
p2.start()
|
||||||
|
p3.start()
|
||||||
|
p4.start()
|
||||||
|
p2.join()
|
36
video_threading.py
Normal file
36
video_threading.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
from threading import Thread
|
||||||
|
import cv2, time
|
||||||
|
|
||||||
|
|
||||||
|
class VideoStreamWidget(object):
|
||||||
|
# modified from Nathancy: https://stackoverflow.com/a/55131226
|
||||||
|
def __init__(self, src=0):
|
||||||
|
self.capture = cv2.VideoCapture(src)
|
||||||
|
# Start the thread to read frames from the video stream
|
||||||
|
self.thread = Thread(target=self.update, args=())
|
||||||
|
self.thread.daemon = True
|
||||||
|
self.thread.start()
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
# Read the next frame from the stream in a different thread
|
||||||
|
while True:
|
||||||
|
if self.capture.isOpened():
|
||||||
|
(self.status, self.frame) = self.capture.read()
|
||||||
|
time.sleep(.01)
|
||||||
|
|
||||||
|
def show_frame(self):
|
||||||
|
# Display frames in main program
|
||||||
|
cv2.imshow('frame', self.frame)
|
||||||
|
key = cv2.waitKey(1)
|
||||||
|
if key == ord('q'):
|
||||||
|
self.capture.release()
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
video_stream_widget = VideoStreamWidget(2)
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
video_stream_widget.show_frame()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
Loading…
Reference in a new issue