如何利用OpenCV轮廓检测八角形停车标志
检测八角形停车标志的实现方案
既然你已经搞定了三角形检测,那扩展到八角形其实思路是一致的,只是调整轮廓近似后的顶点数量,再结合停车标志的特性(比如红底、接近正八角形的形状)来优化检测精度。
核心思路
- 停车标志是正八角形,所以轮廓近似后顶点数应该为8
- 可以先通过颜色过滤(锁定红底区域)提前筛选候选范围,大幅减少误检
- 保留你现有轮廓检测、近似的核心流程,只修改顶点判断的条件,再增加形状校验
修改后的Java OpenCV代码
我基于你现有的代码做了针对性调整,重点优化了检测条件,还加了实用的颜色预处理步骤:
// 第一步:先过滤红色区域(停车标志的核心特征) Mat hsv = new Mat(); Imgproc.cvtColor(bgr, hsv, Imgproc.COLOR_BGR2HSV); // 红色在HSV空间分两段(因为HSV是环形色彩空间) Scalar lowerRed1 = new Scalar(0, 120, 70); Scalar upperRed1 = new Scalar(10, 255, 255); Scalar lowerRed2 = new Scalar(170, 120, 70); Scalar upperRed2 = new Scalar(180, 255, 255); Mat mask1 = new Mat(), mask2 = new Mat(); Core.inRange(hsv, lowerRed1, upperRed1, mask1); Core.inRange(hsv, lowerRed2, upperRed2, mask2); Mat redMask = new Mat(); Core.bitwise_or(mask1, mask2, redMask); // 只保留原图中的红色区域 Mat redRegion = new Mat(); Core.bitwise_and(bgr, bgr, redRegion, redMask); // 第二步:基于红色区域做轮廓检测(复用你原有的流程逻辑) Mat gray = new Mat(); Imgproc.cvtColor(redRegion, gray, Imgproc.COLOR_BGR2GRAY); Imgproc.threshold(gray, gray, 127, 255, Imgproc.THRESH_BINARY); List<MatOfPoint> contourList = new ArrayList<>(); Mat hierarchy = new Mat(); Imgproc.findContours(gray, contourList, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); Mat ROI = new Mat(); Mat bgrClone = bgr.clone(); MatOfPoint approxContour = new MatOfPoint(); MatOfPoint2f approxContour2f = new MatOfPoint2f(); List<MatOfPoint> contourDraw = new ArrayList<MatOfPoint>(); for(int i = 0; i < contourList.size(); i++) { MatOfPoint2f contour2f = new MatOfPoint2f(contourList.get(i).toArray()); double approxDistance = Imgproc.arcLength(contour2f, true) * 0.02; // 系数可根据图像分辨率微调 Imgproc.approxPolyDP(contour2f, approxContour2f, approxDistance, true); approxContour2f.convertTo(approxContour, CvType.CV_32S); // 核心判断:8个顶点 + 面积达标 + 接近正八角形(外接矩形宽高比接近1) if (approxContour.size().height == 8 && Imgproc.contourArea(contour2f) > 3000) { Rect cord = Imgproc.boundingRect(approxContour); double aspectRatio = (double)cord.width / cord.height; if (Math.abs(aspectRatio - 1) < 0.1) { // 宽高比在0.9-1.1之间,排除狭长的伪八角形 contourDraw.add(approxContour); Imgproc.drawContours(bgr, contourDraw, -1, new Scalar(0,255,0), 2); Core.rectangle(bgr, new Point(cord.x, cord.y), new Point(cord.x+cord.width, cord.y+cord.height),new Scalar(0,255,0), 2); ROI = bgrClone.submat(cord.y, cord.y+cord.height, cord.x, cord.x+cord.width); showResult(ROI); } } }
关键优化点说明
- 颜色过滤:停车标志的红色是强特征,提前过滤后能直接排除大部分无关轮廓,减少无效计算
- 宽高比校验:正八角形的外接矩形接近正方形,加这个条件可以避免把8边的狭长轮廓误判为停车标志
- 近似系数微调:
approxDistance的系数(0.02)可以根据你的拍摄场景调整——系数越大,轮廓近似越粗糙,反之越精细
补充Python版本示例(供参考)
如果需要跨语言参考,Python版的核心逻辑完全一致:
import cv2 import numpy as np img = cv2.imread('parking_sign.jpg') hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) # 定义红色HSV范围 lower_red1 = np.array([0, 120, 70]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([170, 120, 70]) upper_red2 = np.array([180, 255, 255]) mask1 = cv2.inRange(hsv, lower_red1, upper_red1) mask2 = cv2.inRange(hsv, lower_red2, upper_red2) red_mask = cv2.bitwise_or(mask1, mask2) red_region = cv2.bitwise_and(img, img, mask=red_mask) gray = cv2.cvtColor(red_region, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * perimeter, True) area = cv2.contourArea(cnt) if len(approx) == 8 and area > 3000: x, y, w, h = cv2.boundingRect(approx) aspect_ratio = w / h if abs(aspect_ratio - 1) < 0.1: cv2.drawContours(img, [approx], -1, (0, 255, 0), 2) cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) roi = img[y:y+h, x:x+w] cv2.imshow('ROI', roi) cv2.imshow('Result', img) cv2.waitKey(0) cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者Dodz
相关产品推荐
相关产品推荐

