栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

2021-10-22

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

2021-10-22

python图片表格单元格切分算法
    • 切分思路
    • 生成表格文件
    • 寻找表格的范围和最小点
    • 查找单元格
    • 寻找坐标的行像素列像素
    • 寻找点周围的单元格

切分思路

首先通过寻找轮廓线的方法找到最大的轮廓线,然后根据轮廓线确定表格的区域,选择坐标最小的点作为种子点通过泛洪填充得到标记的表格图像,通过横向和纵向的腐蚀和膨胀得到横平竖直的表格,再通过蒙特卡洛算法根据表格的坐标范围生成随机点,通过随机点找行像素和列像素,然后找出最接近的标记颜色的坐标,从而定位出该点四周的表格。为了消除个别多余线段的干扰,采用一次验证,验证方法为,通过找到的表格定位中心点,根据中心点的坐标再寻找一次周围表格的坐标,如果两次坐标相同予以采信。不断寻找直到多次寻找不再增加或者超时为止。

生成表格文件
def gen_table_file(imgfile="test.png"):  #  路径不能有中文
    """   生成图表框文件   """
    print("# 找到最大轮廓")
    pt0=getpt0(imgfile)  
    img_org = cv.imread(imgfile, cv.IMREAD_COLOR)
    print("# 获取种子点。。。")
    seed_point = pt0  # 坐标
    new_val = (0, 0, 255)  # 赋新值BGR
    lower_diff =(30, 30, 30)# (30, 30, 30)  # 下灰阶差
    up_diff = (200, 200, 200)#(50, 50, 50)# (30, 30, 30)  # 上灰阶差
    # mask图片
    h, w = img_org.shape[:2]
    img_mask = np.zeros([h + 2, w + 2], np.uint8)  # 需要大一点
    img_copy = img_org.copy()  # 会覆盖原图
    # 3.执行处理
    print("# 标记表格为红色。。。")
    cv.floodFill(img_copy, img_mask, seed_point, new_val, lower_diff, up_diff,flags=4 | (255 << 8) | cv.FLOODFILL_FIXED_RANGE)
    #             flags=4 | (255 << 8) | cv.FLOODFILL_FIXED_RANGE)  # 模式 4连通 + 255白色 + 区域计算
    # 4.显示结果
    #cv.imshow("img_mask", img_mask)
    #cv.imshow("img_org", img_org)
    #cv.imshow("img_copy", img_copy)
    x,y,z=img_copy.shape
    res=img_copy.reshape(x*y*z)         # 变成一维了
    print("# 生成红色蒙板...")
    mask=np.zeros(x*y*z).astype("uint8").reshape(x*y,z)  # 变成2维
    mask=mask+np.array([0,0,255],dtype='uint8')      #   生成红色蒙板
    #print(mask[:20])
    mask=mask.reshape(x*y*z)
    print("# 蒙板运算获取红色区域")
    new=(res==mask).astype("uint8")
    new=new.reshape(x*y,z)
    new=np.apply_along_axis(lambda x:x[0]*x[1]*x[2],1,new)   #  全1为1
    res=res.reshape(x*y,z)
    new2=np.vstack((new,new))
    new=np.vstack((new,new2)).T   #  转置了
    #print(res.shape,new.shape)
    new=new.reshape(x*y*z)
    res=res.reshape(x*y*z)
    res=np.multiply(new,res)
    #res=np.array([np.array([0,0,255],dtype='uint8') if (x==new_val).all() else np.array([255,255,255],dtype='uint8') for x in res ])
    res=res.reshape(x,y,z)
    #cv.imshow("img_contour", res)
    print("# 保存表格框图片")
    #dst = cv.fastNlMeansDenoisingColored(res,None,10,10,7,21)
    #res=cv.GaussianBlur(res, (3, 3), 0)#cv.pyrMeanShiftFiltering(res, 20, 100) #
    #res=cv.pyrMeanShiftFiltering(res, 30, 100)
    res=ero_dialation(res)
    cv.imwrite("table.png",res)    #  到这里都是不错的
