附完整python源码)基于tensorflow、opencv的入门案例_发票识别一:关键区域定位

发票识别一:从一张发票照片精确定位出“发票号码”、“发票代码”的数字区域

注:该代码适用于 “国税通用机打发票”。尽量拍摄下正常摆放的完整发票

发票识别一:区域精确定位1 具体步骤1.1 读入发票1.2 获取矩形框:将发票可能的区域定位出来边缘检测-二值化-形态学-轮廓检测-获取多个矩形框hsv颜色空间 -分别提取“红、黑、蓝”的掩膜-形态学--轮廓检测-获取多个矩形框结果:得到上百个候选框1.3 矩形框进行初步筛选。根据矩形框的长高信息、位置信息进行筛选结果:剩下十多个矩形框1.4 矩形框融合若某个矩形框与其他矩形框重叠、交叉度极高在其他矩形框内部,将其删除若两个矩形框在同一水平线上,有一定的交叉,且框内都包含数字,将两个框融合结果:这一步通常还有3-6个矩形框1.5 定位剩下的矩形框从上到下排序。根据矩形框的相对位置、尺寸,定位出“发票号码”、“发票代码”的两个区域。1.6 找了几张发票,地位区域如下。下一步“字符分割”,见发票识别二。

2.源码如下

2.1 main.py

# encoding: utf-8

import cv2

import numpy as np

import roi_merge as roi_

import util_funs as util

from get_rects import *

def main(img):

    region = get_rects(img)

    roi_solve = roi_.Roi_solve(region)

    roi_solve.rm_inside() 

    roi_solve.rm_overlop()

    region = roi_solve.merge_roi()

    region = util.sort_region(region)

    region = util.get_targetRoi(region)

        for i in range(2):

                rect2 = region[i]

                w1,w2 = rect2[0],rect2[0]+rect2[2]

                h1,h2 = rect2[1],rect2[1]+rect2[3]

                box = [[w1,h2],[w1,h1],[w2,h1],[w2,h2]]

                cv2.drawContours(img, np.array([box]), 0, (0, 255, 0), 1)

                if i == 0:

                        cv2.imwrite('代码'+str(k)+'.jpg', img[h1:h2,w1:w2])

                else:

                        cv2.imwrite('号码'+str(k)+'.jpg', img[h1:h2,w1:w2])

        cv2.imshow('img', img)

    cv2.waitKey(0)

 

if __name__ == '__main__':

    img = cv2.imread("img_path")

    main(img)
2.3 roi_merge.py


#encoding: utf-8

import cv2

import numpy as np

 

