Opencv 在绘制掩模时区域重叠问题

本文针对安防监控中区域重叠导致物体检测失败的问题,通过改进掩模处理方式,采用数组交集判断与掩模或运算,成功解决了区域重叠下物体检测不全的难题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题提出

题主所从事领域为要地安防监控,一般对于广域监测系统,用户会在指定区域后才做有针对性的预警。由于惯性思维,题主会指定几个区域做测试,规避了其他可能性。

昨天请技术支持过来测试软件,小姐姐就凌乱的新建小组,新建区域,在整个屏幕凌乱的画着所谓的指定区域,然后发现一个问题:

在区域交叠的地方,物体并没有检测到。

分析问题

由于指定区域是在Opencv掩模后进行,针对这个问题,倘或区域未检测,一定是掩模出了问题。
然后,调试阶段,将掩模对外可见,果不其然,两个有交叠区域在做掩模效果如图:

在这里插入图片描述

显然,我们希望内部同样填充白点。

解决方法

零散时间思考了这个问题,然后大概思路如下:

  1. 首先判断数组中是否存在有交叠情形;
  2. 如果有交叠,采取独立绘制掩模后或运算,能够有效保留每个亮点。

针对以上思路,修改了点代码,最终效果如下:

在这里插入图片描述

部分代码

1、首先判断两个数组是否存在交集

bool  IsIntersection(vector<cv::Point>arrays_1, vector<cv::Point>arrays_2)

{
	if (arrays_1.empty() || arrays_2.empty())

	{
		return false;
	}

	vector<cv::Point>::iterator iter;

	for (int i = 0; i < arrays_1.size(); i++)
	{
		iter = find(arrays_2.begin(),arrays_2.end(),arrays_1[i]);
		if (iter != arrays_2.end())
		{
			return true;
		}
	}

	return false;
}

2、判断数组中,是否存在有交集情形

bool IsArraysIntersection()

{
	if (RoiContours.size())
	{
		for (int i = 0; i < RoiContours.size(); i++)
		{
			for (int j = i; j < RoiContours.size(); j++)
			{
				if (IsIntersection(RoiContours[i], RoiContours[j]))
				{
					return true;
				}
			}
		}
	}
	return false;
}

3、针对性绘制掩模

cv::Mat DrawRoi()

{
	mask = cv::Mat::zeros(Size(Width, Height), CV_8UC1);

	if (RoiContours.size() > 0)
	{
	//只有区域个数大于等于2时,才有做重叠检测的必要
		if (RoiContours.size() > 1)
		{	
			if (IsArraysIntersection())
			{
				cv::Mat tempMask = cv::Mat::zeros(Size(Width, Height), CV_8UC1); 

				vector<vector<cv::Point>> tempArrays;

				for (int i = 0; i < RoiContours.size(); i++)
				{
					tempArrays.push_back(RoiContours[i]);
					drawContours(tempMask, tempArrays, -1, Scalar::all(255), CV_FILLED);
					//或运算
					bitwise_or(tempMask, mask, mask);
				}
				tempMask.release();

				drawContours(mask, RoiContours, -1, Scalar::all(255), CV_FILLED);
				return mask;	
			}
		
			else
			{
				drawContours(mask, RoiContours, -1, Scalar::all(255), CV_FILLED);
				return mask;
			}
		}	
		else
		{
			drawContours(mask, RoiContours, -1, Scalar::all(255), CV_FILLED);
			return mask;
		}
	}

	else
	{
		return mask;
	}
}

OK,问题解决。