寻找表格的范围和最小点
def get_max_contour(imgfile):
    """   找到最大的轮廓,注意白色被认为是背景色   """
    img = cv2.imread(imgfile)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray_temp = gray.copy()
    ret, dst = cv2.threshold(gray_temp, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)   #   二值化
    gray_temp=255-dst      #   反色处理了
    contours, hierarchy = cv2.findContours(gray_temp, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
    area=[]
    for i in range(len(contours)):
        area.append(cv2.contourArea(contours[i]))
    max_idx = np.argmax(area)
    return contours[max_idx]

def getpt0(imgfile):
    """   得到最大轮廓的最左上的位置   """
    maxcontour=get_max_contour(imgfile)
    pt0=process_contour(maxcontour)
    return pt0
def get_region(imgfile):
    maxcontour=get_max_contour(imgfile)
    region=get_range(maxcontour)
    return region
查找单元格
def get_rects_in_table(tablepng,rang):
    res=[]
    x0,y0,x1,y1=rang
    timeout=90
    t0=time.time()
    l=0
    cnt=0
    while time.time()-t010:
            break
    return res
寻找坐标的行像素列像素
def get_row_col_at_xy(pt,narray):
    """   得到图片某点处的行像素集和列像素集   """
    h,w,z=narray.shape  #  高宽通道数,高--行数,宽--x
    x,y=pt
    narr=narray.reshape(h,w*z)
    row=narray[y].reshape(w,z)#行
    col=narray[:,x].reshape(h,z)# 列
    return row,col
寻找点周围的单元格
def get_rect_around_pt(imgarr,pt,color=[0,0,255]):
    """   如果存在0,那么刚好在线上,数据抛弃
    如果存在一正一负,取正的最小,负的最大,加上减去的就是对应的坐标   """
    row,col=get_row_col_at_xy(pt,imgarr)  #  行像素,列像素
    rflag=(row==color).astype(int)  #  判断3个通道的值是否一致
    rnew=np.apply_along_axis(lambda x:x[0]*x[1]*x[2],axis=1,arr=rflag) #  全部为1才为1
    result1=np.where(rnew==1)[0]-pt[0]   #  找到等于1的那个标号,就是坐标的值,再和当前点的坐标进行比较,得出距离
    if 0 in result1: return None   # 刚好在线上抛弃
    #print(result1)
    try:
        x1,x2=(result1[np.where(result1<0)].max()+pt[0],result1[np.where(result1>0)].min()+pt[0]) #  x1,x2  取正的最小,负的最大,
    except:
        return None
    #下面操作同上面
    cflag=(col==color).astype(int)
    cnew=np.apply_along_axis(lambda x:x[0]*x[1]*x[2],axis=1,arr=cflag)
    result2=np.where(cnew==1)[0]-pt[1]
    if 0 in result2: return None
    #print(result2)
    try:  #  可能出现不规则区域,导致pt在图表外,因此找不到符合要求的两个点
        y1,y2=(result2[np.where(result2<0)].max()+pt[1],result2[np.where(result2>0)].min()+pt[1]) # y1,y2
    except:
        return None
    #print(x1,y1,x2,y2)
    return [x1,y1,x2,y2]

##主程序

if __name__ == '__main__':
    #pytesseract.image_to_string(image,lang="",config="")
    """测试输入为带有表格的imgfile,只考虑了识别最大的那个表格,
    首先通过imgfile,生成table.png,
    然后根据table.png来找到横线竖线,
    得到相应的坐标,并进行切分   """
    imgfile="test.png" #  路径不能有中文
    gen_table_file(imgfile)  # 生成了只含table的文件。
    targetfile=r"table.png"
    target = cv2.imread(targetfile) # trainImage
    region=get_region(imgfile)
    #get_rect_around_pt(target,[365,180])
    print(f"单元格寻找区域:{region}")
    ret=get_rects_in_table(target,region)
    print(len(ret))
    tmpdir="cropped"
    if os.path.exists(tmpdir) and os.path.isdir(tmpdir):
        pass
    else:
        os.mkdir(tmpdir)
    result=[]
    ret=sorted(ret,key=lambda x:x[0])
    ret=sorted(ret,key=lambda x:x[1])
    print(ret)  # 此处进行了排序!二次排序,先排x1,再排y1
    for i,reg in enumerate(ret):
        x0,y0,x1,y1=reg
        cropped=PIL.Image.open(imgfile).crop([x0+1,y0+1,x1,y1])
        fnm=f"{tmpdir}\cropped_{i:03d}.jpg"
        cropped.save(fnm)
        #img=cv.imread(fnm)
        img=np.array(cropped)
        dst = cv.fastNlMeansDenoisingColored(img,None,10,10,7,21)
        #txt=pytesseract.image_to_string(dst, lang='chi_sim')
        txt=get_string(dst)
        #print(str(sn)+":",txt.strip())
        result.append(str(i)+":"+txt.strip())
    print("n".join(result))

具体代码可以见我上传的资源。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/341441.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号