class Roi_solve:

    def __init__(self,rect): 

        self.rect = rect  #所有矩形框

        self.cursor = -1 #初始化游标位置  

        self.rect_num = len(rect) #记录rect的实时数量

    def next(self):  

        #将游标的位置前移一步,并返回所在检索位的矩形框

        self.cursor = self.cursor+1  

        return self.rect[self.cursor]

    def hasNext(self):  

        #判断是否已经检查完了所有矩形框 

        return self.rect_num > self.cursor + 1

    def remove(self,flag = -1):  

        #将非优解从数据集删除

        if flag == -1:

            del self.rect[self.cursor]

            #删除当前游标位置,游标回退一步  

            self.cursor = self.cursor-1  

        else:

            #删除后面位置的rect,游标不动

            del self.rect[flag]

        #rect数量,减1  

        self.rect_num = self.rect_num - 1

    def add(self,add_rect):

        self.rect.append(add_rect)

        self.rect_num = self.rect_num + 1

    def get_u_d_l_r(self,rect_):

        #获取rect的上下左右边界值

        upper_,down_ = rect_[1],rect_[1] + rect_[3]

        left_,right_ = rect_[0],rect_[0] + rect_[2]

        return upper_,down_,left_,right_

    def is_intersect(self,y01, y02 , x01, x02, y11, y12 , x11, x12):  

        # 判断两个矩形是否相交    

        lx = abs((x01 + x02) / 2 - (x11 + x12) / 2)  

        ly = abs((y01 + y02) / 2 - (y11 + y12) / 2)  

        sax = abs(x01 - x02)  

        sbx = abs(x11 - x12)  

        say = abs(y01 - y02)  

        sby = abs(y11 - y12)  

        if lx <= (sax + sbx) / 2 and ly <= (say + sby) / 2:  

        return True  

        else:  

        return False  

    def intersect_area(self,y01, y02 , x01, x02, y11, y12 , x11, x12): 

        #返回两个rect的交叉面积 

            col=min(x02,x12)-max(x01,x11)  

        row=min(y02,y12)-max(y01,y11)  

        return col*row  

    def intersect_height(self,y01, y02, y11, y12): 

        #height轴方向交叉,返回height交叉段占比

        row=float(min(y02,y12)-max(y01,y11))   

        return max(row/float(y02-y01),row/float(y12-y11))

    #remove_inside:如果“本rect”被“其他rect”包围了,则删除

    def remove_inside(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        #判断当前rect是否在内部

        for rect_ in self.rect:

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)        

            if u_curr>u_ and d_curr<d_ and l_curr>l_ and r_curr<r_:  

                self.remove()

                break

    #remove_overlop:如果“本rect”和“其他rect”相交区域达到了95%以上,则删除

    def remove_overlop(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        area_curr = rect_curr[2] * rect_curr[3]

        #判断当前rect是否在内部

        for rect_ in self.rect:

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)    

            if self.is_intersect(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_): 

                if rect_ == rect_curr:

                    continue

                else :

                    area_ = self.intersect_area(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_)

                if float(area_)/float(area_curr) >0.95:

                    self.remove()

                    break

    #如果“两个rect”在同一水平面上,横向坐标

    def merge(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        #判断当前rect是否在内部

        for i in range(self.cursor+1,len(self.rect)):

            print i,self.cursor+1,len(self.rect)

            rect_ = self.rect[i]

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)    

            #判断是否相交    

            if self.is_intersect(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_): 

                if self.intersect_height(u_curr,d_curr,u_,d_) > 0.6:

                    if rect_curr[2] > rect_[2]:

                        new_rect = np.array(rect_curr)

                    else:

                        new_rect = np.array(rect_)

                    new_l = min(l_curr,l_)

                    new_r = max(r_curr,r_)

                    new_rect[0] = new_l

                    new_rect[2] = new_r-new_l

                    self.remove(i)

                    self.remove()

                    self.add(new_rect)

                    break

    def rm_inside(self): 

        self.cursor = -1 

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.remove_inside(rect_curr)

        return self.rect

    def rm_overlop(self):

        self.cursor = -1

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.remove_overlop(rect_curr)

        return self.rect

    def merge_roi(self):

        self.cursor = -1

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.merge(rect_curr)

        return self.rect
2.3 roi_merge.py


#encoding: utf-8

import cv2

import numpy as np

 

