人工智能OCR文字识别研究

news2024/12/27 1:09:48

1 研究背景
人工智能是研究开发能够模拟、延伸和扩展人类智能的理论、方法、技术及应用系统的一门新的技术科学,研究目的是促使智能机器会听(语音识别、机器翻译等)、会看(图像识别、文字识别等)、会说(语音合成、人机对话等)、会思考(人机对弈、定理证明等)、会学习(机器学习、知识表示等)、会行动(机器人、自动驾驶汽车等)。
人工智能充满未知的探索道路曲折起伏。如何描述人工智能自1956年以来60余年的发展历程,学术界可谓仁者见仁、智者见智。通过查阅资料将人工智能的发展历程划分为以下6个阶段:
一是起步发展期:1956年—20世纪60年代初。人工智能概念提出后,相继取得了一批令人瞩目的研究成果,如机器定理证明、跳棋程序等,掀起人工智能发展的第一个高潮。
二是反思发展期:20世纪60年代—70年代初。人工智能发展初期的突破性进展大大提升了人们对人工智能的期望,人们开始尝试更具挑战性的任务,并提出了一些不切实际的研发目标。然而,接二连三的失败和预期目标的落空(例如,无法用机器证明两个连续函数之和还是连续函数、机器翻译闹出笑话等),使人工智能的发展走入低谷。
三是应用发展期:20世纪70年代初—80年代中。20世纪70年代出现的专家系统模拟人类专家的知识和经验解决特定领域的问题,实现了人工智能从理论研究走向实际应用、从一般推理策略探讨转向运用专门知识的重大突破。专家系统在医疗、化学、地质等领域取得成功,推动人工智能走入应用发展的新高潮。
四是低迷发展期:20世纪80年代中—90年代中。随着人工智能的应用规模不断扩大,专家系统存在的应用领域狭窄、缺乏常识性知识、知识获取困难、推理方法单一、缺乏分布式功能、难以与现有数据库兼容等问题逐渐暴露出来。
五是稳步发展期:20世纪90年代中—2010年。由于网络技术特别是互联网技术的发展,加速了人工智能的创新研究,促使人工智能技术进一步走向实用化。1997年国际商业机器公司(简称IBM)深蓝超级计算机战胜了国际象棋世界冠军卡斯帕罗夫,2008年IBM提出“智慧地球”的概念。以上都是这一时期的标志性事件。
六是蓬勃发展期:2011年至今。随着大数据、云计算、互联网、物联网等信息技术的发展,泛在感知数据和图形处理器等计算平台推动以深度神经网络为代表的人工智能技术飞速发展,大幅跨越了科学与应用之间的“技术鸿沟”,诸如图像分类、语音识别、知识问答、人机对弈、无人驾驶等人工智能技术实现了从“不能用、不好用”到“可以用”的技术突破,迎来爆发式增长的新高潮。
2 OCR文字识别
2.1什么是OCR?
图 自然场景OCR文字识别(无乎648水印为刘世鹏博客自带,后续也是)
OCR英文全称是Optical Character Recognition,中文叫做光学字符识别。它是利用光学技术和计算机技术把印在或写在纸上的文字读取出来,并转换成一种计算机能够接受、人又可以理解的格式。文字识别是计算机视觉研究领域的分支之一,而且这个课题已经是比较成熟了,并且在商业中已经有很多落地项目了。比如汉王OCR,百度OCR,阿里OCR等等,很多企业都有能力都是拿OCR技术开始挣钱了。其实我们自己也能感受到,OCR技术确实也在改变着我们的生活:比如一个手机APP就能帮忙扫描名片、身份证,并识别出里面的信息;汽车进入停车场、收费站都不需要人工登记了,都是用车牌识别技术;我们看书时看到不懂的题,拿个手机一扫,APP就能在网上帮你找到这题的答案。太多太多的应用了,OCR的应用在当今时代确实是百花齐放。
2.2 OCR的分类
如果要给OCR进行分类分为两类:手写体识别和印刷体识别。
这两个可以认为是OCR领域两个大主题了,当然印刷体识别较手写体识别要简单得多,也能从直观上理解,印刷体大多都是规则的字体,因为这些字体都是计算机自己生成再通过打印技术印刷到纸上。在印刷体的识别上有其独特的干扰:在印刷过程中字体很可能变得断裂或者墨水粘连,使得OCR识别异常困难。当然这些都可以通过一些图像处理的技术帮他尽可能的还原,进而提高识别率。总的来说,单纯的印刷体识别在业界已经能做到很不错了,但说100%识别是肯定不可能的,但是说识别得不错那是没毛病。

