# 概述 PP-OCRv5 是PP-OCR新一代文字识别解决方案,该方案聚焦于多场景、多文字类型的文字识别。在文字类型方面,PP-OCRv5支持简体中文、中文拼音、繁体中文、英文、日文5大主流文字类型,在场景方面,PP-OCRv5升级了中英复杂手写体、竖排文本、生僻字等多种挑战性场景的识别能力。在内部多场景复杂评估集上,PP-OCRv5较PP-OCRv4端到端提升13个百分点,本sample适配了PPOcrV5字符检测和识别模型,并使用MIGraphX 5.0 的python接口实现推理。 ## 模型简介 ### 文本检测 文本检测使用了dbnet( 论文地址:https://arxiv.org/pdf/1911.08947 ),网络结构: ![alt text](Images/DBNet.png) 模型输出概率图,并用Vatti Clipping算法对字符区域多边形简化处理,sample中借助Clipping 库。 sample中模型输入shape为[1,3,640,640],模型路径:Resource/Models/ppocrv5_server_det_infer.onnx ### 文本识别 文本识别使用了CRNN+CTCDecode( https://arxiv.org/pdf/2009.09941 ),网络结构: ![(Images/CRNN.png)](Images/CRNN.png) sample中模型输入shape为[1,3,48,720],模型路径:Resource/Models/ppocrv5_server_rec_infer.onnx ## 预处理 ### 检测模型预处理 检测模型输入数据预处理: - 图片等比缩放,填充(沿着右、下填充) - 图片归一化,减均值除方差 - transpose ,MigraphX的输入数据排布顺序为[N,C,H,W] 本示例代码主要采用了OpenCV实现了预处理操作: ```python def preprocess(self, src_img, mean: list = [0.485, 0.456, 0.406], std: list = [0.229, 0.224, 0.225], scale: float = 1.0/255): data = dict() img = src_img.copy() src_h, src_w, _ = img.shape #对输入图片等比缩放,确保字符区域不会变形 res_img, [ratio_h, ratio_w] = self.resize_image(img) norm_img = (res_img* scale - mean) / std #HWC->CHW image_data = norm_img.transpose(2, 0, 1) #HWC->NCHW image_data = np.expand_dims(image_data, axis=0).astype(np.float32) image_data = np.ascontiguousarray(image_data) data["image"] = image_data data["shape"] = np.array([src_h, src_w, ratio_h, ratio_w]) return data def resize_image(self, img): h, w, _ = img.shape if h > w: ratio = float(self.db_input_size[1]) / h else: ratio = float(self.db_input_size[0]) / w resize_h = int(h * ratio) resize_w = int(w * ratio) resize_h = max(int(round(resize_h / 32) * 32), 32) resize_w = max(int(round(resize_w / 32) * 32), 32) try: if int(resize_w) <= 0 or int(resize_h) <= 0: return None, (None, None) img = cv2.resize(img, (int(resize_w), int(resize_h))) except: print(img.shape, resize_w, resize_h) raise ValueError("resize error") ratio_h = resize_h / float(h) ratio_w = resize_w / float(w) im_pad = np.zeros((self.db_input_size[1], self.db_input_size[0], 3), np.float32) im_pad[:resize_h, :resize_w, :] = img return im_pad, [ratio_h, ratio_w] ``` ### 字符识别模型预处理 字符识别模型输入数据预处理: - 等比缩放,保留H维度的原始比例,填充(沿着右、下) - 图片归一化,均值方差默认为0.5 - transpose ,MigraphX的输入数据排布顺序为[N,C,H,W] ```python def preprocess(self, img, max_wh_ratio): if isinstance(max_wh_ratio,list) ==False: raise TypeError("max_wh_ratio must be list") imgH, imgW = self.rec_input_size max_h,max_w = self.rec_input_size h, w = img.shape[:2] #保留H的原始维度 if h <= max_h: ratio = max_h / h w = int(w*ratio) if w <= max_w: re_size =(w,max_h) else: re_size = (max_w,max_h) else: ratio = max_h/h w,h = int(w*ratio),max_h if w <= max_w: re_size = (w,h) else: re_size = (max_w,h) max_wh_ratio.append(ratio) resized_image = cv2.resize(img, re_size) resized_image = resized_image.astype("float32") #归一化 resized_image = resized_image.transpose((2, 0, 1)) / 255 resized_image -= 0.5 resized_image /= 0.5 #填充,沿着右、下填充 padding_im = np.zeros((3, imgH, imgW), dtype=np.float32) padding_im[:, :, 0:re_size[0]] = resized_image return padding_im ``` ## 类介绍 PPOcrV5 封装了对外提供的API,TextDetector为文本检测类,TextRecgnizer为文本识别类,BaseRecLabelDecode,实现模型输出的索引序列和实际文本标签之间进行转换,CTCLabelDecode继承BaseRecLabelDecode,实现文本识别模型的输出解码将模型输出的概率值转换为字符并连接成句子。 ```python class PPOcrV5(): def __init__(self, det_model_path:str, rec_model_path:str, char_dict_path:str = "../Resource/ppocr_keys_v5.txt", db_input_size :list = (640,640), rec_input_size :list = (48,720), seg_thresh:float=0.3, box_thresh:float=0.7, precision_mode:str='fp32', offload_copy:bool=True, **kwargs ) """Ocr检测识别推理初始化 字符检测、字符编码、识别。 Args: det_model_path :字符检测模型路径 rec_model_path : 字符分割模型路径。 char_dict_path :字符集路径 db_input_size :检测模型输入size rec_input_size :是被模型输入size seg_thresh :像素分割阈值 box_thresh :字符区域box阈值 precision_mode :精度模式。可选 fp32、fp16 offload_copy : 数据拷贝模式 ,支持两种数据拷贝方式:offload_copy=true、offload_copy=false。当offload_copy为true时,不需要进行内存拷贝,如果为false,需要先预分配输入输出的设备内存,并在推理前,将预处理数据拷贝到设备内存,推理后将模型输出从设备内存中拷贝出来 **kwargs :设置字符检测模型后处理相关参数 Returns: return_type: 无返回值 Examples: det_onnx_path = "PATH/TO/det_onnx_model.onnx" rec_onnx_path = "PATH/TO/rec_onnx_model.onnx" image_path = "PATH/TO/test.png" ppocrv5 = PPOcrV5(det_onnx_path,rec_onnx_path,offload_copy=True) """ class TextDetector(object): def __init__( self, det_model_path, db_input_size=(640,640), thresh=0.3, box_thresh=0.7, max_candidates=1000, unclip_ratio=2.0, use_dilation=False, score_mode="fast", box_type="quad", precision_mode="float32", **kwargs, ) """字符检测模型初始化 字符检测(dbnet)。 Args: det_model_path :字符检测模型路径。 db_input_size :检测模型输入size thresh :像素分割阈值 box_thresh :字符区域box阈值 max_candidates : 字符最大候选数 unclip_ratio :polygon 扩散比例 precision_mode :精度模式。可选 "fp16","int8","float32" use_dilation : 是否对二值图进行膨胀处理 score_mode :评分模式。 box_type :box类型,可选矩形和多边形,这里默认为矩形 offload_copy : 数据拷贝模式 ,支持两种数据拷贝方式:offload_copy=true、offload_copy=false。当offload_copy为true时,不需要进行内存拷贝,如果为false,需要先预分配输入输出的设备内存,并在推理前,将预处理数据拷贝到设备内存,推理后将模型输出从设备内存中拷贝出来 **kwargs :设置字符检测模型后处理相关参数 Returns: return_type: 无返回值。 Examples: self.db_detector = TextDetector( det_model_path, db_input_size, thresh=self.seg_thres, box_thresh=self.box_thresh, max_candidates=self.max_candidates, unclip_ratio=self.unclip_ratio, box_type=self.box_type, use_dilation=self.use_dilation, score_mode=self.score_mode, precision_mode=precision_mode, offload_copy=offload_copy """ class TextRecgnizer(object): def __init__( self, rec_model_path, rec_batch_num=2, rec_input_size=(48, 480),#hw rec_algorithm="SVTR_LCNet", precision_mode = "fp32", **kwargs ) """字符识别模型初始化 字符识别(crnn+ctc)。 Args: rec_model_path :字符识别模型路径。 rec_batch_num :模型推理batch size rec_input_size :模型推理的最大size rec_algorithm : 后处理算法类型 unclip_ratio :polygon 扩散比例 precision_mode :精度模式。可选 "fp16","float32" **kwargs :设置字符识别模型后处理相关参数 Returns: return_type: 无返回值。 Examples: self.text_extractor = TextRecgnizer(rec_model_path=rec_model_path, rec_input_size=rec_input_size, precision_mode=precision_mode, offload_copy=offload_copy) """ class BaseRecLabelDecode(object): def __init__(self, character_dict_path=None, use_space_char=False) """ 字符识别(crnn+ctc)。 Args: character_dict_path :字符集文件路径。 use_space_char :字符集中是否包含空格。 Returns: return_type: 无返回值。 Examples: """ class CTCLabelDecode(BaseRecLabelDecode): def __init__(self, character_dict_path=None, use_space_char=False, **kwargs): super(CTCLabelDecode, self).__init__(character_dict_path, use_space_char) """Convert between text-label and text-index 字符识别(crnn+ctc)。 Args: character_dict_path :字符集文件路径。 use_space_char :字符集中是否包含空格。 Returns: return_type: 无返回值。 Examples: """ ``` ## 推理 ```python def __call__(self, src_img): import time start = time.time() #字符检测 dt_boxs,dt_rects = self.db_detector(src_img) res_img = self.vis_boxes(dt_boxs,src_img) #字符区域图片裁剪 batch_img_list = self.detection_roi_crop(src_img,dt_rects) #字符特征提取 batch_outputs_pre ,batch_max_wh_ratio_pre = self.text_extractor(batch_img_list) #字符编码 batch_text_list, batch_label_list = self.ctc_decoder(batch_outputs_pre,return_word_box=False,wh_ratio_list = batch_max_wh_ratio_pre) ``` # Ocrv5 API调用说明 API调用步骤如下: - 类实例化 - 识别接口调用 例: ```python if __name__ == '__main__': det_onnx_path = "../Resource/Models/ppocrv5_server_det_infer.onnx" rec_onnx_path = "../Resource/Models/ppocrv5_server_rec_infer.onnx" image_path = "../Resource/Images/lite_demo.png" img = cv2.imread(image_path) ppocrv5 = PPOcrV5(det_onnx_path,rec_onnx_path,offload_copy=True,precision_mode="fp16") res_img = ppocrv5(img) cv2.imwrite("res.jpg",res_img) ``` sample支持两种精度推理(fp32和fp16),默认是fp16),精度和内存拷贝方式分别通过precision_mode和offload_copy参数控制。