diff --git a/face_recognition/Dockerfile b/face_recognition/Dockerfile index b93ecfe..5d32648 100644 --- a/face_recognition/Dockerfile +++ b/face_recognition/Dockerfile @@ -1,9 +1,11 @@ FROM python:3.6 +#copy requirements file and install required libraries. CMake is installed seperately because it is needed for face recognition and the way pip installs the requirements file fails as CMake isnt installed in time COPY ./requirements.txt /app/requirements.txt WORKDIR /app RUN pip install CMake RUN pip install -r requirements.txt RUN rm requirements.txt +#make new user so the container duesnt run using root, then copy the api and start it RUN useradd -ms /bin/bash admin USER admin COPY /app . diff --git a/face_recognition/app/app.py b/face_recognition/app/app.py index 51feb40..b8ffc2c 100644 --- a/face_recognition/app/app.py +++ b/face_recognition/app/app.py @@ -11,6 +11,7 @@ import cv2 import numpy as np import imutils app = Flask(__name__) +#establish connection to minio for this instance of the api minioClient = Minio('minio:9000', access_key=os.environ['s3-name'], secret_key=os.environ['s3-password'], secure=False) @app.route('/') @@ -27,25 +28,29 @@ def access_minio(): def new_user_id(): #save demo object to reserve id in minio id = None + #five tries to generate unique id for limited_try in range(0,5): id = str(uuid.uuid4()) if check_id(id) == False: break demo_object = ['test'] + #dump demo object and save it like its a face encoding to reserve id with open('/tmp/demo_object.pkl', 'wb') as f: pickle.dump(demo_object, f) minioClient.fput_object('users', str(id), '/tmp/demo_object.pkl') return jsonify({'id':id}) @app.route('/init_face') -#call like https://face.sguba.de/init_face?id=123&encoded_string=abc +#call like https://face.sguba.de/init_face?id=123&encoded_string=base64encoded_urlsafe_string def new_face(): id = request.args.get('id', None) img = request.args.get('encoded_string', None) temporary_img_path = '/tmp/'+str(id)+'.jpg' temporary_pkl_path = '/tmp/'+str(id)+'.pkl' + #transcode base64_string to img and generate encoding save_img(img, temporary_img_path) face_encoding_response = encode_face(temporary_img_path) + #check if encoding was successfull, if yes save encoding in minio if face_encoding_response['success']==True: with open(temporary_pkl_path, 'wb') as file: pickle.dump(face_encoding_response['encoding'], file) @@ -59,8 +64,10 @@ def check_face(): img = request.args.get('encoded_string', None) temporary_img_path = '/tmp/'+str(id)+'.jpg' temporary_pkl_path = '/tmp/'+str(id)+'.pkl' + #transcode base64_string to img and generate encoding save_img(img, temporary_img_path) face_encoding_response = encode_face(temporary_img_path) + #check if encoding was successfull, if yes get initial encoding from minio and compare encodings if face_encoding_response['success']==True: minioClient.fget_object('users', str(id), temporary_pkl_path) with open(temporary_pkl_path, 'rb') as file: @@ -84,7 +91,9 @@ def check_id(id): return known def save_img(encoded_data, filename): + #length of base64 string is always a multiple of 4, if not its padded with =, here we add padding that might have been lost so there are no errors when decoding the img encoded_data += "=" * ((4 - len(encoded_data) % 4) % 4) + #convert string to binary, decode base64, decode image and rotate it 90 degree because android studio rotates the camera, then save image to temp encoded_data = bytes(encoded_data, encoding='utf-8') nparr = np.fromstring(base64.urlsafe_b64decode(encoded_data), np.uint8) img = cv2.imdecode(nparr, cv2.IMREAD_ANYCOLOR) @@ -92,18 +101,18 @@ def save_img(encoded_data, filename): return cv2.imwrite(filename, img) def encode_face(path): + #create face encoding from data, and check wether it was succesfull. If it was unsuccesfull the encodings list is empty success = None face = face_recognition.load_image_file(path) face_encoding = face_recognition.face_encodings(face) if len(face_encoding)==0: - success = False - response = {'success':success, 'encoding':None} + response = {'success':False, 'encoding':None} else: - success = True - response = {'success':success, 'encoding':face_encoding[0]} + response = {'success':True, 'encoding':face_encoding[0]} return response def setup(): + #create minio buckets that are used at startup if they dont exist already try: minioClient.make_bucket("users") except BucketAlreadyOwnedByYou as err: @@ -113,7 +122,8 @@ def setup(): except ResponseError as err: raise - + #check if the script is run primarily and not imported etc. if __name__ == '__main__': setup() + #start running the api app.run(host='0.0.0.0') \ No newline at end of file diff --git a/face_recognition/docker-compose.yml b/face_recognition/docker-compose.yml index 70337b9..999860a 100644 --- a/face_recognition/docker-compose.yml +++ b/face_recognition/docker-compose.yml @@ -1,5 +1,6 @@ version: "2" +#customized for the environment. Requires traefik reverse proxy networks: face-recognition: external: false @@ -18,9 +19,9 @@ services: image: face-recognition-flask:latest container_name: flask environment: - - s3-adress = minio - - s3-name = face-minio - - s3-password = testpw1 + - s3-adress=minio + - s3-name=face-minio + - s3-password=testpw123 restart: unless-stopped networks: - face-recognition @@ -30,7 +31,7 @@ services: labels: - traefik.enable=true - traefik.frontend.rule=Host:face.sguba.de - - traefik.port=5555 + - traefik.port=5000 - traefik.docker.network=proxy depends_on: - minio @@ -40,19 +41,19 @@ services: restart: unless-stopped container_name: minio ports: - - "9999:9000" + - "9009:9000" environment: - MINIO_ACCESS_KEY=face-minio - - MINIO_ACCESS_KEY=testpw1 + - MINIO_SECRET_KEY=testpw123 networks: - face-recognition - proxy labels: - traefik.enable=true - - treafik.frontend.rule=face-minio.sguba.de - - traefik.port=9999 - - traeffik.docker.network=proxy + - traefik.frontend.rule=Host:face-minio.sguba.de + - traefik.port=9000 + - traefik.docker.network=proxy volumes: - face_minio_data:/data - face_minio_config:/root/.minio - command: server /mlflow-minio/storage + command: minio server /data \ No newline at end of file