如何用Python的OpenCV获取图片最大矩形及付款单数据区域精确边界框
嘿,针对你这个基于CNN的付款单数据识别项目,先搞定最大矩形(付款单区域)检测的需求,我来分享一套适配白色背景扫描件的OpenCV实现方案👇
用OpenCV定位付款单最大矩形区域
核心思路
因为你的扫描件背景是纯白色,付款单本身要么有明显边框,要么和背景存在对比度差异,所以我们可以通过轮廓检测+多边形近似的思路来精准定位:先预处理图像突出前景,再筛选出四边形轮廓,最后取面积最大的那个作为付款单区域。
具体步骤&代码实现
1. 图像预处理:突出付款单前景
首先把彩色图转灰度,然后做反二值化(让白色背景变黑,付款单区域变亮),再通过开运算降噪,去掉扫描产生的小噪点:
import cv2 import numpy as np # 读取扫描的付款单图像 img = cv2.imread('payment_slip_scan.jpg') # 转灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 反二值化:适配白色背景,把背景转为黑色,付款单转为白色 _, thresh = cv2.threshold(gray, 240, 255, cv2.THRESH_BINARY_INV) # 开运算降噪:先腐蚀再膨胀,消除小噪点 kernel = np.ones((5, 5), np.uint8) thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
2. 检测轮廓并筛选最大矩形轮廓
接下来提取所有外部轮廓,对每个轮廓做多边形近似,筛选出4个顶点的轮廓(也就是矩形/四边形),然后计算面积保留最大的那个:
# 提取外部轮廓(只保留最外层,忽略内部小轮廓) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) max_area = 0 target_rect = None for cnt in contours: # 计算轮廓周长,用于多边形近似的精度控制 perimeter = cv2.arcLength(cnt, True) # 多边形近似:epsilon取周长的0.02倍,可根据实际情况微调 approx = cv2.approxPolyDP(cnt, 0.02 * perimeter, True) # 筛选出4个顶点的轮廓(符合矩形的特征) if len(approx) == 4: current_area = cv2.contourArea(cnt) # 更新最大面积的矩形 if current_area > max_area: max_area = current_area target_rect = approx
3. 获取精确Bounding Box并提取区域
拿到目标矩形后,你可以直接获取外接矩形,或者如果付款单是倾斜的,做透视变换把它转正,方便后续CNN识别:
if target_rect is not None: # 获取外接矩形的坐标(x,y)和宽高(w,h) x, y, w, h = cv2.boundingRect(target_rect) # 可视化标记bounding box(可选) cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 3) # 直接提取付款单区域 extracted_slip = img[y:y+h, x:x+w] cv2.imwrite('extracted_payment_slip.jpg', extracted_slip) # 处理倾斜付款单:透视变换转正 # 先对顶点排序,确保顺序是左上、右上、右下、左下 pts = target_rect.reshape(4, 2) rect = np.zeros((4, 2), dtype="float32") # 按顶点和的大小排序:和最小是左上,和最大是右下 s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # 按顶点差的大小排序:差最小是右上,差最大是左下 diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] # 定义目标图像的尺寸 width = int(np.linalg.norm(rect[1] - rect[0])) height = int(np.linalg.norm(rect[2] - rect[1])) dst = np.array([[0, 0], [width-1, 0], [width-1, height-1], [0, height-1]], dtype="float32") # 计算透视变换矩阵并应用 M = cv2.getPerspectiveTransform(rect, dst) warped_slip = cv2.warpPerspective(img, M, (width, height)) cv2.imwrite('warped_payment_slip.jpg', warped_slip) # 显示检测结果 cv2.imshow('Detected Payment Slip', img) cv2.waitKey(0) cv2.destroyAllWindows()
实用调整技巧
- 二值化阈值:如果付款单和背景对比度低,把
240调低(比如230),让付款单区域更清晰地被识别。 - 多边形近似精度:如果轮廓顶点识别不准,微调
0.02这个系数——0.01更严格,0.03更宽松。 - 降噪核大小:扫描件噪点多的话,把
(5,5)调大到(7,7),增强降噪效果。
处理完这一步,你就能得到精准的付款单区域,接下来就可以把这个区域的图像喂给你的CNN模型做数据识别啦~
内容的提问来源于stack exchange,提问作者Josip Mrđen
相关产品推荐
相关产品推荐

