fixed new sample cannot detect boxes due to filter

This commit is contained in:
unknown
2025-12-29 15:06:06 +08:00
parent 4f8165b04b
commit 55228413d4
10 changed files with 180 additions and 92 deletions
+3 -2
View File
@@ -1,7 +1,8 @@
# OrisysSDK API 文档 # OrisysSDK API 文档
本文件提供了 OrisysSDK 的各类方法 本文件提供了 OrisysSDK 的各类方法
## 使用方法
pip install .
--- ---
## `Sensor` 类别 ## `Sensor` 类别
### 描述 ### 描述
@@ -18,7 +19,7 @@
```python ```python
# Example 1: 用相机编号创建 # Example 1: 用相机编号创建
from xensesdk import Sensor from orisys import Sensor
from src.sensor import Sensor from src.sensor import Sensor
sensor = Sensor(0) sensor = Sensor(0)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

+7 -16
View File
@@ -7,10 +7,10 @@ import time
def main(): def main():
# 方式1: 从视频文件读取 # 方式1: 从视频文件读取
sensor = orisys.Sensor("./s1.mp4", isstitch=True, cuda=True, verbose=True) # sensor = orisys.Sensor("./s1.mp4", isstitch=True, cuda=True, verbose=True)
# 方式2: 从摄像头读取(取消注释使用) # 方式2: 从摄像头读取(取消注释使用)
# sensor = orisys.Sensor(0, isstitch=True, cuda=True, verbose=True) sensor = orisys.Sensor(0, isstitch=True, cuda=True, verbose=True)
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器") print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
@@ -36,34 +36,25 @@ def main():
) )
cv2.imshow("形变场 Deformation", arrows) cv2.imshow("形变场 Deformation", arrows)
mag_img = orisys.util.draw_magnitude_map(
sensor.img.copy(),
vnormal,
threshold=5,
colormap=cv2.COLORMAP_HOT,
downsample_factor = 1
)
cv2.imshow("magnitude map", mag_img)
# 计算触碰点 # 计算触碰点
contact_result = sensor.get_contact(min_magnitude=1, expansion_threshold=0.01,denoise_sigma=15) sensor.compute_contact(min_magnitude=0.1,expansion_threshold=0.01,denoise_sigma=15)
# 在图像上触碰点和区域 # 在图像上触碰点和区域
image_with_foe = orisys.util.draw_foe(sensor.img.copy(), contact_result) image_with_foe = orisys.util.draw_contact(sensor.img.copy(), sensor.contour, sensor.centroid)
cv2.imshow("Contact Centroid", image_with_foe) cv2.imshow("Contact Centroid", image_with_foe)
# 计算触碰区域 # 计算触碰区域
divergence = contact_result['divergence_map'] depth_map = sensor.depth_map
# 归一化散度值到0-255范围(使用绝对值,然后归一化) # 归一化散度值到0-255范围(使用绝对值,然后归一化)
div_abs = divergence div_abs = depth_map
if div_abs.max() > div_abs.min(): if div_abs.max() > div_abs.min():
div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8) div_normalized = ((div_abs - div_abs.min()) / (div_abs.max() - div_abs.min()) * 255).astype(np.uint8)
else: else:
div_normalized = np.zeros_like(div_abs, dtype=np.uint8) div_normalized = np.zeros_like(div_abs, dtype=np.uint8)
# 应用颜色映射(使用JET:蓝色=低散度,红色=高散度) # 应用颜色映射(使用JET:蓝色=低散度,红色=高散度)
cv2.imshow("Divergence Map", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET)) cv2.imshow("Depth Map", cv2.applyColorMap(div_normalized, cv2.COLORMAP_JET))
print(f"fps: {fps}") print(f"fps: {fps}")
+1 -3
View File
@@ -4,6 +4,4 @@ opencv_python>=4.10.0.84
opencv-contrib-python opencv-contrib-python
scipy>=1.10.1 scipy>=1.10.1
setuptools>=45.2.0 setuptools>=45.2.0
PyQt5>=5.15.0
pyqtgraph>=0.12.0
PyInstaller>=5.0
+11 -5
View File
@@ -8,9 +8,14 @@ class Stitcher:
def __init__(self,isstitch=False, n=[3,3], index=np.array([1,2,3,6,7,8,11,12,13])): def __init__(self,isstitch=False, n=[3,3], index=np.array([1,2,3,6,7,8,11,12,13])):
# Settings # Settings
self.isstitch = isstitch self.isstitch = isstitch
# Find Boxes Related
self.min_area_frac = 0.01
self.max_area_frac = 0.05
self.max_aspect_ratio = 100
# stitch related # stitch related
self.stitch_valid = False self.stitch_valid = False
self.boxes = self.units = None self.boxes = self.units = None
@@ -21,6 +26,7 @@ class Stitcher:
self.nominal_ov_pos=100 self.nominal_ov_pos=100
self.margin=10 self.margin=10
self.bound=self.nominal_ov_pos - self.margin self.bound=self.nominal_ov_pos - self.margin
# for sensor 1 # for sensor 1
self.pos_default = [(260, 56), (577, 62), (897, 69), (1214, 75), (1530, 81), (256, 391), (570, 397), (889, 403), (1208, 409), (1523, 416), (248, 724), (563, 730), (882, 738), (1201, 743), (1516, 751)] self.pos_default = [(260, 56), (577, 62), (897, 69), (1214, 75), (1530, 81), (256, 391), (570, 397), (889, 403), (1208, 409), (1523, 416), (248, 724), (563, 730), (882, 738), (1201, 743), (1516, 751)]
@@ -542,7 +548,7 @@ class Stitcher:
def __filter_boxes_by_size(self, boxes, img_shape, min_area_frac=0.018, max_area_frac=0.035): def __filter_boxes_by_size(self, boxes, img_shape):
""" """
Remove boxes that are too small or too large relative to the image. Remove boxes that are too small or too large relative to the image.
- boxes: list of (x,y,w,h) - boxes: list of (x,y,w,h)
@@ -552,12 +558,12 @@ class Stitcher:
""" """
h, w = img_shape h, w = img_shape
img_area = h * w img_area = h * w
min_area = img_area * float(min_area_frac) min_area = img_area * float(self.min_area_frac)
max_area = img_area * float(max_area_frac) max_area = img_area * float(self.max_area_frac)
filtered = [] filtered = []
for (x,y,ww,hh) in boxes: for (x,y,ww,hh) in boxes:
area = ww * hh area = ww * hh
if area < min_area or area > max_area or (abs(ww - hh) > 20): if area < min_area or area > max_area or (abs(ww - hh) > self.max_aspect_ratio):
continue continue
filtered.append((x,y,ww,hh)) filtered.append((x,y,ww,hh))
return filtered return filtered
+13
View File
@@ -9,6 +9,19 @@
"resolution_stitch": [400, 400], "resolution_stitch": [400, 400],
"index": [1,2,3,6,7,8,11,12,13], "index": [1,2,3,6,7,8,11,12,13],
"pos_default": [[260, 56], [577, 62], [897, 69], [1214, 75], [1530, 81], [256, 391], [570, 397], [889, 403], [1208, 409], [1523, 416], [248, 724], [563, 730], [882, 738], [1201, 743], [1516, 751]] "pos_default": [[260, 56], [577, 62], [897, 69], [1214, 75], [1530, 81], [256, 391], [570, 397], [889, 403], [1208, 409], [1523, 416], [248, 724], [563, 730], [882, 738], [1201, 743], [1516, 751]]
},
"stitcher": {
"min_area_frac": 0.018,
"max_area_frac": 0.035,
"max_aspect_ratio": 20
},
"contact": {
"min_contour_area": 3000,
"min_magnitude": 0.02,
"expansion_threshold": 0.01,
"downsample_factor": 1,
"kernel_size": [5,5],
"denoise_sigma": 15
} }
} }
+23
View File
@@ -0,0 +1,23 @@
{
"name": "prototype",
"version": "1.0",
"camera": {
"resolution_raw": [1920, 1080],
"fps": 60,
"units": 15,
"units_used": 9,
"resolution_stitch": [400, 400],
"index": [1,2,3,6,7,8,11,12,13],
"pos_default": [[260, 56], [577, 62], [897, 69], [1214, 75], [1530, 81], [256, 391], [570, 397], [889, 403], [1208, 409], [1523, 416], [248, 724], [563, 730], [882, 738], [1201, 743], [1516, 751]]
},
"contact": {
"min_contour_area": 3000,
"min_magnitude": 1.0,
"expansion_threshold": 0.01,
"downsample_factor": 1,
"kernel_size": [5,5],
"denoise_sigma": 15
}
}
+58 -32
View File
@@ -12,6 +12,41 @@ except ImportError:
# 缓存形态学操作的核,避免重复创建 # 缓存形态学操作的核,避免重复创建
_morphology_kernels = {} _morphology_kernels = {}
def _create_foe_result(expansion_mask, contraction_mask, divergence_map,
centroid=None, centroid_found=False, contour=None,
all_contours=None):
"""
创建标准化的FOE结果字典,统一所有函数的返回格式。
Args:
expansion_mask: 扩张区域掩码
contraction_mask: 收缩区域掩码
divergence_map: 散度图
centroid: 质心坐标,可以是单个元组 (x, y) 或列表 [(x1, y1), (x2, y2), ...]
centroid_found: 是否找到质心
contour: 轮廓,可以是单个轮廓或轮廓列表
all_contours: 所有轮廓(仅在return_all_contours=True时使用)
Returns:
dict: 标准化的FOE结果字典
"""
result = {
'expansion_mask': expansion_mask,
'contraction_mask': contraction_mask,
'divergence_map': divergence_map,
'centroid': centroid if centroid is not None else [0,0],
'centroid_found': centroid_found,
}
# 可选字段
if contour is not None:
result['contour'] = contour
if all_contours is not None:
result['_all_contours'] = all_contours
return result
def compute_foe(flow_field, min_magnitude=1.0, expansion_threshold=1, downsample_factor=1, return_all_contours=False, kernel_size=(5,5), denoise_sigma=10): def compute_foe(flow_field, min_magnitude=1.0, expansion_threshold=1, downsample_factor=1, return_all_contours=False, kernel_size=(5,5), denoise_sigma=10):
""" """
计算光流场的扩张焦点(Focus of Expansion)。 计算光流场的扩张焦点(Focus of Expansion)。
@@ -73,20 +108,15 @@ def compute_foe(flow_field, min_magnitude=1.0, expansion_threshold=1, downsample
divergence = cv2.GaussianBlur(divergence, (0, 0), denoise_sigma) divergence = cv2.GaussianBlur(divergence, (0, 0), denoise_sigma)
# 优化:使用原地操作 # 优化:使用原地操作
np.where(divergence > 0.02, divergence, 0, out=divergence) np.where(divergence > min_magnitude, divergence, 0, out=divergence)
# 创建掩码(使用原地操作优化) # 创建掩码(使用原地操作优化)
valid_mask = magnitude > min_magnitude valid_mask = magnitude > min_magnitude
expansion_mask = (valid_mask & (divergence > expansion_threshold)) expansion_mask = (valid_mask & (divergence > expansion_threshold))
contraction_mask = (valid_mask & (divergence < -expansion_threshold)) contraction_mask = (valid_mask & (divergence < -expansion_threshold))
result = { # 初始化结果字典
'expansion_mask': expansion_mask, result = _create_foe_result(expansion_mask, contraction_mask, divergence)
'contraction_mask': contraction_mask,
'divergence_map': divergence,
'foe_points': [0, 0],
'is_foe_found': False
}
# 寻找扩张区域轮廓 # 寻找扩张区域轮廓
# 优化:缓存形态学操作的核 # 优化:缓存形态学操作的核
@@ -100,16 +130,16 @@ def compute_foe(flow_field, min_magnitude=1.0, expansion_threshold=1, downsample
contours, _ = cv2.findContours(expansion_binary_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours, _ = cv2.findContours(expansion_binary_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours: if contours:
result['is_foe_found'] = True result['centroid_found'] = True
if return_all_contours: if return_all_contours:
result['_all_contours'] = contours result['_all_contours'] = contours
largest_contour = max(contours, key=cv2.contourArea) largest_contour = max(contours, key=cv2.contourArea)
result['expansion_region'] = largest_contour result['contour'] = largest_contour
M = cv2.moments(largest_contour) M = cv2.moments(largest_contour)
if M["m00"] > 0: if M["m00"] > 0:
result['foe_point'] = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) result['centroid'] = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
return result return result
@@ -206,7 +236,7 @@ def compute_foe_gpu(flow_field, min_magnitude=1.0, expansion_threshold=1, downsa
# 优化:在GPU上完成掩码操作,减少数据传输 # 优化:在GPU上完成掩码操作,减少数据传输
# CuPy的where不支持out参数,使用赋值代替 # CuPy的where不支持out参数,使用赋值代替
divergence_gpu = cp.where(divergence_gpu > 0.02, divergence_gpu, 0) divergence_gpu = cp.where(divergence_gpu > min_magnitude, divergence_gpu, 0)
# 在GPU上创建掩码 # 在GPU上创建掩码
valid_mask_gpu = magnitude_gpu > min_magnitude valid_mask_gpu = magnitude_gpu > min_magnitude
@@ -219,13 +249,8 @@ def compute_foe_gpu(flow_field, min_magnitude=1.0, expansion_threshold=1, downsa
expansion_mask = cp.asnumpy(expansion_mask_gpu) expansion_mask = cp.asnumpy(expansion_mask_gpu)
contraction_mask = cp.asnumpy(contraction_mask_gpu) contraction_mask = cp.asnumpy(contraction_mask_gpu)
result = { # 初始化结果字典
'expansion_mask': expansion_mask, result = _create_foe_result(expansion_mask, contraction_mask, divergence)
'contraction_mask': contraction_mask,
'divergence_map': divergence,
'foe_points': [0, 0],
'is_foe_found': False
}
# 寻找扩张区域轮廓(在CPU上进行,因为OpenCV的轮廓检测需要CPU) # 寻找扩张区域轮廓(在CPU上进行,因为OpenCV的轮廓检测需要CPU)
# 优化:缓存形态学操作的核 # 优化:缓存形态学操作的核
@@ -239,16 +264,16 @@ def compute_foe_gpu(flow_field, min_magnitude=1.0, expansion_threshold=1, downsa
contours, _ = cv2.findContours(expansion_binary_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contours, _ = cv2.findContours(expansion_binary_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if contours: if contours:
result['is_foe_found'] = True result['centroid_found'] = True
if return_all_contours: if return_all_contours:
result['_all_contours'] = contours result['_all_contours'] = contours
largest_contour = max(contours, key=cv2.contourArea) largest_contour = max(contours, key=cv2.contourArea)
result['expansion_region'] = largest_contour result['contour'] = largest_contour
M = cv2.moments(largest_contour) M = cv2.moments(largest_contour)
if M["m00"] > 0: if M["m00"] > 0:
result['foe_point'] = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])) result['centroid'] = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
return result return result
@@ -275,7 +300,7 @@ def compute_multiple_foe(flow_field, min_magnitude=1.0, expansion_threshold=0.00
- 'divergence_map': 散度图 - 'divergence_map': 散度图
""" """
# 调用 compute_foe 获取基础数据和所有轮廓 # 调用 compute_foe 获取基础数据和所有轮廓
foe_result = compute_foe( foe_result = compute_foe_gpu(
flow_field, flow_field,
min_magnitude=min_magnitude, min_magnitude=min_magnitude,
expansion_threshold=expansion_threshold, expansion_threshold=expansion_threshold,
@@ -286,13 +311,12 @@ def compute_multiple_foe(flow_field, min_magnitude=1.0, expansion_threshold=0.00
) )
# 构建多焦点结果,复用基础数据 # 构建多焦点结果,复用基础数据
result = { result = _create_foe_result(
'expansion_mask': foe_result['expansion_mask'], expansion_mask=foe_result['expansion_mask'],
'contraction_mask': foe_result['contraction_mask'], contraction_mask=foe_result['contraction_mask'],
'divergence_map': foe_result['divergence_map'], divergence_map=foe_result['divergence_map'],
'foe_points': [], centroid_found=foe_result['centroid_found']
'expansion_regions': [] )
}
# 处理所有轮廓,过滤并计算焦点 # 处理所有轮廓,过滤并计算焦点
if '_all_contours' in foe_result: if '_all_contours' in foe_result:
@@ -304,7 +328,9 @@ def compute_multiple_foe(flow_field, min_magnitude=1.0, expansion_threshold=0.00
if M["m00"] > 0: if M["m00"] > 0:
cx = int(M["m10"] / M["m00"]) cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"]) cy = int(M["m01"] / M["m00"])
result['foe_points'].append((cx, cy)) result['centroid'].append((cx, cy))
result['expansion_regions'].append(contour) if 'contour' not in result:
result['contour'] = []
result['contour'].append(contour)
return result return result
+53 -21
View File
@@ -45,6 +45,8 @@ class Sensor:
FNORMAL = auto() FNORMAL = auto()
FSHEARX = auto() FSHEARX = auto()
FSHEARY = auto() FSHEARY = auto()
CENTROID = auto()
CONTOUR = auto()
DEPTH = auto() DEPTH = auto()
# ========================================================================= # =========================================================================
@@ -91,15 +93,22 @@ class Sensor:
self.vupdate = False self.vupdate = False
self.stitch_calibration = [] self.stitch_calibration = []
# Data variables # Data Variables
self.img = None self.img = None
self.img_size_raw = None
self.img_filtered = None self.img_filtered = None
self.flow = None self.flow = None
self.vnormal = None self.vnormal = None
self.shear = None self.vshear = None
self.depth = None # self.depth = None
self.n_boxes = None self.n_boxes = None
self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0 self.fnormal = self.fshearx = self.fsheary = self.ftor = 0.0
# Contact Data Variables
self.centroid = None
self.centroid_found = False
self.contour = None
self.depth_map = None
# Timing # Timing
self.last_log_time = time.perf_counter() self.last_log_time = time.perf_counter()
@@ -116,7 +125,11 @@ class Sensor:
self._stop_event = None self._stop_event = None
# Load configuration # Load configuration
self.config_cam = None
self.config_stitcher = None
self.config_contact = None
self._load_config() self._load_config()
# Initialize components # Initialize components
self._stitcher = Stitcher(isstitch=isstitch, index=self.config_index) self._stitcher = Stitcher(isstitch=isstitch, index=self.config_index)
@@ -167,7 +180,8 @@ class Sensor:
if log_timing: t0 = time.perf_counter() if log_timing: t0 = time.perf_counter()
img = np.ascontiguousarray(self._stitcher.stitch_rt_optimized()) img = np.ascontiguousarray(self._stitcher.stitch_rt_optimized())
if log_timing: timings['stitch'] = (time.perf_counter() - t0) * 1000 if log_timing: timings['stitch'] = (time.perf_counter() - t0) * 1000
self.img_size_raw = img.shape
# Resize # Resize
img = cv2.resize(img, tuple(self.config_resolution_stitch), interpolation=cv2.INTER_CUBIC) img = cv2.resize(img, tuple(self.config_resolution_stitch), interpolation=cv2.INTER_CUBIC)
@@ -222,7 +236,7 @@ class Sensor:
# Get components # Get components
self.vnormal = self.hhd.get_normal() self.vnormal = self.hhd.get_normal()
self.shear = self.hhd.get_shear() self.vshear = self.hhd.get_shear()
# # Depth computation # # Depth computation
# if log_timing: t0 = time.perf_counter() # if log_timing: t0 = time.perf_counter()
@@ -244,21 +258,31 @@ class Sensor:
return self.flow return self.flow
def get_contact(self, multiple_foe=False, min_contour_area=3000, min_magnitude=1.0, expansion_threshold=0.01, downsample_factor=1, kernel_size=(5,5), denoise_sigma=15): def compute_contact(self, multiple_foe=False, min_contour_area=3000, min_magnitude=1.0, expansion_threshold=0.01, downsample_factor=1, kernel_size=(5,5), denoise_sigma=15):
if multiple_foe: if multiple_foe:
return compute_multiple_foe(self.vnormal, min_contour_area=min_contour_area, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma) result = compute_multiple_foe(self.vnormal, min_contour_area=min_contour_area, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma)
else:
if self.cuda: else:
return compute_foe_gpu(self.vnormal, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma) if self.cuda:
result = compute_foe_gpu(self.vnormal, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma)
else:
return compute_foe(self.vnormal, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma) else:
result = compute_foe(self.vnormal, min_magnitude=min_magnitude, expansion_threshold=expansion_threshold, downsample_factor=downsample_factor, kernel_size=kernel_size, denoise_sigma=denoise_sigma)
self.centroid = result['centroid']
self.centroid_found = result['centroid_found']
if 'contour' in result:
self.contour = result['contour']
self.depth_map = result['divergence_map']
return result
# def get_depth(self, denoise_sigma=15):
# return getFilteredDensity(self.vnormal, use_cuda=self.cuda)
# self.depth = getFilteredDensity(self.vnormal, use_cuda=self.cuda)
def get_depth(self, denoise_sigma=15):
return getFilteredDensity(self.vnormal, use_cuda=self.cuda)
self.depth = getFilteredDensity(self.vnormal, use_cuda=self.cuda)
def update(self): def update(self):
""" """
Convenience method: get_img + compute_deformation in one call. Convenience method: get_img + compute_deformation in one call.
@@ -270,7 +294,7 @@ class Sensor:
self.compute_deformation() self.compute_deformation()
return { return {
'img': self.img, 'flow': self.flow, 'img': self.img, 'flow': self.flow,
'normal': self.vnormal, 'shear': self.shear, 'depth': self.depth, 'normal': self.vnormal, 'shear': self.vshear, 'depth': self.depth,
'fps': self.fps, 'fnormal': self.fnormal, 'fps': self.fps, 'fnormal': self.fnormal,
'fshearx': self.fshearx, 'fsheary': self.fsheary, 'ftor': self.ftor, 'fshearx': self.fshearx, 'fsheary': self.fsheary, 'ftor': self.ftor,
} }
@@ -291,13 +315,15 @@ class Sensor:
mapping = { mapping = {
self.info.VRAW: lambda: self.flow if self.flow is not None else np.array([]), self.info.VRAW: lambda: self.flow if self.flow is not None else np.array([]),
self.info.VNORMAL: lambda: self.vnormal if self.vnormal is not None else np.array([]), self.info.VNORMAL: lambda: self.vnormal if self.vnormal is not None else np.array([]),
self.info.VSHEAR: lambda: self.shear if self.shear is not None else np.array([]), self.info.VSHEAR: lambda: self.vshear if self.vshear is not None else np.array([]),
self.info.FNORMAL: lambda: self.fnormal, self.info.FNORMAL: lambda: self.fnormal,
self.info.FSHEARX: lambda: self.fshearx, self.info.FSHEARX: lambda: self.fshearx,
self.info.FSHEARY: lambda: self.fsheary, self.info.FSHEARY: lambda: self.fsheary,
self.info.FPS: lambda: self.fps, self.info.FPS: lambda: self.fps,
self.info.TIMESTAMP: lambda: time.strftime('%H:%M:%S'), self.info.TIMESTAMP: lambda: time.strftime('%H:%M:%S'),
self.info.DEPTH: lambda: self.depth if self.depth is not None else np.array([]), self.info.DEPTH: lambda: self.depth_map if self.depth_map is not None else np.array([]),
self.info.CENTROID: lambda: self.centroid if self.centroid is not None else np.array([]),
self.info.CONTOUR: lambda: self.contour if self.contour is not None else np.array([]),
} }
return tuple(mapping.get(arg, lambda: None)() for arg in args if isinstance(arg, self.info)) return tuple(mapping.get(arg, lambda: None)() for arg in args if isinstance(arg, self.info))
@@ -431,6 +457,12 @@ class Sensor:
self.config_index = cam["index"] self.config_index = cam["index"]
self.config_pos_default = cam["pos_default"] self.config_pos_default = cam["pos_default"]
config_stitcher = self.config["stitcher"]
config_contact = self.config["contact"]
if self.verbose: if self.verbose:
print(f"Config: {self.config_name}") print(f"Config: {self.config_name}")
print(f" Resolution: {self.config_resolution_raw} → {self.config_resolution_stitch}") print(f" Resolution: {self.config_resolution_raw} → {self.config_resolution_stitch}")
@@ -579,7 +611,7 @@ class Sensor:
while not self._stop_event.is_set(): while not self._stop_event.is_set():
try: try:
img, flow = self._flow_queue.get(timeout=0.1) img, flow = self._flow_queue.get(timeout=0.1)
contact_result = self.get_contact(flow) contact_result = self.compute_contact(flow)
self._queue_put(self._result_queue, contact_result) self._queue_put(self._result_queue, contact_result)
except Empty: except Empty:
continue continue
+11 -13
View File
@@ -132,37 +132,35 @@ def draw_fps(image, fps):
return image return image
def draw_foe(image, foe_result, point_color=(0, 255, 0), region_color=(0, 255, 255)): def draw_contact(image, contour, centroid, point_color=(0, 255, 0), region_color=(0, 255, 255)):
""" """
在图像上绘制扩张焦点和扩张区域。 在图像上绘制扩张焦点和扩张区域。
Args: Args:
image: 背景图像 image: 背景图像
foe_result: compute_foe 函数的返回结果 contour: 轮廓
point_color: 焦点点的颜色 (BGR) centroid: 触碰点
region_color: 扩张区域轮廓的颜色 (BGR) point_color: 触碰点的颜色 (BGR)
region_color: 轮廓的颜色 (BGR)
Returns: Returns:
np.ndarray: 绘制了焦点和区域的图像 np.ndarray: 绘制了触碰点和轮廓的图像
""" """
image = image.copy() image = image.copy()
# 绘制扩张区域轮廓 # 绘制扩张区域轮廓
if 'expansion_region' in foe_result: if contour is not None:
cv2.drawContours(image, [foe_result['expansion_region']], -1, region_color, 2) cv2.drawContours(image, [contour], -1, region_color, 2)
# 绘制焦点点 # 绘制触碰点
if 'foe_point' in foe_result: if centroid is not None:
cx, cy = foe_result['foe_point'] cx, cy = centroid
cv2.circle(image, (cx, cy), 8, point_color, -1) cv2.circle(image, (cx, cy), 8, point_color, -1)
cv2.circle(image, (cx, cy), 10, point_color, 2) cv2.circle(image, (cx, cy), 10, point_color, 2)
cv2.putText(image, "FOE", (cx + 15, cy),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, point_color, 2)
return image return image
def draw_multiple_foe(image, foe_result, point_color=(0, 255, 0), def draw_multiple_foe(image, foe_result, point_color=(0, 255, 0),
region_color=(0, 255, 255), label_colors=None): region_color=(0, 255, 255), label_colors=None):
""" """