图 手写字体展示
印刷体已经识别得不错了,那么手写体呢?手写体识别一直是OCR界一直想攻克的难关,但是时至今天,感觉这个难关还没攻破,还有很多学者和公司在研究。为什么手写体识别的难度在于因为人类手写的字往往带有个人特色,每个人写字的风格基本不一样,印刷体一般都比较规则,字体都基本就那几十种,机器学习这几十种字体并不是一件难事,但是手写体,每个人都有一种字体的话,那机器该学习大量字体,这就是难度所在。
2.3 OCR流程
假如输入系统的图像是一页文本,那么识别时的第一件事情是判断页面上的文本朝向,因为得到的这页文档往往都不是很完美的,很可能带有倾斜或者污渍,那么要做的第一件事就是进行图像预处理,做角度矫正和去噪。然后要对文档版面进行分析,进每一行进行行分割,把每一行的文字切割下来,最后再对每一行文本进行列分割,切割出每个字符,将该字符送入训练好的OCR识别模型进行字符识别,得到结果。但是模型识别结果往往是不太准确的,需要对其进行识别结果的矫正和优化,比如可以设计一个语法检测器,去检测字符的组合逻辑是否合理。比如,考虑单词Because,设计的识别模型把它识别为8ecause,那么就可以用语法检测器去纠正这种拼写错误,并用B代替8并完成识别矫正。这样子,整个OCR流程就走完了。从大的模块总结而言,一套OCR流程可以分为:
从上面的流程图可以看出,要做字符识别并不是单纯一个OCR模块就能实现的(如果单纯的OCR模块,识别率相当低),都要各个模块的组合来保证较高的识别率。上面的流程分的比较粗,每个模块下还是有很多更细节的操作,每个操作都关系着最终识别结果的准确性。做过OCR的童鞋都知道,送入OCR模块的图像越清晰(即预处理做的越好),识别效果往往就越好。那现在对这流程中最为重要的字符识别技术做一个总结。
2.4 OCR的简单应用

图 瓶盖的生产日期识别
在一些简单环境下OCR的准确度已经比较高了(比如电子文档),但是在一些复杂环境下的字符识别,在当今还没有人敢说自己能做的很好。现在大家都很少会把目光还放在如何对电子文档的文字识别该怎么进一步提高准确率了,因为他们把目光放在更有挑战性的领域。OCR传统方法在应对复杂图文场景的文字识别显得力不从心,越来越多人把精力都放在研究如何把文字在复杂场景读出来,并且读得准确作为研究课题,用学界术语来说,就是场景文本识别(文字检测+文字识别)。
对图片进行水平投影,找到每一行的上界限和下界限,进行行切割
对切割出来的每一行,进行垂直投影,找到每一个字符的左右边界,进行单个字符的切割。
首先是行切割。这里提到了水平投影的概念。水平投影,就是对一张图片的每一行元素进行统计(就是往水平方向统计),然后根据这个统计结果画出统计结果图,进而确定每一行的起始点和结束点。下面提到的垂直投影也是类似的,只是它的投影方向是往下的,即统计每一列的元素个数。
根据上面的解释,可以写出一个用于水平投影和垂直投影的函数。

#define V_PROJECT 1  //垂直投影(vertical)
#define H_PROJECT 2  //水平投影(horizational)
typedef struct
{
	int begin;
	int end;
}char_range_t;
//获取文本的投影以用于分割字符(垂直,水平),默认图片是白底黑色
int GetTextProjection(Mat& src, vector<int>& pos, int mode)
{
	if (mode == V_PROJECT)
	{
		for (int i = 0; i < src.rows; i++)
		{
			uchar* p = src.ptr<uchar>(i);
			for (int j = 0; j < src.cols; j++)
			{
				if (p[j] == 0)  //是黑色像素
				{
					pos[j]++;
				}
			}
		}
	}
	else if (mode == H_PROJECT)
	{
		for (int i = 0; i < src.cols; i++)
		{

			for (int j = 0; j < src.rows; j++)
			{
				if (src.at<uchar>(j, i) == 0)
				{
					pos[j]++;
				}
			}
		}
	}
	return 0;}

