《Opencv》图像的透视变换--处理发票

目录

1. 引言

2. 准备工作

3. 图像预处理

4. 轮廓检测

5. 获取最大轮廓

6. 透视变换

7. 二值化处理

8. 结果展示

9、完整代码

 10、结果展示

(1)、原图

(2)、处理后的图

11. 结论


在计算机视觉领域,透视变换是一种常用的技术,用于将图像从一个视角转换到另一个视角。透视变换在图像处理中有着广泛的应用,例如在文档扫描、车牌识别、以及本文将要介绍的发票处理中。本文将详细介绍如何使用Python和OpenCV库对发票图像进行透视变换,并对其进行后续处理。

1. 引言

在实际应用中,我们经常会遇到需要从图像中提取特定区域的需求。例如,在处理发票时,我们可能需要将发票从背景中提取出来,并将其转换为一个标准的矩形图像,以便后续的OCR(光学字符识别)处理。透视变换正是实现这一目标的关键技术。

2. 准备工作

在开始之前,我们需要安装并导入必要的Python库:

import numpy as np
import cv2

3. 图像预处理

首先,我们需要加载并预处理图像。为了便于处理,我们将图像的高度调整为500像素,并保持其宽高比不变。

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))
    resized = cv2.resize(image, dim, interpolation=inter)
    return resized

img = cv2.imread('./images/fapiao.jpg')
ratio = img.shape[0] / 500.0
orig = img.copy()
img = resize(orig, height=500)

4. 轮廓检测

接下来,我们需要检测图像中的轮廓。首先将图像转换为灰度图,然后使用二值化处理来突出边缘。

gray = cv2.cvtColor(img, 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)[-2]
img_contours = cv2.drawContours(img.copy(), cnts, -1, (0, 0, 255), 1)

5. 获取最大轮廓

我们假设发票是图像中最大的轮廓,因此我们可以通过计算轮廓的面积来找到最大的轮廓。

screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
peri = cv2.arcLength(screenCnt, True)
screenCnt = cv2.approxPolyDP(screenCnt, 0.02 * peri, True)

6. 透视变换

找到发票的轮廓后,我们需要对其进行透视变换,将其转换为一个标准的矩形图像。

def four_point_transform(image, pts):
    rect = order_points(pts)
    (tl, tr, br, bl) = rect
    widthA = distance(br, bl)
    widthB = distance(tr, tl)
    maxWidth = max(int(widthA), int(widthB))
    hightA = distance(tl, bl)
    hightB = distance(tr, br)
    maxHeight = max(int(hightA), int(hightB))
    dst = np.array([[0, 0], [maxWidth, 0], [maxWidth, maxHeight], [0, maxHeight]], dtype="float32")
    M = cv2.getPerspectiveTransform(rect, dst)
    warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
    return warped

warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
warped = cv2.rotate(warped, cv2.ROTATE_90_COUNTERCLOCKWISE)

7. 二值化处理

为了便于后续的OCR处理,我们需要对透视变换后的图像进行二值化处理。

warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
warped_th = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
kernel = np.ones((2, 2), np.uint8)
warped_th = cv2.erode(warped_th, kernel, iterations=2)
warped_th = cv2.dilate(warped_th, kernel, iterations=1)
warped_th = resize(warped_th, height=800)

8. 结果展示

最后,我们将处理后的图像显示出来,并保存结果。

cv2.namedWindow('xx', cv2.WINDOW_NORMAL)
cv2.imshow("xx", warped)
cv2.waitKey(0)

b = np.zeros((800, 544), 'uint8')
r = np.zeros((800, 544), 'uint8')
warped_th[warped_th == 0] = 100
AA = cv2.merge((b, warped_th, r))
cv_show('AA', AA)

9、完整代码

import numpy as np
import cv2

# 变换图片大小的函数
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))
    resized = cv2.resize(image, dim, interpolation=inter)
    #参数interpolation指定了在图像大小调整过程中如何处理像素插值的方法。cv2.INTER_AREA具体意味着使用面积插值方法。
    return resized
# 定义显示图片函数
def cv_show(name, image):
    cv2.imshow(name, image)
    cv2.waitKey(0)
# 计算欧氏距离
def distance(p1,p2):
    return np.sqrt(((p1[0] - p2[0]) ** 2) + ((p1[1] - p2[1]) ** 2))
# 确定原图四个顶点(排序)
def order_points(pts):
    rect = np.zeros((4,2),dtype="float32")
    s = pts.sum(axis=1)
    rect[0] = pts[np.argmin(s)]
    rect[2] = pts[np.argmax(s)]

    d = np.diff(pts,axis=1)
    rect[1] = pts[np.argmin(d)]
    rect[3] = pts[np.argmax(d)]
    return rect

def four_point_transform(image,pts):
    rect = order_points(pts)
    (tl,tr,br,bl) = rect
    # 计算w,h
    widthA = distance(br,bl)
    widthB = distance(tr,tl)
    maxWidth = max(int(widthA),int(widthB))
    hightA = distance(tl,bl)
    hightB = distance(tr,br)
    maxHeight = max(int(hightA),int(hightB))
    dst = np.array([[0,0],[maxWidth,0],[maxWidth,maxHeight],[0,maxHeight]],dtype="float32")
    M = cv2.getPerspectiveTransform(rect,dst)
    warped = cv2.warpPerspective(image,M,(maxWidth,maxHeight))
    return warped


