Commit 1ce5f8e2 authored by Bernhard Heinloth's avatar Bernhard Heinloth
Browse files

Improve: Sound, args

parent 7f948d0c
__pycache__/ __pycache__/
*.pem
*.crt
*.json
...@@ -39,22 +39,15 @@ Rekursiv (mit Submodule) klonen, Abhängigkeiten installieren und ausführen, z. ...@@ -39,22 +39,15 @@ Rekursiv (mit Submodule) klonen, Abhängigkeiten installieren und ausführen, z.
cd covpass-check cd covpass-check
sudo apt install libzbar0 sudo apt install libzbar0
pip3 install -r requirements.txt pip3 install -r requirements.txt
python3 run.py python3 webcam.py
Für Teilnehmerbeschränkung entsprechend Weitere Informationen via
python3 run.py students.txt python3 webcam.py -h
Für akustische Teilnehmerbeschränkung bei einer geringen Webcamauflösung beispielsweise
Entwicklungshinweise python3 webcam.py -a students.txt -s -r 640x480 -l log.txt
--------------------
Wer ein besseres System als den T470s bestitzt, will vielleicht die Kameraauflösung in `run.py:15` hochdrehen -- sie ist derzeit auf 640×480 eingestellt:
cap.set(cv2.CAP_PROP_FRAME_WIDTH,640)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,480)
Die Standardausgabe gibt derzeit Zertifikatsinformationen aus, auf dem Fehlerstrom fallen die Namen raus.
Weiterführende Informationen Weiterführende Informationen
...@@ -62,3 +55,6 @@ Weiterführende Informationen ...@@ -62,3 +55,6 @@ Weiterführende Informationen
* https://github.com/panzi/verify-ehc * https://github.com/panzi/verify-ehc
* https://github.com/Digitaler-Impfnachweis/certification-apis * https://github.com/Digitaler-Impfnachweis/certification-apis
Audiodateien von
* https://notificationsounds.com/
File added
File added
...@@ -14,3 +14,4 @@ pdf2image ...@@ -14,3 +14,4 @@ pdf2image
pycv2 pycv2
opencv-python opencv-python
numpy numpy
playsound
#!/bin/bash
#echo "Downloading root certs"
export CH_TOKEN='0795dc8b-d8d0-4313-abf2-510b12d50939'
export FR_TOKEN='eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqUG0zZ1BzUlZaMWRRUmhHOG1HMGhFN3Jlb2ZXTTNINzJCV1RtajdJcFd3In0.eyJleHAiOjE2ODUxODU5MDYsImlhdCI6MTYyMjExMzkwNiwianRpIjoiOTdjODgyM2EtNjlhZS00NzA4LWE4N2UtNzYxM2NhNGU3ODU5IiwiaXNzIjoiaHR0cHM6Ly9hdXRoLm1lc3NlcnZpY2VzLmluZ3JvdXBlLmNvbS9hdXRoL3JlYWxtcy9QSU5HIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImVhMWY1NWVlLTUxMGMtNGMxNi05MWQ4LTE1MjI4OGJhZDViYSIsInR5cCI6IkJlYXJlciIsImF6cCI6InRhY3YtY2xpZW50LWxpdGUiLCJzZXNzaW9uX3N0YXRlIjoiNjk5ODExY2YtODFlZS00ZmNkLTk4NDctY2FkMGJmYjZhOTdiIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJST0xFX1ZFUklGWV9DT05UUk9MXzJERE9DX0wxIiwiUk9MRV9WRVJJRllfQ09OVFJPTF8yRERPQ19CMiIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6ImVtYWlsIHByb2ZpbGUgb2ZmbGluZV9hY2Nlc3MiLCJzaXJlbiI6IjAwMDAwMDAwMCIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoidGFjdi1tb2JpbGUtbGl0ZSIsImdpdmVuX25hbWUiOiIiLCJmYW1pbHlfbmFtZSI6IiJ9.mpfrIP8ayElTm7yoVayCF11oYrDQEnauk9hbbVBw8idAiE6OsMlWNloZtUbbnwrJZsMX3_NoEyzkiB3HNbxyhPWp7eRZ7qhn8XjZVgg6sVytXqcVZo9R5-Q9JftMKv7JelsY3PsaOo5x-pYOX30ancPRjd78TeenorGopsVN_LLRLQpenfgjjgwx-srZnLa-TFYTcbSvXozfJT7uk5CHyz_MIFLM7pl9Zdt66yTGBkLIyOLFsV5vPeH5SYvgRNDYdxZy4XMo6Gyfz0lAI9Xfcjs20NBoOQMV4JREH4Z-IcJJXeszC9QeA1-tRmxujqIRuyvBal7msLy7Zimd2q7i3Q'
if [[ ! -f 'DE.pem' ]] ; then
echo "Downloading root certs"
python3 verify_ehc.py --download-all-root-certs
fi
CERTS="FR,GB"
for ROOT_CERT in *.pem ; do
if [[ ${#ROOT_CERT} -eq 6 ]] ; then
COUNTRY=${ROOT_CERT%.pem}
echo "Root cert from $COUNTRY"
#openssl x509 -text -in $ROOT_CERT
CERTS+=",$COUNTRY"
export ${COUNTRY}_ROOT_CERT=$ROOT_CERT
fi
done
TRUSTLIST='trustlist.json'
TRUSTLIST_MAX_AGE='10 days'
if [[ ! -f "$TRUSTLIST" ]] ; then
echo "Downloading (new) trust list"
python3 verify_ehc.py --certs-from $CERTS --save-certs $TRUSTLIST
elif [[ $(date --date="$TRUSTLIST_MAX_AGE ago" +%s) -gt $(date -r "$TRUSTLIST" +%s || echo 0) ]]; then
echo "Downloading (new) trust list in background"
bash -c "python3 verify_ehc.py --certs-from $CERTS --save-certs tmp-$TRUSTLIST && mv -f tmp-$TRUSTLIST $TRUSTLIST && echo done" &
fi
python3 webcam.py -s -t $TRUSTLIST
Subproject commit 7051af78ac7ff2756bbab63ad3e80f563254796c Subproject commit 2d29bbe8ba89c5498b6c7f953b0cd80a83e40e25
#!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import print_function from __future__ import print_function
import os
import sys import sys
import json import json
from verify_ehc import * from verify_ehc import *
...@@ -10,33 +12,50 @@ import pyzbar.pyzbar as pyzbar ...@@ -10,33 +12,50 @@ import pyzbar.pyzbar as pyzbar
import numpy as np import numpy as np
import cv2 import cv2
import unicodedata import unicodedata
import argparse
from playsound import playsound
parser = argparse.ArgumentParser(description='CovPass Check via Webcam (PoC)')
parser.add_argument('-v', '--verbose', action='store_true', help='verbose output')
parser.add_argument('-d', '--device', type=int,help='webcam device number (/dev/videoX)', default=0)
parser.add_argument('-r', '--resolution', help='webcam resolution (WxH)', default='1280x720')
parser.add_argument('-c', '--count', type=int, help='initial count value', default=0)
parser.add_argument('-s', '--sound', action='store_true', help='accoustic notification after each new (!) scan')
parser.add_argument('-f', '--freeze', type=float, help='seconds to freeze image after each new (!) scan', default=0.7)
parser.add_argument('-t', '--trustlist', type=argparse.FileType('rb'), help='use given trustlist (json) instead of downloading new one')
parser.add_argument('-a', '--allowed', type=argparse.FileType('r'), help='list of allowed names')
parser.add_argument('-l', '--log', type=argparse.FileType('a'), help='access log file', default=sys.stderr)
parser.add_argument('-w', '--window', help='window name', default='CovPass Check')
args = parser.parse_args()
# Webcam # Webcam
# device (X = /dev/videoX) cap = cv2.VideoCapture(args.device)
WEBCAM_DEVICE = 0 w,h = args.resolution.split('x')
WEBCAM_WIDTH = 1280 cap.set(cv2.CAP_PROP_FRAME_WIDTH, int(w))
WEBCAM_HEIGHT = 720 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, int(h))
WINDOW_NAME = 'CovPass Check'
cap = cv2.VideoCapture(WEBCAM_DEVICE)
cap.set(cv2.CAP_PROP_FRAME_WIDTH,WEBCAM_WIDTH)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT,WEBCAM_HEIGHT)
font_simplex = cv2.FONT_HERSHEY_SIMPLEX font_simplex = cv2.FONT_HERSHEY_SIMPLEX
font_duplex = cv2.FONT_HERSHEY_DUPLEX font_duplex = cv2.FONT_HERSHEY_DUPLEX
cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL) cv2.namedWindow(args.window, cv2.WINDOW_NORMAL)
#cv2.namedWindow(args.window, cv2.WND_PROP_FULLSCREEN)
#cv2.setWindowProperty(args.window, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)
qrcodes={} qrcodes={}
process=[] process=[]
uniqusers=[] uniqusers=[]
validusers=0 validusers=args.count
EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) EPOCH = datetime(1970, 1, 1)
print("Downloading...")
certs_table: Dict[str, CertList] = {} certs_table: Dict[str, CertList] = {}
certs: Optional[CertList] = download_ehc_certs(['DE', 'AT', 'SE', 'GB', 'NL'], certs_table); certs: Optional[CertList] = None
if args.trustlist:
certs = load_hack_certs_json(args.trustlist.read(), args.trustlist.name)
else:
print("Downloading...")
certs = download_ehc_certs(['DE', 'AT', 'SE', 'GB', 'NL'], certs_table);
if not certs: if not certs:
print("empty trust list!") print("empty trustlist!")
sys.exit(1) sys.exit(1)
print("Ready...") print("Ready...")
...@@ -62,12 +81,53 @@ def validate(ehc_msg, ehc_payload): ...@@ -62,12 +81,53 @@ def validate(ehc_msg, ehc_payload):
print('Failed', sys.exc_info()) print('Failed', sys.exc_info())
return False return False
class Color:
RED = (41,20,141)
GREEN = (119,155,0)
YELLOW = (19,147,201)
BLUE = (101,56,0)
def highlight_object(frame, obj, text, color):
points = obj.polygon
# If the points do not form a quad, find convex hull
if len(points) > 4 :
hull = cv2.convexHull(np.array([point for point in points], dtype=np.float32))
hull = list(map(tuple, np.squeeze(hull)))
else:
hull = points;
# Number of points in the convex hull
n = len(hull)
# Draw the convext hull
for j in range(0, n):
cv2.line(frame, hull[j], hull[ (j+1) % n], color, 3)
# Text
x = obj.rect.left
y = obj.rect.top
cv2.putText(frame, text, (x, y - 10), font_simplex, 1, color, 2, cv2.LINE_AA)
def highlight_ehc(frame, obj, qrcode):
if qrcode['valid']:
if qrcode['unique'] and (len(allowed) == 0 or qrcode['allowed']):
col = Color.GREEN
else:
col = Color.YELLOW
else:
col = Color.RED
highlight_object(frame, obj, qrcode['name'], col)
def wait(millis = 1):
if millis > 0:
key = cv2.waitKey(millis)
if key & 0xFF == ord('q'):
cap.release()
cv2.destroyAllWindows()
sys.exit(0)
allowed=[] allowed=[]
if len(sys.argv) > 1: if args.allowed:
with open(sys.argv[1], "r") as data: for line in args.allowed.readlines():
for line in data.readlines(): allowed.append(normalize(line).split(";"))
allowed.append(normalize(line).split(";"))
print("Only allowed person")
if not cap.isOpened(): if not cap.isOpened():
cap.open(WEBCAM_DEVICE) cap.open(WEBCAM_DEVICE)
...@@ -77,58 +137,33 @@ while cap.isOpened(): ...@@ -77,58 +137,33 @@ while cap.isOpened():
ret, frame = cap.read() ret, frame = cap.read()
# Our operations on the frame come here # Our operations on the frame come here
im = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) im = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
decodedObjects = pyzbar.decode(im) objs = pyzbar.decode(im)
for decodedObject in decodedObjects: for obj in objs:
# Check barcode # Check barcode
if decodedObject.type == 'QRCODE': if obj.type == 'QRCODE':
# Is qr code known? # Is qr code known?
qrcode = decodedObject.data.decode('ascii') ehc_code = obj.data.decode('ascii')
if qrcode in qrcodes: if ehc_code in qrcodes:
if qrcodes[qrcode]['valid']: highlight_ehc(frame, obj, qrcodes[ehc_code])
if qrcodes[ehc_code]['unique'] and (len(allowed) == 0 or qrcodes[ehc_code]['allowed']):
color = (119,155,0)
else:
color = (19,147,201)
else:
color = (41,20,141)
text = qrcodes[qrcode]['name']
else: else:
# Add to process list # Add to process list
process.append(qrcode) process.append((obj, ehc_code))
color = (101,56,0) highlight_object(frame, obj, '', Color.BLUE)
text = '???'
points = decodedObject.polygon
# If the points do not form a quad, find convex hull
if len(points) > 4 :
hull = cv2.convexHull(np.array([point for point in points], dtype=np.float32))
hull = list(map(tuple, np.squeeze(hull)))
else:
hull = points;
# Number of points in the convex hull
n = len(hull)
# Draw the convext hull
for j in range(0,n):
cv2.line(frame, hull[j], hull[ (j+1) % n], color, 3)
# Text
x = decodedObject.rect.left
y = decodedObject.rect.top
cv2.putText(frame, text, (x, y - 10), font_simplex, 1, color, 2, cv2.LINE_AA)
# Full counter # Full counter
cv2.putText(frame, str(validusers), (10, 60), cv2.FONT_HERSHEY_DUPLEX, 2, (119,155,0), 4, cv2.LINE_AA) cv2.putText(frame, str(validusers), (10, 60), cv2.FONT_HERSHEY_DUPLEX, 2, Color.GREEN, 4, cv2.LINE_AA)
# Display the resulting frame # Display the resulting frame
cv2.imshow(WINDOW_NAME, frame) cv2.imshow(args.window, frame)
# Check unprocessed qr codes # Check unprocessed qr codes
for ehc_code in process: for obj, ehc_code in process:
print("New qr code", ehc_code) print("New qr code", ehc_code)
qrcodes[ehc_code] = {} qrcodes[ehc_code] = {}
try: try:
ehc_msg = decode_ehc(ehc_code) ehc_msg = decode_ehc(ehc_code)
ehc_payload = cbor2.loads(ehc_msg.payload) ehc_payload = cbor2.loads(ehc_msg.payload)
print("Payload ", ehc_payload[-260][1])
gn = normalize(ehc_payload[-260][1]['nam']['gn']) gn = normalize(ehc_payload[-260][1]['nam']['gn'])
fn = normalize(ehc_payload[-260][1]['nam']['fn']) fn = normalize(ehc_payload[-260][1]['nam']['fn'])
qrcodes[ehc_code]['name'] = gn + ' ' + fn qrcodes[ehc_code]['name'] = gn + ' ' + fn
...@@ -150,21 +185,28 @@ while cap.isOpened(): ...@@ -150,21 +185,28 @@ while cap.isOpened():
if (fn in a[0] and gn in a[1]) or (fnt in a[0].upper() and gnt in a[1].upper()): if (fn in a[0] and gn in a[1]) or (fnt in a[0].upper() and gnt in a[1].upper()):
qrcodes[ehc_code]['allowed'] = True qrcodes[ehc_code]['allowed'] = True
break break
if len(allowed) == 0 or qrcodes[ehc_code]['allowed']: if len(allowed) == 0 or qrcodes[ehc_code]['allowed']:
validusers = validusers + 1 validusers = validusers + 1
print(qrcodes[ehc_code]['name'], file=sys.stderr) note = ''
else: else:
print(qrcodes[ehc_code]['name'], "(but not in list)", file=sys.stderr) note = '(not allowed)'
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), qrcodes[ehc_code]['name'], note, file=args.log)
except: except:
qrcodes[ehc_code]['valid'] = False qrcodes[ehc_code]['valid'] = False
if not 'name' in qrcodes[ehc_code]: if not 'name' in qrcodes[ehc_code]:
qrcodes[ehc_code]['name'] = '(Invalid EHC QR)' qrcodes[ehc_code]['name'] = '(Invalid EHC QR)'
print('Failed', sys.exc_info()) print('Failed', sys.exc_info())
process.clear()
key = cv2.waitKey(1) highlight_ehc(frame, obj, qrcodes[ehc_code])
if key & 0xFF == ord('q'): cv2.imshow(args.window, frame)
break
if args.sound:
playsound('ok.mp3' if qrcodes[ehc_code]['valid'] and (len(allowed) == 0 or qrcodes[ehc_code]['allowed']) else 'error.mp3', False)
wait(int(args.freeze * 1000))
process.clear()
wait(1)
# When everything done, release the capture # When everything done, release the capture
cap.release() cap.release()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment