python+opencv实现文字分割(横板-小票文字分割/竖版-古文文字分割)(代码片段)

告白少年 告白少年     2022-12-02     161

关键词:

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

投影法

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

算法步骤:

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

横板文字-小票文字分割

#小票水平分割
import cv2
import numpy as np

img = cv2.imread(r"C:\\Users\\An\\Pictures\\1.jpg")
cv2.imshow("Orig Image", img)
# 输出图像尺寸和通道信息
sp = img.shape
print("图像信息:", sp)
sz1 = sp[0]  # height(rows) of image
sz2 = sp[1]  # width(columns) of image
sz3 = sp[2]  # the pixels value is made up of three primary colors
print('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] = 255
cv2.imshow("hori_projection_img", hori_projection_img)
text_rect = []
# 根据水平投影分割识别行
inline_x = 0
start_x = 0
text_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 cv2
import numpy as np
import os

img = 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.shape
print("图像信息:", sp)
sz1 = sp[0]  # height(rows) of image
sz2 = sp[1]  # width(columns) of image
sz3 = sp[2]  # the pixels value is made up of three primary colors
print('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] = 255
cv2.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 = 0
start_y = 0
text_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:OCR 图像分割

】Python+OpenCV:OCR图像分割【英文标题】:Python+OpenCV:OCRImageSegmentation【发布时间】:2017-03-1913:05:47【问题描述】:我正在尝试从收据的这个玩具示例中进行OCR。使用Python2.7和OpenCV3.1。灰度+模糊+外部边缘检测+收据中每个区域的分... 查看详情

在 python 中使用 OpenCV 分割图像

】在python中使用OpenCV分割图像【英文标题】:SplittingImageusingOpenCVinpython【发布时间】:2013-10-1111:13:01【问题描述】:我有一个图像,想在python中使用CV2将其拆分为三个RGB通道图像。我还想要好的文档,我可以在其中找到openCV的所... 查看详情

opencv+python实现将车牌数字分割为单个的字符图片(代码片段)

文章目录一、实现代码1.图片预处理读取图片处理车牌上的螺丝转灰度二值化闭运算找字符边界绘制边界预处理效果2.切割字符预处理图转灰度计算每一列的黑色和白色数量以及最大值定义找右边界函数切割字符以及保存切割结... 查看详情

css怎么实现文字和分割线在同一行

分割线要在行内垂直居中html与css如何实现中间有文字的分割线效果?参考技术A——————————分割线——————————直接打出来不行么?追问———怎么打出来追答把输入法调到搜狗或者QQ拼音然后按shift+减号减... 查看详情

opencv+python识别车牌和字符分割(代码片段)

本篇文章主要基于python语言和OpenCV库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置!车牌号检测需要大致分为四个部分:1.车辆图像获取2.... 查看详情

运动对象检测和描述

...踪,背景分割器:KNN、MOG2和GMGBasicmotiondetectionandtrackingwithPythonandOpenCV使用背景减除进行目标检测用OpenCV实现多目标追踪(C++/Python)通过形态学改善图像过滤,追踪人脸,检测前景/背景区域和深度,I/O功能等Opencv:视频颜色识别与... 查看详情

k-means实现图像分割(代码片段)

使用的环境,python3.5,opencv2函数的格式为:cv2.kmeans(data,K,bestLabels,criteria,attempts,flags)'''参数:data:分类数据,最好是np.float32的数据,每个特征放一列。K:分类数,opencv2的kmeans分类是需要已知分类数的。bestLabels:预设的... 查看详情

opencv—python分水岭算法图像分割

文章目录​​一、功能与函数介绍​​​​1.1cv2.distanceTransform(src,distanceType,maskSize)​​​​1.2基于标记的分水岭分割功能​​​​1.3示例代码​​​​二、分水岭方法​​一、功能与函数介绍分水岭算法是一种图像区域分割法,... 查看详情

opencv-实现天空变换(图像分割)(代码片段)

作者:Steven版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处实现原理   天空变换是图像分割的一种应用,把图像中的天空与非天空区分割开,结合掩膜将天空更改为... 查看详情

opencv中的meanshift图像分割和视频背景分离(python实现)(代码片段)

文章目录1.MeanShift原理2.视频前后景分离(1)MOG2去除背景1.MeanShift原理(1)严格来说该方法并不是用来对图像进行分割的,而是在彩色层面的平滑滤波;(2)它会中和色彩分布相近的颜色,平... 查看详情

opencv-python实战——opencv用于图像分割的阈值技术(含大量示例,建议收藏)(代码片段)

OpenCV-Python实战(9)——OpenCV用于图像分割的阈值技术(含大量示例,建议收藏)0.前言1.阈值技术简介2.简单的阈值技术2.1阈值类型2.2简单阈值技术的实际应用3.自适应阈值算法4.Otsu阈值算法5.Triangle阈值算法6.... 查看详情

opencv实现图片的水平投影与垂直投影,并进行行分割

...进行水平投影和垂直投影可以很快的进行分割,本文就在OpenCV中如何进行水平投影和垂直投影通过代码进行说明。水平投影:二维图像在y轴上的投影垂直投影:二维图像在x轴上的投影由于投影的图像需要进行二值化,本文采用... 查看详情

如何使用 Python/Opencv 分割二进制图像中的附近元素

】如何使用Python/Opencv分割二进制图像中的附近元素【英文标题】:HowtosegmentnearbyelementsinabinaryimageusingPython/Opencv【发布时间】:2017-08-0301:08:49【问题描述】:我有这个二进制图像,其中每个“曲线”代表一堆这些物体中的一顶帽... 查看详情

opencv完整例程23.图像添加中文文字(代码片段)

...标点符号)。在图像中添加中文字符,可以使用python+opencv+PIL实现,或使用python+opencv+freetype实现。具体方法详见扩展例程1.32。扩展例程:1.32图像中添加中文文字#1.32图像中添加中文文字imgBGR=cv2.imrea... 查看详情

使用opencv分水岭算法实现图像分割(代码片段)

一、简介分水岭算法的思想是把图像看作是一个拓扑地貌,同类区域就相当于陡峭边缘内相对平摊的盆地。当从高度为0开始逐步用“水”淹没图像时,会形成好多个聚水的盆地,随着盆地的面积逐渐增大,两个盆... 查看详情

opencv实现车牌识别,ocr分割,ann神经网络(代码片段)

主要步骤:准备车牌单个字符图像作为神经网络分类器的训练数据,越多越好。当然需要对每幅图像提取特征,这里使用的是水平和垂直累计直方图和缩小后的图像信息。获取车牌图像,这里的车牌图像已经完成... 查看详情

实战|opencv+ocr实现环形文字识别实例(详细步骤+代码)

导读本文将介绍使用OpenCV+OCR实现环形文字识别的详细步骤和代码演示。(来源公众号:OpenCV与AI深度学习) 背景介绍  光学字符识别(OCR)场景中有很多特殊情况,比如噪声、脏污、倾斜、变形等,都会对识别造成影响... 查看详情

OpenCV中前景-背景分割方法的区别

】OpenCV中前景-背景分割方法的区别【英文标题】:Differencebetweenforeground-backgroundsegmentationmethodsinOpenCV【发布时间】:2013-04-2221:47:18【问题描述】:OpenCV2.4.5版提供了几种不同的实现,可用于跟踪使用统计方法估计背景的移动对象... 查看详情