Trong phần 1, ta đã xây dựng hệ thống AI cho server. Trong phần 2, ta sẽ xây dựng server cho bài toán.
Hãy tạo các file:
config.py
utils.py
main.py
Dockerfile
Trước khi đi vào coding cụ thể, mình muốn giải thích qua các giải quyết logic của mình ở server. Mỗi ngày, khi khởi động hệ thống sẽ tạo ra 1 folder của ngày hôm đó, bên trong là các folder từng quận để lưu trữ mặt người đến lấy gạo. Lí do mà mình không sử dụng database là vì trong này chúng ta chỉ cần lưu trữ ảnh thôi. Nếu bạn muốn “fancy” hơn có thể cân nhắc đến các storage như Minio, …
Coding
Đầu tiên, ta hãy viết file config.py
. Trong file này chúng ta sẽ có các giá trị cần thiết cho server thay vì mỗi nơi bạn config 1 tí.
1 2 3 4 5 6 7 8 9 10 11 12 13 | # Face detect_config = {'image_size': 160, 'keep_all': False} search_config = {'threshold': 0.5, 'dim': 512, 'threshold': 0.7} # Geometry district = 'Tây Hồ' wards = ['Bưởi', 'Nhật Tân', 'Phú Thượng', 'Quảng An', 'Thụy Khuê', 'Tú Liên', 'Xuân La', 'Yên Phụ', ] #Data save path data_path = 'data' |
Trên cùng là ta config các tham số cho 2 class mà ta đã viết ở phần 1. Tiếp theo là về các địa điểm địa lý như Quận và các phường. Việc lưu dữ liệu riêng biệt như thế này có thể giúp bạn triển khai thêm cả ví dụ một hệ thống thống kê người đi mua gọa các phương ra sao, …
Còn data_path
: vị trí root cảu nơi lưu trữ ảnh.
Tiếp theo là, file utils.py
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | import sys sys.path.append('./backend') # nopep8 import os import csv from datetime import date import config as cfg def init_folder(root, wards): today = date.today() # dd/mm/YY d1 = today.strftime("%d-%m-%Y") os.makedirs(root, exist_ok=True) new_root = os.path.join(root, d1) os.makedirs(new_root, exist_ok=True) ward_path = [os.path.join(new_root, ward) for ward in wards] for wp in ward_path: os.makedirs(wp, exist_ok=True) os.makedirs(wp+'/false', exist_ok=True) os.makedirs(wp+'/true', exist_ok=True) return new_root |
Hàm trên có tác dụng tạo ra các folder của ngày hôm đó theo dạng như dưới đây:
Giờ thì chúng ta sẽ xây dựng server FLASK ở trên main.py
:
- Khởi tạo app Flask và các biến:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | # initialize our Flask application app = Flask(__name__) fd = None fs = None today_dir = None index = 0 img_path_map = list() def init(): # Init all variable global fs, fd, today_dir, index, img_path_map fs = FaceSearcher(**cfg.search_config) fd = FaceDetector(**cfg.detect_config) # Make folder today_dir = init_folder(cfg.data_path, cfg.wards) # If data exist ==> add them to graph w_paths = [os.path.join(today_dir, w, 'true') for w in cfg.wards] features = [] for w_path in w_paths: for image_name in os.listdir(w_path): image_path = os.path.join(w_path, image_name) # Add path to path map img_path_map.append(image_path) # convert image to pytorch tensor image = pil_loader(image_path) image = ToTensor()(image) # extract all tensor = fd.extract_feature(image) # print(tensor.shape) features.append(tensor) index += 1 # Add to graph print('Getting {} images'.format(index)) if index > 0: features = np.array(features) # print(features.shape) fs.add_faces(features, np.arange(index)) |
Ta sẽ khởi tạo các biến như mô hình nhận diện mặt và đồ thị tìm kiếm, … Tất cả đượ khởi tọa trong hàm init. Sau khi khởi tạo, ta sẽ thêm tất cả các ảnh đúng cảu ngày hôm đó vào đồ thị , trừng hợp server tắt giữa chừng và khởi động lại.
Các hàm phụ, mọi người nhìn cũng biết tác dụng rồi phải không.
1 2 3 4 5 6 7 8 9 | @app.route("/wards", methods=["GET"]) def get_wards(): return jsonify({'wards': cfg.wards}) @app.route("/district", methods=["GET"]) def get_district(): return jsonify({'district': cfg.district}) |
Và API hàm xử lý ảnh:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | def random_string(stringLength=5): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(stringLength)) @app.route("/face", methods=["POST"]) def analyze_face(): global index, img_path_map data = {"success": False} if request.form and request.files: ward = request.form.get('ward') image_data = request.files['image'].read() # REformat image image = Image.open(io.BytesIO(image_data)).convert('RGB') # Set image path false_path = os.path.join( today_dir, ward, 'false', random_string())+'.jpg' face = fd.extract_face(image, false_path) s_idx = fs.query_faces(face) print('nQuery result: {}n') if s_idx is None: data['permission'] = True data['success'] = True face = np.expand_dims(face, axis=0) fs.add_faces(face, np.array([index])) # TODO: Save them true_path = os.path.join( today_dir, ward, 'true', str(index))+'.jpg' os.rename(false_path, true_path) index += 1 img_path_map.append(true_path) print('Saved ', true_path) else: data['permission'] = False data['success'] = True print('Saved ', false_path) return jsonify(data) |
Ở đây mình sẽ lưu toàn bộ ảnh được query đến. Nếu chính xác sẽ ở thư mục true, sai thì sẽ là nằm ở thwu mục false. Mình nghĩ đây là một thói quen tốt để lưu toàn bộ dữ liệu lại cho dù có sai, vì dữ liệu có thể chuyển đội sang các bài toán khác nữa. (Hoặc là bán )
Giờ thì khởi tạo và chạy nào:
1 2 3 4 | if __name__ == "__main__": init() app.run(host='0.0.0.0', debug=True, port=3500) |
Các bạn có thể sử dụng flask thay thế cho việc viết main để chạy python như vậy. Nhưng mình chỉ lưu ý các bạn để host là 0.0.0.0
thay vì mặc định nhé, vì mặc định localhost thì khi sử dụng docker sẽ không expose được cổng ra ngoài đâu.
Viết Dockerfile
:
- Hãy cài đặt pipreqs để tạo ra file reuirements.txt gồm các thư viện cần thiết, sau đó chạy lệnh pipreqs .
- Viết nội dung Dockerfile như dưới đây
1 2 3 4 5 6 7 8 9 10 11 | FROM python:3.6.9 WORKDIR /app/backend COPY ./requirements.txt ./requirements.txt RUN pip install -r requirements.txt COPY . . # start app CMD ["python", "main.py"] |
Kết quả
Đây là vài kết quả mà bạn có sau khi xây dụng xong server:
api /district
api /wards
api /face
Vậy là ta đã hoàn thiện phần server, phần cuối cùng chúng ta sẽ xây dựng hệ thống Frontend bằng ReactJS.