博客列表 >Opencv创建车牌图片识别系统方法详解

Opencv创建车牌图片识别系统方法详解

原创
2022年01月13日 16:56:261261浏览

目录
前言
包含功能
软件版本
软件架构
参考文档
效果图展示
车牌检测过程
图片车牌文字识别过程
部分核心代码
前言
这是一个基于spring boot + maven + opencv 实现的图像识别及训练的Demo项目

包含车牌识别、人脸识别等功能,贯穿样本处理、模型训练、图像处理、对象检测、对象识别等技术点

java语言的深度学习项目,在整个开源社区来说都相对较少;

拥有完整的训练过程、检测、识别过程的开源项目更是少之又少!!

包含功能
蓝、绿、黄车牌检测及车牌号码识别
网上常见的轮廓提取车牌算法JAVA实现
hsv色彩分割提取车牌算法JAVA实现
基于svm算法的车牌检测训练JAVA实现
基于ann算法的车牌号码识别训练JAVA实现
人脸检测 接下来将实现人脸识别
图片处理工具,目前实现了HSV色彩切割,后续将添加更多使用的图片处理工具,用于辅助算法优化
软件版本
jdk 1.8.61+
maven 3.0+
opencv 4.0.1 ; javacpp1.4.4;opencv-platform 4.0.1-1.4.4
spring boot 2.1.5.RELEASE
yx-image-recognition 1.0.0版本
软件架构
B/S 架构,前端html + requireJS,后端java

数据库使用 sqlite3.0

接口文档使用swagger 2.0

参考文档
参考了EasyPR C++项目、以及fan-wenjie的EasyPR-Java项目;同时查阅了部分opencv官方4.0.1版本C++的源码,结合个人对java语言的理解,整理出当前项目

liuruoze/EasyPR

fan-wenjie/EasyPR-Java

opencv官方

效果图展示

车牌识别

黄牌识别

绿牌识别

夜间识别

图片提取工具

人脸识别

训练

接口文档

车牌检测过程
高斯模糊:

图像灰度化:

Sobel 算子:

图像二值化:

图像闭操作:

二值图像降噪:

提取外部轮廓:

外部轮廓筛选:

切图:

重置切图尺寸:

车牌检测结果:

图片车牌文字识别过程
车牌检测结果:

debug_char_threshold:

debug_char_clearLiuDing:

debug_specMat:

debug_chineseMat:

debug_char_auxRoi:

部分核心代码
package com.yuxue.service.impl;

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.yuxue.constant.Constant;
import com.yuxue.entity.PlateFileEntity;
import com.yuxue.entity.TempPlateFileEntity;
import com.yuxue.enumtype.PlateColor;
import com.yuxue.mapper.PlateFileMapper;
import com.yuxue.mapper.TempPlateFileMapper;
import com.yuxue.service.PlateService;
import com.yuxue.util.FileUtil;
import com.yuxue.util.GenerateIdUtil;
import com.yuxue.util.PlateUtil;

@Service
public class PlateServiceImpl implements PlateService {