<think>我们当前的目标是:在图像处理中,对未被正确识别的三角形进行轮廓描边显示。 根据之前的讨论,我们已经有了一个识别旋转黑框内三角形的流程。但是,当三角形未被正确识别(即识别失败),我们希望在图像上突出显示这些区域(例如用红色轮廓描边),以便于调试和观察。 思路: 1. 在之前的流程中,我们首先检测黑色边框,然后通过透视变换得到校正后的图像。 2. 在校正后的图像中,我们尝试检测三角形轮廓。如果检测到三角形,我们将其绘制出来;如果未检测到,我们希望将整个黑色边框内部区域(即应该包含三角形的区域)用轮廓描边(比如红色)显示,以指示该区域未被正确识别。 具体步骤: a) 在检测到黑色边框(四边形)并进行透视变换后,我们尝试检测三角形。 b) 如果三角形检测成功,则按原方案绘制三角形轮廓(比如绿色)。 c) 如果三角形检测失败(即detect_triangle_in_warped返回None),则我们可以在原始图像上,将整个黑色边框用红色轮廓描边,或者将透视变换后的整个区域(即四边形内部)在原始图像上用红色轮廓描边。 但是,注意:我们已经在原始图像上绘制了绿色边框(表示检测到的黑色边框)。如果三角形检测失败,我们可以在同一个四边形内部再绘制一个轮廓(比如红色)来表示内部三角形未被识别。 然而,我们也可以选择在透视变换后的图像上,将整个内部区域(即整个校正后的图像)视为“未识别区域”,然后映射回原始图像。但这样会绘制整个四边形,而我们希望突出的是内部三角形区域。 另一种做法:既然三角形检测失败,我们可以将透视变换后的图像区域(即四边形内部)视为一个整体,然后在原始图像上绘制这个四边形的内部轮廓(比如用红色虚线描边整个四边形,或者用半透明填充)?但这样可能不够明确。 更直接的方法:在透视变换后的图像中,整个区域都应该是三角形所在的区域。如果三角形检测失败,我们可以将整个校正后的图像区域(即黑色边框内的区域)在原始图像上用一个矩形(或四边形)轮廓描边,并标记为红色。 但是,我们注意到:在透视变换后,我们得到的图像是一个矩形,但原始图像中的四边形可能是任意四边形(经过透视变换后变成矩形)。因此,在原始图像上,这个区域就是那个四边形。 因此,我们可以这样实现: - 当三角形检测失败,我们在原始图像上绘制之前检测到的四边形(黑色边框)的轮廓,但这次用红色(或另一种颜色)并加粗,以表示该区域内的三角形未被识别。 然而,这样可能会和之前绘制的边框(绿色)重叠。所以,我们可以: - 如果三角形检测成功,绘制绿色边框(表示边框正确)和红色三角形(表示三角形正确识别)。 - 如果三角形检测失败,则绘制红色边框(表示该边框内部三角形未被识别)或者同保留绿色边框(表示边框检测正确)再在内部绘制一个红色的轮廓(比如在四边形内部再画一个稍小的四边形)? 但是,我们也可以改变边框的颜色来表示状态: - 边框为绿色:表示边框和三角形都识别成功。 - 边框为红色:表示边框识别成功,但内部三角形识别失败。 这样,通过边框颜色就可以知道内部三角形的识别状态。 因此,我们可以修改原始代码中绘制边框的部分: 在原始代码中,我们绘制边框是用绿色: cv2.drawContours(result, [approx], -1, (0, 255, 0), 2) 现在,我们根据三角形是否被检测到来改变边框颜色: if triangle is None: # 三角形检测失败 border_color = (0, 0, 255) # 红色边框 else: border_color = (0, 255, 0) # 绿色边框 cv2.drawContours(result, [approx], -1, border_color, 2) 这样,当三角形未被识别,整个黑色边框会变成红色,从而提示用户该区域内的三角形未被正确识别。 但是,这种方法只改变了边框颜色,可能不够明显。我们还可以在内部三角形位置添加文字提示。 另一种更直接的方法:在四边形内部(即三角形应该出现的位置)绘制一个特定的标记,比如一个红色的“X”或文字“Not Detected”。 考虑到简单性,我们采用改变边框颜色的方式。 修改后的代码片段: ```python # 原代码中检测三角形部分 triangle = detect_triangle_in_warped(warped) # 初始化边框颜色和三角形轮廓 border_color = (0, 255, 0) # 默认绿色 triangle_orig = None if triangle is None: border_color = (0, 0, 255) # 红色,表示三角形未识别 else: # 将三角形轮廓映射回原始图像 triangle_orig = perspective_transform(triangle, rect_points, warped.shape) # 在原图上绘制边框 result = frame.copy() cv2.drawContours(result, [approx], -1, border_color, 2) # 如果检测到三角形,绘制三角形 if triangle_orig is not None: cv2.drawContours(result, [triangle_orig], -1, (0, 0, 255), 2) ``` 此外,我们还可以在图像上添加文字标签: ```python if triangle is None: border_color = (0, 0, 255) # 在边框中心位置添加文字 M = cv2.moments(approx) if M['m00'] != 0: cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00']) cv2.putText(result, "Triangle Not Detected", (cx-100, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2) ``` 这样,当三角形未被识别,我们会在黑色边框的中心位置显示红色文字提示。 总结:我们通过以下方式对未被正确识别的三角形进行描边处理: - 将检测到的黑色边框的轮廓颜色改为红色(原本是绿色) - 在黑色边框的中心位置添加文字提示 注意:这里我们并没有直接对三角形本身描边(因为未识别到三角形轮廓),而是通过改变其外部边框的颜色和添加文字来提示。 如果希望更明显地标记整个三角形区域,由于未识别到三角形,我们无法知道其精确轮廓,因此只能标记整个四边形内部区域。我们可以用半透明填充整个四边形区域: ```python if triangle is None: # 创建原始图像的拷贝,用于绘制半透明填充 overlay = result.copy() cv2.fillPoly(overlay, [approx], (0, 0, 255)) # 叠加半透明层 result = cv2.addWeighted(overlay, 0.2, result, 0.8, 0) # 绘制边框 cv2.drawContours(result, [approx], -1, (0,0,255), 2) # 添加文字 # ... 同上 ``` 这样,未被正确识别的区域会被半透明红色填充,边框为红色,同还有文字提示。 请根据实际需求选择合适的方式。 下面给出修改后的完整函数(包含半透明填充和文字提示):</think># 图像处理中未识别三角形的轮廓描边处理 在图像处理中,对未被正确识别的三角形进行轮廓描边显示是调试和可视化的重要步骤。根据您提供的引用[2]中描边实现的思路,结合OpenCV图像处理技术,以下是完整的解决方案: ## 核心实现思路 1. **双通道处理**:类似UnityShader的双Pass渲染,我们使用两次轮廓检测 2. **轮廓扩张**:对未识别三角形进行轮廓扩张,实现描边效果 3. **颜色区分**:使用不同颜色区分正确识别和未识别的三角形 ```python import cv2 import numpy as np def highlight_unrecognized_triangles(frame, recognized_triangles): """ 对未被正确识别的三角形进行轮廓描边处理 参数: frame: 原始图像(BGR格式) recognized_triangles: 已识别三角形的轮廓列表 返回: 带有描边效果的图像 """ # 创建原始图像的拷贝 result = frame.copy() # 转换为灰度图并二值化 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV) # 查找所有可能的三角形轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) all_triangles = [] for cnt in contours: area = cv2.contourArea(cnt) if area < 100: # 忽略小轮廓 continue peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) if len(approx) == 3: # 三角形 all_triangles.append(approx) # 找出未识别的三角形 unrecognized = [t for t in all_triangles if not is_recognized(t, recognized_triangles)] # 对未识别的三角形进行描边处理 for triangle in unrecognized: # 方法1: 轮廓扩张描边 (类似UnityShader的背面扩张) dilated = dilate_contour(triangle, 5) cv2.drawContours(result, [dilated], -1, (0, 0, 255), 2) # 红色描边 # 方法2: 半透明填充 mask = np.zeros_like(gray) cv2.drawContours(mask, [triangle], -1, 255, -1) overlay = result.copy() overlay[mask == 255] = (0, 0, 255) # 红色填充 result = cv2.addWeighted(overlay, 0.3, result, 0.7, 0) return result def dilate_contour(contour, dilation_size): """轮廓扩张函数,实现描边效果""" # 创建空白图像 mask = np.zeros((1000, 1000), dtype=np.uint8) # 假设最大尺寸 # 绘制原始轮廓 cv2.drawContours(mask, [contour], -1, 255, 1) # 创建膨胀核 kernel = np.ones((dilation_size*2+1, dilation_size*2+1), np.uint8) # 膨胀操作 dilated = cv2.dilate(mask, kernel, iterations=1) # 查找膨胀后的轮廓 contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: # 返回最大轮廓(即扩张后的轮廓) return max(contours, key=cv2.contourArea) return contour def is_recognized(triangle, recognized_list, threshold=10.0): """检查三角形是否在已识别列表中""" for rec_tri in recognized_list: # 计算两个三角形中心的距离 M1 = cv2.moments(triangle) M2 = cv2.moments(rec_tri) if M1['m00'] == 0 or M2['m00'] == 0: continue cx1 = int(M1['m10']/M1['m00']) cy1 = int(M1['m01']/M1['m00']) cx2 = int(M2['m10']/M2['m00']) cy2 = int(M2['m01']/M2['m00']) distance = np.sqrt((cx1-cx2)**2 + (cy1-cy2)**2) if distance < threshold: return True return False ``` ## 完整使用示例 ```python # 主处理流程 def main_processing(frame): # 步骤1: 识别旋转黑框 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2) # 查找轮廓 contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 识别四边形 quads = [] for cnt in contours: peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) if len(approx) == 4: quads.append(approx) # 识别三角形 recognized_triangles = [] for quad in quads: # 透视变换矫正 warped = four_point_transform(gray, quad.reshape(4, 2)) # 检测三角形 triangle = detect_triangle_in_warped(warped) if triangle is not None: # 将三角形映射回原始图像 triangle_orig = perspective_transform(triangle, quad.reshape(4, 2), warped.shape) recognized_triangles.append(triangle_orig) # 对未识别三角形进行描边处理 result = highlight_unrecognized_triangles(frame, recognized_triangles) # 绘制已识别的三角形(绿色) for tri in recognized_triangles: cv2.drawContours(result, [tri], -1, (0, 255, 0), 2) return result # 辅助函数(透视变换等) def four_point_transform(image, pts): # 实现透视变换 rect = order_points(pts) (tl, tr, br, bl) = rect 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(rect, dst) warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warped def detect_triangle_in_warped(warped): """在矫正后的图像中检测三角形""" thresh = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 15, 3) contours, _ = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: area = cv2.contourArea(cnt) if area < 100: continue peri = cv2.arcLength(cnt, True) approx = cv2.approxPolyDP(cnt, 0.02 * peri, True) if len(approx) == 3: # 三角形 return approx return None ``` ## 描边技术详解 ### 1. 轮廓扩张法(核心方法) 基于引用[2]的思路,通过膨胀操作实现描边效果: ```python def dilate_contour(contour, dilation_size): """轮廓扩张函数,实现描边效果""" # 创建空白图像 mask = np.zeros((1000, 1000), dtype=np.uint8) # 绘制原始轮廓 cv2.drawContours(mask, [contour], -1, 255, 1) # 创建膨胀核 kernel = np.ones((dilation_size*2+1, dilation_size*2+1), np.uint8) # 膨胀操作 dilated = cv2.dilate(mask, kernel, iterations=1) # 查找膨胀后的轮廓 contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if contours: return max(contours, key=cv2.contourArea) return contour ``` 原理:通过在原始轮廓外部创建扩张区域,形成描边效果[^2]。 ### 2. 半透明填充法 ```python # 在highlight_unrecognized_triangles函数中 mask = np.zeros_like(gray) cv2.drawContours(mask, [triangle], -1, 255, -1) # 创建三角形掩模 overlay = result.copy() overlay[mask == 255] = (0, 0, 255) # 红色填充 result = cv2.addWeighted(overlay, 0.3, result, 0.7, 0) # 半透明混合 ``` 效果:使用半透明红色填充未识别三角形区域,同保留原始图像内容。 ### 3. 轮廓差异法 ```python def contour_difference(inner, outer): """计算两个轮廓的差异区域""" mask1 = np.zeros((1000, 1000), np.uint8) mask2 = np.zeros((1000, 1000), np.uint8) cv2.drawContours(mask1, [outer], -1, 255, -1) cv2.drawContours(mask2, [inner], -1, 255, -1) # 计算差异 diff = cv2.subtract(mask1, mask2) # 查找轮廓 contours, _ = cv2.findContours(diff, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) return contours ``` ## 应用场景与优化建议 ### 实际应用场景 1. **工业质检**:在自动化检测中标记未识别的零件 2. **机器人视觉**:为导航系统提供未识别障碍物的视觉反馈 3. **教育演示**:在计算机视觉教学中展示识别结果差异 ### 参数调优建议 1. **描边粗细**:通过调整`dilation_size`控制描边宽度 ```python # 根据图像尺寸动态调整描边大小 dilation_size = max(2, int(frame.shape[1] / 200)) ``` 2. **颜色选择**:使用明显的对比色 ```python # 使用HSV颜色空间选择高对比度颜色 hue = 0 # 0=红色, 60=黄色, 120=绿色 outline_color = tuple(cv2.cvtColor(np.uint8([[[hue, 255, 255]]]), cv2.COLOR_HSV2BGR)[0][0]) ``` 3. **动态阈值**:根据图像内容自适应调整 ```python # 基于局部对比度调整阈值 thresh = cv2.adaptiveThreshold( gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 21, int(np.mean(gray)/10) ) ``` ## 效果对比 | 识别状态 | 可视化效果 | 实现方法 | |---------|----------|---------| | 正确识别 | 绿色实线轮廓 | `cv2.drawContours(..., (0, 255, 0), 2)` | | 未识别 | 红色描边 | 轮廓扩张法 | | 未识别(强调) | 半透明红色填充 | 半透明混合 | | 部分识别 | 黄色虚线轮廓 | `cv2.drawContours(..., (0, 255, 255), 1, cv2.LINE_AA)` | 通过上述方法,您可以清晰地可视化图像中未被正确识别的三角形区域,显著提高调试效率和结果可视化效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值