下面是画出水平(垂直)投影图的代码实现。

void draw_projection(vector<int>& pos, int mode)
{
	vector<int>::iterator max = std::max_element(std::begin(pos), std::end(pos)); //求最大值
	if (mode == H_PROJECT)
	{
		int height = pos.size();
		int width = *max;
		Mat project = Mat::zeros(height, width, CV_8UC1);
		for (int i = 0; i < project.rows; i++)
		{
			for (int j = 0; j < pos[i]; j++)
			{
				project.at<uchar>(i, j) = 255;
			}
		}
		imshow("horizational projection", project);
	}
	else if (mode == V_PROJECT)
	{
		int height = *max;
		int width = pos.size();
		Mat project = Mat::zeros(height, width, CV_8UC1);
		for (int i = 0; i < project.cols; i++)
		{
			for (int j = project.rows - 1; j >= project.rows - pos[i]; j--)
			{
				//std::cout << "j:" << j << "i:" << i << std::endl;
				project.at<uchar>(j, i) = 255;
			}
		}
		imshow("vertical projection", project);
	}
	waitKey();
}

投影图:

图 行分割投影图

通过上面的水平投影图,很容易就能确定每一行文字的位置,确定的思路如下:可以以每个小山峰的起始结束点作为文本行的起始结束点,当然要对这些山峰做些约束,比如这些山峰的跨度不能太小。这样子就得到每一个文本行的位置,接着就根据这些位置将每个文本行切割下来用于接下来的单个字符的切割。

//获取每个分割字符的范围,min_thresh:波峰的最小幅度,min_range:两个波峰的最小间隔

int GetPeekRange(vector<int>& vertical_pos, vector<char_range_t>& peek_range, int min_thresh = 2, int min_range = 10)
{
	int begin = 0;
	int end = 0;
	for (int i = 0; i < vertical_pos.size(); i++)
	{
		if (vertical_pos[i] > min_thresh && begin == 0)
		{
			begin = i;
		}
		else if (vertical_pos[i] > min_thresh && begin != 0)
		{
			continue;
		}
		else if (vertical_pos[i] < min_thresh && begin != 0)
		{
			end = i;
			if (end - begin >= min_range)
			{
				char_range_t tmp;
				tmp.begin = begin;
				tmp.end = end;
				peek_range.push_back(tmp);
				begin = 0;
				end = 0;
			}
		}
		else if (vertical_pos[i] < min_thresh || begin == 0)
		{
			continue;
		}
		else
		{
			//printf("raise error!\n");
		}
	}
	return 0;
}

图 人工智能课本识别图
3 文字数据加强
首先是定义好输入参数,其中包括输出目录、字体目录、测试集大小、图像尺寸、图像旋转幅度等等。

def args_parse() :
    #解析输入参数
    parser = argparse.ArgumentParser(
        description = description, formatter_class = RawTextHelpFormatter)
    parser.add_argument('--out_dir', dest = 'out_dir',
        default = None, required = True,
        help = 'write a caffe dir')
    parser.add_argument('--font_dir', dest = 'font_dir',
        default = None, required = True,
        help = 'font dir to to produce images')
    parser.add_argument('--test_ratio', dest = 'test_ratio',
        default = 0.2, required = False,
        help = 'test dataset size')
    parser.add_argument('--width', dest = 'width',
        default = None, required = True,
        help = 'width')
    parser.add_argument('--height', dest = 'height',
        default = None, required = True,
        help = 'height')
    parser.add_argument('--no_crop', dest = 'no_crop',
        default = True, required = False,
        help = '', action = 'store_true')
    parser.add_argument('--margin', dest = 'margin',
        default = 0, required = False,
        help = '', )
    parser.add_argument('--rotate', dest = 'rotate',
        default = 0, required = False,
        help = 'max rotate degree 0-45')
    parser.add_argument('--rotate_step', dest = 'rotate_step',
        default = 0, required = False,
        help = 'rotate step for the rotate angle')
    parser.add_argument('--need_aug', dest = 'need_aug',
        default = False, required = False,
        help = 'need data augmentation', action = 'store_true')
    args = vars(parser.parse_args())
    return args

接下来需要将第一步得到的对应表读入内存,因为这个表示ID到汉字的映射,在做一下转换,改成汉字到ID的映射,用于后面的字体生成。

#将汉字的label读入,得到(ID:汉字)的映射表label_dict
label_dict = get_label_dict()

