- 1、傅里叶变换 + 霍夫变换+ 直线 + 角度 + 旋转
- 2、边缘检测 + 霍夫变换 + 直线+角度 + 旋转
- 3、四点透视 + 角度 + 旋转
- 4、检测矩形轮廓 + 角度 + 旋转
1.目的
实现类似全能扫面王的图像校正功能
2. 基于轮廓提取和透射变换
基于轮廓提取和透射变换的矫正算法更适用于车牌、身份证、人民币、书本、 发票一类矩形形状而且边界明显的物体矫正。所校正的内容必须要有清晰的完整轮廓。
基本步骤:
- 图片灰度化
- 二值化/canny边缘检测等操作
- 检测轮廓,并筛选出目标轮廓(通过横纵比或面积去除干扰轮廓)
- 获取图像顶点
- 透视变换
代码:
import cv2
import numpy as np
def contour_to_rect(contour):
pts = contour.reshape(4, 2)
print(pts)
rect = np.zeros((4, 2), dtype = "float32")
# top-left point has the smallest sum
# bottom-right has the largest sum
s = pts.sum(axis = 1)
# print(s)
rect[0] = pts[np.argmin(s)]
# print(pts[np.argmin(s)])
rect[2] = pts[np.argmax(s)]
# compute the difference between the points:
# the top-right will have the minumum difference
# the bottom-left will have the maximum difference
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
# approximate the contour by a more primitive polygon shape
def approximate_contour(contour):
peri = cv2.arcLength(contour, True)
return cv2.approxPolyDP(contour, 0.032 * peri, True)
# 获取顶点坐标
def get_receipt_contour(contours):
# loop over the contours
for c in contours:
approx = approximate_contour(c)
# if our approximated contour has four points, we can assume it is receipt's rectangle
if len(approx) == 4:
return approx
def wrap_perspective(img, rect):
# unpack rectangle points: top left, top right, bottom right, bottom left
(tl, tr, br, bl) = rect
# compute the width of the new image
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))
# compute the height of the new image
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))
# take the maximum of the width and height values to reach
# our final dimensions
maxWidth = max(int(widthA), int(widthB))
maxHeight = max(int(heightA), int(heightB))
# destination points which will be used to map the screen to a "scanned" view
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype = "float32")
# calculate the perspective transform matrix
M = cv2.getPerspectiveTransform(rect, dst)
# warp the perspective to grab the screen
return cv2.warpPerspective(img, M, (maxWidth, maxHeight))
if __name__ == "__main__":
img = cv2.imread("./images/1.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 用高斯滤波处理原图像降噪
blur = cv2.GaussianBlur(gray, (5, 5), 0)
#canny边缘检测(仅针对这次的输入图片)
edged = cv2.Canny(blur, 50, 150)
contours, h = cv2.findContours(edged.copy(), mode=cv2.RETR_EXTERNAL, method=cv2.CHAIN_APPROX_SIMPLE)
# img_contours = cv2.drawContours(img.copy(), contours, -1, (0, 0, 255), 3)
largest_contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
# img_largest_contours = cv2.drawContours( img.copy(), largest_contours, -1, (0, 0, 255), 3)
receipt_contour = get_receipt_contour(largest_contours)
# img_receipt_contour = cv2.drawContours(img.copy(), [receipt_contour], -1, (0, 0, 255), 3)
ori_img = img.copy()
for coor in contour_to_rect(receipt_contour):
cv2.circle(ori_img, (int(coor[0]), int(coor[1])), 1, (0, 0, 255), 4)
# 进行透视变换
scanned = wrap_perspective(img.copy(), contour_to_rect(receipt_contour))
2.1 图像灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (5, 5), 0)(如果有噪声可以加个高斯滤波)
2.2 canny边缘检测
edged = cv2.Canny(blur, 50, 150) (三个参数分别输入图像,最小阈值,最大阈值)
下图中左为输出,右为边缘检测结果。看起来效果还行


左为所有轮廓绘制,右为面积前五的绘制。可以看到,书中长方形的框并不在检测中,这是因为在进行边缘检测时色差不大导致没检测出来,后续使用闭运算处理将连断出填充完。

最低0.47元/天 解锁文章
3722

被折叠的 条评论
为什么被折叠?



