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

基于OpenCV-Python的相机帧动态分区与光斑自比对实现问询

基于OpenCV的动态光斑分区与跟踪方案

针对你需要根据视频中可变数量的固定位置光斑生成对应分区,并独立跟踪每个光斑变化的需求,以下是一套实用的实现思路和代码示例:

核心思路

因为光斑位置固定但会动态出现/消失,且每次视频光斑总数可变,所以需要先通过前期采样确定所有光斑的固定位置,再根据这些位置生成专属分区,最后在视频全程将每个光斑匹配到对应分区进行独立分析。

步骤1:采样视频提取所有光斑的固定位置

首先遍历视频前N帧(比如30帧,可根据光斑出现频率调整),用你已有的光斑检测方法提取每帧的光斑中心,然后对这些中心去重(合并距离极近的中心,视为同一个光斑的位置)。

import cv2
import numpy as np
from collections import defaultdict

# 替换成你已有的单帧光斑检测函数,返回(中心坐标(x,y), 宽度w, 高度h)
def detect_glows(frame):
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 200, 255, cv2.THRESH_BINARY)
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    glows = []
    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        center = (x + w//2, y + h//2)
        glows.append((center, w, h))
    return glows

# 初始化视频捕获(相机或本地视频)
cap = cv2.VideoCapture(0)  # 0为相机ID,替换为视频路径可处理本地文件
sample_frame_count = 30  # 采样帧数
all_glow_centers = []

# 采样前N帧收集光斑中心
for _ in range(sample_frame_count):
    ret, frame = cap.read()
    if not ret:
        break
    glows = detect_glows(frame)
    all_glow_centers.extend([center for center, _, _ in glows])

# 去重:合并距离小于阈值的中心(视为同一光斑)
distance_threshold = 20  # 可根据光斑大小调整
unique_centers = []
for center in all_glow_centers:
    if not unique_centers:
        unique_centers.append(center)
        continue
    # 计算当前中心与已有中心的最小距离
    min_dist = min(np.linalg.norm(np.array(center) - np.array(uc)) for uc in unique_centers)
    if min_dist > distance_threshold:
        unique_centers.append(center)

if not unique_centers:
    print("未检测到任何光斑")
    cap.release()
    exit()

步骤2:基于光斑位置动态生成专属分区

以每个去重后的光斑中心为原点,结合采样到的光斑最大尺寸生成足够大的正方形分区,确保每个分区仅容纳一个光斑,同时避免超出帧边界。

# 统计采样帧中光斑的最大尺寸(用于确定分区大小)
all_glow_sizes = []
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)  # 重置视频到开头
for _ in range(sample_frame_count):
    ret, frame = cap.read()
    if not ret:
        break
    glows = detect_glows(frame)
    all_glow_sizes.extend([max(w, h) for _, w, h in glows])
max_glow_size = max(all_glow_sizes) if all_glow_sizes else 50  # 默认尺寸

# 生成每个光斑的专属分区
regions = []
frame_h, frame_w = frame.shape[:2]
size_scale = 1.5  # 分区尺寸相对于光斑最大尺寸的倍数,确保完全覆盖光斑
for (cx, cy) in unique_centers:
    half_size = int(max_glow_size * size_scale // 2)
    # 确保分区不超出视频帧范围
    x1 = max(0, cx - half_size)
    y1 = max(0, cy - half_size)
    x2 = min(frame_w, cx + half_size)
    y2 = min(frame_h, cy + half_size)
    regions.append((x1, y1, x2, y2))

# 可选:可视化分区
for (x1, y1, x2, y2) in regions:
    cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow("Generated Regions", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()

步骤3:帧级分区匹配与光斑跟踪

重新遍历视频,将每帧检测到的光斑匹配到对应分区,独立记录每个分区内光斑的变化(如亮度、尺寸),未出现光斑时标记为None。

# 重置视频到开头,开始跟踪
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
# 记录每个分区的光斑历史数据
region_track_history = defaultdict(list)

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break
    glows = detect_glows(frame)
    # 提取当前帧光斑的中心、尺寸、平均亮度
    current_glows = []
    for (center, w, h) in glows:
        x, y = center
        # 计算光斑区域的平均亮度
        roi = frame[y - h//2:y + h//2, x - w//2:x + w//2]
        if roi.size == 0:
            brightness = 0
        else:
            gray_roi = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
            brightness = np.mean(gray_roi)
        current_glows.append({
            "center": center,
            "size": max(w, h),
            "brightness": brightness
        })
    
    # 匹配光斑到对应分区
    for region_idx, (x1, y1, x2, y2) in enumerate(regions):
        matched_glow = None
        # 找当前帧中落在该分区内的光斑
        for glow in current_glows:
            gx, gy = glow["center"]
            if x1 <= gx <= x2 and y1 <= gy <= y2:
                matched_glow = glow
                break  # 每个分区只匹配一个光斑
        # 记录数据
        if matched_glow:
            region_track_history[region_idx].append({
                "brightness": matched_glow["brightness"],
                "size": matched_glow["size"]
            })
        else:
            region_track_history[region_idx].append(None)
    
    # 实时可视化跟踪状态
    for region_idx, (x1, y1, x2, y2) in enumerate(regions):
        cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
        # 显示当前状态
        latest_data = region_track_history[region_idx][-1]
        if latest_data:
            text = f"R{region_idx}: B={int(latest_data['brightness'])} S={latest_data['size']}"
            cv2.putText(frame, text, (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,255,0), 2)
        else:
            cv2.putText(frame, f"R{region_idx}: No glow", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0,0,255), 2)
    cv2.imshow("Glow Tracking", frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

# 后续可分析region_track_history,比如绘制每个光斑的亮度变化曲线

关键调整点

  • 采样帧数:如果光斑出现频率低,可增加sample_frame_count确保覆盖所有光斑的出现场景。
  • 距离阈值:distance_threshold需根据实际光斑大小调整,避免误合并不同光斑。
  • 分区尺寸系数:size_scale可根据光斑的移动范围微调,确保光斑始终在分区内。
  • 光斑匹配逻辑:如果同一分区出现多个光斑,可改为匹配距离分区中心最近的光斑,提升准确性。

内容的提问来源于stack exchange,提问作者RawPasta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:37:01