Zhu-FaceOnLive commited on
Commit
666d384
1 Parent(s): 0a6e93e

Initial commit.

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ 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
+ ocrengine/libttvcore.so filter=lfs diff=lfs merge=lfs -text
37
+ ocrengine/libttvifchecker.so filter=lfs diff=lfs merge=lfs -text
38
+ ocrengine/libttvocrengine.so filter=lfs diff=lfs merge=lfs -text
39
+ ocrengine/ttvocrsrv filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:20.04
2
+ RUN ln -snf /usr/share/zoneinfo/$CONTAINER_TIMEZONE /etc/localtime && echo $CONTAINER_TIMEZONE > /etc/timezone
3
+ RUN apt-get update -y
4
+ RUN apt-get install -y python3 python3-pip python3-opencv libcurl4-openssl-dev libssl-dev libpcsclite-dev psmisc
5
+ RUN mkdir -p /home/faceonlive_id_recognition
6
+ WORKDIR /home/faceonlive_id_recognition
7
+ COPY ./requirements.txt .
8
+ COPY ./ocrengine ./ocrengine
9
+ COPY ./ocrengine/libimutils.so /usr/lib
10
+ COPY ./ocrengine/libttvcore.so /usr/lib
11
+ COPY ./app.py .
12
+ COPY ./demo.py .
13
+ COPY ./run.sh .
14
+ RUN pip3 install -r requirements.txt
15
+ RUN chmod a+x ./ocrengine/ttvocrsrv run.sh
16
+ CMD ["./run.sh"]
17
+ EXPOSE 8080
app.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ sys.path.append('.')
3
+
4
+ from flask import Flask, render_template, request, jsonify, send_from_directory
5
+ from time import gmtime, strftime
6
+ import os
7
+ import base64
8
+ import json
9
+ import uuid
10
+ import cv2
11
+ import numpy as np
12
+
13
+ from ocrengine.ocrengine import TTVOcrGetHWID
14
+ from ocrengine.ocrengine import TTVOcrSetActivation
15
+ from ocrengine.ocrengine import TTVOcrInit
16
+ from ocrengine.ocrengine import TTVOcrProcess
17
+ from ocrengine.ocrengine import TTVOcrCreditCard
18
+ from ocrengine.ocrengine import TTVOcrBarCode
19
+ from ocrengine.ocrengine import ttv_if_checker
20
+
21
+ app = Flask(__name__)
22
+
23
+ ocrHWID = TTVOcrGetHWID()
24
+ licenseKey = os.environ.get("LICENSE_KEY")
25
+ ocrRet = TTVOcrSetActivation(licenseKey.encode('utf-8'))
26
+ print('ocr activation: ', ocrRet.decode('utf-8'))
27
+
28
+ dictPath = os.path.abspath(os.path.dirname(__file__)) + '/ocrengine/dict'
29
+ ocrRet = TTVOcrInit(dictPath.encode('utf-8'))
30
+ print('ocr engine init: ', ocrRet.decode('utf-8'))
31
+
32
+ @app.route('/ocr/idcard', methods=['POST'])
33
+ def ocr_idcard():
34
+ file1 = request.files['image1']
35
+
36
+ file_name1 = uuid.uuid4().hex[:6]
37
+ save_path1 = '/tmp/' + file_name1 + '_' + file1.filename
38
+ file1.save(save_path1)
39
+
40
+ file_path1 = os.path.abspath(save_path1)
41
+
42
+ if 'image2' not in request.files:
43
+ file_path2 = ''
44
+ else:
45
+ file2 = request.files['image2']
46
+
47
+ file_name2 = uuid.uuid4().hex[:6]
48
+ save_path2 = '/tmp/' + file_name2 + '_' + file2.filename
49
+ file2.save(save_path2)
50
+
51
+ file_path2 = os.path.abspath(save_path2)
52
+
53
+
54
+ ocrResult = TTVOcrProcess(file_path1.encode('utf-8'), file_path2.encode('utf-8'))
55
+ ocrResDict = json.loads(ocrResult)
56
+ status = "ok"
57
+
58
+ if_check = ttv_if_checker(file_path1.encode('utf-8'))
59
+ response = jsonify({"status": status, "data": ocrResDict, "authenticity": if_check})
60
+
61
+ os.remove(file_path1)
62
+ if 'image2' in request.files:
63
+ os.remove(file_path2)
64
+
65
+ response.status_code = 200
66
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
67
+ return response
68
+
69
+ @app.route('/ocr/idcard_base64', methods=['POST'])
70
+ def ocr_idcard_base64():
71
+ content = request.get_json()
72
+ imageBase64 = content['image']
73
+
74
+ file_name = uuid.uuid4().hex[:6]
75
+ save_path = '/tmp/' + file_name
76
+ with open(save_path, "wb") as fh:
77
+ fh.write(base64.b64decode(imageBase64))
78
+
79
+ file_path = os.path.abspath(save_path)
80
+
81
+ ocrResult = TTVOcrProcess(file_path.encode('utf-8'))
82
+ ocrResDict = json.loads(ocrResult)
83
+ status = "ok"
84
+
85
+ if_check = ttv_if_checker(file_path.encode('utf-8'))
86
+ response = jsonify({"status": status, "data": ocrResDict, "authenticity": if_check})
87
+
88
+ os.remove(file_path)
89
+
90
+ response.status_code = 200
91
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
92
+ return response
93
+
94
+
95
+ @app.route('/ocr/credit', methods=['POST'])
96
+ def ocr_credit():
97
+ file = request.files['image']
98
+ print('ocr_credit ', file)
99
+
100
+ image = cv2.imdecode(np.fromstring(file.read(), np.uint8), cv2.IMREAD_COLOR)
101
+ file_name = uuid.uuid4().hex[:6]
102
+ save_path = '/tmp/' + file_name + '.png'
103
+ cv2.imwrite(save_path, image)
104
+
105
+ file_path = os.path.abspath(save_path)
106
+
107
+ ocrResult = TTVOcrCreditCard(file_path.encode('utf-8'))
108
+ ocrResDict = json.loads(ocrResult)
109
+ status = "ok"
110
+
111
+ response = jsonify({"status": status, "data": ocrResDict})
112
+
113
+ os.remove(file_path)
114
+
115
+ response.status_code = 200
116
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
117
+ return response
118
+
119
+ @app.route('/ocr/credit_base64', methods=['POST'])
120
+ def ocr_credit_base64():
121
+ print('ocr_credit_base64');
122
+ content = request.get_json()
123
+ imageBase64 = content['image']
124
+ image = cv2.imdecode(np.frombuffer(base64.b64decode(imageBase64), dtype=np.uint8), cv2.IMREAD_COLOR)
125
+
126
+ file_name = uuid.uuid4().hex[:6]
127
+ save_path = '/tmp/' + file_name + '.png'
128
+ cv2.imwrite(save_path, image)
129
+
130
+ file_path = os.path.abspath(save_path)
131
+
132
+ ocrResult = TTVOcrCreditCard(file_path.encode('utf-8'))
133
+ ocrResDict = json.loads(ocrResult)
134
+ status = "ok"
135
+
136
+ response = jsonify({"status": status, "data": ocrResDict})
137
+
138
+ os.remove(file_path)
139
+
140
+ response.status_code = 200
141
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
142
+ return response
143
+
144
+ @app.route('/ocr/barcode', methods=['POST'])
145
+ def ocr_barcode():
146
+ file = request.files['image']
147
+ print('ocr_barcode ', file)
148
+
149
+ image = cv2.imdecode(np.fromstring(file.read(), np.uint8), cv2.IMREAD_COLOR)
150
+ file_name = uuid.uuid4().hex[:6]
151
+ save_path = '/tmp/' + file_name + '.png'
152
+ cv2.imwrite(save_path, image)
153
+
154
+ file_path = os.path.abspath(save_path)
155
+
156
+ ocrResult = TTVOcrBarCode(file_path.encode('utf-8'))
157
+ ocrResDict = json.loads(ocrResult)
158
+ status = "ok"
159
+
160
+ response = jsonify({"status": status, "data": ocrResDict})
161
+
162
+ os.remove(file_path)
163
+
164
+ response.status_code = 200
165
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
166
+ return response
167
+
168
+ @app.route('/ocr/barcode_base64', methods=['POST'])
169
+ def ocr_barcode_base64():
170
+ content = request.get_json()
171
+ imageBase64 = content['image']
172
+ image = cv2.imdecode(np.frombuffer(base64.b64decode(imageBase64), dtype=np.uint8), cv2.IMREAD_COLOR)
173
+
174
+ file_name = uuid.uuid4().hex[:6]
175
+ save_path = '/tmp/' + file_name + '.png'
176
+ cv2.imwrite(save_path, image)
177
+
178
+ file_path = os.path.abspath(save_path)
179
+ print('file_path: ', file_path)
180
+
181
+ ocrResult = TTVOcrBarCode(file_path.encode('utf-8'))
182
+ ocrResDict = json.loads(ocrResult)
183
+ status = "ok"
184
+
185
+ response = jsonify({"status": status, "data": ocrResDict})
186
+
187
+ os.remove(file_path)
188
+
189
+ response.status_code = 200
190
+ response.headers["Content-Type"] = "application/json; charset=utf-8"
191
+
192
+ return response
193
+
194
+
195
+
196
+ if __name__ == '__main__':
197
+ port = int(os.environ.get("PORT", 8000))
198
+ app.run(host='0.0.0.0', port=port)
demo.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ from PIL import Image
5
+
6
+ def idcard_recognition(frame1, frame2):
7
+ url = "http://127.0.0.1:8000/ocr/idcard"
8
+ files = None
9
+ if frame1 is not None and frame2 is not None:
10
+ files = {'image1': open(frame1, 'rb'), 'image2': open(frame2, 'rb')}
11
+ elif frame1 is not None and frame2 is None:
12
+ files = {'image1': open(frame1, 'rb')}
13
+ elif frame1 is None and frame2 is not None:
14
+ files = {'image1': open(frame2, 'rb')}
15
+ else:
16
+ return ['', None]
17
+
18
+ print(frame1, files)
19
+ r = requests.post(url=url, files=files)
20
+
21
+ images = None
22
+ resultValues = {}
23
+ table_value = ""
24
+ for key, value in r.json().items():
25
+
26
+ if key == 'data':
27
+ if 'image' in value:
28
+ del value['image']
29
+ resultValues[key] = value
30
+ else:
31
+ resultValues[key] = value
32
+
33
+
34
+ if 'data' in r.json():
35
+ for key, value in r.json()['data'].items():
36
+ if key == 'image':
37
+ for image_key, image_value in value.items():
38
+ row_value = ("<tr>"
39
+ "<td>{key}</td>"
40
+ "<td><img src=""data:image/png;base64,{base64_image} width = '200' height= '100' /></td>"
41
+ "</tr>".format(key=image_key, base64_image=image_value))
42
+ table_value = table_value + row_value
43
+
44
+ images = ("<table>"
45
+ "<tr>"
46
+ "<th>Field</th>"
47
+ "<th>Image</th>"
48
+ "</tr>"
49
+ "{table_value}"
50
+ "</table>".format(table_value=table_value))
51
+
52
+ json_result = json.dumps(resultValues, indent=4)
53
+ return [json_result, images]
54
+
55
+ def barcode_recognition(frame):
56
+ url = "http://127.0.0.1:8000/ocr/barcode"
57
+ files = None
58
+ if frame is None:
59
+ return ['', None]
60
+
61
+ files = {'image': open(frame, 'rb')}
62
+ r = requests.post(url=url, files=files)
63
+
64
+ images = None
65
+ resultValues = {}
66
+ table_value = ""
67
+ for key, value in r.json().items():
68
+
69
+ if key == 'data':
70
+ if 'image' in value:
71
+ del value['image']
72
+ resultValues[key] = value
73
+ else:
74
+ resultValues[key] = value
75
+
76
+
77
+ if 'data' in r.json():
78
+ for key, value in r.json()['data'].items():
79
+ if key == 'image':
80
+ for image_key, image_value in value.items():
81
+ row_value = ("<tr>"
82
+ "<td>{key}</td>"
83
+ "<td><img src=""data:image/png;base64,{base64_image} width = '200' height= '100' /></td>"
84
+ "</tr>".format(key=image_key, base64_image=image_value))
85
+ table_value = table_value + row_value
86
+
87
+ images = ("<table>"
88
+ "<tr>"
89
+ "<th>Field</th>"
90
+ "<th>Image</th>"
91
+ "</tr>"
92
+ "{table_value}"
93
+ "</table>".format(table_value=table_value))
94
+
95
+ json_result = json.dumps(resultValues, indent=4)
96
+ return [json_result, images]
97
+
98
+ def credit_recognition(frame):
99
+ url = "http://127.0.0.1:8000/ocr/credit"
100
+ files = None
101
+ if frame is None:
102
+ return ['', None]
103
+
104
+ files = {'image': open(frame, 'rb')}
105
+ r = requests.post(url=url, files=files)
106
+
107
+ images = None
108
+ resultValues = {}
109
+ table_value = ""
110
+ for key, value in r.json().items():
111
+
112
+ if key == 'data':
113
+ if 'image' in value:
114
+ del value['image']
115
+ resultValues[key] = value
116
+ else:
117
+ resultValues[key] = value
118
+
119
+
120
+ if 'data' in r.json():
121
+ for key, value in r.json()['data'].items():
122
+ if key == 'image':
123
+ for image_key, image_value in value.items():
124
+ row_value = ("<tr>"
125
+ "<td>{key}</td>"
126
+ "<td><img src=""data:image/png;base64,{base64_image} width = '200' height= '100' /></td>"
127
+ "</tr>".format(key=image_key, base64_image=image_value))
128
+ table_value = table_value + row_value
129
+
130
+ images = ("<table>"
131
+ "<tr>"
132
+ "<th>Field</th>"
133
+ "<th>Image</th>"
134
+ "</tr>"
135
+ "{table_value}"
136
+ "</table>".format(table_value=table_value))
137
+
138
+ json_result = json.dumps(resultValues, indent=4)
139
+ return [json_result, images]
140
+
141
+ with gr.Blocks() as demo:
142
+ gr.Markdown(
143
+ """
144
+ # ID Document Recognition
145
+ Get your own ID Document Recognition Server by duplicating this space.<br/>
146
+ Contact us at contact@faceonlive.com for issues and support.<br/>
147
+ """
148
+ )
149
+ with gr.TabItem("ID Card Recognition"):
150
+ with gr.Row():
151
+ with gr.Column(scale=3):
152
+ id_image_input1 = gr.Image(type='filepath', label='Front')
153
+ id_image_input2 = gr.Image(type='filepath', label='Back')
154
+ id_recognition_button = gr.Button("ID Card Recognition")
155
+ with gr.Column(scale=5):
156
+ id_result_output = gr.JSON()
157
+
158
+ with gr.Column(scale=2):
159
+ image_result_output = gr.HTML()
160
+
161
+ id_recognition_button.click(idcard_recognition, inputs=[id_image_input1, id_image_input2], outputs=[id_result_output, image_result_output])
162
+ with gr.TabItem("Barcode Recognition"):
163
+ with gr.Row():
164
+ with gr.Column(scale=3):
165
+ barcode_image_input = gr.Image(type='filepath')
166
+ barcode_recognition_button = gr.Button("Barcode Recognition")
167
+ with gr.Column(scale=5):
168
+ barcode_result_output = gr.JSON()
169
+
170
+ with gr.Column(scale=2):
171
+ image_result_output = gr.HTML()
172
+
173
+ barcode_recognition_button.click(barcode_recognition, inputs=barcode_image_input, outputs=[barcode_result_output, image_result_output])
174
+
175
+ with gr.TabItem("Credit Card Recognition"):
176
+ with gr.Row():
177
+ with gr.Column(scale=3):
178
+ credit_image_input = gr.Image(type='filepath')
179
+ credit_recognition_button = gr.Button("Credit Card Recognition")
180
+ with gr.Column(scale=5):
181
+ credit_result_output = gr.JSON()
182
+
183
+ with gr.Column(scale=2):
184
+ image_result_output = gr.HTML()
185
+
186
+ credit_recognition_button.click(credit_recognition, inputs=credit_image_input, outputs=[credit_result_output, image_result_output])
187
+
188
+ demo.launch(server_name="0.0.0.0", server_port=7860)
ocrengine/dict/data1.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3984b15b29f28886e3ce8634b3fdc93633becb9f34a0c9aed152ce2b7126d5a9
3
+ size 142606336
ocrengine/dict/data2.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6bdd1d2f0701d443e771b04eef5adb7b752961b6ace6f60eb1c2c5d533a65fda
3
+ size 9212703
ocrengine/dict/data3.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0244637e40fa09ad753ab0299d39360acc8b69afd3883296def40aeaa984ec26
3
+ size 188743680
ocrengine/dict/data4.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:7f7d151420087f4c2b5c7d47a7d37310ae4c40fc9f2cb129866e8ff1132c0d2f
3
+ size 96905216
ocrengine/libimutils.so ADDED
Binary file (412 kB). View file
 
