您当前的位置:首页 > IT编程 > python
| C语言 | Java | VB | VC | python | Android | TensorFlow | C++ | oracle | 学术与代码 | cnn卷积神经网络 | gnn | 图像修复 | Keras | 数据集 | Neo4j | 自然语言处理 | 深度学习 | 医学CAD | 医学影像 | 超参数 | pointnet | pytorch | 异常检测 | Transformers | 情感分类 | 知识图谱 |

自学教程:python中opencv实现文字分割的实践

51自学网 2021-10-30 22:32:17
  python
这篇教程python中opencv实现文字分割的实践写得很实用,希望能帮到您。

图片文字分割的时候,常用的方法有两种。一种是投影法,适用于排版工整,字间距行间距比较宽裕的图像;还有一种是用OpenCV的轮廓检测,适用于文字不规则排列的图像。

投影法

对文字图片作横向和纵向投影,即通过统计出每一行像素个数,和每一列像素个数,来分割文字。
分别在水平和垂直方向对预处理(二值化)的图像某一种像素进行统计,对于二值化图像非黑即白,我们通过对其中的白点或者黑点进行统计,根据统计结果就可以判断出每一行的上下边界以及每一列的左右边界,从而实现分割的目的。

算法步骤:

  • 使用水平投影和垂直投影的方式进行图像分割,根据投影的区域大小尺寸分割每行和每块的区域,对原始图像进行二值化处理。
  • 投影之前进行图像灰度学调整做膨胀操作
  • 分别进行水平投影和垂直投影
  • 根据投影的长度和高度求取完整行和块信息

横板文字-小票文字分割

#小票水平分割import cv2import numpy as npimg = cv2.imread(r"C:/Users/An/Pictures/1.jpg")cv2.imshow("Orig Image", img)# 输出图像尺寸和通道信息sp = img.shapeprint("图像信息:", sp)sz1 = sp[0]  # height(rows) of imagesz2 = sp[1]  # width(columns) of imagesz3 = sp[2]  # the pixels value is made up of three primary colorsprint('width: %d /n height: %d /n number: %d' % (sz2, sz1, sz3))gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)retval, threshold_img = cv2.threshold(gray_img, 120, 255, cv2.THRESH_BINARY_INV)cv2.imshow("threshold_img", threshold_img)# 水平投影分割图像gray_value_x = []for i in range(sz1):    white_value = 0    for j in range(sz2):        if threshold_img[i, j] == 255:            white_value += 1    gray_value_x.append(white_value)print("", gray_value_x)# 创建图像显示水平投影分割图像结果hori_projection_img = np.zeros((sp[0], sp[1], 1), np.uint8)for i in range(sz1):    for j in range(gray_value_x[i]):        hori_projection_img[i, j] = 255cv2.imshow("hori_projection_img", hori_projection_img)text_rect = []# 根据水平投影分割识别行inline_x = 0start_x = 0text_rect_x = []for i in range(len(gray_value_x)):    if inline_x == 0 and gray_value_x[i] > 10:        inline_x = 1        start_x = i    elif inline_x == 1 and gray_value_x[i] < 10 and (i - start_x) > 5:        inline_x = 0        if i - start_x > 10:            rect = [start_x - 1, i + 1]            text_rect_x.append(rect)print("分行区域,每行数据起始位置Y:", text_rect_x)# 每行数据分段kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 3))dilate_img = cv2.dilate(threshold_img, kernel)cv2.imshow("dilate_img", dilate_img)for rect in text_rect_x:    cropImg = dilate_img[rect[0]:rect[1],0:sp[1]]  # 裁剪图像y-start:y-end,x-start:x-end    sp_y = cropImg.shape    # 垂直投影分割图像    gray_value_y = []    for i in range(sp_y[1]):        white_value = 0        for j in range(sp_y[0]):            if cropImg[j, i] == 255:                white_value += 1        gray_value_y.append(white_value)    # 创建图像显示水平投影分割图像结果    veri_projection_img = np.zeros((sp_y[0], sp_y[1], 1), np.uint8)    for i in range(sp_y[1]):        for j in range(gray_value_y[i]):            veri_projection_img[j, i] = 255    cv2.imshow("veri_projection_img", veri_projection_img)    # 根据垂直投影分割识别行    inline_y = 0    start_y = 0    text_rect_y = []    for i in range(len(gray_value_y)):        if inline_y == 0 and gray_value_y[i] > 2:            inline_y = 1            start_y = i        elif inline_y == 1 and gray_value_y[i] < 2 and (i - start_y) > 5:            inline_y = 0            if i - start_y > 10:                rect_y = [start_y - 1, i + 1]                text_rect_y.append(rect_y)                text_rect.append([rect[0], rect[1], start_y - 1, i + 1])                cropImg_rect = threshold_img[rect[0]:rect[1], start_y - 1:i + 1]  # 裁剪图像                cv2.imshow("cropImg_rect", cropImg_rect)                # cv2.imwrite("C:/Users/ThinkPad/Desktop/cropImg_rect.jpg",cropImg_rect)                # break        # break# 在原图上绘制截图矩形区域print("截取矩形区域(y-start:y-end,x-start:x-end):", text_rect)rectangle_img = cv2.rectangle(img, (text_rect[0][2], text_rect[0][0]), (text_rect[0][3], text_rect[0][1]),                              (255, 0, 0), thickness=1)for rect_roi in text_rect:    rectangle_img = cv2.rectangle(img, (rect_roi[2], rect_roi[0]), (rect_roi[3], rect_roi[1]), (255, 0, 0), thickness=1)cv2.imshow("Rectangle Image", rectangle_img)key = cv2.waitKey(0)if key == 27:    print(key)    cv2.destroyAllWindows()

