如何使用OpenCV获取扫描图像的边角坐标?
我来帮你搞定这个扫描图裁剪的问题!要自动识别四个角的矩形标记并获取裁剪坐标,咱们可以用OpenCV的图像处理工具一步步实现,下面是完整的解决方案:
一、图像预处理
首先得对原始图像做预处理,突出边缘信息,方便后续找轮廓:
import cv2 import numpy as np # 读取图像(替换成你的图像路径) image = cv2.imread("your_scanned_image.jpg") # 转灰度图,减少计算维度 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 高斯模糊去噪,避免边缘检测误判 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # Canny边缘检测,提取图像中的边缘特征 edged = cv2.Canny(blurred, 50, 150)
二、检测并筛选目标轮廓
接下来从边缘图里找出所有轮廓,然后筛选出四个角的小矩形标记:
# 查找图像中的所有外部轮廓 contours, _ = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 筛选符合特征的标记轮廓 marker_contours = [] for c in contours: # 计算轮廓的近似多边形,简化轮廓形状 peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) # 筛选条件:近似为4边形(矩形),且面积在合理范围(根据你的图像尺寸调整) if len(approx) == 4 and 100 < cv2.contourArea(c) < 1000: marker_contours.append(approx)
说明:面积阈值可以根据你扫描图里标记的实际大小微调,确保只留下四个角的小矩形。
三、提取裁剪用的四个角点
现在我们有了四个标记矩形的轮廓,接下来要从每个矩形中提取出对应文档的角点(比如每个矩形最靠近图像角落的顶点),并按裁剪要求排序:
# 存储最终的文档角点 corners = [] h, w = image.shape[:2] # 获取图像的宽高 for cnt in marker_contours: # 把轮廓顶点转换成(x,y)格式 pts = cnt.reshape(4, 2) # 计算每个顶点到图像四个角落的距离,找到距离最近的点(即文档的实际角点) min_dist = float("inf") target_pt = None for (x, y) in pts: # 计算当前点到四个图像角落的距离最小值 dists = [ np.sqrt((x-0)**2 + (y-0)**2), # 到左上角 np.sqrt((x-w)**2 + (y-0)**2), # 到右上角 np.sqrt((x-0)**2 + (y-h)**2), # 到左下角 np.sqrt((x-w)**2 + (y-h)**2) # 到右下角 ] current_min = min(dists) if current_min < min_dist: min_dist = current_min target_pt = (x, y) corners.append(target_pt) # 将角点按「左上→右上→右下→左下」的顺序排序(匹配你提供的裁剪代码要求) corners = np.array(corners, dtype="float32") # 先按y坐标排序,区分顶部和底部的点 sorted_by_y = corners[corners[:,1].argsort()] # 顶部两个点按x从小到大排序(左上在前,右上在后) top_corners = sorted_by_y[:2][sorted_by_y[:2,0].argsort()] # 底部两个点按x从大到小排序(右下在前,左下在后) bottom_corners = sorted_by_y[2:][sorted_by_y[2:,0].argsort()[::-1]] # 组合成最终的有序角点 ordered_corners = np.concatenate([top_corners, bottom_corners])
四、应用四点变换完成裁剪
现在可以用你提供的裁剪代码完成透视变换了,如果没有four_point_transform函数,也可以自己实现一个:
先实现四点变换函数(如果需要)
def four_point_transform(image, pts): # 提取有序的四个角点 (tl, tr, br, bl) = pts # 计算目标图像的宽度 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(pts, dst) warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warped
执行裁剪并查看结果
# 应用透视变换 warped = four_point_transform(image, ordered_corners) # 显示原始图和裁剪后的结果 cv2.imshow("Original Scanned Image", image) cv2.imshow("Cropped Result", warped) cv2.waitKey(0) cv2.destroyAllWindows()
注意事项
- 如果轮廓筛选不准确,可以调整面积阈值(
100 < cv2.contourArea(c) < 1000),适配你的扫描图标记大小。 - 若角点排序出现错误,可以微调排序逻辑,确保最终顺序是「左上→右上→右下→左下」。
内容的提问来源于stack exchange,提问作者yozawiratama
相关产品推荐
相关产品推荐

