Zhu-FaceOnLive
commited on
Upload 20 files
Browse files- .gitattributes +7 -0
- Dockerfile +30 -0
- app.py +132 -0
- demo.py +71 -0
- examples/1.jpg +0 -0
- examples/2.jpg +0 -0
- examples/3.jpg +3 -0
- idlivesdk.py +24 -0
- lib/libDocSdk.so +3 -0
- lib/libidlivesdk.so +3 -0
- lib/libopenvino.so.2300 +3 -0
- lib/libopenvino.so.2330 +3 -0
- lib/libopenvino_intel_cpu_plugin.so +3 -0
- lib/libopenvino_ir_frontend.so.2330 +0 -0
- lib/libopenvino_onnx_frontend.so.2300 +3 -0
- lib/libopyenv.so +0 -0
- lib/libopyhas.so +0 -0
- lib/libtbb.so.12 +0 -0
- lib/plugins.xml +6 -0
- requirements.txt +6 -0
- run.sh +6 -0
.gitattributes
CHANGED
@@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
examples/3.jpg filter=lfs diff=lfs merge=lfs -text
|
37 |
+
lib/libDocSdk.so filter=lfs diff=lfs merge=lfs -text
|
38 |
+
lib/libidlivesdk.so filter=lfs diff=lfs merge=lfs -text
|
39 |
+
lib/libopenvino_intel_cpu_plugin.so filter=lfs diff=lfs merge=lfs -text
|
40 |
+
lib/libopenvino_onnx_frontend.so.2300 filter=lfs diff=lfs merge=lfs -text
|
41 |
+
lib/libopenvino.so.2300 filter=lfs diff=lfs merge=lfs -text
|
42 |
+
lib/libopenvino.so.2330 filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM ubuntu:22.04
|
2 |
+
RUN ln -snf /usr/share/zoneinfo/$CONTAINER_TIMEZONE /etc/localtime && echo $CONTAINER_TIMEZONE > /etc/timezone
|
3 |
+
RUN apt-get update && \
|
4 |
+
apt-get install -y binutils python3 python3-pip python3-opencv && \
|
5 |
+
rm -rf /var/lib/apt/lists/*
|
6 |
+
|
7 |
+
RUN useradd -m -u 1000 user
|
8 |
+
USER user
|
9 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
10 |
+
|
11 |
+
WORKDIR /app
|
12 |
+
COPY --chown=user ./idlivesdk.py .
|
13 |
+
COPY --chown=user ./app.py .
|
14 |
+
COPY --chown=user ./demo.py .
|
15 |
+
COPY --chown=user ./requirements.txt .
|
16 |
+
COPY --chown=user ./run.sh .
|
17 |
+
COPY --chown=user ./examples ./examples
|
18 |
+
COPY --chown=user ./model ./model
|
19 |
+
COPY --chown=user ./lib /usr/lib
|
20 |
+
COPY --chown=user ./lib/libidlivesdk.so ./lib/libidlivesdk.so
|
21 |
+
|
22 |
+
ADD https://huggingface.co/datasets/FaceOnLive/IDL-Models/resolve/main/model.tar.gz .
|
23 |
+
RUN tar -xvzf ./model.tar.gz -C . && rm ./model.tar.gz
|
24 |
+
|
25 |
+
RUN chmod -R 777 ./model/
|
26 |
+
|
27 |
+
RUN pip3 install -r requirements.txt
|
28 |
+
RUN chmod a+x run.sh
|
29 |
+
CMD ["./run.sh"]
|
30 |
+
EXPOSE 7860
|
app.py
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import sys
|
2 |
+
sys.path.append('.')
|
3 |
+
|
4 |
+
import os
|
5 |
+
import numpy as np
|
6 |
+
import base64
|
7 |
+
import json
|
8 |
+
import io
|
9 |
+
|
10 |
+
from PIL import Image, ExifTags
|
11 |
+
from flask import Flask, request, jsonify
|
12 |
+
from idlivesdk import getHWID
|
13 |
+
from idlivesdk import setLicenseKey
|
14 |
+
from idlivesdk import initSDK
|
15 |
+
from idlivesdk import processImage
|
16 |
+
|
17 |
+
licenseKeyPath = "license.txt"
|
18 |
+
license = os.environ.get("LICENSE_KEY")
|
19 |
+
|
20 |
+
if license is None:
|
21 |
+
try:
|
22 |
+
with open(licenseKeyPath, 'r') as file:
|
23 |
+
license = file.read().strip()
|
24 |
+
except IOError as exc:
|
25 |
+
print("failed to open license.txt: ", exc.errno)
|
26 |
+
print("License Key: ", license)
|
27 |
+
|
28 |
+
hwid = getHWID()
|
29 |
+
print("HWID: ", hwid.decode('utf-8'))
|
30 |
+
|
31 |
+
ret = setLicenseKey(license.encode('utf-8'))
|
32 |
+
print("Set License: ", ret)
|
33 |
+
|
34 |
+
ret = initSDK("model".encode('utf-8'))
|
35 |
+
print("Init: ", ret)
|
36 |
+
|
37 |
+
app = Flask(__name__)
|
38 |
+
|
39 |
+
def apply_exif_rotation(image):
|
40 |
+
try:
|
41 |
+
exif = image._getexif()
|
42 |
+
if exif is not None:
|
43 |
+
for orientation in ExifTags.TAGS.keys():
|
44 |
+
if ExifTags.TAGS[orientation] == 'Orientation':
|
45 |
+
break
|
46 |
+
|
47 |
+
# Get the orientation value
|
48 |
+
orientation = exif.get(orientation, None)
|
49 |
+
|
50 |
+
# Apply the appropriate rotation based on the orientation
|
51 |
+
if orientation == 3:
|
52 |
+
image = image.rotate(180, expand=True)
|
53 |
+
elif orientation == 6:
|
54 |
+
image = image.rotate(270, expand=True)
|
55 |
+
elif orientation == 8:
|
56 |
+
image = image.rotate(90, expand=True)
|
57 |
+
|
58 |
+
except AttributeError:
|
59 |
+
print("No EXIF data found")
|
60 |
+
|
61 |
+
return image
|
62 |
+
|
63 |
+
|
64 |
+
@app.route('/process_image', methods=['POST'])
|
65 |
+
def process_image():
|
66 |
+
file = request.files['image']
|
67 |
+
|
68 |
+
try:
|
69 |
+
image = apply_exif_rotation(Image.open(file)).convert('RGB')
|
70 |
+
except:
|
71 |
+
result = "Failed to open file"
|
72 |
+
response = jsonify({"resultCode": "Error", "result": result})
|
73 |
+
|
74 |
+
response.status_code = 200
|
75 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
76 |
+
return response
|
77 |
+
|
78 |
+
image_np = np.asarray(image)
|
79 |
+
result = processImage(image_np, image_np.shape[1], image_np.shape[0])
|
80 |
+
|
81 |
+
if result is None:
|
82 |
+
result = "Failed to process image"
|
83 |
+
response = jsonify({"resultCode": "Error", "result": result})
|
84 |
+
|
85 |
+
response.status_code = 200
|
86 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
87 |
+
return response
|
88 |
+
else:
|
89 |
+
result_dict = json.loads(result.decode('utf-8'))
|
90 |
+
response = jsonify({"resultCode": "Ok", "result": result_dict})
|
91 |
+
|
92 |
+
response.status_code = 200
|
93 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
94 |
+
return response
|
95 |
+
|
96 |
+
@app.route('/process_image_base64', methods=['POST'])
|
97 |
+
def process_image_base64():
|
98 |
+
try:
|
99 |
+
content = request.get_json()
|
100 |
+
base64_image = content['base64']
|
101 |
+
|
102 |
+
image_data = base64.b64decode(base64_image)
|
103 |
+
image = apply_exif_rotation(Image.open(io.BytesIO(image_data))).convert("RGB")
|
104 |
+
except:
|
105 |
+
result = "Failed to parse base64"
|
106 |
+
response = jsonify({"resultCode": "Error", "result": result})
|
107 |
+
|
108 |
+
response.status_code = 200
|
109 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
110 |
+
return response
|
111 |
+
|
112 |
+
image_np = np.asarray(image)
|
113 |
+
result = processImage(image_np, image_np.shape[1], image_np.shape[0])
|
114 |
+
|
115 |
+
if result is None:
|
116 |
+
result = "Failed to process image"
|
117 |
+
response = jsonify({"resultCode": "Error", "result": result})
|
118 |
+
|
119 |
+
response.status_code = 200
|
120 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
121 |
+
return response
|
122 |
+
else:
|
123 |
+
result_dict = json.loads(result.decode('utf-8'))
|
124 |
+
response = jsonify({"resultCode": "Ok", "result": result_dict})
|
125 |
+
|
126 |
+
response.status_code = 200
|
127 |
+
response.headers["Content-Type"] = "application/json; charset=utf-8"
|
128 |
+
return response
|
129 |
+
|
130 |
+
if __name__ == '__main__':
|
131 |
+
port = int(os.environ.get("PORT", 9000))
|
132 |
+
app.run(host='0.0.0.0', port=port)
|
demo.py
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import gradio as gr
|
3 |
+
import requests
|
4 |
+
import json
|
5 |
+
import io
|
6 |
+
import base64
|
7 |
+
import cv2
|
8 |
+
import numpy as np
|
9 |
+
from gradio.components import Image
|
10 |
+
|
11 |
+
screenReplayThreshold = 0.5
|
12 |
+
portraitReplaceThreshold = 0.5
|
13 |
+
printedCopyThreshold = 0.5
|
14 |
+
|
15 |
+
def proc_output(result):
|
16 |
+
if result.ok:
|
17 |
+
json_result = result.json()
|
18 |
+
if json_result.get("resultCode") == "Error":
|
19 |
+
return {"status": "error", "result": "failed to process image"}
|
20 |
+
|
21 |
+
process_results = json_result.get("result")
|
22 |
+
status = process_results.get("status")
|
23 |
+
if status == "Ok":
|
24 |
+
screenReply = process_results.get("screenReply")
|
25 |
+
portraitReplace = process_results.get("portraitReplace")
|
26 |
+
printedCopy = process_results.get("printedCopy")
|
27 |
+
detResult = "genuine"
|
28 |
+
|
29 |
+
# Check for "Spoof" condition
|
30 |
+
if screenReply < screenReplayThreshold or portraitReplace < portraitReplaceThreshold or printedCopy < printedCopyThreshold:
|
31 |
+
detResult = "spoof"
|
32 |
+
|
33 |
+
# Update json_result with the modified process_results
|
34 |
+
return {"status": "ok", "data": {"result": detResult, "screenreplay_integrity_score": screenReply, "portraitreplace_integrity_score": portraitReplace, "printedcutout_integrity_score": printedCopy}}
|
35 |
+
|
36 |
+
return {"status": "error", "result": "document not found!"}
|
37 |
+
else:
|
38 |
+
return {"status": "error", "result": result.text}
|
39 |
+
|
40 |
+
def id_liveness(path):
|
41 |
+
# Convert PIL image to bytes to send in POST request
|
42 |
+
img_bytes = io.BytesIO()
|
43 |
+
path.save(img_bytes, format="JPEG")
|
44 |
+
img_bytes.seek(0)
|
45 |
+
|
46 |
+
url = "http://127.0.0.1:9000/process_image"
|
47 |
+
files = {'image': img_bytes}
|
48 |
+
result = requests.post(url=url, files=files)
|
49 |
+
return proc_output(result)
|
50 |
+
|
51 |
+
with gr.Blocks() as demo:
|
52 |
+
gr.Markdown(
|
53 |
+
"""
|
54 |
+
# ID Document Liveness Detection
|
55 |
+
Contact us at https://faceonlive.com for issues and support.<br/><br/>
|
56 |
+
** For security and privacy, kindly refrain from uploading real ID card or credit card information on this platform.
|
57 |
+
"""
|
58 |
+
)
|
59 |
+
with gr.Row():
|
60 |
+
with gr.Column():
|
61 |
+
image_input = gr.Image(type='pil')
|
62 |
+
gr.Examples(['examples/1.jpg', 'examples/2.jpg', 'examples/3.jpg'],
|
63 |
+
inputs=image_input)
|
64 |
+
process_button = gr.Button("ID Liveness Detection")
|
65 |
+
|
66 |
+
with gr.Column():
|
67 |
+
json_output = gr.JSON()
|
68 |
+
|
69 |
+
process_button.click(id_liveness, inputs=image_input, outputs=[json_output])
|
70 |
+
|
71 |
+
demo.launch(server_name="0.0.0.0")
|
examples/1.jpg
ADDED
examples/2.jpg
ADDED
examples/3.jpg
ADDED
Git LFS Details
|
idlivesdk.py
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
from ctypes import *
|
4 |
+
from numpy.ctypeslib import ndpointer
|
5 |
+
|
6 |
+
libPath = os.path.abspath(os.path.dirname(__file__)) + '/lib/idlivesdk.so'
|
7 |
+
libidlivesdk = cdll.LoadLibrary(libPath)
|
8 |
+
|
9 |
+
getHWID = libidlivesdk.getHWID
|
10 |
+
getHWID.argtypes = []
|
11 |
+
getHWID.restype = c_char_p
|
12 |
+
|
13 |
+
setLicenseKey = libidlivesdk.setLicenseKey
|
14 |
+
setLicenseKey.argtypes = [c_char_p]
|
15 |
+
setLicenseKey.restype = c_int32
|
16 |
+
|
17 |
+
initSDK = libidlivesdk.initSDK
|
18 |
+
initSDK.argtypes = [c_char_p]
|
19 |
+
initSDK.restype = c_int32
|
20 |
+
|
21 |
+
processImage = libidlivesdk.processImage
|
22 |
+
processImage.argtypes = [ndpointer(c_ubyte, flags='C_CONTIGUOUS'), c_int32, c_int32]
|
23 |
+
processImage.restype = c_char_p
|
24 |
+
|
lib/libDocSdk.so
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:9e4fb0d2e876a1ef5d30ba988ac09d295ce9a38572479ae6dce6cf0c3f2ecb01
|
3 |
+
size 22300226
|
lib/libidlivesdk.so
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5acf23f4525ca1b24c9ba8e59a37cf72b4a4e601c5fa0d4a0079aae86ac21ee1
|
3 |
+
size 4194187
|
lib/libopenvino.so.2300
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:42d0084280661a5138d9b2cbfba28c6d0df9e85c9d371b96d471b1e3b403e9cd
|
3 |
+
size 21726552
|
lib/libopenvino.so.2330
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:3be612fe4606381cfddf1198b3ab1a851c0fdbd8490ddb101577f82b1678ce78
|
3 |
+
size 22302048
|
lib/libopenvino_intel_cpu_plugin.so
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:1de80e57d72249ab45de0f0ab807f729a86360248ca4f4d23c203882e7736f21
|
3 |
+
size 45820616
|
lib/libopenvino_ir_frontend.so.2330
ADDED
Binary file (594 kB). View file
|
|
lib/libopenvino_onnx_frontend.so.2300
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:924bb07b0964f3657a51772fc788c2a44d028439d6898d5d7e9ab889f34660de
|
3 |
+
size 5568776
|
lib/libopyenv.so
ADDED
Binary file (26.8 kB). View file
|
|
lib/libopyhas.so
ADDED
Binary file (17.2 kB). View file
|
|
lib/libtbb.so.12
ADDED
Binary file (366 kB). View file
|
|
lib/plugins.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<ie>
|
2 |
+
<plugins>
|
3 |
+
<plugin name="CPU" location="libopenvino_intel_cpu_plugin.so">
|
4 |
+
</plugin>
|
5 |
+
</plugins>
|
6 |
+
</ie>
|
requirements.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask
|
2 |
+
flask-cors
|
3 |
+
gradio==5.4.0
|
4 |
+
datadog_api_client
|
5 |
+
opencv-python
|
6 |
+
numpy==1.24.4
|
run.sh
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
cd /app
|
4 |
+
exec python3 app.py &
|
5 |
+
sleep 10
|
6 |
+
exec python3 demo.py
|