fixed new sample cannot detect boxes due to filter
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
|
||||||
|
|||||||
+10
-4
@@ -11,6 +11,11 @@ class Stitcher:
|
|||||||
# 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
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|||||||
+47
-15
@@ -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,16 +93,23 @@ 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()
|
||||||
self.fps = 0
|
self.fps = 0
|
||||||
@@ -116,8 +125,12 @@ 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)
|
||||||
self.tracker = Tracker(adaptive=True, cuda=cuda)
|
self.tracker = Tracker(adaptive=True, cuda=cuda)
|
||||||
@@ -168,6 +181,7 @@ class Sensor:
|
|||||||
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:
|
else:
|
||||||
if self.cuda:
|
if self.cuda:
|
||||||
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)
|
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:
|
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)
|
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):
|
# def get_depth(self, denoise_sigma=15):
|
||||||
return getFilteredDensity(self.vnormal, use_cuda=self.cuda)
|
# return getFilteredDensity(self.vnormal, use_cuda=self.cuda)
|
||||||
self.depth = 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
@@ -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):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user