  1. @Autowired
  2. private PlateFileMapper plateFileMapper;
  3. @Autowired
  4. private TempPlateFileMapper tempPlateFileMapper;
  5. static {
  6. System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
  7. }
  8. @Override
  9. @Transactional(propagation = Propagation.REQUIRED)
  10. public Object refreshFileInfo() {
  11. File baseDir = new File(Constant.DEFAULT_DIR);
  12. if(!baseDir.exists() || !baseDir.isDirectory()) {
  13. return null;
  14. }
  15. List<TempPlateFileEntity> resultList = Lists.newArrayList();
  16. // 获取baseDir下第一层级的目录, 仅获取文件夹,不递归子目录,遍历
  17. List<File> folderList = FileUtil.listFile(baseDir, ";", false);
  18. folderList.parallelStream().forEach(folder -> {
  19. if(!folder.getName().equals("temp")) {
  20. // 遍历每一个文件夹, 递归获取文件夹下的图片
  21. List<File> imgList = FileUtil.listFile(folder, Constant.DEFAULT_TYPE, true);
  22. if(null != imgList && imgList.size() > 0) {
  23. imgList.parallelStream().forEach(n->{
  24. TempPlateFileEntity entity = new TempPlateFileEntity();
  25. entity.setFilePath(n.getAbsolutePath().replaceAll("\\\\", "/"));
  26. entity.setFileName(n.getName());
  27. entity.setFileType(n.getName().substring(n.getName().lastIndexOf(".") + 1));
  28. resultList.add(entity);
  29. });
  30. }
  31. }
  32. });
  33. tempPlateFileMapper.turncateTable();
  34. tempPlateFileMapper.batchInsert(resultList);
  35. tempPlateFileMapper.updateFileInfo();
  36. return 1;
  37. }
  38. @Override
  39. public Object recognise(String filePath, boolean reRecognise) {
  40. filePath = filePath.replaceAll("\\\\", "/");
  41. File f = new File(filePath);
  42. PlateFileEntity entity = null;
  43. Map<String, Object> paramMap = Maps.newHashMap();
  44. paramMap.put("filePath", filePath);
  45. List<PlateFileEntity> list= plateFileMapper.selectByCondition(paramMap);
  46. if(null == list || list.size() <= 0) {
  47. if(FileUtil.checkFile(f)) {
  48. entity = new PlateFileEntity();
  49. entity.setFileName(f.getName());
  50. entity.setFilePath(f.getAbsolutePath().replaceAll("\\\\", "/"));
  51. entity.setFileType(f.getName().substring(f.getName().lastIndexOf(".") + 1));
  52. plateFileMapper.insertSelective(entity);
  53. }
  54. reRecognise = true;
  55. } else {
  56. entity = list.get(0);
  57. }
  58. if(reRecognise || StringUtils.isEmpty(entity.getTempPath())) {
  59. doRecognise(f, entity); // 重新识别
  60. entity = plateFileMapper.selectByPrimaryKey(entity.getId()); // 重新识别之后,重新获取一下数据
  61. }
  62. // 查询debug文件
  63. if(!StringUtils.isEmpty(entity.getTempPath())) {
  64. Vector<String> debugFiles = new Vector<String>();
  65. FileUtil.getFiles(entity.getTempPath(), debugFiles);
  66. entity.setDebugFiles(debugFiles);
  67. }
  68. return entity;
  69. }
  70. @Override
  71. public Object recogniseAll() {
  72. // 查询到还没有进行车牌识别的图片
  73. List<PlateFileEntity> list = plateFileMapper.getUnRecogniseList();
  74. list.parallelStream().forEach(n->{
  75. File f = new File(n.getFilePath());
  76. if(FileUtil.checkFile(f)) {
  77. doRecognise(f, n);
  78. }
  79. });
  80. return 1;
  81. }
  82. /**
  83. * 单张图片 车牌识别
  84. * 拷贝文件到临时目录
  85. * 过程及结果更新数据库
  86. * @param f
  87. * @param e
  88. * @return
  89. */
  90. public Object doRecognise(File f, PlateFileEntity e) {
  91. if(!f.exists()) {
  92. return null;
  93. }
  94. String ct = GenerateIdUtil.getStrId();
  95. String targetPath = Constant.DEFAULT_TEMP_DIR + ct + (f.getName().substring(f.getName().lastIndexOf(".")));
  96. FileUtil.copyAndRename(f.getAbsolutePath(), targetPath); // 拷贝文件并且重命名
  97. // 创建临时目录, 存放过程图片
  98. String tempPath = Constant.DEFAULT_TEMP_DIR + ct + "/";
  99. FileUtil.createDir(tempPath);
  100. e.setTempPath(tempPath);
  101. Boolean debug = false;
  102. Vector<Mat> dst = new Vector<Mat>();
  103. PlateUtil.getPlateMat(targetPath, dst, debug, tempPath);
  104. Set<String> plates = Sets.newHashSet();
  105. dst.stream().forEach(inMat -> {
  106. PlateColor color = PlateUtil.getPlateColor(inMat, true, false, tempPath);
  107. String plate = PlateUtil.charsSegment(inMat, color, debug, tempPath);
  108. plates.add("<" + plate + "," + color.desc + ">");
  109. });
  110. e.setRecoPlate(plates.toString());
  111. new File(targetPath).delete(); // 删除拷贝的临时文件
  112. plateFileMapper.updateByPrimaryKeySelective(e);
  113. return 1;
  114. }
  115. @Override
  116. public Object getImgInfo(String imgPath) {
  117. Map<String, Object> result = Maps.newHashMap();
  118. String ct = GenerateIdUtil.getStrId();
  119. File f = new File(imgPath);
  120. if(f.exists()) {
  121. String targetPath = Constant.DEFAULT_TEMP_DIR + ct + (f.getName().substring(f.getName().lastIndexOf(".")));
  122. FileUtil.copyAndRename(f.getAbsolutePath(), targetPath);
  123. result.put("targetPath", targetPath); // 返回临时路径给前端
  124. // 获取图片的基本信息
  125. Mat inMat = Imgcodecs.imread(targetPath);
  126. result.put("rows", inMat.rows());
  127. result.put("cols", inMat.cols());
  128. }
  129. return result;
  130. }
  131. @Override
  132. public Object getHSVValue(String imgPath, Integer row, Integer col) {
  133. Map<String, Object> result = Maps.newHashMap();
  134. Mat inMat = Imgcodecs.imread(imgPath);
  135. double[] rgb = inMat.get(row, col);
  136. result.put("RGB", JSONObject.toJSONString(rgb));
  137. Mat dst = new Mat(inMat.rows(), inMat.cols(), CvType.CV_32FC3);
  138. Imgproc.cvtColor(inMat, dst, Imgproc.COLOR_BGR2HSV); // 转到HSV空间进行处理
  139. double[] hsv = dst.get(row, col);
  140. result.put("HSV", (int)hsv[0] + ", " + (int)hsv[1] + ", " + (int)hsv[2]);
  141. return result;
  142. }

}
package com.znz.service.impl;

