opencv学习:通过图像透视进行发票识别完整代码流程

概念:

        使用OpenCV库实现图像的透视变换处理,以矫正图像中的透视失真。通过本实验,学习者将掌握图像处理的基本操作,包括图像的读取、显示、大小调整、灰度转换、二值化、轮廓检测、轮廓近似以及透视变换。

步骤:

1. 导入库

导入必要的Python库,包括OpenCV和NumPy。

import numpy as np
import cv2

2. 定义函数

定义几个函数来处理图像的不同步骤。

cv_show(name, img)
  • 显示图像的函数,接受窗口名称和图像作为参数。
    def cv_show(name,img):
        cv2.imshow(name,img)
        cv2.waitKey(0)
order_points(pts)
  • 对四个点进行排序,确保它们按照左上、右上、右下、左下的顺序。
    def order_points(pts):
        rect = np.zeros((4,2),dtype='float32')   # 创建一个4x2的数组,指定了数组中元素的数据类型为32位浮点数
        # 按顺序找到对应坐标0123分别是左上、右上、右下、左下
        s = pts.sum(axis=1)   # 对pts矩阵的每一行进行求和操作
        rect[0] = pts[np.argmin(s)]#左上角的点
        rect[2] = pts[np.argmax(s)]#右下角的点
        diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行进行差值
        rect[1] = pts[np.argmin(diff)]#右上角的点
        rect[3] = pts[np.argmax(diff)]#左下角的点
        return rect
four_point_transform(image, pts)
  • 根据四个点进行图像的透视变换。
    def four_point_transform(image,pts):
        # 获取输入坐标点
        rect = order_points(pts)
        (tl,tr,br,bl) = rect#tl 是左上角点,tr 是右上角点,br 是右下角点,bl 是左下角点。
    
        # 计算两个可能的宽度,取这两个宽度的最大值作为新图像的宽度。
        widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))
        widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))
        maxWidth = max(int(widthA) , int(widthB))
        ## 计算两个可能的高度,取这两个高度的最大值作为新图像的高度。
        heightA = np.sqrt(((tr[0]-br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
        heightB = np.sqrt(((tl[0]-bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
        maxHeight = max(int(heightA),int(heightB))
    
        # 变换后对应坐标位置
        dst = np.array([[0,0],
                        [maxWidth-1,0],
                        [maxWidth-1,maxHeight-1],
                        [0,maxHeight-1]],dtype='float32')
    
        # 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ MP获得转换之间的关系
        # cy2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst
        # #参数说明:
        # src:变换前图像四边形顶点坐标/第2个是原图
        # MP:透视变换矩阵,3行3列
        # dsize:输出图像的大小,二元元组(width,heiqht)
        M = cv2.getPerspectiveTransform(rect,dst)#计算透视变换矩阵 M,它描述了如何将原始图像中的点映射到目标坐标。
        warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))#透视变换
        # 返回变换后的结果
        return warped
resize(image, width=None, height=None, inter=cv2.INTER_AREA)
  • 调整图像大小的函数,接受图像、宽度、高度和插值方法作为参数。
    #用于调整图像的大小。
    def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):
        dim=None
        (h,w) = image.shape[:2]#获取输入图像的高度和宽度。
        if width is None and height is None:#如果宽度和高度都没有指定,则直接返回原始图像。
            return image
    
        if width is None:#如果只指定了高度,计算新的宽度以保持图像的宽高比。
            r=height/float(h)
            dim=(int(w*r),height)
        else:#如果只指定了宽度,计算新的高度以保持图像的宽高比。
            r=width/float(w)
            dim=(width,int(h*r))
        #根据计算出的尺寸 dim 调整图像大小。
        resized=cv2.resize(image,dim,interpolation=inter)     # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
        return resized

3. 读取和显示原始图像

读取名为fapiao.jpg的发票图像,并显示。

# 读取输入
image = cv2.imread('fapiao.jpg')
cv_show('image',image)

4. 图像缩小处理

将图像缩小到一定高度(例如500像素),并显示缩小后的图像,以便于处理。

# 图片过大,进行缩小处理
ratio = image.shape[0] / 500.0  # 计算缩小比率
orig = image.copy()
image = resize(orig, height=500)
cv_show('1',image)

5. 轮廓检测

将图像转换为灰度图,进行二值化处理,并检测图像轮廓。

# 轮廓检测
print('step 1 : 轮廓检测')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
#二值化处理
edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 自动寻找网值二位化
#检测二值化图像中的轮廓。
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]
#绘制轮廓
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)

6. 获取最大轮廓

找到面积最大的轮廓,并进行轮廓近似。

print('step 2 :获取最大轮廓')
#获取最大轮廓
screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 获取面积最大的轮廓
#计算轮廓周长
peri = cv2.arcLength(screenCnt,True)   # 计算轮廓周长
#轮廓近似
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 轮廓近似
#绘制近似轮廓
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)

cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)

7. 透视变换

使用近似后的轮廓点进行透视变换,矫正发票图像的透视失真。

# 透视变换
warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)

8. 显示和保存结果

显示透视变换后的图像,并将其保存为新文件。

