使用OpenCV提取图像矩形轮廓时触发AxisError的原因排查
OpenCV提取矩形区域触发AxisError的原因及解决办法
我最近在用Python和OpenCV做图像矩形区域提取(比如文档、护照照片这类),大部分图像都能正常运行,但处理某张特定图像时,突然抛出了这个错误:
axis = normalize_axis_index(axis, nd) numpy.core._internal.AxisError: axis 1 is out of bounds for array of dimension 1
报错的代码段是这段循环轮廓的逻辑:
for cnt in contours: perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.03 * perimeter, True) if (len(approx) == 4 and cv2.isContourConvex(approx) and maxAreaFound < cv2.contourArea(approx) < MAX_COUNTOUR_AREA): maxAreaFound = cv2.contourArea(approx) pageContour = approx
相关参考图像
- 原图:

- 阈值处理图:

- 边缘检测图:

完整代码
import numpy as np import cv2 def resize(img, height=800): """ Resize image to given height """ rat = height / img.shape[0] return cv2.resize(img, (int(rat * img.shape[1]), height)) def fourCornersSort(pts): diff = np.diff(pts, axis=1) summ = pts.sum(axis=1) return np.array( [pts[np.argmin(summ)], pts[np.argmax(diff)], pts[np.argmax(summ)], pts[np.argmin(diff)]]) def contourOffset(cnt, offset): """ Offset contour, by 5px border """ # Matrix addition cnt += offset # if value < 0 => replace it by 0 cnt[cnt < 0] = 0 return cnt image = cv2.cvtColor(cv2.imread("9.jpg"), cv2.COLOR_BGR2RGB) img = cv2.cvtColor(resize(image), cv2.COLOR_BGR2GRAY) img = cv2.bilateralFilter(img, 9, 75, 75) img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 115, 4) img = cv2.medianBlur(img, 11) img = cv2.copyMakeBorder(img, 5, 5, 5, 5, cv2.BORDER_CONSTANT, value=[0, 0, 0]) edges = cv2.Canny(img, 200, 250) im2, contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) height = edges.shape[0] width = edges.shape[1] MAX_COUNTOUR_AREA = (width - 10) * (height - 10) maxAreaFound = MAX_COUNTOUR_AREA * 0.5 pageContour = np.array([[5, 5], [5, height-5], [width-5, height-5], [width-5, 5]]) for cnt in contours: perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.03 * perimeter, True) if (len(approx) == 4 and cv2.isContourConvex(approx) and maxAreaFound < cv2.contourArea(approx) < MAX_COUNTOUR_AREA): maxAreaFound = cv2.contourArea(approx) pageContour = approx pageContour = fourCornersSort(pageContour[:, 0]) pageContour = contourOffset(pageContour, (-5, -5)) sPoints = pageContour.dot(image.shape[0] / 800) height = max(np.linalg.norm(sPoints[0] - sPoints[1]), np.linalg.norm(sPoints[2] - sPoints[3])) width = max(np.linalg.norm(sPoints[1] - sPoints[2]), np.linalg.norm(sPoints[3] - sPoints[0])) tPoints = np.array([[0, 0], [0, height], [width, height], [width, 0]], np.float32) if sPoints.dtype != np.float32: sPoints = sPoints.astype(np.float32) M = cv2.getPerspectiveTransform(sPoints, tPoints) newImage = cv2.warpPerspective(image, M, (int(width), int(height))) cv2.imwrite("resultImage.jpg", cv2.cvtColor(newImage, cv2.COLOR_BGR2RGB))
错误原因分析
我帮你排查了下,这个AxisError的核心问题出在轮廓数据的维度不匹配上:
正常情况下,
cv2.findContours返回的轮廓cnt是(N,1,2)形状的3维数组,经过cv2.approxPolyDP处理后得到的4边形approx也应该是(4,1,2)的3维数组。但在这张特定图像里,可能存在一些细碎的异常小轮廓,这些轮廓的点集是1维数组,导致近似多边形后得到的approx也变成了1维。当你用
len(approx) ==4判断时,1维数组的长度确实可能是4,但后续把这个1维的approx赋值给pageContour后,执行pageContour[:, 0]就会直接报错——因为1维数组根本没有axis=1这个维度。另外还有一种小概率情况:如果循环里完全没找到符合条件的4边形轮廓,
pageContour还是你初始定义的2维数组,这时候pageContour[:,0]是没问题的,但显然你的情况是找到了异常的4点轮廓。
解决办法
你只需要在循环里增加对approx维度的检查,确保它是符合预期的3维数组,再进行后续判断就行:
修改循环部分的代码:
for cnt in contours: perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.03 * perimeter, True) # 先检查维度,确保是3维的轮廓数据,再判断其他条件 if approx.ndim == 3 and len(approx) == 4 and cv2.isContourConvex(approx): area = cv2.contourArea(approx) if maxAreaFound < area < MAX_COUNTOUR_AREA: maxAreaFound = area pageContour = approx
保险起见,你还可以在处理pageContour前再做一次维度校验,防止意外情况:
# 确保pageContour是3维,转换为(4,2)的点集用于排序 if pageContour.ndim == 3: pageContour = fourCornersSort(pageContour[:, 0]) else: # 如果是异常情况,直接用初始定义的矩形轮廓(或者根据你的需求做其他处理) pageContour = fourCornersSort(pageContour)
这样就能彻底避免因为异常轮廓的维度问题触发AxisError了。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