ocrengine/libttvcore.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2ca8d29bc483e307ad8f4b2e92acc7b2a97460e0f8ddbbce05b8d7a7764d3c7c
3
+ size 95098400
ocrengine/libttvifchecker.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e5e3b916d3674b7b41a0b8f3f1a6331d83057a37012074e2c119362fcb9dd5a9
3
+ size 1613856
ocrengine/libttvocrengine.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d92e12ef538a606ebf35e83d362bf76024e297220b20c707721057ee9fcd688f
3
+ size 4010600
ocrengine/ocrengine.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes, ctypes.util
2
+ from ctypes import *
3
+ from numpy.ctypeslib import ndpointer
4
+ import sys
5
+ import os
6
+
7
+ dll_path = os.path.abspath(os.path.dirname(__file__)) + '/libttvocrengine.so'
8
+ ocr_engine = cdll.LoadLibrary(dll_path)
9
+
10
+ TTVOcrInit = ocr_engine.TTVOcrInit
11
+ TTVOcrInit.argtypes = [ctypes.c_char_p]
12
+ TTVOcrInit.restype = ctypes.c_char_p
13
+
14
+ TTVOcrProcess = ocr_engine.TTVOcrProcess
15
+ TTVOcrProcess.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
16
+ TTVOcrProcess.restype = ctypes.c_char_p
17
+
18
+ TTVOcrCreditCard = ocr_engine.TTVOcrCreditCard
19
+ TTVOcrCreditCard.argtypes = [ctypes.c_char_p]
20
+ TTVOcrCreditCard.restype = ctypes.c_char_p
21
+
22
+ TTVOcrBarCode = ocr_engine.TTVOcrBarCode
23
+ TTVOcrBarCode.argtypes = [ctypes.c_char_p]
24
+ TTVOcrBarCode.restype = ctypes.c_char_p
25
+
26
+ TTVOcrGetHWID = ocr_engine.TTVOcrGetHWID
27
+ TTVOcrGetHWID.argtypes = []
28
+ TTVOcrGetHWID.restype = ctypes.c_char_p
29
+
30
+ TTVOcrSetActivation = ocr_engine.TTVOcrSetActivation
31
+ TTVOcrSetActivation.argtypes = []
32
+ TTVOcrSetActivation.restype = ctypes.c_char_p
33
+
34
+ dll_path = os.path.abspath(os.path.dirname(__file__)) + '/libttvifchecker.so'
35
+ if_engine = cdll.LoadLibrary(dll_path)
36
+
37
+ ttv_if_checker = if_engine.ttv_if_checker
38
+ ttv_if_checker.argtypes = [ctypes.c_char_p]
39
+ ttv_if_checker.restype = ctypes.c_int32
ocrengine/ttvocrsrv ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:54a0ada00cad7cec6d8521715a206f279f3624f3046ca9e307d569161ffc027c
3
+ size 1225592
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ opencv-python
2
+ flask
3
+ flask-cors
4
+ gradio
5
+ numpy==1.20.3
run.sh ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ #!/bin/bash
2
+
3
+ exec ./ocrengine/ttvocrsrv &
4
+ exec python3 app.py &
5
+ exec python3 demo.py