基于OpenCV的环形区域目标着陆位置判定技术问询
问题描述
我用顶置摄像头结合OpenCV实现了目标跟踪功能,当前需要判定目标的着陆位置。目标区域为一个大直径薄环形结构,被划分为72个5度扇区,每个扇区又分为内、中、外3个环带(A、B、C区),共216个着陆区域。通过目标轮廓中心点可获取着陆点的[x,y]像素坐标,需要输出“目标着陆于第__扇区,__区域”。
我曾尝试用嵌套字典存储每个扇区-环带对应的所有像素坐标,通过比对着陆点坐标来判定位置,但在绘制像素区域时出现重复点和漏点问题,尝试Bresenham算法也未解决。
相关说明
- 程序为内部测试用,优先保证判定准确性,对性能要求不高;
- 目标与摄像头位置固定;
- 目标着陆后停止跟踪,随即判定位置。
我的疑问
- 存储像素坐标后比对的方案是否可行?
- 是否有更优方案?能否便捷定义216个环形扇区ROI?有无可用工具库?
- 如何精准无重复地逐像素绘制这些区域?
附像素绘制示例代码(非Bresenham方法)
step_size = 0.1 angle_start = 32.5 angle_end = angle_start + 398.2 + step_size r_in_end = radius_inner + ring_thickness/2 r_out_in = r_out - ring_thickness print(f'Inner radius (float): {r_out_in} | Inner radius (int): {int(r_out_in)}') r_out_end = r_out print(f'Outer radius (float): {r_out_end} | Inner radius (int): {int(r_out_end)}') angle_value = np.arange(angle_start, angle_end, 0.2) # print(angle_value) # print(f'angle_value: {angle_value}') # print(f'angle_value size: {len(angle_value)}') section_length = r_out_end - r_out_in + 2 # Calculate the sizes of the outer for loop (height) # calculate the size of the inner for loop (width) # Initialize an empty array of the size needed (height X width) to fill width = int((r_out_end + 1.5 - r_out_in - .5)) height = len(angle_value) pixel_locations = np.zeros((width * height, 2), dtype=np.int32) # Intitialize variables for storing points in the array and checking repeats in the array index = 0 px_match = 0 prev_pt = (0, 0) # Nest the loops to color pixels at every point along the arc # for every step in the direction of the inner section radius to the outer section radius. for r in range(int(r_out_in+.5), int(r_out_end+1.5)): for angle in angle_value: # Convert angle to radians and rename as theta for understandibility # Calculate the x and y positions of the pixel along the arc length theta = math.radians(angle) x = int(center_loc[0] + math.sin(theta) * r) y = int(center_loc[1] + math.cos(theta) * r) # Store the x and y coords in the empty array # Increment the index pixel_locations[index] = [x, y] index += 1 # Error checking. We don't want a location to repeat if (x, y) == prev_pt: # print(f'The current position is:{x, y} | The previous position was: {prev_pt}') px_match += 1 # Draw the new point on the image to give visual check that the function is working # cv2.circle(overlay, (x, y), radius=1, color=(255, 0, 0), thickness=-1) overlay[y, x] = (255, 0, 0) # store the current location as the new previous for the next loop. prev_pt = (x, y) # Print out number of repeats. print(f'The number of overlapping pixels is: {px_match}') Target_transparent = cv2.addWeighted(overlay, alpha, TargetImg_Resized, 1 - alpha, 0)
解答
1. 存储像素坐标后比对的方案是否可行?
可行,但不推荐。环形扇区的像素量极大,存储所有坐标会占用大量内存,且比对时查找效率低。你遇到的重复点、漏点问题,本质是固定步长的采样逻辑缺陷——多个(r, angle)组合会对应同一个像素,同时部分像素会因步长设置被遗漏,即便调整步长也很难彻底解决,还会增加计算量。
2. 更优方案及ROI定义方法
有更高效的方案,无需预存像素坐标,通过实时计算着陆点的极坐标直接匹配扇区和环带:
- 步骤1:将[x,y]像素坐标转换为相对环形中心的极坐标(r, theta)
- 计算偏移量:
dx = x - center_x,dy = y - center_y - 半径r:
r = math.sqrt(dx**2 + dy**2) - 角度theta:用
math.atan2(dy, dx)计算,再转换为0-360度范围(注意和你的扇区起始方向对齐,可能需要调整偏移量)
- 计算偏移量:
- 步骤2:匹配环带
预先定义三个环带的半径区间(比如A区为[r_inner, r_mid),B区为[r_mid, r_outer_mid),C区为[r_outer_mid, r_outer]),直接比对r的数值即可判定所属环带。 - 步骤3:匹配扇区
每个扇区对应5度,用theta // 5(根据你的编号规则调整是否+1)即可得到扇区编号,比如theta=32度时,32//5=6,对应第7扇区。
这种方案计算量极小,准确性仅依赖环形中心坐标和半径区间的校准,完全适配你固定摄像头的场景。
若需可视化定义ROI,OpenCV可结合鼠标交互绘制,但手动处理216个区域不现实。可以用代码批量生成每个扇区-环带的轮廓点,再用cv2.fillPoly绘制,无需逐像素遍历。
3. 精准无重复地逐像素绘制区域
如果仅用于可视化验证,无需手动逐像素遍历,直接用OpenCV的图形绘制函数即可高效解决:
- 对于单个环形扇区,先生成闭合轮廓点:
- 生成内圆弧(半径r1,角度范围start_angle到end_angle)和外圆弧(半径r2,同角度范围)的点集
- 将内圆弧点反转,与外圆弧点拼接成闭合多边形顶点
- 用
cv2.fillPoly填充区域,OpenCV会自动精准覆盖所有像素,无重复或漏点。
示例代码片段:
import numpy as np import cv2 import math def draw_annular_sector(overlay, center, r_inner, r_outer, start_angle, end_angle, color): # 生成圆弧点,1度步长足够精准 angles = np.arange(start_angle, end_angle, 1) outer_points = [] inner_points = [] for angle in angles: theta = math.radians(angle) x_outer = int(center[0] + math.cos(theta) * r_outer) y_outer = int(center[1] + math.sin(theta) * r_outer) outer_points.append([x_outer, y_outer]) x_inner = int(center[0] + math.cos(theta) * r_inner) y_inner = int(center[1] + math.sin(theta) * r_inner) inner_points.append([x_inner, y_inner]) # 拼接成闭合多边形 inner_points.reverse() sector_points = np.array(outer_points + inner_points, np.int32) # 填充区域 cv2.fillPoly(overlay, [sector_points], color) # 调用示例:绘制第1扇区A区(中心(400,400),A区r_inner=100, r_outer=150,角度0-5度) overlay = np.zeros((800,800,3), dtype=np.uint8) draw_annular_sector(overlay, (400,400), 100, 150, 0, 5, (255,0,0))
内容的提问来源于stack exchange,提问作者Learning4Fun
相关产品推荐
相关产品推荐