char_list = []  # 汉字列表
value_list = [] # label列表
for (value, chars) in label_dict.items() :
    print(value, chars)
    char_list.append(chars)
    value_list.append(value)  # 合并成新的映射关系表:(汉字:ID)
    lang_chars = dict(zip(char_list, value_list))
    font_check = FontCheck(lang_chars)
对旋转的角度存储到列表中,旋转角度的范围是[-rotate,rotate]if rotate < 0:
roate = -rotate

if rotate > 0 and rotate <= 45:
all_rotate_angles = []
for i in range(0, rotate + 1, rotate_step) :
    all_rotate_angles.append(i)
    for i in range(-rotate, 0, rotate_step) :
        all_rotate_angles.append(i)
        #print(all_rotate_angles)

现在说一下字体图像是怎么生成的,首先使用的工具是PIL。PIL里面有很好用的汉字生成函数,我们用这个函数再结合我们提供的字体文件,就可以生成我们想要的数字化的汉字了。我们先设定好我们生成的字体颜色为黑底白色,字体尺寸由输入参数来动态设定。

生成字体图像


```python
class Font2Image(object) :

    def __init__(self,
        width, height,
        need_crop, margin) :
    self.width = width
    self.height = height
    self.need_crop = need_crop
    self.margin = margin

    def do(self, font_path, char, rotate = 0) :
    find_image_bbox = FindImageBBox()
    # 黑色背景
    img = Image.new("RGB", (self.width, self.height), "black")
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype(font_path, int(self.width * 0.7), )
    # 白色字体
    draw.text((0, 0), char, (255, 255, 255),
        font = font)
    if rotate != 0:
img = img.rotate(rotate)
data = list(img.getdata())
sum_val = 0
for i_data in data :
sum_val += sum(i_data)
if sum_val > 2:
np_img = np.asarray(data, dtype = 'uint8')
np_img = np_img[:, 0]
np_img = np_img.reshape((self.height, self.width))
cropped_box = find_image_bbox.do(np_img)
left, upper, right, lower = cropped_box
np_img = np_img[upper:lower + 1, left : right + 1]
if not self.need_crop:
preprocess_resize_keep_ratio_fill_bg = \
PreprocessResizeKeepRatioFillBG(self.width, self.height,
    fill_bg = False,
    margin = self.margin)
    np_img = preprocess_resize_keep_ratio_fill_bg.do(
        np_img)
    # cv2.imwrite(path_img, np_img)
    return np_img
else:
print("img doesn't exist.")

额外的图像增强
第三步生成的汉字图像是最基本的数据集,它所做的图像处理仅有旋转这么一项,如果想在数据增强上再做多点东西,想必最终训练出来的OCR模型的性能会更加优秀。使用opencv来完成定制的汉字图像增强任务。
因为生成的图像比较小,仅仅是30*30,如果对这么小的图像加噪声或者形态学处理,得到的字体图像会很糟糕,所以在做数据增强时,把图片尺寸适当增加,比如设置为100×100,再进行相应的数据增强,效果会更好。
噪音增加

```python

def add_noise(cls, img) :
    for i in range(20) : #添加点噪声
        temp_x = np.random.randint(0, img.shape[0])
        temp_y = np.random.randint(0, img.shape[1])
        img[temp_x][temp_y] = 255
        return img
适当腐蚀
def add_erode(cls, img) :
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    img = cv2.erode(img, kernel)
    return img
适当膨胀
def add_dilate(cls, img) :
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    img = cv2.dilate(img, kernel)
    return img
然后做随机扰动
def do(self, img_list = []) :
    aug_list = copy.deepcopy(img_list)
    for i in range(len(img_list)) :
        im = img_list[i]
        if self.noiseand random.random() < 0.5 :
            im = self.add_noise(im)
            if self.dilateand random.random() < 0.25 :
                im = self.add_dilate(im)
                if self.erodeand random.random() < 0.25 :
                    im = self.add_erode(im)
                    aug_list.append(im)
                    return aug_list

使用这种生成的图像如下图所示,第一数据集扩大了两倍,第二图像的丰富性进一步提高,效果还是明显的。当然,如果要获得最好的效果,还需要调一下里面的参数。

图 增加噪音后的数据

