建筑项目图纸附加文件:云状图形及内嵌数字三角形检测
建筑图纸附加文件检测:云状图形/带数字三角形的鲁棒性实现
问题背景
需要判断图像是否为建筑项目图纸的附加文件,核心是检测图像里的云状对象,如果这个实现难度太高,退而求其次检测内部带数字的三角形也可以。目前用OpenCV写的Python代码只能处理只有两个云状图形的简单图,碰到有大量线条、文字的复杂图像就直接失效,甚至还会误识别。
现有问题根源
简单场景里用的基础检测逻辑(比如靠轮廓面积、简单多边形匹配)扛不住复杂背景的干扰:
- 密密麻麻的线条、文字会生成一堆噪声轮廓,直接把目标轮廓给盖过去了
- 云状图形本身边缘不规则,在复杂背景下根本没法准确提取
- 基础形状匹配对轮廓的完整性、纯净度要求太高,一有干扰就匹配失败
改进方案
方案一:让云状图形检测更抗造
针对复杂背景里的云状图形,可以按这几步优化:
- 先做降噪预处理
- 先用
cv2.GaussianBlur()做高斯模糊,把细线条和文字的干扰弱化 - 换成自适应阈值二值化
cv2.adaptiveThreshold(),比全局阈值更能应对光照不均和复杂背景
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5,5), 0) thresh = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) - 先用
- 精准筛选轮廓特征
- 提取轮廓后先过滤掉面积太小或太大的,把文字和大背景区域排除
- 计算轮廓的形状复杂度:云状图形的近似多边形顶点数远多于普通几何图形,用
cv2.approxPolyDP()控制近似精度后统计顶点数;同时算圆度(4*math.pi*area / (perimeter**2)),云状图形的圆度一般在规则圆形和尖锐多边形之间
import math contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area = cv2.contourArea(cnt) # 根据实际场景调整面积阈值 if area < 100 or area > 10000: continue perimeter = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02*perimeter, True) circularity = 4*math.pi*area / (perimeter**2) # 云状图形的典型特征:顶点数多(比如>15),圆度适中(0.3-0.8) if len(approx) > 15 and 0.3 < circularity < 0.8: cv2.drawContours(img, [cnt], -1, (0,255,0), 2) - 用形态学操作强化轮廓
- 先开运算(腐蚀+膨胀)去掉小噪声轮廓,再闭运算填补云状图形内部的小空洞,把目标轮廓变清晰
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)) thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
方案二:检测内部带数字的三角形
如果云状检测实在搞不定,就转去检测带数字的三角形,步骤如下:
- 先揪出三角形轮廓
- 预处理后提取轮廓,通过近似多边形筛选出顶点数为3的轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: approx = cv2.approxPolyDP(cnt, 0.04*cv2.arcLength(cnt, True), True) if len(approx) == 3: x,y,w,h = cv2.boundingRect(approx) roi = gray[y:y+h, x:x+w] - 检查三角形内部的数字
- 对提取的ROI区域做二值化,要么用
cv2.matchTemplate()匹配提前做的0-9数字模板,要么用pytesseract这类OCR工具识别区域里有没有数字
import pytesseract roi_thresh = cv2.threshold(roi, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1] # 只识别数字,避免文字干扰 text = pytesseract.image_to_string(roi_thresh, config='--psm 10 --oem 3 -c tessedit_char_whitelist=0123456789') if text.strip().isdigit(): cv2.drawContours(img, [approx], -1, (0,0,255), 2) - 对提取的ROI区域做二值化,要么用
- 验证三角形的真实性
- 计算三角形的边长比例,排除那些长得像三角但实际不是的干扰轮廓
测试建议
- 多找些不同复杂程度的样本图来测,根据实际情况调整面积、顶点数、圆度这些阈值
- 碰到误识别的情况,分析下干扰轮廓的特征,针对性优化筛选条件
内容的提问来源于stack exchange,提问作者Vicente Guedes
相关产品推荐
相关产品推荐