小票图像二值化结果如下:

在这里插入图片描述

小票图像结果分割如下:

在这里插入图片描述

竖版-古文文字分割

对于古籍来说,古籍文字书写在习惯是从上到下的,所以说在扫描的时候应该扫描列投影,在扫描行投影。

1.原始图像进行二值化

使用水平投影和垂直投影的方式进行图像分割,根据投影的区域大小尺寸分割每行和每块的区域,对原始图像进行二值化处理。

原始图像:

在这里插入图片描述

二值化后的图像:

在这里插入图片描述

2.图像膨胀

投影之前进行图像灰度学调整做膨胀操作,选取适当的核,对图像进行膨胀处理。

在这里插入图片描述

3.垂直投影

定位该行文字区域:
数值不为0的区域就是文字存在的地方(即二值化后白色部分的区域),为0的区域就是每行之间相隔的距离。
1、如果前一个数为0,则记录第一个不为0的坐标。
2、如果前一个数不为0,则记录第一个为0的坐标。形象的说就是从出现第一个非空白列到出现第一个空白列这段区域就是文字存在的区域。
通过以上规则就可以找出每一列文字的起始点和终止点,从而确定每一列的位置信息。

垂直投影结果:

在这里插入图片描述

通过上面的垂直投影,根据其白色小山峰的起始位置就可以界定出每一列的起始位置,从而把每一列分割出来。

4.水平投影

根据投影的长度和高度求取完整行和块信息
通过水平投影可以获得每一个字符左右的起始位置,这样也就可以获得到每一个字符的具体坐标位置,即一个矩形框的位置。