class Roi_solve:

    def __init__(self,rect): 

        self.rect = rect  #所有矩形框

        self.cursor = -1 #初始化游标位置  

        self.rect_num = len(rect) #记录rect的实时数量

    def next(self):  

        #将游标的位置前移一步,并返回所在检索位的矩形框

        self.cursor = self.cursor+1  

        return self.rect[self.cursor]

    def hasNext(self):  

        #判断是否已经检查完了所有矩形框 

        return self.rect_num > self.cursor + 1

    def remove(self,flag = -1):  

        #将非优解从数据集删除

        if flag == -1:

            del self.rect[self.cursor]

            #删除当前游标位置,游标回退一步  

            self.cursor = self.cursor-1  

        else:

            #删除后面位置的rect,游标不动

            del self.rect[flag]

        #rect数量,减1  

        self.rect_num = self.rect_num - 1

    def add(self,add_rect):

        self.rect.append(add_rect)

        self.rect_num = self.rect_num + 1

    def get_u_d_l_r(self,rect_):

        #获取rect的上下左右边界值

        upper_,down_ = rect_[1],rect_[1] + rect_[3]

        left_,right_ = rect_[0],rect_[0] + rect_[2]

        return upper_,down_,left_,right_

    def is_intersect(self,y01, y02 , x01, x02, y11, y12 , x11, x12):  

        # 判断两个矩形是否相交    

        lx = abs((x01 + x02) / 2 - (x11 + x12) / 2)  

        ly = abs((y01 + y02) / 2 - (y11 + y12) / 2)  

        sax = abs(x01 - x02)  

        sbx = abs(x11 - x12)  

        say = abs(y01 - y02)  

        sby = abs(y11 - y12)  

        if lx <= (sax + sbx) / 2 and ly <= (say + sby) / 2:  

        return True  

        else:  

        return False  

    def intersect_area(self,y01, y02 , x01, x02, y11, y12 , x11, x12): 

        #返回两个rect的交叉面积 

            col=min(x02,x12)-max(x01,x11)  

        row=min(y02,y12)-max(y01,y11)  

        return col*row  

    def intersect_height(self,y01, y02, y11, y12): 

        #height轴方向交叉,返回height交叉段占比

        row=float(min(y02,y12)-max(y01,y11))   

        return max(row/float(y02-y01),row/float(y12-y11))

    #remove_inside:如果“本rect”被“其他rect”包围了,则删除

    def remove_inside(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        #判断当前rect是否在内部

        for rect_ in self.rect:

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)        

            if u_curr>u_ and d_curr<d_ and l_curr>l_ and r_curr<r_:  

                self.remove()

                break

    #remove_overlop:如果“本rect”和“其他rect”相交区域达到了95%以上,则删除

    def remove_overlop(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        area_curr = rect_curr[2] * rect_curr[3]

        #判断当前rect是否在内部

        for rect_ in self.rect:

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)    

            if self.is_intersect(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_): 

                if rect_ == rect_curr:

                    continue

                else :

                    area_ = self.intersect_area(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_)

                if float(area_)/float(area_curr) >0.95:

                    self.remove()

                    break

    #如果“两个rect”在同一水平面上,横向坐标

    def merge(self,rect_curr):

        #获取当前rect的上下左右边界信息  

        u_curr,d_curr,l_curr,r_curr = self.get_u_d_l_r(rect_curr)

        #判断当前rect是否在内部

        for i in range(self.cursor+1,len(self.rect)):

            print i,self.cursor+1,len(self.rect)

            rect_ = self.rect[i]

            u_,d_,l_,r_ = self.get_u_d_l_r(rect_)    

            #判断是否相交    

            if self.is_intersect(u_curr,d_curr,l_curr,r_curr,u_,d_,l_,r_): 

                if self.intersect_height(u_curr,d_curr,u_,d_) > 0.6:

                    if rect_curr[2] > rect_[2]:

                        new_rect = np.array(rect_curr)

                    else:

                        new_rect = np.array(rect_)

                    new_l = min(l_curr,l_)

                    new_r = max(r_curr,r_)

                    new_rect[0] = new_l

                    new_rect[2] = new_r-new_l

                    self.remove(i)

                    self.remove()

                    self.add(new_rect)

                    break

    def rm_inside(self): 

        self.cursor = -1 

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.remove_inside(rect_curr)

        return self.rect

    def rm_overlop(self):

        self.cursor = -1

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.remove_overlop(rect_curr)

        return self.rect

    def merge_roi(self):

        self.cursor = -1

        while(self.hasNext()):  

            rect_curr = self.next()  

            self.merge(rect_curr)

        return self.rect
2.4 util_funs.py


#encoding:utf-8

import cv2

import numpy as np

 

def get_u_d_l_r(rect_):

    #获取rect的上下左右边界值

    upper_,down_ = rect_[1],rect_[1] + rect_[3]

    left_,right_ = rect_[0],rect_[0] + rect_[2]

    return upper_,down_,left_,right_

 

#region排序。flag=1时:从上到下;flag=0时:从左到右

def sort_region(region,flag = 1):

    temp = []

    region_new = []

    for rect in region:

        temp.append(rect[flag])

    temp_sort = sorted(temp)

    for height_ in temp_sort:

        index_ = temp.index(height_)

        region_new.append(region[index_])

    return region_new

 

#判断上下两个相邻框框是否为发票代码、发票号码

def judge_(rect_0,rect_1):

    u_d_l_r_0 = get_u_d_l_r(rect_0)

    u_d_l_r_1 = get_u_d_l_r(rect_1)

    #两个rect上边界之间的距离不超过box_height的四倍

    distance_ = rect_1[1] - rect_0[1]

    box_height = float(rect_0[3] + rect_1[3])/2

    #上边框的右边界值更大

    if (distance_ > 0 and distance_ < box_height*3 and rect_0[0]+rect_0[2] > rect_1[0]+rect_1[2]):

            return True

    return False

 

#获取 “发票代码”、“发票号码”的区域

def get_targetRoi(region):

    if len(region) > 1:

        #按照从上到下排序

        new_region = sort_region(region)

        for i in range(len(new_region)-1):

            if judge_(new_region[i],new_region[i+1]):

                return [new_region[i],new_region[i+1]]
 

 


--------------------- 
作者:远上寒山 
来源:CSDN 
原文:https://blog.csdn.net/m0_38097087/article/details/80281835 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值