图 采集的试卷上的数据集
上图为通过试卷上的手写文字采集到数据集,通过文字定位与分割,将分割后的文字进行人工标注分类。
4 文本检测CTPN
2016年出了一篇很有名的文本检测的论文:《Detecting Text in Natural Image withConnectionist Text Proposal Network》,这个深度神经网络叫做CTPN,直到今天这个网络框架一直是OCR系统中做文本检测的一个常用网络,极大地影响了后面文本检测算法的方向。
回顾一下Faster RCNN做目标检测的一个缺点就是,没有考虑带文本自身的特点。文本行一般以水平长矩形的形式存在,而且文本行中每个字都有间隔。针对这个特点,CTPN剔除一个新奇的想法,把文本检测的任务拆分,第一步检测文本框中的一部分,判断它是不是一个文本的一部分,当对一幅图里所有小文本框都检测之后,将属于同一个文本框的小文本框合并,合并之后得到一个完整的、大的文本框了,也就完成了文本的检测任务。这个想法很有创造性,有点像“分治法”,先检测大物体的一小部分,等所有小部分都检测出来,大物体也就可以检测出来了。
在这里插入图片描述

图 RPN和CTPN对比
如图所示,左边的图是直接使用Faster RCNN中的RPN来进行候选框提取,可以看出,这种候选框太粗糙了,效果并不好。而右图是利用许多小候选框来合并成一个大文本预测框,可以看出这个算法的效果非常不错,需要说明的是,红色框表示这个小候选框的置信度比较高,而其他颜色的候选框的置信度比较低,可以看到,一个大文本的边界都是比较难预测的,那怎么解决这个边界预测不准的问题呢?后面会提到。
刚提到CTPN的其中一个闪光点,即检测小框代替直接检测大文本框。除了这个新意,CTPN还提出了在文本检测中应加入RNN来进一步提升效果。为什么要用RNN来提升检测效果?文本具有很强的连续字符,其中连续的上下文信息对于做出可靠决策来说很重要。RNN常用于序列模型,比如事件序列,语言序列等等,那CTPN算法中,把一个完整的文本框拆分成多个小文本框集合,其实这也是一个序列模型,可以利用过去或未来的信息来学习和预测,所以同样可以使用RNN模型。而且,在CTPN中,用的还是BiLSTM(双向LSTM),因为一个小文本框,对于它的预测,不仅与其左边的小文本框有关系,而且还与其右边的小文本框有关系!这个解释就很有说服力了,如果仅仅根据一个文本框的信息区预测该框内含不含有文字其实是很草率的,应该多参考这个框的左边和右边的小框的信息后(尤其是与其紧挨着的框)再做预测准确率会大大提升。
在这里插入图片描述

图 CTPN候选框
如上图所示,如果单纯依靠1号框内的信息来直接预测1号框中否存在文字(或者说是不是文本的一部分),其实难度相当大,因为1号框只包含文字的很小一部分。但是如果把2号框和3号框的信息都用上,来预测1号框是否存在文字,那么就会有比较大的把握来预测1号框确实有文字。还可以看看为什么边缘的文本框的置信度会较中间的低呢?个人认为很大一部分原因就在于因为这些框都位于总文本的边缘,没有办法充分利用左右相邻序列的信息做预测(比如位于最左的文本框丢失了其右边的信息)。这就是双向LSTM的作用,把左右两个方向的序列信息都加入到学习的过程中去。
CTPN借助了Faster RCNN中anchor回归机制,使得RPN能有效地用单一尺寸的滑动窗口来检测多尺寸的物体。当然CTPN根据文本检测的特点做了比较多的创新。比如RPN中anchor机制是直接回归预测物体的四个参数(x,y,w,h),但是CTPN采取之回归两个参数(y,h),即anchor的纵向偏移以及该anchor的文本框的高度,因为每个候选框的宽度w已经规定为16个像素,不需要再学习,而x坐标直接使用anchor的x坐标,也不用学习,所以CTPN的思路就是只学习y和h这两个参数来完成小候选框的检测!跟RPN相类似,CTPN中对于每个候选框都使用了K个不同的anchors(k在这里默认是10),但是与RPN不同的是,这里的anchors的width是固定的16个像素,而height的高度范围为11~273(每次对输入图像的height除以0.7,一共K个高度)。当然CTPN中还是保留了RPN大多数的思路,比如还是需要预测候选框的分数score(该候选框有文本和无文本的得分)。
文本行构建很简单,通过将那些text/no-text score > 0.7的连续的text proposals相连接即可。文本行的构建如下。首先,为一个proposal Bi定义一个邻居(Bj):Bj−>Bi,其中,Bj在水平距离上离Bi最近,该距离小于50 pixels它们的垂直重叠(vertical overlap) > 0.7,另外,如果同时满足Bj−>Bi和Bi−>Bj,会将两个proposals被聚集成一个pair。接着,一个文本行会通过连续将具有相同proposal的pairs来进行连接来构建。
在这里插入图片描述

