Deployment reference: https://github.com/PaddlePaddle/FastDeploy/blob/develop/tutorials/multi_thread/python/pipeline/README_CN.md
Install
cpu: pip install fastdeploy-python
gpu: pip install fastdeploy-gpu-python
#Download deployment sample code git clone https://github.com/PaddlePaddle/FastDeploy.git cd FastDeploy/tutorials/multi_thread/python/pipeline # Download model, image and dictionary files wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_det_infer.tar tar xvf ch_PP-OCRv3_det_infer.tar wget https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_infer.tar tar -xvf ch_ppocr_mobile_v2.0_cls_infer.tar wget https://paddleocr.bj.bcebos.com/PP-OCRv3/chinese/ch_PP-OCRv3_rec_infer.tar tar xvf ch_PP-OCRv3_rec_infer.tar wget https://gitee.com/paddlepaddle/PaddleOCR/raw/release/2.6/doc/imgs/12.jpg wget https://gitee.com/paddlepaddle/PaddleOCR/raw/release/2.6/ppocr/utils/ppocr_keys_v1.txt
Order:
Multithreading
python multi_thread_process_ocr.py --det_model ch_PP-OCRv3_det_infer --cls_model ch_ppocr_mobile_v2.0_cls_infer --rec_model ch_PP-OCRv3_rec_infer --rec_label_file ppocr_keys_v1.txt --image_path xxx/xxx --device gpu --thread_num 3
multi-Progress
Python Multhread_process_ocr.py-Det_model ch_pp-OCRV3_Infer-CLS_MODEL CH_MOBILE_V2.0_Infer-R_MODEL ch_PP-OCRV3_re c_infer -REC_LABEL_FILE PPOCR_KEYS_V1.TXT -Image_path xxx/xxx --Device GPU -Use_multi_process True -Process_num 3
Question
There is a bug in multi-process image allocation
File: multi_thread_process_ocr.py
Original code: 270 lines
Modify it as follows, remove 1
ModuleNotFoundError: No module named example’
Because the installation package is incorrect, fastdeploy and fastdeploy-python are not the same package.
CUDA error(3), initialization error.
---------------------- Error Message Summary: ---------------------- ExternalError: CUDA error(3), initialization error. [Hint: Please search for the error code(3) on website (https://docs.nvidia.com/cuda/cuda-runtime-api/group__CUDART__TYPES.html#group__CUDART__TYPES_1g3f51e3575c2178246db0a94a430e0038) to get Nvidia's official solution and advice about CUDA Error.] (at /home/fastdeploy/develop/paddle_build/v0.0.0/Paddle/paddle/phi/backends/gpu/cuda/cuda_info.cc:251)
refer to:
PaddlePaddle–Problem Solution: When using Python multiprocessing, an error occurs: CUDA error(3), initialization error.
https://github.com/PaddlePaddle/PaddleDetection/issues/2241
Paddle related modules are only referenced in the method, and these modules must be import
outside the multi-process
flask deployment
Send the image base64 encoding of the list type, and return the string of the list type
Note that the server-side files are placed in the FastDeploy/tutorials/multi_thread/python/pipeline directory
Create a server
from threading import Thread import cv2 import os from multiprocessing import Pool importsys import fastdeploy as fd import numpy as np import base64 from PIL import Image from io import BytesIO from sqlalchemy import create_engine, text from flask import Flask, request, jsonify import argparse import ast # watch -n 0.1 nvidia-smi def parse_arguments(): parser = argparse.ArgumentParser() parser.add_argument( "--det_model", #required=True, type=str, default='ch_PP-OCRv3_det_infer', help="Path of Detection model of PPOCR.") parser.add_argument( "--cls_model", #required=True, type=str, default='ch_ppocr_mobile_v2.0_cls_infer', help="Path of Classification model of PPOCR.") parser.add_argument( "--rec_model", #required=True, type=str, default='ch_PP-OCRv3_rec_infer', help="Path of Recognition model of PPOCR.") parser.add_argument( "--rec_label_file", #required=True, type=str, default='ppocr_keys_v1.txt', help="Path of Recognition model of PPOCR.") # parser.add_argument( # "--image_path", #type=str, # required=True, # help="The directory or path or file list of the images to be predicted." # ) parser.add_argument( "--device", type=str, default='gpu', #cpu help="Type of inference device, support 'cpu', 'kunlunxin' or 'gpu'.") parser.add_argument( "--backend", type=str, default="default", help="Type of inference backend, support ort/trt/paddle/openvino, default 'openvino' for cpu, 'tensorrt' for gpu" ) parser.add_argument( "--device_id", type=int, default=0, help="Define which GPU card used to run model.") parser.add_argument( "--cpu_thread_num", type=int, default=9, help="Number of threads while inference on CPU.") parser.add_argument( "--cls_bs", type=int, default=1, help="Classification model inference batch size.") parser.add_argument( "--rec_bs", type=int, default=6, help="Recognition model inference batch size") parser.add_argument("--thread_num", type=int, default=1, help="thread num") parser.add_argument( "--use_multi_process", type=ast.literal_eval, default=True, help="Wether to use multi process.") parser.add_argument( "--process_num", type=int, default=5, help="process num") return parser.parse_args() def get_image_list(image_path): image_list = [] if os.path.isfile(image_path): image_list.append(image_path) # load image in a directory elif os.path.isdir(image_path): for root, dirs, files in os.walk(image_path): for f in files: image_list.append(os.path.join(root, f)) else: raise FileNotFoundError( '{} is not found. it should be a path of image, or a directory including images.'. format(image_path)) if len(image_list) == 0: raise RuntimeError( 'There are not image file in `--image_path`={}'.format(image_path)) return image_list def build_option(args): option = fd.RuntimeOption() if args.device.lower() == "gpu": option.use_gpu(args.device_id) option.set_cpu_thread_num(args.cpu_thread_num) if args.device.lower() == "kunlunxin": option.use_kunlunxin() return option if args.backend.lower() == "trt": assert args.device.lower( ) == "gpu", "TensorRT backend require inference on device GPU." option.use_trt_backend() elif args.backend.lower() == "pptrt": assert args.device.lower( ) == "gpu", "Paddle-TensorRT backend require inference on device GPU." option.use_trt_backend() option.enable_paddle_trt_collect_shape() option.enable_paddle_to_trt() elif args.backend.lower() == "ort": option.use_ort_backend() elif args.backend.lower() == "paddle": option. use_paddle_infer_backend() elif args.backend.lower() == "openvino": assert args.device.lower( ) == "cpu", "OpenVINO backend require inference on device CPU." option. use_openvino_backend() return option def load_model(args, runtime_option): # Detection model, detect text box det_model_file = os.path.join(args.det_model, "inference.pdmodel") det_params_file = os.path.join(args.det_model, "inference.pdiparams") # Classification model, direction classification, optional cls_model_file = os.path.join(args.cls_model, "inference.pdmodel") cls_params_file = os.path.join(args.cls_model, "inference.pdiparams") # Recognition model, text recognition model rec_model_file = os.path.join(args.rec_model, "inference.pdmodel") rec_params_file = os.path.join(args.rec_model, "inference.pdiparams") rec_label_file = args.rec_label_file # PPOCR's cls and rec models now support reasoning about a Batch of data # After defining the following two variables, they can be used to set the trt input shape, and after the PPOCR model is initialized, complete the Batch inference settings. cls_batch_size = 1 rec_batch_size = 6 # When using TRT, set dynamic shapes for the runtime of the three models and complete the creation of the models. # Note: After the detection model is created, you need to set the dynamic input of the classification model and create the classification model. The same goes for the recognition model. # If the user wants to change the input shape of the detection model, we recommend that the user set the length and height of the detection model to multiples of 32. det_option = runtime_option det_option.set_trt_input_shape("x", [1, 3, 64, 64], [1, 3, 640, 640], [1, 3, 960, 960]) # Users can save TRT engine files locally #det_option.set_trt_cache_file(args.det_model + "/det_trt_cache.trt") global det_model det_model = fd.vision.ocr.DBDetector( det_model_file, det_params_file, runtime_option=det_option) cls_option = runtime_option cls_option.set_trt_input_shape("x", [1, 3, 48, 10], [cls_batch_size, 3, 48, 320], [cls_batch_size, 3, 48, 1024]) # Users can save TRT engine files locally # cls_option.set_trt_cache_file(args.cls_model + "/cls_trt_cache.trt") global cls_model cls_model = fd.vision.ocr.Classifier( cls_model_file, cls_params_file, runtime_option=cls_option) rec_option = runtime_option rec_option.set_trt_input_shape("x", [1, 3, 48, 10], [rec_batch_size, 3, 48, 320], [rec_batch_size, 3, 48, 2304]) # Users can save TRT engine files locally #rec_option.set_trt_cache_file(args.rec_model + "/rec_trt_cache.trt") global rec_model rec_model = fd.vision.ocr.Recognizer( rec_model_file, rec_params_file, rec_label_file, runtime_option=rec_option) #Create PP-OCR and connect 3 models in series. cls_model is optional. If there is no requirement, it can be set to None. global ppocr_v3 ppocr_v3 = fd.vision.ocr.PPOCRv3( det_model=det_model, cls_model=cls_model, rec_model=rec_model) # Set the batch size for inference for the cls and rec models # This value can be -1, and 1 to positive infinity # When this value is -1, the batch size of the cls and rec models will be the same as the number of frames detected by the det model by default. ppocr_v3.cls_batch_size = cls_batch_size ppocr_v3.rec_batch_size = rec_batch_size def predict(model, img_list): result_list = [] # predict ppocr result for image in img_list: im = cv2.imread(image) result = model. predict(im) result_list.append(result) return result_list def process_predict(image): # predict ppocr result im = cv2.imread(image) result = ppocr_v3.predict(im) print(result) def process_predict_text(base64_str): image = base64_to_bgr(base64_str) result = ppocr_v3. predict(image) # print(result) return ''.join(result.text) #If you cannot directly return the OCR object, serialization will fail. def cv_show(img): ''' Show pictures @param img: @param name: @return: ''' cv2.namedWindow('name', cv2.WINDOW_KEEPRATIO) # cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO cv2.imshow('name', img) cv2.waitKey(0) cv2.destroyAllWindows() def base64_to_bgr(base64_str): base64_hex = base64.b64decode(base64_str) image = BytesIO(base64_hex) img = Image.open(image) if img.mode=='RGBA': width = img.width height = img.height img2 = Image.new('RGB', size=(width, height), color=(255, 255, 255)) img2. paste(img, (0, 0), mask=img) image_array = np.array(img2) else: image_array = np.array(img) image = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR) return image class WrapperThread(Thread): def __init__(self, func, args): super(WrapperThread, self).__init__() self.func = func self.args = args # self.result = self.func(*self.args) def run(self): self.result = self.func(*self.args) def get_result(self): return self.result def ocr_image_list(imgs_list): args = parse_arguments() # For the three models, use the same deployment configuration # Users can also configure according to their own needs runtime_option = build_option(args) if args.use_multi_process: process_num = args.process_num with Pool( process_num, initializer=load_model, initargs=(args, runtime_option)) as pool: # results = pool. map(process_predict_text, imgs_list) # pool. map(process_predict, imgs_list) # Further process the results for i, result in enumerate(results): print(i, result) else: load_model(args, runtime_option) threads = [] thread_num = args. thread_num image_num_each_thread = int(len(imgs_list) / thread_num) # unless you want independent model in each thread, actually model. clone() # is the same as model when creating thead because of the existence of # GIL(Global Interpreter Lock) in python. In addition, model. clone() will consume # additional memory to store independent member variables for i in range(thread_num): if i == thread_num - 1: t = WrapperThread( predict, args=(ppocr_v3.clone(), imgs_list[i * image_num_each_thread:])) else: t = WrapperThread( predict, args=(ppocr_v3.clone(), imgs_list[i * image_num_each_thread:(i + 1) * image_num_each_thread])) # - 1 threads.append(t) t. start() for i in range(thread_num): threads[i].join() for i in range(thread_num): for result in threads[i].get_result(): print('thread:', i, ', result: ', result) @app.route('/ocr/submit', methods=['POST']) def ocr(): args = parse_arguments() process_num = 1#args. process_num runtime_option = build_option(args) data = request.get_json() # Get Base64 data base64_str = data['img_base64'] with Pool( process_num, initializer=load_model, initargs=(args, runtime_option)) as pool: results = pool. map(process_predict_text, base64_str) # Return response response = {<!-- -->'message': 'Data received', 'result': results} return jsonify(response) import json import pandas as pd import time if __name__ == '__main__': app.run(host='192.168.xxx.xxx', port=5000)
client side
import base64 importsys import requests import json # read image file with open('./pic/img.png', 'rb') as image_file: # Read the image file content as a byte stream image_data = image_file.read() # Base64 encode the image byte stream img_base64 = base64.b64encode(image_data) data = {<!-- --> 'img_base64': [img_base64.decode('utf-8')] } headers = {<!-- --> 'Content-Type': 'application/json' } response = requests.post("http://192.168.xxx.xxx:5000/ocr/submit", data=json.dumps(data), headers = headers) if response.status_code == 200: result = response.json() print(result['result']) else: print('Error:', response.status_code)