前言
在机器视觉中,对于图像存在ROI区域倾斜现象,我们需要将其校正为正确的角度视角,方便下一步的布局分析与文字识别,通过透视变换可以取得比较好的裁剪效果。
一、基于轮廓提取和透射变换
⭐ 基于轮廓提取和透射变换的矫正算法更适用于车牌、身份证、人民币、书本、 发票一类矩形形状而且边界明显的物体矫正。
算法步骤
- 图片灰度化
- 二值化/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))
最终效果
文章来源:https://www.toymoban.com/news/detail-774964.html
二、基于霍夫直线探测和仿射变换
⭐ 基于霍夫直线探测的矫正算法更适用于文本类等无明显边界图像的矫正。文章来源地址https://www.toymoban.com/news/detail-774964.html
算法步骤
- 用霍夫线变换探测出图像中的所有直线。
- 计算出每条直线的倾斜角,求他们的平均值。
- 根据倾斜角旋转矫正。
- 最后根据文本尺寸裁剪图片。
实现代码
import cv2
import numpy as np
img = cv2.imread("./images/4.jpg")
src = img.copy()
gray = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY);
edged = cv2.Canny(src, 50, 200, apertureSize=3);
# 第5个参数就是阈值,阈值越大,检测精度越高
plines = cv2.HoughLines(edged, 1, np.pi / 180, 200);
if plines is not None:
plines = plines.reshape(-1, 2)
print(len(plines), "lines detected")
sum_theta = 0.0
for rho, theta in plines:
# rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
sum_theta += theta
# 绘制直线
cv2.line(src, (x1, y1), (x2, y2), (0, 0, 255), 2)
# 计算平均角度
average_theta = sum_theta / len(plines)
angle = np.degrees(average_theta) - 90
# 计算旋转中心
center = (float(img.shape[1] / 2.0), float(img.shape[0] / 2.0))
# 计算对角线长度作为旋转后图像的尺寸
length = int(np.sqrt(img.shape[0]**2 + img.shape[1]**2))
# 计算旋转矩阵
M = cv2.getRotationMatrix2D(center, angle, 1)
# 进行仿射变换并填充背景色为白色
src_rotate = cv2.warpAffine(img, M, (length, length), borderValue=(255, 255, 255))
最终效果
到了这里,关于OpenCV实战之三 | 基于OpenCV实现图像校正的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!