From 04bba3ee4b9c2fa0cb1529b0b789046c21f34f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BD=95=E7=86=99?= Date: Fri, 25 Oct 2024 16:33:04 +0800 Subject: [PATCH] update rapidocr_api --- api/Dockerfile | 18 ++++++++++++++ api/README.md | 53 ++++++++++++++++++++++++++++++++++++++++ api/demo.py | 36 +++++++++++++++++++++++++-- api/rapidocr_api/main.py | 48 ++++++++++++++++++++++-------------- 4 files changed, 134 insertions(+), 21 deletions(-) create mode 100644 api/Dockerfile diff --git a/api/Dockerfile b/api/Dockerfile new file mode 100644 index 000000000..9a0ba263d --- /dev/null +++ b/api/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.10.11-slim-buster + +ENV DEBIAN_FRONTEND=noninteractive + +# 设置工作目录 +WORKDIR /app + +RUN pip install --no-cache-dir rapidocr_api -i https://mirrors.aliyun.com/pypi/simple + +RUN pip uninstall -y opencv-python && \ + pip install --no-cache-dir opencv-python-headless -i https://mirrors.aliyun.com/pypi/simple + +EXPOSE 9003 + +# 升级后可用 +# CMD ["bash", "-c", "rapidocr_api -ip 0.0.0.0 -p 9003 -workers 2"] +CMD ["rapidocr_api"] + diff --git a/api/README.md b/api/README.md index 3d153be04..367fa02e3 100644 --- a/api/README.md +++ b/api/README.md @@ -1 +1,54 @@ ### See [Documentation](https://rapidai.github.io/RapidOCRDocs/install_usage/rapidocr_api/usage/) + + +### API 修改说明 + +* uvicorn启动时,reload参数设置为False,避免反复加载; +* 增加了启动参数: workers,可启动多个实例,以满足多并发需求。 +* 可通过环境变量传递模型参数:det_model_path, cls_model_path, rec_model_path; +* 接口中可传入参数,控制是否使用检测、方向分类和识别这三部分的模型;客户端调用见`demo.py` +* 增加了Dockerfile,可自行构建镜像。 + +启动服务端: + +Windows下启动: + +```for win shell +set det_model_path=I:\models\图像相关\OCR\RapidOCR\PP-OCRv4\ch_PP-OCRv4_det_server_infer.onnx +set det_model_path= + +set rec_model_path=I:\models\图像相关\OCR\RapidOCR\PP-OCRv4\ch_PP-OCRv4_rec_server_infer.onnx +rapidocr_api +``` + +Linux下启动: +```shell +# 默认参数启动 +rapidocr_api + +# 指定参数:端口与进程数量; +rapidocr_api -ip 0.0.0.0 -p 9005 -workers 2 + +# 指定模型 +expert det_model_path=/mnt/sda1/models/PP-OCRv4/ch_PP-OCRv4_det_server_infer.onnx +expert rec_model_path=/mnt/sda1/models/PP-OCRv4/ch_PP-OCRv4_rec_server_infer.onnx +rapidocr_api -ip 0.0.0.0 -p 9005 -workers 2 +``` + + +客户端调用说明: +``` +cd api +python demo.py +``` + +构建镜像: +``` +cd api +sudo docker build -t="rapidocr_api:0.1.1" . +``` +启动镜像: + +``` +docker run -p 9003:9003 --name rapidocr_api1 --restart always -d rapidocr_api:0.1.1 +``` \ No newline at end of file diff --git a/api/demo.py b/api/demo.py index 865292d45..cbf160065 100644 --- a/api/demo.py +++ b/api/demo.py @@ -13,17 +13,49 @@ # print(response.text) +import time import base64 - import requests url = "http://localhost:9003/ocr" img_path = "../python/tests/test_files/ch_en_num.jpg" +# 方式一:使用base64编码传 +stime = time.time() with open(img_path, "rb") as fa: img_str = base64.b64encode(fa.read()) payload = {"image_data": img_str} -response = requests.post(url, data=payload, timeout=60) +response = requests.post(url, data=payload) #, timeout=60 print(response.json()) +etime = time.time() - stime +print(f'用时:{etime:.3f}秒') + +print('-'*40) + +# 方式二:使用文件上传方式 +stime = time.time() +with open(img_path, 'rb') as f: + file_dict = {'image_file': (img_path, f, 'image/png')} + response = requests.post(url, files=file_dict) #, timeout=60 + print(response.json()) + +etime = time.time() - stime +print(f'用时:{etime:.3f}秒') +print('-'*40) + +# 方式三:控制是否使用检测、方向分类和识别这三部分的模型; 不使用检测模型:use_det=False +stime = time.time() +img_path = "../python/tests/test_files/test_without_det.jpg" + +with open(img_path, 'rb') as f: + file_dict = {'image_file': (img_path, f, 'image/png')} + # 添加控制参数 + data = {"use_det":False, "use_cls":True, "use_rec":True} + response = requests.post(url, files=file_dict, data=data) #, timeout=60 + print(response.json()) + +etime = time.time() - stime +print(f'用时:{etime:.3f}秒') +print('-'*40) diff --git a/api/rapidocr_api/main.py b/api/rapidocr_api/main.py index 5ac113c3d..7c3a2c3d3 100644 --- a/api/rapidocr_api/main.py +++ b/api/rapidocr_api/main.py @@ -1,9 +1,11 @@ # -*- encoding: utf-8 -*- # @Author: SWHL # @Contact: liekkaskono@163.com + import argparse import base64 import io +import os import sys from pathlib import Path from typing import Dict @@ -19,37 +21,47 @@ class OCRAPIUtils: def __init__(self) -> None: - self.ocr = RapidOCR() + # 从环境变量中读取参数 + det_model_path = os.getenv("det_model_path", None) + cls_model_path = os.getenv("cls_model_path", None) + rec_model_path = os.getenv("rec_model_path", None) + + self.ocr = RapidOCR(det_model_path=det_model_path, cls_model_path=cls_model_path, rec_model_path=rec_model_path) - def __call__(self, img: Image.Image) -> Dict: + def __call__(self, img: Image.Image, use_det=None, use_cls=None, use_rec=None) -> Dict: img = np.array(img) - ocr_res, _ = self.ocr(img) + ocr_res, _ = self.ocr(img, use_det=use_det, use_cls=use_cls, use_rec=use_rec) if not ocr_res: return {} - out_dict = { - str(i): { - "rec_txt": rec, - "dt_boxes": dt_box, - "score": f"{score:.4f}", - } - for i, (dt_box, rec, score) in enumerate(ocr_res) - } + # 转换为字典格式: 兼容所有参数情况 + out_dict = {} + for i, dats in enumerate(ocr_res): + values = {} + for dat in dats: + if type(dat) == str: + values["rec_txt"] = dat + if type(dat) == np.float64: + values["score"] = f"{dat:.4f}" + if type(dat) == list: + values["dt_boxes"] = dat + out_dict[str(i)] = values + return out_dict app = FastAPI() processor = OCRAPIUtils() - @app.get("/") async def root(): return {"message": "Welcome to RapidOCR API Server!"} - @app.post("/ocr") -async def ocr(image_file: UploadFile = None, image_data: str = Form(None)): +async def ocr(image_file: UploadFile = None, image_data: str = Form(None), + use_det: bool = Form(None), use_cls: bool = Form(None), use_rec: bool = Form(None)): + if image_file: img = Image.open(image_file.file) elif image_data: @@ -60,19 +72,17 @@ async def ocr(image_file: UploadFile = None, image_data: str = Form(None)): raise ValueError( "When sending a post request, data or files must have a value." ) - - ocr_res = processor(img) + ocr_res = processor(img, use_det=use_det, use_cls=use_cls, use_rec=use_rec) return ocr_res - def main(): parser = argparse.ArgumentParser("rapidocr_api") parser.add_argument("-ip", "--ip", type=str, default="0.0.0.0", help="IP Address") parser.add_argument("-p", "--port", type=int, default=9003, help="IP port") + parser.add_argument('-workers', "--workers", type=int, default=1, help='number of worker process') args = parser.parse_args() - uvicorn.run("rapidocr_api.main:app", host=args.ip, port=args.port, reload=True) - + uvicorn.run("rapidocr_api.main:app", host=args.ip, port=args.port, reload=0, workers=args.workers) if __name__ == "__main__": main()