图 CTPN网络架构
首先CTPN的基础网络使用了VGG16用于特征提取,在VGG的最后一个卷积层CONV5,CTPN用了3×3的卷积核来对该feature map做卷积,这个CVON5 特征图的尺寸由输入图像来决定,而卷积时的步长却限定为16,感受野被固定为228个像素。卷积后的特征将送入BLSTM继续学习,最后接上一层全连接层FC输出要预测的参数:2K个纵向坐标y,2k个分数,k个x的水平偏移量。看到这里大家可能有个疑问,这个x的偏移到底是什么,为什么需要回归这个参数?如果需要X的参数,为什么不在候选框参数回归时直接预测成(x,y,h)三个参数呢,而要多此一举把该参数单独预测,这个X的作用作者提到这也是他们论文的一大亮点,称之为Side-refinement,可以理解为文本框边缘优化。回顾一下上面提到的一个问题,文本框检测中边缘部分的预测并不准确。那么改咋办,CTPN就是用这个X的偏移量来精修边缘问题。这个X是指文本框在水平方向的左边界和右边界,通过回归这个左边界和右边界参数进而可以使得对文本框的检测更为精准。在这里想举个例子说明一下回归这个x参数的重要性。
通过观察下图,第一幅图张看到有很多小候选框,位于左边的候选框我标记为1、2、3、4号框,1号框和2号框为蓝色,表明得分不高就不把这两个框合并到大文本框内,对于3号框和4号框那就比较尴尬了,如果取3号框作为文本框的边缘框,那么显然左边边缘留白太多,精准度不够,但如果去掉3号框而使用4号框作为左边缘框,则有些字体区域没有检测出来,同样检测精度不足。这种情况其实非常容易出现,所以CTPN采取了Side-refinement 思路进一步优化边缘位置的预测即引入回归X参数,X参数直接标定了完整文本框的左右边界,做到精确的边界预测。第二幅图中的红色框就是经过Side-refinement后的检测结果,可以看出检测准确率有了很大的提升。 side-refinement确实可以进一步提升位置准确率,在SWT的Multi-Lingual datasets上产生2%的效果提升。
在这里插入图片描述

再看多几幅图,体验一下Side-refinement后的效果。
在这里插入图片描述

最后总结一下CTPN这个流行的文本检测框架的三个闪光点:
将文本检测任务转化为一连串小尺度文本框的检测;
引入RNN提升文本检测效果;
Side-refinement(边界优化)提升文本框边界预测精准度。
当然,CTPN也有一个很明显的缺点:对于非水平的文本的检测效果并不好。CTPN论文中给出的文本检测效果图都是文本位于水平方向的,显然CTPN并没有针对多方向的文本检测有深入的探讨。
5 运行效果展示
在这里插入图片描述

6总结
通过查阅相关资料学习什么是OCR并且查阅了如何实现OCR文字识别中的文字检测,并且通过理论实现流程并且用代码对CPTN文字检测进行复现。通过对文字数据预处理并且进行文字字符分割,看似简单,做起来其实很难做得很好,我们也对此查阅了很多论文,发现其实很多论文也谈到了,汉字确实很那做到一个高正确率的分割,直至现在还没有一统江湖的解决方案。汉字切割的失败,就会直接导致了后面OCR识别的失败,这也是当前很多一些很厉害的OCR公司都没法把汉字做到100%识别的一个原因。所以这个问题就必须得到很好的解决。最后我们解决汉字切割的较好方法是,在OCR识别中再把它修正。并且通过文字分割后对数据进行数据增强生成了大量数据防止模型的过拟合。并且通过学习CPTN论文,学习到了思路上的创新,在检测水平上的文字置信度很高,但是也有一些弊端对于非水平的文本检测效果并不好。
7引用文献
[1] Detecting Text in Natural Image with Connectionist Text Proposal Network.作者: Zhi Tian; Weilin Huang; Tong He; Pan He; Yu Qiao 0001
[2]基于深度学习的汉字识别方法研究[D].任凤丽.东华大学. 2021
[3]基于深度学习的光学字符识别技术研究[D].冯亚南.南京邮电大学 2020
[4]基于卷积神经网络的手写数字识别研究与设计[D].刘辰雨.成都理工大学2018
[5]基于CRNN的中文手写识别方法研究[J]. 石鑫,董宝良,王俊丰.信息技术. 2019(11)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/140334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Jiralert实现AlertManager告警对接Jira

