这段代码的主要功能是对输入的图像进行透视矫正。它会读取一张图像,检测图像中最大的四边形轮廓,然后对该四边形区域进行透视变换,将其矫正为正视图,最后保存矫正后的图像。
模块导入说明
python
import cv2
import numpy as np
import os
cv2
:OpenCV 库,用于图像处理和计算机视觉任务,如读取图像、边缘检测、轮廓查找、透视变换等。numpy
:用于数值计算,在处理图像数据和坐标计算时非常有用。os
:提供了与操作系统进行交互的功能,例如文件路径操作和目录创建。
函数 detect_and_correct_perspective
说明
python
def detect_and_correct_perspective(input_path, output_path):
- 功能:检测输入图像中的四边形轮廓,并对其进行透视矫正,最后将结果保存到指定路径。
- 参数:
input_path
:输入图像的文件路径。output_path
:矫正后图像的保存路径。
函数内部步骤及代码解释
- 读取图像
python
image = cv2.imread(input_path)
if image is None:
print(f"无法加载图像,请检查路径: {os.path.abspath(input_path)}")
return
尝试使用 cv2.imread
读取输入图像。如果图像读取失败,会打印错误信息并终止函数。
- 转换为灰度图
python
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
将彩色图像转换为灰度图像,便于后续的边缘检测。
- 高斯模糊降噪
python
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
使用高斯模糊对灰度图像进行降噪处理,减少边缘检测时的噪声干扰。
- Canny 边缘检测
python
edges = cv2.Canny(blurred, 50, 150)
使用 Canny 边缘检测算法检测图像中的边缘。
- 查找轮廓
python
contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
查找图像中的所有外部轮廓。
- 找到最大的轮廓
python
largest_contour = max(contours, key=cv2.contourArea)
找到所有轮廓中面积最大的轮廓,假设这个轮廓就是需要矫正的对象。
- 计算轮廓的近似多边形
python
epsilon = 0.02 * cv2.arcLength(largest_contour, True)
approx = cv2.approxPolyDP(largest_contour, epsilon, True)
使用 cv2.approxPolyDP
对最大轮廓进行多边形近似,简化轮廓。
- 绘制轮廓和近似多边形
python
cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)
cv2.drawContours(image, [approx], -1, (0, 0, 255), 2)
在原始图像上绘制最大轮廓(绿色)和近似多边形(红色),用于可视化。
- 确保找到四边形
python
if len(approx) == 4:
检查近似多边形是否有 4 个顶点,如果是,则进行透视变换;否则,打印错误信息。
- 对四个顶点进行排序
python
pts = approx.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)] # 左下
对四个顶点进行排序,确定左上、右上、右下、左下的顺序。
- 计算目标矩形的宽度和高度
python
(tl, tr, br, bl) = rect
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))
maxWidth = max(int(widthA), int(widthB))
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))
maxHeight = max(int(heightA), int(heightB))
根据四个顶点的坐标计算目标矩形的宽度和高度。
- 定义目标矩形的四个顶点
python
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
定义目标矩形的四个顶点坐标。
- 计算透视变换矩阵并应用
python
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
使用 cv2.getPerspectiveTransform
计算透视变换矩阵,然后使用 cv2.warpPerspective
应用该矩阵进行透视变换。
- 确保输出目录存在
python
os.makedirs(os.path.dirname(output_path), exist_ok=True)
确保输出目录存在,如果不存在则创建。
- 保存结果
python
cv2.imwrite(output_path, warped)
print(f"结果已保存到: {os.path.abspath(output_path)}")
将矫正后的图像保存到指定路径,并打印保存信息。
- 显示结果(可选)
python
cv2.imshow("Original with Contours", image)
cv2.imshow("Perspective Corrected", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()
显示原始图像(带有轮廓)和矫正后的图像,按任意键关闭窗口。
主程序部分说明
python
current_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前脚本所在目录
input_image = os.path.join(current_dir, "girl.png") # 输入图像路径
output_image = os.path.join(current_dir, "corrected_girl.png") # 输出图像路径
# 执行函数
detect_and_correct_perspective(input_image, output_image)
- 获取当前脚本所在的目录。
- 构造输入图像和输出图像的路径。
- 调用
detect_and_correct_perspective
函数进行透视矫正。
完整代码
import cv2
import numpy as np
import os
def detect_and_correct_perspective(input_path, output_path):
# 读取图像
image = cv2.imread(input_path)
if image is None:
print(f"无法加载图像,请检查路径: {os.path.abspath(input_path)}")
return
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 高斯模糊降噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny边缘检测
edges = cv2.Canny(blurred, 50, 150)
# 查找轮廓
contours, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 找到最大的轮廓(假设这是我们想要矫正的对象)
largest_contour = max(contours, key=cv2.contourArea)
# 计算轮廓的近似多边形(简化轮廓)
epsilon = 0.02 * cv2.arcLength(largest_contour, True)
approx = cv2.approxPolyDP(largest_contour, epsilon, True)
# 绘制轮廓和近似多边形(用于可视化)
cv2.drawContours(image, [largest_contour], -1, (0, 255, 0), 2)
cv2.drawContours(image, [approx], -1, (0, 0, 255), 2)
# 确保我们找到了四边形(4个顶点)
if len(approx) == 4:
# 对四个顶点进行排序:左上、右上、右下、左下
pts = approx.reshape(4, 2)
rect = np.zeros((4, 2), dtype="float32")
# 计算四个点的总和,左上点有最小的x+y,右下点有最大的x+y
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # 左上
rect[2] = pts[np.argmax(s)] # 右下
# 计算点之间的差异,右上点有最小的x-y,左下点有最大的x-y
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 右上
rect[3] = pts[np.argmax(diff)] # 左下
# 计算目标矩形的宽度和高度
(tl, tr, br, bl) = rect
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))
maxWidth = max(int(widthA), int(widthB))
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))
maxHeight = max(int(heightA), int(heightB))
# 定义目标矩形的四个顶点
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32")
# 计算透视变换矩阵并应用
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
# 确保输出目录存在
os.makedirs(os.path.dirname(output_path), exist_ok=True)
# 保存结果
cv2.imwrite(output_path, warped)
print(f"结果已保存到: {os.path.abspath(output_path)}")
# 显示结果(可选)
cv2.imshow("Original with Contours", image)
cv2.imshow("Perspective Corrected", warped)
cv2.waitKey(0)
cv2.destroyAllWindows()
else:
print("未检测到四边形轮廓,无法进行透视变换")
# 使用相对路径
current_dir = os.path.dirname(os.path.abspath(__file__)) # 获取当前脚本所在目录
input_image = os.path.join(current_dir, "girl.png") # 输入图像路径
output_image = os.path.join(current_dir, "corrected_girl.png") # 输出图像路径
# 执行函数
detect_and_correct_perspective(input_image, output_image)