import com.znz.service.PlateTypeService;
import com.znz.entity.PlateTypeEntity;
import com.znz.mapper.PlateTypeMapper;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.List;

/**

  • 服务实现层
  • @author znz
  • @date 2020-09-30T16:54:41.823
    */
    @Service
    public class PlateTypeServiceImpl implements PlateTypeService {

    @Autowired
    private PlateTypeMapper plateTypeMapper;

    @Override
    public PlateTypeEntity getByPrimaryKey(Integer id) {

    1. PlateTypeEntity entity = plateTypeMapper.selectByPrimaryKey(id);
    2. return entity;

    }

    @Override
    public PageInfo<PlateTypeEntity> queryByPage(Integer pageNo, Integer pageSize, Map<String, Object> map) {

    1. PageHelper.startPage(pageNo, pageSize);
    2. PageInfo<PlateTypeEntity> page = new PageInfo(plateTypeMapper.selectByCondition(map));
    3. return page;

    }

    @Override
    public List<PlateTypeEntity> queryByCondition(Map<String, Object> map) {

    1. return plateTypeMapper.selectByCondition(map);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Map<String, Object> save(PlateTypeEntity plateTypeEntity) {

    1. plateTypeEntity.setId(0);
    2. plateTypeMapper.insertSelective(plateTypeEntity);
    3. Map<String, Object> result = new HashMap<>();
    4. result.put("id" , plateTypeEntity.getId());
    5. return result;

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer deleteById(Integer id){

    1. return plateTypeMapper.deleteByPrimaryKey(id);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer updateById(PlateTypeEntity plateTypeEntity) {

    1. if(null == plateTypeEntity || plateTypeEntity.getId() <= 0){
    2. return 0;
    3. }
    4. return plateTypeMapper.updateByPrimaryKeySelective(plateTypeEntity);

    }

}
package com.znz.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.znz.entity.SystemMenuEntity;
import com.znz.mapper.SystemMenuMapper;
import com.znz.service.SystemMenuService;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.annotation.Propagation;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**

  • 服务实现层
  • @author znz
  • @date 2021-06-20 16:15:23
    */
    @Service
    public class SystemMenuServiceImpl implements SystemMenuService {

    @Autowired
    private SystemMenuMapper systemMenuMapper;

    @Override
    public SystemMenuEntity getByPrimaryKey(Integer id) {

    1. SystemMenuEntity entity = systemMenuMapper.selectByPrimaryKey(id);
    2. return entity;

    }

    @Override
    public PageInfo<SystemMenuEntity> queryByPage(Integer pageNo, Integer pageSize, Map<String, Object> map) {

    1. PageHelper.startPage(pageNo, pageSize);
    2. PageInfo<SystemMenuEntity> page = new PageInfo(systemMenuMapper.selectByCondition(map));
    3. return page;

    }

    @Override
    public List<SystemMenuEntity> queryByCondition(Map<String, Object> map) {

    1. return systemMenuMapper.selectByCondition(map);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Map<String, Object> save(SystemMenuEntity entity) {

    1. entity.setId(0);
    2. systemMenuMapper.insertSelective(entity);
    3. Map<String, Object> result = new HashMap<>();
    4. result.put("id" , entity.getId());
    5. return result;

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer deleteById(Integer id){

    1. return systemMenuMapper.deleteByPrimaryKey(id);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED)
    public Integer updateById(SystemMenuEntity systemMenuEntity) {

    1. if(null == systemMenuEntity || systemMenuEntity.getId() <= 0){
    2. return 0;
    3. }
    4. return systemMenuMapper.updateByPrimaryKeySelective(systemMenuEntity);

    }

    @Override
    public Object getUserMenu() {

    1. Map<String, Object> map = Maps.newHashMap();
    2. map.put("showFlag", 1);
    3. List<SystemMenuEntity> menus = systemMenuMapper.selectByCondition(map);
    4. //按层级封装,最多三级
    5. Map<String, Object> result = Maps.newHashMap();
    6. result.put("first", menus.stream().filter(n -> {
    7. return n.getMenuLevel() == 1;
    8. }));
    9. result.put("second", menus.stream().filter(n -> {
    10. return n.getMenuLevel() == 2;
    11. }));
    12. result.put("third", menus.stream().filter(n -> {
    13. return n.getMenuLevel() == 3;
    14. }));
    15. return result;

    }

}
package com.znz.service.impl;

import java.io.File;
import java.util.List;

import org.springframework.stereotype.Service;

import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.znz.constant.Constant;
import com.znz.exception.ResultReturnException;
import com.znz.service.FileService;
import com.znz.util.FileUtil;

@Service
public class FileServiceImpl implements FileService {

  1. @Override
  2. public List<JSONObject> getFileTreeByDir(String rootPath, String dir, String typeFilter) {
  3. if(StringUtils.isEmpty(dir)){
  4. if(StringUtils.isEmpty(rootPath)){
  5. dir = Constant.DEFAULT_DIR;
  6. } else {
  7. dir = rootPath;
  8. }
  9. }
  10. if(StringUtils.isEmpty(typeFilter)){
  11. typeFilter = Constant.DEFAULT_TYPE;
  12. }
  13. File f = new File(dir);
  14. List<File> list = FileUtil.listFile(f, typeFilter, false);
  15. List<JSONObject> result = Lists.newArrayList();
  16. list.stream().forEach(n->{
  17. JSONObject jo = new JSONObject();
  18. jo.put("id", n.getAbsolutePath());
  19. jo.put("pid", n.getParentFile().getAbsolutePath());
  20. jo.put("filePath", n.getAbsolutePath());
  21. jo.put("fileName", n.getName());
  22. jo.put("isDir", n.isDirectory());
  23. result.add(jo);
  24. });
  25. return result;
  26. }
  27. @Override
  28. public File readFile(String filePath) {
  29. File f = new File(filePath);
  30. if(!f.exists() || f.isDirectory()) {
  31. throw new ResultReturnException("filePath参数异常,找不到指定的文件: " + filePath);
  32. }
  33. if(!f.exists() || f.isDirectory()) {
  34. throw new ResultReturnException("读取图片异常:" + f.getName());
  35. }
  36. return f;
  37. }

}
以上就是Opencv创建车牌图片识别系统方法详解的详细内容,更多关于Opencv车牌图片识别系统的资料请关注我们其它相关文章!

时间: 2022-01-05
Python+OpenCV实现车牌字符分割和识别

最近做一个车牌识别项目,入门级别的,十分简单. 车牌识别总体分成两个大的步骤: 一.车牌定位:从照片中圈出车牌 二.车牌字符识别 这里只说第二个步骤,字符识别包括两个步骤: 1.图像处理 原本的图像每个像素点都是RGB定义的,或者称为有R/G/B三个通道.在这种情况下,很难区分谁是背景,谁是字符,所以需要对图像进行一些处理,把每个RGB定义的像素点都转化成一个bit位(即0-1代码),具体方法如下: ①将图片灰度化 名字拗口,但是意思很好理解,就是把每个像素的RGB都变成灰色的RGB值,而灰色的
Java通过百度API实现图片车牌号识别

本代码功能是通过调用百度API实现的,所有你需要去百度API官网申请下你的API Key 以及Secret Key才能使用它的功能哦! 拟采用百度AI实现该功能(http://ai.baidu.com/docs#/OCR-API/5116ac95) 根据百度的文档描述,初步明确需要的几个参数为: 1.应用的API Key 2.应用的Secret Key 3.access_token 4.图片数据 首先导入maven依赖 <dependency> <groupId>com.baidu

python+OpenCV实现车牌号码识别

基于python+OpenCV的车牌号码识别,供大家参考,具体内容如下 车牌识别行业已具备一定的市场规模,在电子警察.公路卡口.停车场.商业管理.汽修服务等领域已取得了部分应用.一个典型的车辆牌照识别系统一般包括以下4个部分:车辆图像获取.车牌定位.车牌字符分割和车牌字符识别 1.车牌定位的主要工作是从获取的车辆图像中找到汽车牌照所在位置,并把车牌从该区域中准确地分割出来 这里所采用的是利用车牌的颜色(黄色.蓝色.绿色) 来进行定位 #定位车牌 def color_position(img,ou
OpenCV+Python识别车牌和字符分割的实现
本篇文章主要基于python语言和OpenCV库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置! 车牌号检测需要大致分为四个部分: 1.车辆图像获取 2.车牌定位. 3.车牌字符分割 4.车牌字符识别 具体介绍 车牌定位需要用到的是图片二值化为黑白后进canny边缘检测后多次进行开运算与闭运算用于消除小块的区域,保留大块的区域,后用cv2.rectangle选取矩形框,从而定位车牌位置 车牌字符的分割前需要准备的是只保留车牌

python按照多个字符对字符串进行分割的方法
本文实例讲述了python按照多个字符对字符串进行分割的方法.分享给大家供大家参考.具体分析如下: 这段python代码通过这规则表达式对字符串进行分割,使用\w作为分割符,只要不是字母和数字的就会被分割开来. import re DATA = “Hey, you - what are you doing here! welcome to jb51?” print re.findall(r”[\w’]+”, DATA) 输出结果如下 复制代码 代码如下: [‘Hey
使用Python的OpenCV模块识别滑动验证码的缺口(推荐)

最近终于找到一个好的方法,使用Python的OpenCV模块识别滑动验证码的缺口,可以将滑动验证码中的缺口识别出来了. 测试使用如下两张图片: target.jpg template.png 现在想要通过”template.png”在”target.jpg”中找到对应的缺口,代码实现如下: # encoding=utf8 import cv2 import numpy as np def show(name): cv2.imshow(‘Show’, name) cv
python识别验证码的思路及解决方案
1.介绍 在爬虫中经常会遇到验证码识别的问题,现在的验证码大多分计算验证码.滑块验证码.识图验证码.语音验证码等四种.本文就是识图验证码,识别的是简单的验证码,要想让识别率更高,识别的更加准确就需要花很多的精力去训练自己的字体库. 识别验证码通常是这几个步骤: (1)灰度处理 (2)二值化 (3)去除边框(如果有的话) (4)降噪 (5)切割字符或者倾斜度矫正 (6)训练字体库 (7)识别 这6个步骤中前三个步骤是基本的,4或者5可根据实际情况选择是否需要. 经常用的库有pytesseract(
Python将图片转换为字符画的方法
最近在学习Python,看到网上用Python将图片转换成字符画便来学习一下 题目意思是,程序读入一个图片,以txt格式输出图片对应的字符画,如图所示: 以下是Python代码: # coding:utf-8 # 为一张图片生成对应的字符集图片 from PIL import Image import argparse # 命令行输入参数处理 parser = argparse.ArgumentParser() parser.add_argument(‘file’) # 输入文件 parser.

OpenCV+python手势识别框架和实例讲解
基于OpenCV2.4.8和 python 2.7实现简单的手势识别. 以下为基本步骤 1.去除背景,提取手的轮廓 2. RGB->YUV,同时计算直方图 3.进行形态学滤波,提取感兴趣的区域 4.找到二值化的图像轮廓 5.找到最大的手型轮廓 6.找到手型轮廓的凸包 7.标记手指和手掌 8.把提取的特征点和手势字典中的进行比对,然后判断手势和形状 提取手的轮廓 cv2.findContours() 找到最大凸包cv2.convexHull(),然后找到手掌和手指的相对位置,定位手型的轮廓和关键点
python识别验证码图片实例详解

在编写自动化测试用例的时候,每次登录都需要输入验证码,后来想把让python自己识别图片里的验证码,不需要自己手动登陆,所以查了一下识别功能怎么实现,做一下笔记. 首选导入一些用到的库,re.Image.pytesseract.selenium.time import re # 用于正则 from PIL import Image # 用于打开图片和对图片处理 import pytesseract # 用于图片转文字 from selenium import webdriver # 用于打开网站
20行Python代码实现视频字符化功能
我们经常在B站上看到一些字符鬼畜视频,主要就是将一个视频转换成字符的样子展现出来.看起来是非常高端,但是实际实现起来确是非常简单,我们只需要接触opencv模块,就能很快的实现视频字符化.但是在此之前,我们先看看我们实现的效果是怎样的: 上面就是截取的一部分效果图,下面开始进入我们的主题. 一.OpenCV的安装及图片读取 在Python中我们只需要用pip安装即可,我们在控制台执行下列语句: pip install opencv-python 安装完成就可以开始使用.我们先读取一个图片: im
如何利用Python识别图片中的文字
一.前言 不知道大家有没有遇到过这样的问题,就是在某个软件或者某个网页里面有一篇文章,你非常喜欢,但是不能复制.或者像百度文档一样,只能复制一部分,这个时候我们就会选择截图保存.但是当我们想用到里面的文字时,还是要一个字一个字打出来.那么我们能不能直接识别图片中的文字呢?答案是肯定的. 二.Tesseract 文字识别是ORC的一部分内容,ORC的意思是光学字符识别,通俗讲就是文字识别.Tesseract是一个用于文字识别的工具,我们结合Python使用可以很快的实现文字识别.但是在此之前我们需

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议