简介 Alertmanager 处理由客户端应用程序&#xff08;如 Prometheus server&#xff09;发送的警报。它负责去重(deduplicating)&#xff0c;分组(grouping)&#xff0c;并将它们路由(routing)到正确的接收器(receiver)集成&#xff0c;如电子邮件&#xff0c;微信&#xff0c…

MMYOLO 自定义数据集从标注到部署保姆级教程

theme: juejin 来自社区 PeterH0323 投稿 AI 已经被应用到各行各业&#xff0c;现如今任何人都可以轻松基于开源框架快速搭建符合自身需求的 AI 应用。本文将基于 MMYOLO 开源框架&#xff0c;基于生活中收集的猫猫数据集&#xff0c;教你如何从零开始训练一个可部署检测模型…

TiCDC 源码阅读(二)TiKV CDC 模块介绍

内容概要 TiCDC 是一款 TiDB 增量数据同步工具&#xff0c;通过拉取上游 TiKV 的数据变更日志&#xff0c;TiCDC 可以将数据解析为有序的行级变更数据输出到下游。 本文是 TiCDC 源码解读的第二篇&#xff0c;将于大家介绍 TiCDC 的重要组成部分&#xff0c;TiKV 中的 CDC 模…

【C++】命名空间(namespace) 以及理解using namespace std

命名空间1.命名空间使用的背景1.背景2.命名空间的定义&#xff08;namespace&#xff09;2.1正常的定义2.2 命名空间可以嵌套定义2.3允许命名空间相同3.域作用限定符&#xff08;&#xff1a;&#xff1a;&#xff09;和命名空间的使用3.1域作用限定符&#xff08;&#xff1a;…

【nodejs】模块化

一、概念 1、模块化 编程领域中的模块化&#xff0c;就是遵守固定的规则&#xff0c;把一个大文件拆成独立并相互依赖的多个小模块 把代码进行模块化拆分的好处&#xff1a; 1、提高代码的复用性 2、提高代码的可维护性 3、可以实现按需加载 2、模块化规范 对代码进行模块化…

《CSS新世界》读书笔记

前言 本文为《CSS新世界》的读书笔记。推荐去读原著。 《CSS新世界》微信读书APP链接&#xff1a;CSS新世界-张鑫旭-微信读书 (qq.com) 1. 尺寸属性值&#xff1a;fit-content 描述 fit-content 不是一个属性&#xff0c;它是 css 尺寸系列属性的一个新属性值。可用在 wid…

lammps教程:旋转模型的技巧

大家好&#xff0c;我是小马老师。 本文介绍lammps翻转模型的方法。 在进行分子动力学模拟时&#xff0c;可能需要特定的面位于设定的方向。 如Al2O3的力学性能模拟中&#xff0c;需要分别对A、B、C面进行压痕或者摩擦模拟。 按照研究界面垂直z轴&#xff0c;并且面法线沿着z轴…

转转测试环境治理的高效能实践

文章目录1. 背景及需求1.1 系统架构的发展1.2 测试环境的需求2. 传统的测试环境解决方案-物理隔离3. 转转测试环境V1-改进的物理隔离3.1 稳定环境3.2 动态环境3.3 优缺点3.3.1 优点3.3.2 缺点4. 转转测试环境V2-基于自动IP标签的流量路由5. 转转测试环境V3-基于手动标签的流量路…

大数据挖掘-伤寒论和金匮要略(COVID-19用药启示录,1.4万字收藏)

来自Toby老师&#xff0c;大数据挖掘-伤寒论和金匮要略 大家好&#xff0c;我是Toby老师&#xff0c;三年来新冠病毒肆虐全球&#xff0c;带来一些列症状&#xff0c;例如发热&#xff0c;恶寒&#xff0c;咳嗽&#xff0c;咽喉痛&#xff0c;腹泻&#xff0c;心脑血管疾病等…

C语言_动态内存管理

