• Python+OpenCV竖版古籍文字分割


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

    1. 思路

    一开始想偷个懒,直接用OpenCV的模型,结果发现效果不佳。文字出现了过度分割的问题,部分文字甚至没有被识别:

    于是只好使用传统方法,投影法。对文字图片作横向和纵向投影,即通过统计出每一行像素个数,和每一列像素个数,来分割文字。代码参考https://www.cnblogs.com/zxy-joy/p/10687152.html,但是对于古籍来说,需要做一些修改。比如,古籍文字书写在习惯是从上到下的,所以说在扫描的时候应该扫描列投影,在扫描行投影,搞定这次简单的操作顺序修改以后,分割结果如下:

    很显然,虽然说没有出现过度分割的问题,但是由于字体有大有小,有的地方两个字被合起来识别成了一个字。那么很显然,只要把这些地方再进行一次列投影,把它们再度拆分成两个字,问题不就解决了么。添加代码:

    # 再进行一次列扫描
    DcropImg = cropImg[H_start[pos]:H_end[pos], 0:w]
    d_h, d_w = DcropImg.shape 
    # cv2.imshow("dcrop", DcropImg)
    sec_V = getVProjection(DcropImg)
    c1, c2 = scan(sec_V, 0)
    if len(c1) > len(c2):
      c2.append(d_w)
    
    # cv2.waitKey(0)
    if len(c1) == 1:
        Position.append([V_start[i],H_start[pos],V_end[i],H_end[pos]])
    else:
        for x in range(len(c1)):
            Position.append([V_start[i]+c1[x], H_start[pos],V_start[i]+c2[x], H_end[pos]])

    2. 优化

    对单行文本做列扫描,很容易出现过度分割的问题。因为只有一行,会扫描到很多没有像素点的列,最终就会出现这种情况:

    为了避免这种过度分割的情况,可以添加一个检测两个分割之间距离的代码,使距离较近的分割进行合并。

    x = 1
    while x < len(c1):
      if c1[x] - c2[x-1] < 12:
        c2.pop(x-1)
        c1.pop(x)
        x -= 1
        x += 1

    3. 代码

    再通过添加一些属性来限制一个字的最大长度宽度、两个字之间的最小间距,来避免过度分割,最终效果如下:

    虽然仍然存在一些小瑕疵,但是总体效果还算不错。

    详细代码如下:

    import cv2
    import numpy as np
    
    HIOG = 50
    VIOG = 3
    Position = []
     
    '''水平投影'''
    def getHProjection(image):
        hProjection = np.zeros(image.shape,np.uint8)
        # 获取图像大小
        (h,w)=image.shape
        # 统计像素个数
        h_ = [0]*h
        for y in range(h):
            for x in range(w):
                if image[y,x] == 255:
                    h_[y]+=1
        #绘制水平投影图像
        for y in range(h):
            for x in range(h_[y]):
                hProjection[y,x] = 255
        # cv2.imshow('hProjection2',cv2.resize(hProjection, None, fx=0.3, fy=0.5, interpolation=cv2.INTER_AREA))
        # cv2.waitKey(0)
        return h_
     
    def getVProjection(image):
        vProjection = np.zeros(image.shape,np.uint8);
        (h,w) = image.shape
        w_ = [0]*w
        for x in range(w):
            for y in range(h):
                if image[y,x] == 255:
                    w_[x]+=1
        for x in range(w):
            for y in range(h-w_[x],h):
                vProjection[y,x] = 255
        # cv2.imshow('vProjection',cv2.resize(vProjection, None, fx=1, fy=0.1, interpolation=cv2.INTER_AREA))
        # cv2.waitKey(0)
        return w_
    
    
    def scan(vProjection, iog, pos = 0):
        start = 0
        V_start = []
        V_end = []
    
        for i in range(len(vProjection)):
            if vProjection[i] > iog and start == 0:
                V_start.append(i)
                start = 1
            if vProjection[i] <= iog and start == 1:
                if i - V_start[-1] < pos:
                    continue
                V_end.append(i)
                start = 0
        return V_start, V_end
    
    
    def checkSingle(image):
        h = getHProjection(image)
        start = 0
        end = 0
    
        for i in range(h):
            pass
    
     
    if __name__ == "__main__":
        # 读入原始图像
        origineImage = cv2.imread('test_data/test2.jpg')
        # 图像灰度化   
        #image = cv2.imread('test.jpg',0)
        image = cv2.cvtColor(origineImage,cv2.COLOR_BGR2GRAY)
        
        # cv2.imshow('gray',image)
        # 将图片二值化
        retval, img = cv2.threshold(image,127,255,cv2.THRESH_BINARY_INV)
        # kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
        # img = cv2.erode(img, kernel)
        # cv2.imshow('binary',cv2.resize(img, None, fx=0.3, fy=0.3, interpolation=cv2.INTER_AREA))
        #图像高与宽
        (h,w)=img.shape
        #垂直投影
        V = getVProjection(img)
     
        start = 0
        V_start = []
        V_end = []
    
        # 对垂直投影水平分割
        V_start, V_end = scan(V, HIOG)
        if len(V_start) > len(V_end):
            V_end.append(w-5)
    
        # 分割行,分割之后再进行列分割并保存分割位置
        for i in range(len(V_end)):
            #获取行图像
            if V_end[i] - V_start[i] < 30:
                continue
    
            cropImg = img[0:h, V_start[i]:V_end[i]]
            # cv2.imshow('cropImg',cropImg)
            # cv2.waitKey(0)
            #对行图像进行垂直投影
            H = getHProjection(cropImg)
            H_start, H_end = scan(H, VIOG, 40)
    
    
            if len(H_start) > len(H_end):
                H_end.append(h-5)
    
            for pos in range(len(H_start)):
                # 再进行一次列扫描
                DcropImg = cropImg[H_start[pos]:H_end[pos], 0:w]
                d_h, d_w = DcropImg.shape
                # cv2.imshow("dcrop", DcropImg)
                sec_V = getVProjection(DcropImg)
                c1, c2 = scan(sec_V, 0)
                if len(c1) > len(c2):
                    c2.append(d_w)
    
                x = 1
                while x < len(c1):
                    if c1[x] - c2[x-1] < 12:
                        c2.pop(x-1)
                        c1.pop(x)
                        x -= 1
                    x += 1
    
                # cv2.waitKey(0)
                if len(c1) == 1:
                    Position.append([V_start[i],H_start[pos],V_end[i],H_end[pos]])
                else:
                    for x in range(len(c1)):
                        Position.append([V_start[i]+c1[x], H_start[pos],V_start[i]+c2[x], H_end[pos]])
    
        #根据确定的位置分割字符
        for m in range(len(Position)):
            cv2.rectangle(origineImage, (Position[m][0]-5,Position[m][1]-5), (Position[m][2]+5,Position[m][3]+5), (0 ,0 ,255), 2)
        cv2.imshow('image',cv2.resize(origineImage, None, fx=0.6, fy=0.6, interpolation=cv2.INTER_AREA))
        cv2.waitKey(0)

    4. 总结

      果然,在面对具体问题时,一个再优秀的普适模型往往都不如优化的比较好的传统方法。就像调参得当的网络,再具体问题上往往比一些十分优秀的网络模型效果还要好一样。

    参考文献:https://www.cnblogs.com/zxy-joy/p/10687152.html

  • 相关阅读:
    使用命令安装laravel 项目
    laravel如何输出最后一条执行的SQL
    Laravel5.4 队列简单配置与使用
    PHP使用RabbitMQ实例
    初识RabbitMQ,附RabbitMQ+PHP演示实例
    Laravel 5.3 单用户登录的简单实现
    redis+thinkphp5的注册、登陆、关注基础例子
    Laravel 5.3 单用户登录的简单实现
    php面试题汇总
    (转)php读取文件使用redis的pipeline导入大批量数据
  • 原文地址:https://www.cnblogs.com/SaltyFishQF/p/11474482.html
Copyright © 2020-2023  润新知