赞
踩
在本文中,我们将探讨如何使用 Python 和 OpenCV 库来检测图像中的水平线,并对图像进行旋转校正以使这些线条水平。这种技术可广泛应用于文档扫描、建筑摄影校正以及机器视觉中的各种场景。
首先,确保您的环境中安装了 OpenCV 库。如果还没有安装,可以通过以下命令安装,要注意尽管代码里我们都是使用的cv2,但是安装包要选opencv-python:
pip install opencv-python
image = cv2.imread('test.png') # 读取图片
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 转换成灰度图像
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
参数详解
gray
:这是输入图像,Canny 边缘检测通常在灰度图像上进行,因为边缘检测是基于图像亮度变化的。
50
:第一个阈值用于边缘检测的低阈值。这是用于Canny算法中的双阈值过程的较低边界。低于此阈值的像素点不会被视为边缘。
150
:第二个阈值用于边缘检测的高阈值。这是用于Canny算法中的双阈值过程的较高边界。高于此阈值的像素点将被视为边缘的强候选者。
apertureSize=3
:这是用于内部边缘检测的Sobel算子的大小。apertureSize定义了计算图像梯度所用的Sobel核的大小。常用的尺寸是3,但也可以使用更大的尺寸如5或7,这在处理较大的边缘时可以提供更平滑的结果。
def filter_horizontal_lines(lines, angle_threshold=10):
horizontal_lines = []
if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
angle = np.degrees(np.arctan2(y2 - y1, x2 - x1))
if abs(angle) < angle_threshold:
horizontal_lines.append(line)
return horizontal_lines
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=100, maxLineGap=10)
horizontal_lines = filter_horizontal_lines(lines)
HoughLinesP
参数说明
# HoughLinesP使用概率霍夫变换检测图像中的线段 (注意HoughLines和HoughLinesP是两个函数方法)
lines = cv2.HoughLinesP(
edges, # 边缘图像,通常是Canny边缘检测的输出
1, # rho - 累加器的距离精度,以像素为单位
np.pi / 180, # theta - 累加器的角度精度,以弧度为单位
100, # threshold - 累加器的阈值,仅返回大于此阈值的线段
minLineLength=100, # minLineLength - 线段的最小长度
maxLineGap=10 # maxLineGap - 同一线条上允许的最大间隙
)
np.average(angles, weights=lengths)
使用了加权,也就是这个函数会基于找到的线段长度,进行角度的加权平均,如果你只是单纯的关注线段的所有角度,可以删掉weights
这个参数。def calculate_average_angle(lines): """ 计算线条的加权平均角度。 参数: lines (list): 包含线条的列表,每条线条由两个点的坐标表示,格式为 [x1, y1, x2, y2]。 返回: float: 线条的加权平均角度,以度为单位。如果没有符合条件的线条,则返回 0。 """ angles = [] lengths = [] if lines: for line in lines: x1, y1, x2, y2 = line[0] # 计算线条的长度 length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) # 计算线条的角度,以度为单位 angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) # 角度校正,确保处理的角度是接近水平的 if abs(angle) > 90: angle -= 180 # 只考虑接近水平的线条 if abs(angle) < 20: # 可调整此阈值以更好地适应具体情况 angles.append(angle) lengths.append(length) # 计算加权平均角度 if lengths: average_angle = np.average(angles, weights=lengths) else: average_angle = 0 return average_angle average_angle = calculate_average_angle(horizontal_lines) # 调用函数完成平均角度计算
def rotate_image(image, angle):
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h))
return rotated
rotated_image = rotate_image(image, average_angle)
cv2.imwrite('rotated_image.jpg', rotated_image)
import cv2 import numpy as np def filter_horizontal_lines(lines, angle_threshold=10): horizontal_lines = [] if lines is not None: for line in lines: x1, y1, x2, y2 = line[0] angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) if abs(angle) < angle_threshold: horizontal_lines.append(line) return horizontal_lines # # def calculate_average_angle(lines): angles = [] lengths = [] if lines: for line in lines: x1, y1, x2, y2 = line[0] length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) # 角度校正,确保处理的角度是接近水平的 if abs(angle) > 90: angle -= 180 # 只考虑接近水平的线条 if abs(angle) < 20: # 可调整此阈值以更好地适应具体情况 angles.append(angle) lengths.append(length) # 计算加权平均角度 if lengths: average_angle = np.average(angles, weights=lengths) else: average_angle = 0 return average_angle # def calculate_average_angle(lines): # angles = [] # lengths = [] # filtered_lines = [] # # if lines: # # 计算每条线的长度和角度 # for line in lines: # x1, y1, x2, y2 = line[0] # length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) # angle = np.degrees(np.arctan2(y2 - y1, x2 - x1)) # # # 角度校正,确保处理的角度是接近水平的 # if abs(angle) > 90: # angle -= 180 # if abs(angle) < 20: # 只考虑接近水平的线条 # filtered_lines.append((angle, length)) # # # 按长度排序,并取最长的前9条线 # filtered_lines.sort(key=lambda x: x[1], reverse=True) # top_lines = filtered_lines[:9] # # # 分割角度和长度 # angles, lengths = zip(*top_lines) if top_lines else ([], []) # # # 计算加权平均角度 # if lengths: # average_angle = np.average(angles, weights=lengths) # else: # average_angle = 0 # # return average_angle # 使用此函数时,确保传入的lines是过滤后只包含接近水平的线条 def draw_lines(image, lines): for line in lines: x1, y1, x2, y2 = line[0] cv2.line(image, (x1, y1), (x2, y2), (0, 255, 0), 3) return image def rotate_image(image, angle): (h, w) = image.shape[:2] center = (w // 2, h // 2) M = cv2.getRotationMatrix2D(center, angle, 1.0) rotated = cv2.warpAffine(image, M, (w, h)) return rotated # 加载图像 image = cv2.imread('test.png') # 转换为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 边缘检测 edges = cv2.Canny(gray, 50, 150, apertureSize=3) # 使用霍夫变换检测线条 lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=100, maxLineGap=10) # 过滤出横向线条 horizontal_lines = filter_horizontal_lines(lines) # 在原图上绘制检测到的横向线条 image_with_lines = draw_lines(np.copy(image), horizontal_lines) # 保存带线条的图像 cv2.imwrite('image_with_horizontal_lines.jpg', image_with_lines) # 计算线条的加权平均角度 average_angle = calculate_average_angle(horizontal_lines) print("计算得到的加权平均角度为:", average_angle) # 旋转整个图像使线条水平 rotated_image = rotate_image(image, average_angle) # 根据角度旋转 正角度表示逆时针旋转,而负角度表示顺时针旋转 # 保存旋转后的图像 cv2.imwrite('rotated_image.jpg', rotated_image) print("旋转后的图像已保存为 'rotated_image.jpg'")
通过上述步骤,我们能够自动检测并校正图像中的水平线,这对于许多自动化处理任务来说是非常有用的。本文介绍的方法仅依赖于 OpenCV,易于实现且效果显著。了解相关函数,通过适当调整参数,该技术可以适应不同的应用需求和条件。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。