目录 1. 为什么存在动态内存管理 2. 动态内存函数介绍 2.1 开辟内存块函数_malloc 2.2 动态内存释放和回收函数_free 2.3 开辟空间初始化元素为0的函数_calloc 2.4 调整动态内存开辟大小的函数_realloc 3. 常见的动态内存错误 3.1 对NULL进行解引用操作 3.2 对动态开辟…

aloam学习笔记(二)

学习aloam框架中前端对于点云部分的预处理和点面特征提取。 这些功能在scanRegistration.cpp部分实现&#xff0c;所以也是对于这个源码的学习。 一、main函数 从main函数开始分析。 首先整个完整的main函数内容&#xff1a; int main(int argc, char **argv) {ros::init(…

RSA、MD5加密解密算法全套解析安装教程

第一部分介绍加密解密算法&#xff0c; 第二部分介绍我小组成功应用的RSA、MD5两种加密解密算法&#xff0c;以及心得体会。 1、加密解密算法介绍 应用的开发中安全很重要&#xff0c;所以信息加密技术显得尤为重要。我们需要对应用中的多项数据进行加密处理&#xff0c;从而来…

(人工智能的数学基础)第一章特征向量与矩阵分析——第三节:特征向量与特征值

参考 3Blue1Brown系列&#xff1a;特征向量和特征值第十章 线性代数之 特征向量与特征值】3Blue1Brown知乎&#xff1a;线性代数的本质10 特征向量和特征值 文章目录一&#xff1a;特征向量与特征值概念引入二&#xff1a;特征向量与特征值概念求解三&#xff1a;特征向量与特…

谷粒学院——第七章、课程分类管理

EasyExcel 介绍 简介 Excel导入导出的应用场景 1、数据导入:减轻录入工作量 2、数据导出:统计信息归档 3、数据传输:异构系统之间数据传输 EasyExcel的特点 Java 领域解析、生成 Excel 比较有名的框架有 Apache poi、jxl 等。但他们都存在一个严重的问题就是非常的耗内存。…

Python数据分析三剑客之Pandas

写在前面的话&#xff1a; 开始之前请确保已经配置好python环境&#xff0c;并安装好第三方库pandas和numpy。 1. pandas库介绍 什么是pandas&#xff1f;pandas是提供高性能易用数据类型和数据分析工具的第三方库。简单讲&#xff0c;pandas主要作用有两个&#xff1a;提供了…

电子学会2020年6月青少年软件编程(图形化)等级考试试卷(二级)答案解析

目录 一、单选题&#xff08;共25题&#xff0c;每题2分&#xff0c;共50分&#xff09; 二、判断题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 三、编程题&#xff08;共3题&#xff0c;共30分&#xff09; 青少年软件编程&#xff08;Scratch&…

谷粒学院——第八章、课程管理

一、课程添加功能 概览 课程添加的步骤 课程相关表的关系 后端实现 1、代码生成器 只修改表名即可&#xff0c;依次填入&#xff1a;“edu_course”, “edu_course_description”, “edu_chapter”, “edu_video” 生成完成后&#xff0c; 删除EduCourseDescriptionContr…

力扣 1801. 积压订单中的订单总数

题目 给你一个二维整数数组 orders &#xff0c;其中每个 orders[i] [pricei, amounti, orderTypei] 表示有 amounti 笔类型为 orderTypei 、价格为 pricei 的订单。 订单类型 orderTypei 可以分为两种&#xff1a; 0 表示这是一批采购订单 buy 1 表示这是一批销售订单 sel…

学习疑惑:用什么方法进行产品原型设计

对于在互联网行业的各位来讲&#xff0c;应该很清楚原型设计在应用开发中的重要性。它所起到的不仅是沟通的作用&#xff0c;更有体现之效。通过内容和结构展示&#xff0c;以及粗略布局&#xff0c;能够说明用户将如何与产品进行交互&#xff0c;体现开发者及UI设计师的idea&a…

pytorch拼接函数:torch.stack()和torch.cat()详解

在pytorch中&#xff0c;常见的拼接函数主要是两个&#xff0c;分别是&#xff1a;stack()和cat()。 torch.stack()函数的意义&#xff1a;使用stack可以保留两个信息&#xff1a;[1. 序列] 和 [2. 张量矩阵] 信息&#xff0c;属于【扩张再拼接】的函数。 形象的理解&#xff…