简介:本资源是一套基于Python+OpenCV+tesseract实现的中文扫描票据OCR识别完整项目,面向计算机、软件工程、人工智能等专业的本科生及课程设计/毕业设计实践者,解决纸质票据图像预处理、文字定位与高准确率中文识别等典型CV应用问题。压缩包共144个文件,含83个核心Python脚本(涵盖图像二值化、倾斜校正、ROI提取、tesseract调用与后处理)、12个XML配置文件(用于模板匹配与区域定义)、7个PDF使用文档与技术说明、7个PNG示例图及5个.gz模型数据,整体容量105.23MB,结构清晰,模块解耦度高。已有204人学习下载,项目源自高分毕业设计(答辩95分),代码经实测可稳定运行,附详细部署指南与常见问题排错说明。使用者可直接用于毕设演示、课程实践或二次开发,亦能通过源码深入理解OCR流水线中OpenCV图像增强与tesseract中文适配的关键实现细节。
1. 不是“装完tesseract就能识别中文票据”,而是让OpenCV预处理+Python调度+tesseract精准定位字段的闭环
很多刚接触OCR的朋友,下载完tesseract ocr w64 setup 5.3.0.20221222.exe、pip install opencv-python、pip install pytesseract,跑通一行pytesseract.image_to_string(img, lang='chi_sim')就以为完成了——结果拍一张超市小票,输出全是乱码或空字符串。问题不在tesseract本身,而在于中文扫描票据的特殊性:低对比度、倾斜、印章遮挡、手写体混排、固定区域但格式不统一(如发票代码/号码/金额位置有微小偏移)、以及最关键的——tesseract默认对中文字符切分和上下文建模能力远弱于英文。真正能落地的方案,必须用OpenCV做几何校正+光照归一化+ROI动态裁剪,再把清洗后的子图喂给tesseract,并用Python控制流程、校验置信度、结构化提取。这个项目不是教你怎么装软件,而是展示一套针对真实场景中文票据(增值税专用发票、电子普通发票、银行回单、医疗收费票据)的端到端识别链路,适合需要嵌入业务系统、要求字段级准确率>92%的开发者,而非仅做demo演示。
2. OpenCV图像预处理:从模糊扫描件到tesseract可读子图的四步精炼
tesseract对输入图像质量极其敏感,尤其在中文场景下。直接传入原始扫描图,即使指定lang='chi_sim',识别率常低于40%。核心矛盾在于:tesseract擅长识别清晰、高对比、水平对齐的印刷体,而实际票据存在倾斜、反光、阴影、印章覆盖、局部模糊等问题。OpenCV的作用不是简单“二值化”,而是构建一个面向OCR任务的图像增强流水线。以下四步为生产环境验证过的最小有效组合,每步都对应具体物理问题,且参数可调。
2.1 灰度化与自适应直方图均衡(CLAHE)解决低对比度与局部过曝
扫描件常见整体发灰或某区域过亮(如印章红章导致周围文字变淡)。全局直方图均衡会放大噪声,而CLAHE(Contrast Limited Adaptive Histogram Equalization)在局部块内均衡,同时限制对比度放大倍数,避免噪点爆炸。
import cv2 import numpy as np def preprocess_contrast(img): # 转灰度(若输入为BGR) if len(img.shape) == 3: gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) else: gray = img.copy() # CLAHE参数:clipLimit控制对比度提升上限,tileGridSize决定局部块大小 clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8)) enhanced = clahe.apply(gray) return enhanced # 示例:对一张发票扫描图应用 original = cv2.imread("invoice_scan.jpg") enhanced_gray = preprocess_contrast(original) cv2.imwrite("enhanced_gray.jpg", enhanced_gray) # 保存用于后续步骤提示:
clipLimit=2.0是平衡效果与噪声的起点;若票据有大面积纯色区域(如红色发票底纹),可降至1.5;tileGridSize设为(8,8)适配A4尺寸扫描图(约2480×3508像素),若处理手机拍摄小图(如1200×1600),应改为(4,4)。
2.2 倾斜校正:基于霍夫变换检测主文本行角度并旋转
票据常因扫描仪未摆正产生1°~5°倾斜,tesseract对此极不敏感。OpenCV的cv2.HoughLinesP可检测长直线(如表格边框、文本行基线),取众数角度作为校正依据,比单纯找最大轮廓更鲁棒。
def deskew_by_hough(img): # 边缘检测(Canny)突出线条 edges = cv2.Canny(img, 50, 150, apertureSize=3) # 霍夫直线检测,minLineLength过滤短干扰线,maxLineGap连接断裂线段 lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=100, minLineLength=100, maxLineGap=10) if lines is None: return img # 无足够直线,不校正 # 计算所有检测线的角度(弧度转角度),过滤接近水平(-10°~10°)的线 angles = [] for line in lines: x1, y1, x2, y2 = line[0] angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) if -10 < angle < 10: # 只取近水平线,排除垂直边框干扰 angles.append(angle) if not angles: return img # 取中位数角度(比平均值抗异常值) median_angle = np.median(angles) # 旋转校正(正角度逆时针转,需取负号使文本变正) h, w = img.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, -median_angle, 1.0) rotated = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE) return rotated # 应用校正 deskewed = deskew_by_hough(enhanced_gray) cv2.imwrite("deskewed.jpg", deskewed)注意:
threshold=100需根据图像质量调整;扫描清晰时可设为120,手机拍摄模糊图则降至70。borderMode=cv2.BORDER_REPLICATE确保旋转后边缘填充原图最近像素,避免黑边影响tesseract。
2.3 动态ROI裁剪:用形态学操作定位票据主体区域,排除页眉页脚干扰
票据四周常有扫描仪阴影、装订孔、无关边框。全图送入tesseract会浪费算力且引入噪声。OpenCV通过闭运算连接文字块 → 膨胀扩大连通域 → 寻找最大外接矩形,自动框出主体内容区。
def crop_main_content(img): # 二值化(Otsu自动阈值,适应不同亮度) _, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # 形态学操作:先闭运算(填文字间小空隙),再膨胀(合并相邻字块) kernel = np.ones((3, 3), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel, iterations=2) dilated = cv2.dilate(closed, kernel, iterations=3) # 查找所有轮廓,按面积排序,取最大者(即票据主体) contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: return img largest_contour = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(largest_contour) # 添加10像素安全边距,避免裁掉文字边缘 x, y, w, h = max(0, x-10), max(0, y-10), min(w+20, img.shape[1]-x), min(h+20, img.shape[0]-y) cropped = img[y:y+h, x:x+w] return cropped # 应用裁剪 cropped = crop_main_content(deskewed) cv2.imwrite("cropped.jpg", cropped)关键参数说明:
iterations=2闭运算次数防止过度连接;iterations=3膨胀次数确保小字号文字(如发票备注栏)被纳入同一连通域;boundingRect返回的是(x,y,w,h),非(x1,y1,x2,y2),务必用img[y:y+h, x:x+w]切片。
2.4 最终二值化与去噪:针对中文字符优化的自适应阈值
tesseract要求输入为高对比黑白图。全局阈值失效于光照不均的扫描件,而cv2.adaptiveThreshold以像素邻域为单位计算阈值,更适配中文密集排版。
def final_binarize(img): # 自适应阈值:blockSize=31(奇数,邻域大小),C=10(常数偏移补偿) binary = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, blockSize=31, C=10) # 中值滤波去椒盐噪声(保留文字边缘) denoised = cv2.medianBlur(binary, 3) return denoised # 输出最终预处理图 final_img = final_binarize(cropped) cv2.imwrite("final_for_ocr.jpg", final_img) # 此图直接送入tesseract参数调试指南:
blockSize必须为奇数,典型值21(小图)、31(A4扫描)、41(大幅面);C=10是起点,若文字断笔则减小(如5),若背景残留灰点则增大(如15)。
| 预处理步骤 | 输入图像特征 | OpenCV核心操作 | tesseract收益 |
|---|---|---|---|
| CLAHE增强 | 整体发灰/局部过曝 | createCLAHE | 提升字符灰度分离度,减少漏字 |
| 倾斜校正 | 文本行倾斜1°~5° | HoughLinesP+warpAffine | 避免字符粘连,提升单字识别率15%+ |
| ROI裁剪 | 四周有阴影/边框 | morphologyEx+findContours | 减少无效区域处理,加速30%,降低误识率 |
| 自适应二值化 | 光照渐变明显 | adaptiveThreshold | 解决传统二值化导致的断笔、虚边问题 |
3. tesseract精准配置与中文字段结构化提取:不只是image_to_string
预处理后的图像质量达标,但tesseract默认配置仍会将“金额:¥1,234.50”识别成“金額:¥1 234 50”,或把“纳税人识别号”错成“纳税人识别号”。问题根源在于:tesseract的chi_sim模型未针对票据字段优化,且缺乏上下文约束。必须通过config参数强制其使用PSM(Page Segmentation Mode)模式、指定字符白名单、并结合Python后处理实现结构化。
3.1 PSM模式选择与中文语言包加载验证
tesseract的PSM模式决定其如何理解图像布局。票据是单列文本+固定字段标签,绝不能用默认的PSM.AUTO(自动检测布局,易误判为多列或图片)。
import pytesseract from PIL import Image # 验证中文语言包是否正确安装(关键!) # 在命令行执行:tesseract --list-langs,应输出包含chi_sim或chi_tra # 若无,需下载https://github.com/tesseract-ocr/tessdata/blob/main/chi_sim.traineddata 放入tessdata目录 def ocr_with_config(img_path): # 使用PIL打开(支持更多格式),转为OpenCV兼容格式 pil_img = Image.open(img_path) cv_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR) gray = cv2.cvtColor(cv_img, cv2.COLOR_BGR2GRAY) # 关键配置:PSM 6(假设为单均匀块文本),oem 3(LSTM OCR引擎),lang指定中文 config = r'--oem 3 --psm 6 -l chi_sim' # 执行OCR,返回详细数据(含置信度、文本框坐标) data = pytesseract.image_to_data(gray, config=config, output_type=pytesseract.Output.DICT) return data # 获取识别结果 result_data = ocr_with_config("final_for_ocr.jpg") print(f"识别到{len(result_data['text'])}个文本块")注意:
--psm 6是票据识别黄金配置,强制tesseract将整图视为一个文本块处理,避免其错误分割为多列;--oem 3启用LSTM深度学习引擎,对中文支持优于旧版;-l chi_sim必须与实际安装的语言包名一致(如chi_tra为繁体)。
3.2 字段级提取:基于关键词定位+正则校验的结构化解析
tesseract输出的是文本块列表,每个块含text、left、top、width、height、conf(置信度)。直接拼接text不可靠,需按空间位置关系和语义规则提取字段。
import re def extract_invoice_fields(data): fields = { 'invoice_code': '', # 发票代码(12位数字) 'invoice_number': '', # 发票号码(8位数字) 'amount': '', # 金额(含¥符号和小数点) 'date': '' # 开票日期(YYYY-MM-DD格式) } # 遍历所有识别块,筛选高置信度(>60)且非空文本 for i in range(len(data['text'])): text = data['text'][i].strip() conf = int(data['conf'][i]) if conf < 60 or not text: continue # 利用文本内容和位置双重匹配 # 发票代码:通常在左上角,文本含"发票代码"且后跟12位数字 if '发票代码' in text or '代码' in text: # 向右邻近块找12位数字(同一行或下一行) for j in range(i, min(i+5, len(data['text']))): next_text = data['text'][j].strip().replace(' ', '') if re.match(r'^\d{12}$', next_text): fields['invoice_code'] = next_text break # 金额:文本含"金额"或"¥",且匹配金额正则 elif re.search(r'(金额|¥)', text): amount_match = re.search(r'¥\s*[\d,]+\.?\d{0,2}', text) if amount_match: fields['amount'] = amount_match.group(0).replace(' ', '') # 日期:匹配标准日期格式 elif re.search(r'\d{4}年\d{1,2}月\d{1,2}日', text): # 标准化为YYYY-MM-DD date_match = re.search(r'(\d{4})年(\d{1,2})月(\d{1,2})日', text) if date_match: y, m, d = date_match.groups() fields['date'] = f"{y}-{int(m):02d}-{int(d):02d}" return fields # 执行提取 extracted = extract_invoice_fields(result_data) print(extracted) # 输出示例:{'invoice_code': '123456789012', 'invoice_number': '', 'amount': '¥1234.50', 'date': '2023-12-01'}逻辑说明:此方法不依赖tesseract的行/列结构,而是利用关键词触发+邻近块搜索+正则校验,对扫描件倾斜、部分遮挡有强鲁棒性。
conf < 60过滤掉低置信度结果,避免噪声干扰。
3.3 置信度阈值与失败重试机制:保障生产环境稳定性
单次OCR可能因局部污渍失败。需设计重试策略:降低PSM严格度、调整二值化参数、或对ROI子区域单独识别。
def robust_ocr(img_path, max_retries=3): for attempt in range(max_retries): try: # 尝试不同PSM模式(首次用PSM 6,失败后降级为PSM 7) psm = 6 if attempt == 0 else 7 config = f'--oem 3 --psm {psm} -l chi_sim' # 若是PSM 7,增加字符白名单限定为数字和中文常用字 if psm == 7: config += ' -c tessedit_char_whitelist=0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz¥元角分年月日发票代码号码金额' data = pytesseract.image_to_data( cv2.imread(img_path, cv2.IMREAD_GRAYSCALE), config=config, output_type=pytesseract.Output.DICT ) # 检查关键字段是否提取成功 extracted = extract_invoice_fields(data) if extracted['invoice_code'] and extracted['amount']: return extracted except Exception as e: print(f"Attempt {attempt+1} failed: {e}") continue # 所有重试失败,返回空字段并记录日志 return {k: 'OCR_FAILED' for k in ['invoice_code', 'invoice_number', 'amount', 'date']} # 调用稳健OCR final_result = robust_ocr("final_for_ocr.jpg")参数说明:
psm=7(单行文本模式)在PSM 6失效时作为备选,特别适合识别独立字段(如金额栏);tessedit_char_whitelist大幅减少误识,但需根据票据类型定制字符集,避免遗漏(如“税”字未加入则无法识别“税率”)。
4. 中文OCR常见坑与绕过方案:从“显示不出中文”到“识别不出中文”的实战排错
即使按前述流程操作,仍可能遇到“tesseract输出乱码”、“OpenCV图像处理后变黑”、“pytesseract报错ModuleNotFoundError”等问题。这些不是代码bug,而是环境与配置的隐性冲突。以下是高频问题的根因分析与可立即执行的解决方案。
4.1 “tesseract输出全是问号或方框”:语言包路径与编码的双重陷阱
现象:pytesseract.image_to_string(img, lang='chi_sim')返回??????或空字符串。
根本原因:tesseract未找到chi_sim.traineddata,或Python进程编码与tesseract内部不一致。
立即修复步骤:
确认语言包位置:
# Windows命令行 tesseract --list-langs # 若输出不含chi_sim,说明未安装。下载chi_sim.traineddata至: # C:\Program Files\Tesseract-OCR\tessdata\ (默认安装路径) # 或设置环境变量TESSDATA_PREFIX指向自定义路径强制Python使用UTF-8编码(Windows关键!):
import os # 在import pytesseract前执行 os.environ['PYTHONIOENCODING'] = 'utf-8' # 或在脚本开头添加(适用于Windows cmd/powershell) # chcp 65001 # 切换代码页为UTF-8(需管理员权限)验证tesseract命令行是否正常:
# 直接调用tesseract,绕过Python tesseract "final_for_ocr.jpg" stdout -l chi_sim --psm 6 # 若命令行输出正常中文,则问题在Python环境;若仍乱码,则语言包路径错误。
提示:
tesseract ocr w64 setup 5.3.0.20221222.exe安装包自带chi_sim,但某些精简版安装器会跳过。务必手动检查tessdata目录是否存在chi_sim.traineddata文件(大小约45MB)。
4.2 “OpenCV处理后图像全黑或全白”:数据类型溢出与阈值逻辑错误
现象:cv2.threshold或cv2.adaptiveThreshold输出纯黑/纯白图。
根因:输入图像为uint16(如TIFF扫描件)或float32(如某些PIL转换),而OpenCV阈值函数要求uint8;或cv2.THRESH_BINARY_INV误用导致逻辑反转。
修复代码:
def safe_threshold(img): # 统一转为uint8(0-255) if img.dtype == np.float32 or img.dtype == np.float64: img = (img * 255).astype(np.uint8) elif img.dtype == np.uint16: img = (img / 256).astype(np.uint8) # 16位转8位 # 使用THRESH_BINARY而非THRESH_BINARY_INV,除非明确需要反转 _, binary = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) return binary # 替换原代码中的threshold调用 safe_binary = safe_threshold(cropped)注意:
cv2.THRESH_BINARY_INV会将白色设为0(黑),黑色设为255(白),tesseract要求文字为白色(255),背景为黑色(0),故绝大多数情况用THRESH_BINARY。
4.3 “ModuleNotFoundError: No module named 'cv2'”:OpenCV安装的ABI兼容性雷区
现象:pip install opencv-python后仍报错No module named 'cv2'。
真相:Python环境与OpenCV编译版本不匹配(如Python 3.11需opencv-python>=4.8.0,旧版不支持)。
三步确诊法:
- 查Python版本:
python --version - 查pip源:
pip config list,确认未指向国内镜像导致下载损坏包 - 强制重装匹配版本:
# 卸载所有opencv相关包 pip uninstall opencv-python opencv-contrib-python opencv-python-headless # 清理缓存 pip cache purge # 安装最新稳定版(截至2024年,推荐4.9.0+) pip install opencv-python==4.9.0.80 # 验证 python -c "import cv2; print(cv2.__version__)"
关键点:
opencv 4.5.2 原生支持 code128是条无关信息,本项目无需二维码识别;专注opencv-python核心包即可,勿安装opencv-contrib-python(含非必要模块,易引发冲突)。
4.4 “识别速度慢,CPU占用100%”:tesseract并发与LSTM引擎的资源权衡
现象:单张票据识别耗时>5秒,服务器CPU飙升。
优化方案:
- 禁用LSTM的非必要层:在config中添加
-c tessedit_enable_doc_dict=0关闭词典校验; - 限制CPU核心数:
--tessdata-dir指定路径后,tesseract自动多线程,可通过taskset(Linux)或start /affinity(Windows)绑定核心; - 批量处理时复用tesseract实例:避免反复初始化引擎。
# 使用tesserocr(更底层封装)实现持久化实例 import tesserocr from PIL import Image # 初始化一次,复用 api = tesserocr.PyTessBaseAPI(lang='chi_sim', oem=tesserocr.OEM.LSTM_ONLY) api.SetVariable('tessedit_enable_doc_dict', '0') # 关闭词典 def fast_ocr_pil(pil_img): api.SetImage(pil_img) return api.GetUTF8Text() # 比pytesseract快30%~50%,且支持更细粒度控制性能对比:在Intel i5-10210U上,
pytesseract单图平均4.2秒,tesserocr复用API后降至2.8秒;若开启--tessdata-dir缓存,首次加载慢但后续提速显著。
5. 实战技巧:用OpenCV ROI模板匹配快速定位固定字段,绕过tesseract识别瓶颈
当票据格式高度统一(如某银行回单、某省增值税发票),字段位置偏差<5像素,此时不依赖tesseract的文字识别能力,而用OpenCV模板匹配直接定位字段区域,再对ROI做极简OCR,可将准确率推至99%+,且速度提升10倍。这是生产环境应对“识别不出中文”最有效的降维打击方案。
5.1 构建字段模板库:从标准票据图截取高精度ROI样本
以“金额”字段为例,在一张清晰的标准发票上,用OpenCV手动框出“金额:¥1,234.50”所在矩形区域,保存为amount_template.png。关键要求:
- 模板尺寸尽量小(如120×40像素),只含字段标签+数值;
- 使用灰度图,避免颜色干扰;
- 多角度采集(±2°倾斜、±5%缩放)以增强鲁棒性。
def create_template_roi(img, x, y, w, h, save_path): """从原图截取ROI并保存为模板""" roi = img[y:y+h, x:x+w] # 转灰度并标准化对比度 if len(roi.shape) == 3: roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) else: roi_gray = roi.copy() roi_norm = cv2.normalize(roi_gray, None, 0, 255, cv2.NORM_MINMAX) cv2.imwrite(save_path, roi_norm) # 示例:在标准发票上标注金额区域坐标后调用 # create_template_roi(original_img, 210, 350, 120, 40, "templates/amount.png")5.2 多尺度模板匹配:应对扫描缩放与轻微形变
真实票据扫描件存在1%~3%缩放差异,单一尺寸模板匹配会失败。需在多个缩放比例下并行匹配,取最高响应值。
def multi_scale_match(img, template, scales=[0.8, 0.9, 1.0, 1.1, 1.2]): """在多尺度下匹配模板,返回最佳匹配位置""" best_match = None best_val = -1 for scale in scales: # 缩放模板 h, w = template.shape[:2] scaled_template = cv2.resize(template, (int(w*scale), int(h*scale))) # 匹配 res = cv2.matchTemplate(img, scaled_template, cv2.TM_CCOEFF_NORMED) _, max_val, _, max_loc = cv2.minMaxLoc(res) if max_val > best_val: best_val = max_val best_match = (max_loc, scaled_template.shape[1], scaled_template.shape[0]) # 返回匹配中心坐标(用于后续OCR) if best_match: (x, y), w, h = best_match center_x = x + w // 2 center_y = y + h // 2 return (center_x, center_y, w, h) return None # 加载模板与待识别图 template = cv2.imread("templates/amount.png", cv2.IMREAD_GRAYSCALE) processed_img = cv2.imread("final_for_ocr.jpg", cv2.IMREAD_GRAYSCALE) # 执行匹配 match_pos = multi_scale_match(processed_img, template) if match_pos: cx, cy, w, h = match_pos # 提取金额ROI(扩大10像素防裁切) roi_x, roi_y = max(0, cx - w//2 - 10), max(0, cy - h//2 - 10) roi_w, roi_h = w + 20, h + 20 amount_roi = processed_img[roi_y:roi_y+roi_h, roi_x:roi_x+roi_w] # 对ROI做极简OCR(只需识别数字和符号) config = r'--oem 3 --psm 8 -c tessedit_char_whitelist=0123456789.,¥' amount_text = pytesseract.image_to_string(amount_roi, config=config) print("提取金额:", amount_text.strip())参数说明:
--psm 8(单行单词模式)专为小ROI设计;tessedit_char_whitelist锁定字符集,使tesseract忽略所有非数字字符,彻底杜绝“金额:¥1234.50”被识别成“金額:¥1 234 50”的空格问题。
5.3 模板匹配置信度阈值与失败降级策略
匹配响应值max_val<0.7时,认为模板不匹配(如票据版本更新、印章遮挡)。此时启动降级流程:
- 尝试其他相似模板(如“小写金额”、“大写金额”);
- 回退到全文OCR+关键词搜索;
- 记录日志供人工复核。
# 在multi_scale_match后添加 if best_val < 0.7: print(f"模板匹配置信度{best_val:.3f}不足,启动降级OCR") # 执行3.3节的robust_ocr全文识别 fallback_result = robust_ocr("final_for_ocr.jpg") amount_fallback = fallback_result.get('amount', 'UNRECOGNIZED') else: # 使用模板匹配结果 amount_fallback = amount_text.strip()此技巧将OCR从“识别文字”降维为“定位区域”,彻底规避tesseract对中文复杂字体、低质量扫描的固有缺陷,是工业级票据识别系统的标配方案。
本文还有配套的精品资源,点击获取