img = cv2.imread('./images/fapiao.jpg')
cv_show('image',img)

ratio = img.shape[0] / 500.0
orig = img.copy()
img = resize(orig,height=500)
cv_show('1',img)

# 轮廓检测
gray = cv2.cvtColor(img,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)[-2]
img_contours = cv2.drawContours(img.copy(),cnts,-1,(0,0,255),1)
cv_show('img_contours',img_contours)

# 获取最大轮廓
screenCnt = sorted(cnts,key=cv2.contourArea,reverse=True)[0]
# 周长
peri = cv2.arcLength(screenCnt,True)
# 轮廓近似
screenCnt = cv2.approxPolyDP(screenCnt,0.02*peri,True)
print(screenCnt.shape)
img_contour = cv2.drawContours(img.copy(),[screenCnt],-1,(0,255,0),2)
cv_show('img_contour',img_contour)

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

# 二值化处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
warped_th = cv2.threshold(warped,0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
kernel = np.ones((2,2),np.uint8)
# warped_th = cv2.morphologyEx(warped_th,cv2.MORPH_CLOSE,kernel)
warped_th = cv2.erode(warped_th,kernel,iterations=2)
warped_th = cv2.dilate(warped_th,kernel,iterations=1)

warped_th = resize(warped_th,height=800)
cv_show('warped_th',warped_th)


# 800*544
b = np.zeros((800,544),'uint8')
r = np.zeros((800,544),'uint8')
# b[b==0]=255
# r[r==0]=255
warped_th[warped_th==0] = 100
AA = cv2.merge((b,warped_th,r))
cv_show('AA',AA)

 10、结果展示

(1)、原图

(2)、处理后的图

 

11. 结论

通过本文的介绍,我们学习了如何使用Python和OpenCV对发票图像进行透视变换。透视变换不仅可以帮助我们提取图像中的特定区域,还可以为后续的图像处理任务(如OCR)提供标准化的输入。

### 使用OpenCV实现发票图像处理与文字识别 #### 加载并预览原始发票图像 为了开始处理发票图像,首先需要加载图像文件,并将其展示出来以便确认读取成功。 ```python import cv2 # 读取输入图像 image = cv2.imread('fapiao.jpg') cv2.imshow('Original Image', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 此部分操作确保能够正确访问待处理发票图片[^1]。 #### 缩小图像尺寸 考虑到计算效率,在不影响最终效果的前提下适当减小图像规模是有益处的。这一步骤有助于加速后续处理过程而不损失太多细节信息。 ```python resized_image = cv2.resize(image, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA) cv2.imshow('Resized Image', resized_image) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这里采用双线性插值法(`INTER_AREA`)对原图进行了缩放处理。 #### 应用滤波器去除噪声 为进一步提升OCR(光学字符识别)的效果,可以利用高斯模糊或双边过滤器来减少不必要的干扰因素,使文本区域更为突出。 ```python gray_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2GRAY) gaussian_blurred = cv2.GaussianBlur(gray_image, (5, 5), 0) bilateral_filtered = cv2.bilateralFilter(gray_image, 9, 75, 75) cv2.imshow('Gaussian Blurred Image', gaussian_blurred) cv2.imshow('Bilateral Filtered Image', bilateral_filtered) cv2.waitKey(0) cv2.destroyAllWindows() ``` 上述代码展示了两种不同的降噪方法——高斯平滑和双边滤波的应用方式[^3]。 #### 执行形态学变换改善结构特征 对于某些特定类型的文档扫描件来说,可能还需要借助开闭运算等手段调整其内部连通域特性,从而更好地分离前景对象与背景。 ```python kernel = np.ones((2, 2), dtype=np.uint8) morph_closing = cv2.morphologyEx(bilateral_filtered, cv2.MORPH_CLOSE, kernel) cv2.imshow('Morphological Closing Result', morph_closing) cv2.waitKey(0) cv2.destroyAllWindows() ``` 这段程序实现了闭合运算,即先执行膨胀再做腐蚀的操作序列,目的是填充细小孔洞并连接相邻组件[^4]。 #### 实施透视变换校正视角偏差 当拍摄角度不理想时,可能会造成所获取到的画面存在一定程度上的变形现象;此时则可通过寻找四个角点坐标进而构建仿射矩阵完成矫正作业。 ```python pts1 = np.float32([[x1,y1], [x2,y2], [x3,y3], [x4,y4]]) # 原始四点位置 pts2 = np.float32([[0, 0], [w, 0], [0, h], [w, h]]) # 目标矩形框顶点集合 matrix = cv2.getPerspectiveTransform(pts1, pts2) warped = cv2.warpPerspective(morph_closing, matrix, (w,h)) cv2.imshow('Warped Perspective View', warped) cv2.waitKey(0) cv2.destroyAllWindows() ``` 注意:实际应用中需根据具体情况手动标注出四个关键定位点的位置参数(xn,yn),同时设定好期望输出的目标宽度(w)及高度(h)。 #### 文字提取阶段 最后便是调用Tesseract OCR引擎解析经过前序多步优化后的二值化位图数据流,从中抽取出有用的文字串信息供进一步分析使用。 ```python try: from PIL import Image except ImportError: import Image import pytesseract pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe' # Tesseract路径配置 text = pytesseract.image_to_string(Image.fromarray(warped)) print(text) ``` 以上就是整个基于OpenCV框架下针对纸质票据类影像资料自动化处理流程的一个概括介绍[^2].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值