cv2.imwrite('invoice_new.jpg',warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv2.imshow('xx',warped)
cv2.waitKey(0)

9.运用形态学变换图像

形态学闭运算使图像更加清晰

a=cv2.imread('invoice_new.jpg')
a=cv2.cvtColor(a,cv2.COLOR_BGR2GRAY)
a=cv2.threshold(a,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
a=cv2.resize(a,(800,600))
a=np.rot90(a,1)

kernel = np.ones((1,1), np.uint8)

c=cv2.morphologyEx(a,cv2.MORPH_CLOSE,kernel)

cv2.imshow('a',c)
cv2.waitKey(0)

代码结果

完整代码

import numpy as np
import cv2
#透视变换
def cv_show(name,img):
    cv2.imshow(name,img)
    cv2.waitKey(0)

def order_points(pts):
    rect = np.zeros((4,2),dtype='float32')   # 创建一个4x2的数组,指定了数组中元素的数据类型为32位浮点数
    # 按顺序找到对应坐标0123分别是左上、右上、右下、左下
    s = pts.sum(axis=1)   # 对pts矩阵的每一行进行求和操作
    rect[0] = pts[np.argmin(s)]#左上角的点
    rect[2] = pts[np.argmax(s)]#右下角的点
    diff = np.diff(pts,axis=1)   # 对pts矩阵的每一行进行差值
    rect[1] = pts[np.argmin(diff)]#右上角的点
    rect[3] = pts[np.argmax(diff)]#左下角的点
    return rect

def four_point_transform(image,pts):
    # 获取输入坐标点
    rect = order_points(pts)
    (tl,tr,br,bl) = rect#tl 是左上角点,tr 是右上角点,br 是右下角点,bl 是左下角点。

    # 计算两个可能的宽度,取这两个宽度的最大值作为新图像的宽度。
    widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))
    widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1]-tl[1]) ** 2))
    maxWidth = max(int(widthA) , int(widthB))
    ## 计算两个可能的高度,取这两个高度的最大值作为新图像的高度。
    heightA = np.sqrt(((tr[0]-br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
    heightB = np.sqrt(((tl[0]-bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
    maxHeight = max(int(heightA),int(heightB))

    # 变换后对应坐标位置
    dst = np.array([[0,0],
                    [maxWidth-1,0],
                    [maxWidth-1,maxHeight-1],
                    [0,maxHeight-1]],dtype='float32')

    # 图像透视变换 cv2.getPerspectiveTransform(src,dst[,solveMethod])→ MP获得转换之间的关系
    # cy2.warpPerspective(src, Mp, dsizel, dstl, flagsl, borderModel, borderValue]]1])- dst
    # #参数说明:
    # src:变换前图像四边形顶点坐标/第2个是原图
    # MP:透视变换矩阵,3行3列
    # dsize:输出图像的大小,二元元组(width,heiqht)
    M = cv2.getPerspectiveTransform(rect,dst)#计算透视变换矩阵 M,它描述了如何将原始图像中的点映射到目标坐标。
    warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))#透视变换
    # 返回变换后的结果
    return warped

#用于调整图像的大小。
def resize(image,width=None,height=None ,inter=cv2.INTER_AREA):
    dim=None
    (h,w) = image.shape[:2]#获取输入图像的高度和宽度。
    if width is None and height is None:#如果宽度和高度都没有指定,则直接返回原始图像。
        return image

    if width is None:#如果只指定了高度,计算新的宽度以保持图像的宽高比。
        r=height/float(h)
        dim=(int(w*r),height)
    else:#如果只指定了宽度,计算新的高度以保持图像的宽高比。
        r=width/float(w)
        dim=(width,int(h*r))
    #根据计算出的尺寸 dim 调整图像大小。
    resized=cv2.resize(image,dim,interpolation=inter)     # 默认为cV2.INTER_AREA,即面积插值,适用于缩放图像。
    return resized

# 读取输入
image = cv2.imread('fapiao.jpg')
cv_show('image',image)

# 图片过大,进行缩小处理
ratio = image.shape[0] / 500.0  # 计算缩小比率
orig = image.copy()
image = resize(orig, height=500)
cv_show('1',image)


# 轮廓检测
print('step 1 : 轮廓检测')
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)  # 灰度图
#二值化处理
edged = cv2.threshold(gray,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]  # 自动寻找网值二位化
#检测二值化图像中的轮廓。
cnts = cv2.findContours(edged.copy(),cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)[1]
#绘制轮廓
image_contours = cv2.drawContours(image.copy(),cnts,-1,(0,0,255),1)
cv_show('image_contours',image_contours)


print('step 2 :获取最大轮廓')
#获取最大轮廓
screenCnt = sorted(cnts,key = cv2.contourArea,reverse=True)[0]   # 获取面积最大的轮廓
#计算轮廓周长
peri = cv2.arcLength(screenCnt,True)   # 计算轮廓周长
#轮廓近似
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)  # 轮廓近似
#绘制近似轮廓
image_contour = cv2.drawContours(image.copy(),[screenCnt],-1,(0,255,0),2)

cv2.imshow('image_contour',image_contour)
cv2.waitKey(0)

# 透视变换
warped = four_point_transform(orig,screenCnt.reshape(4,2)*ratio)
cv2.imwrite('invoice_new.jpg',warped)
cv2.namedWindow('xx',cv2.WINDOW_NORMAL)
cv2.imshow('xx',warped)
cv2.waitKey(0)

a=cv2.imread('invoice_new.jpg')
a=cv2.cvtColor(a,cv2.COLOR_BGR2GRAY)
a=cv2.threshold(a,0,255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
a=cv2.resize(a,(800,600))
a=np.rot90(a,1)

kernel = np.ones((1,1), np.uint8)

c=cv2.morphologyEx(a,cv2.MORPH_CLOSE,kernel)

cv2.imshow('a',c)
cv2.waitKey(0)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值