如何使用cv2隔离条形图中无白色填充的条形
解决方案
核心思路是利用条形端边的固定长度特征,直接从原图中筛选条形专属的水平线段,避开和长网格线的冲突,不需要提前移除网格线也不需要机器学习:
具体步骤
- 第一步:提取指定长度的水平线段
用自定义水平形态学核提取所有长度落在20~30px区间的水平线段,这个区间专门匹配你提到的26px宽的条形端边,自动过滤掉整行跨度的水平网格线,哪怕端边和网格线有重叠,也能通过长度阈值把属于条形的部分筛出来。 - 第二步:配对同一条形的上下端边
对提取到的所有水平线段,按左端点x坐标、右端点x坐标分组,只要两个水平线段的x左右端点偏差不超过2px,且纵向y坐标差在合理的条形高度范围,就判定为同一个条形的上下端边。 - 第三步:生成完整的条形掩膜
根据配对得到的上下端边坐标,直接绘制对应尺寸的空心矩形,即可得到完全隔离的无填充条形,哪怕原条形竖边和垂直网格线有重叠也不影响结果。
示例代码
import cv2 import numpy as np # 读取二值图像(注意默认读取是BGR,需转灰度,二值化阈值可根据实际调整) img = cv2.imread("bar_chart.png", 0) _, binary = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV) # 反转后前景为白色 # 1. 提取20-30px长度的水平线段 horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1)) detected_lines = cv2.morphologyEx(binary, cv2.MORPH_OPEN, horizontal_kernel, iterations=2) # 过滤长度符合要求的线段 contours, _ = cv2.findContours(detected_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) bar_edges = [] for cnt in contours: x, y, w, h = cv2.boundingRect(cnt) if 20 <= w <= 30: bar_edges.append((x, y, x+w, y+h)) # 2. 配对上下端边 bar_edges.sort(key=lambda x: (x[0], x[1])) # 按左x、上y排序 paired_bars = [] used = set() for i in range(len(bar_edges)): if i in used: continue x1, y1, x2, y2 = bar_edges[i] # 找同x范围的下端边 for j in range(i+1, len(bar_edges)): if j in used: continue x1_, y1_, x2_, y2_ = bar_edges[j] # x偏差不超过2px,y差大于最小条形高度(可调整) if abs(x1 - x1_) <= 2 and abs(x2 - x2_) <= 2 and (y1_ - y1) > 10: paired_bars.append((x1, y1, x2_, y2_)) used.add(i) used.add(j) break # 3. 生成结果掩膜 result = np.zeros_like(img) for (x1, y1, x2, y2) in paired_bars: cv2.rectangle(result, (x1, y1), (x2, y2), 255, 2) # 边框宽度和原图一致即可 # 保存或展示结果 cv2.imwrite("extracted_bars.png", result)
方案适配性说明
如果后续条形端边长度有调整,只需要修改步骤1中的长度过滤阈值和形态学核宽度即可,完全适配网格线/轴线缺口位置可变的场景,不需要调整其他逻辑。
内容的提问来源于stack exchange,提问作者Griffin
相关产品推荐
相关产品推荐

