You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于OpenCV的环形区域目标着陆位置判定技术问询

问题描述

我用顶置摄像头结合OpenCV实现了目标跟踪功能,当前需要判定目标的着陆位置。目标区域为一个大直径薄环形结构,被划分为72个5度扇区,每个扇区又分为内、中、外3个环带(A、B、C区),共216个着陆区域。通过目标轮廓中心点可获取着陆点的[x,y]像素坐标,需要输出“目标着陆于第__扇区,__区域”。

我曾尝试用嵌套字典存储每个扇区-环带对应的所有像素坐标,通过比对着陆点坐标来判定位置,但在绘制像素区域时出现重复点和漏点问题,尝试Bresenham算法也未解决。

相关说明

  • 程序为内部测试用,优先保证判定准确性,对性能要求不高;
  • 目标与摄像头位置固定;
  • 目标着陆后停止跟踪,随即判定位置。

我的疑问

  1. 存储像素坐标后比对的方案是否可行?
  2. 是否有更优方案?能否便捷定义216个环形扇区ROI?有无可用工具库?
  3. 如何精准无重复地逐像素绘制这些区域?

附像素绘制示例代码(非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的图形绘制函数即可高效解决:

  • 对于单个环形扇区,先生成闭合轮廓点:
    1. 生成内圆弧(半径r1,角度范围start_angle到end_angle)和外圆弧(半径r2,同角度范围)的点集
    2. 将内圆弧点反转,与外圆弧点拼接成闭合多边形顶点
    3. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:27:09