import cv2import numpy as npimport osimg = cv2.imread(r"C:/Users/An/Pictures/3.jpg")save_path=r"E:/crop_img/result" #图像分解的每一步保存的地址crop_path=r"E:/crop_img/img" #图像切割保存的地址cv2.imshow("Orig Image", img)# 输出图像尺寸和通道信息sp = img.shapeprint("图像信息:", sp)sz1 = sp[0]  # height(rows) of imagesz2 = sp[1]  # width(columns) of imagesz3 = sp[2]  # the pixels value is made up of three primary colorsprint('width: %d /n height: %d /n number: %d' % (sz2, sz1, sz3))gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)retval, threshold_img = cv2.threshold(gray_img, 120, 255, cv2.THRESH_BINARY_INV)cv2.imshow("threshold_img", threshold_img)cv2.imwrite(os.path.join(save_path,"threshold_img.jpg"),threshold_img)# 垂直投影分割图像gray_value_y = []for i in range(sz2):    white_value = 0    for j in range(sz1):        if threshold_img[j, i] == 255:            white_value += 1    gray_value_y.append(white_value)print("", gray_value_y)#创建图像显示垂直投影分割图像结果veri_projection_img = np.zeros((sp[0], sp[1], 1), np.uint8)for i in range(sz2):    for j in range(gray_value_y[i]):        veri_projection_img[j, i] = 255cv2.imshow("veri_projection_img", veri_projection_img)cv2.imwrite(os.path.join(save_path,"veri_projection_img.jpg"),veri_projection_img)text_rect = []# 根据垂直投影分割识别列inline_y = 0start_y = 0text_rect_y = []for i in range(len(gray_value_y)):    if inline_y == 0 and gray_value_y[i]> 30:        inline_y = 1        start_y = i    elif inline_y == 1 and gray_value_y[i] < 30 and (i - start_y) > 5:        inline_y = 0        if i - start_y > 10:            rect = [start_y - 1, i + 1]            text_rect_y.append(rect)print("分列区域,每列数据起始位置Y:", text_rect_y)# 每列数据分段# kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 3))kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))dilate_img = cv2.dilate(threshold_img, kernel)cv2.imshow("dilate_img", dilate_img)cv2.imwrite(os.path.join(save_path,"dilate_img.jpg"),dilate_img)for rect in text_rect_y:    cropImg = dilate_img[0:sp[0],rect[0]:rect[1]]  # 裁剪图像y-start:y-end,x-start:x-end    sp_x = cropImg.shape    # 垂直投影分割图像    gray_value_x = []    for i in range(sp_x[0]):        white_value = 0        for j in range(sp_x[1]):            if cropImg[i, j] == 255:                white_value += 1        gray_value_x.append(white_value)    # 创建图像显示水平投影分割图像结果    hori_projection_img = np.zeros((sp_x[0], sp_x[1], 1), np.uint8)    for i in range(sp_x[0]):        for j in range(gray_value_x[i]):            veri_projection_img[i, j] = 255    # cv2.imshow("hori_projection_img", hori_projection_img)    # 根据水平投影分割识别行    inline_x = 0    start_x = 0    text_rect_x = []    ind=0    for i in range(len(gray_value_x)):        ind+=1        if inline_x == 0 and gray_value_x[i] > 2:            inline_x = 1            start_x = i        elif inline_x == 1 and gray_value_x[i] < 2 and (i - start_x) > 5:            inline_x = 0            if i - start_x > 10:                rect_x = [start_x - 1, i + 1]                text_rect_x.append(rect_x)                text_rect.append([start_x - 1, i + 1,rect[0], rect[1]])                cropImg_rect = threshold_img[start_x - 1:i + 1,rect[0]:rect[1]]  # 裁剪二值化图像                crop_img=img[start_x - 1:i + 1,rect[0]:rect[1]] #裁剪原图像                # cv2.imshow("cropImg_rect", cropImg_rect)                # cv2.imwrite(os.path.join(crop_path,str(ind)+".jpg"),crop_img)                # break        # break# 在原图上绘制截图矩形区域print("截取矩形区域(y-start:y-end,x-start:x-end):", text_rect)rectangle_img = cv2.rectangle(img, (text_rect[0][2], text_rect[0][0]), (text_rect[0][3], text_rect[0][1]),                              (255, 0, 0), thickness=1)for rect_roi in text_rect:    rectangle_img = cv2.rectangle(img, (rect_roi[2], rect_roi[0]), (rect_roi[3], rect_roi[1]), (255, 0, 0), thickness=1)cv2.imshow("Rectangle Image", rectangle_img)cv2.imwrite(os.path.join(save_path,"rectangle_img.jpg"),rectangle_img)key = cv2.waitKey(0)if key == 27:    print(key)    cv2.destroyAllWindows()

分割结果如下:

在这里插入图片描述

从分割的结果上看,基本上实现了图片中文字的分割。但由于中文结构复杂性,对于一些文字的分割并不理想,字会出现过度分割、有粘连的两个字会出现分割不够的现象。可以从图像预处理(图像腐蚀膨胀),边界判断阈值的调整等方面进行优化。

到此这篇关于python中opencv实现文字分割的实践的文章就介绍到这了,更多相关opencv 文字分割内容请搜索51zixue.net以前的文章或继续浏览下面的相关文章希望大家以后多多支持51zixue.net!


用Python手把手教你实现2048小游戏
用Python简单实现个贪吃蛇小游戏(保姆级教程)
万事OK自学网:51自学网_软件自学网_CAD自学网自学excel、自学PS、自学CAD、自学C语言、自学css3实例,是一个通过网络自主学习工作技能的自学平台,网友喜欢的软件自学网站。