detect motion by capturing reference image

This commit is contained in:
unknown
2026-01-13 12:39:05 +08:00
parent 0923e7e451
commit f3f28c2c4c
3 changed files with 57 additions and 69 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

After

Width:  |  Height:  |  Size: 2.0 MiB

+13 -12
View File
@@ -14,15 +14,16 @@ os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0"
import cv2 import cv2
import orisys import orisys
import numpy as np import numpy as np
import time
def main(): def main():
# 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径) # 步骤1:创建传感器对象 (必要步骤),config_name为配置文件名(内置配置文件名)或路径(自定义配置文件路径)
# 方式1: 从视频文件读取 # 方式1: 从视频文件读取
sensor = orisys.Sensor("s1.mp4", config_name="prototype", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True) # sensor = orisys.Sensor("s1.mp4", config_name="prototype", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
# 方式2: 从摄像头读取(取消注释使用) # 方式2: 从摄像头读取(取消注释使用)
# sensor = orisys.Sensor(0, config_name="prototype", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True) sensor = orisys.Sensor(0, config_name="./config/lens_test.json", cal_path= "./config/cal.npy", isstitch=True, cuda=True, verbose=True)
print("\n按 'q' 退出程序,按 'r' 重置光流追踪器") print("\n按 'q' 退出程序,按 'r' 重置光流追踪器")
@@ -32,14 +33,14 @@ def main():
# 步骤 2.1: 获取并拼接图像 (必要步骤) # 步骤 2.1: 获取并拼接图像 (必要步骤)
sensor.get_img() sensor.get_img()
print("img_size_raw:", sensor.img_size_raw) print("img_size_raw:", sensor.img_size_raw)
if sensor.detect_motion(): if sensor.detect_motion(threshold=2):
print("motion detected") print("motion detected")
break # 步骤 2.2: 计算形变场 (必要步骤)
# 步骤 2.2: 计算形变场 (必要步骤) sensor.compute_deformation()
sensor.compute_deformation() # 步骤 2.3: 计算触碰点
# 步骤 2.3: 计算触碰点 sensor.compute_contact()
sensor.compute_contact()
#步骤 2.4: 读取信息 #步骤 2.4: 读取信息
fps, flow, vnormal, img, contour, centroid, depth_map = sensor.read_info( fps, flow, vnormal, img, contour, centroid, depth_map = sensor.read_info(
sensor.info.FPS, sensor.info.FPS,
@@ -79,7 +80,7 @@ def main():
#步骤 3.4: 打印资料 #步骤 3.4: 打印资料
print(f"fps: {fps}") print(f"fps: {fps}")
#步骤 3.5: 键盘控制 #步骤 3.5: 键盘控制
key = cv2.waitKey(1) & 0xFF key = cv2.waitKey(1) & 0xFF
if key == ord("q"): if key == ord("q"):
@@ -88,8 +89,8 @@ def main():
if key == ord("r"): if key == ord("r"):
# 重置光流追踪器(当追踪漂移时使用) # 重置光流追踪器(当追踪漂移时使用)
sensor.reset() sensor.reset()
print("光流追踪器已重置") print("传感器已重置")
# ========================================================================= # =========================================================================
# 4. 清理资源 # 4. 清理资源
# ========================================================================= # =========================================================================
+44 -57
View File
@@ -86,6 +86,13 @@ class Sensor:
self.cal_path = cal_path self.cal_path = cal_path
self.mirror = mirror self.mirror = mirror
# Load configuration
self.__config_cam = None
self.__config_stitcher = None
self.__config_contact = None
self._load_config()
# State variables # State variables
self.stitch_valid = False self.stitch_valid = False
self.stitch_calibration = [] self.stitch_calibration = []
@@ -93,28 +100,29 @@ class Sensor:
self.compute_contact_valid = False self.compute_contact_valid = False
# Data Variables (public) # Data Variables (public)
shape = (self.__config_cam["resolution_stitch"][1], self.__config_cam["resolution_stitch"][0])
self.img = None self.img = None
self.img_size_raw = None self.img_size_raw = None
self.img_filtered = None self.img_filtered = None
self.prev_img = None self.initial_img = None
self.flow = None self.flow = np.zeros(shape + (2,), dtype=np.float32)
self.vnormal = None self.vnormal = np.zeros_like(self.flow)
self.vshear = None self.vshear = np.zeros_like(self.flow)
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 (public) # Contact Data Variables (public)
self.centroid = None self.centroid = (0, 0)
self.centroid_found = False self.centroid_found = False
self.contour = None self.contour = np.empty(0)
self.depth_map = None self.depth_map = np.zeros_like(self.img)
# Timing # Timing
self.last_log_time = time.perf_counter() self.last_log_time = time.perf_counter()
self._fps = 0 self._fps = 0
# Async pipeline state # Async pipeline state
self._async_enabled = False # self._async_enabled = False
self._capture_thread = None self._capture_thread = None
self._flow_thread = None self._flow_thread = None
self._compute_thread = None self._compute_thread = None
@@ -123,11 +131,6 @@ class Sensor:
self._result_queue = None self._result_queue = None
self._stop_event = None self._stop_event = None
# Load configuration
self.__config_cam = None
self.__config_stitcher = None
self.__config_contact = None
self._load_config()
# Initialize components # Initialize components
@@ -150,7 +153,7 @@ class Sensor:
# Public API - Core Methods (Most Frequently Used) # Public API - Core Methods (Most Frequently Used)
# ========================================================================= # =========================================================================
def get_img(self, log_timing=False): def get_img(self):
""" """
Capture and preprocess a frame. Capture and preprocess a frame.
@@ -162,59 +165,36 @@ class Sensor:
print("Failed to get_img from video source.") print("Failed to get_img from video source.")
return None return None
if log_timing:
timings = {}
t_start = time.perf_counter()
# Convert to grayscale # Convert to grayscale
if log_timing: t0 = time.perf_counter()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# gray = frame
if log_timing: timings['cvt_color'] = (time.perf_counter() - t0) * 1000
# Extract and stitch tiles # Extract and stitch tiles
if log_timing: t0 = time.perf_counter()
self.stitcher.get_stitch_unit(gray) self.stitcher.get_stitch_unit(gray)
self._extract_tiles(gray) self._extract_tiles(gray)
if log_timing: timings['get_tiles'] = (time.perf_counter() - t0) * 1000
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
self.img_size_raw = img.shape 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)
# # Temporal filtering (noise reduction)
# alpha = 0.7
# if self.img_filtered is None:
# self.img_filtered = img.astype(np.float32)
# else:
# self.img_filtered = alpha * img + (1 - alpha) * self.img_filtered
# Update previous image
if self.mirror: if self.mirror:
img = cv2.flip(img, 1) img = cv2.flip(img, 1)
if self.img is not None:
self.prev_img = self.img.copy()
self.img = img.astype(np.uint8) self.img = img.astype(np.uint8)
if self.prev_img is None:
self.prev_img = img.copy()
if log_timing:
timings['total'] = (time.perf_counter() - t_start) * 1000
self._print_timings("get_img", timings)
# FPS calculation
self._fps = 1.0 / (time.perf_counter() - self.last_log_time)
self.last_log_time = time.perf_counter()
return self.img.copy() return self.img.copy()
def detect_motion(self, threshold=10): def detect_motion(self, threshold=4):
if self.prev_img is None: if self.initial_img is None:
return False return False
diff = cv2.absdiff(self.img, self.prev_img) diff = cv2.absdiff(self.img, self.initial_img)
return diff.mean() > threshold return diff.mean() > threshold
def compute_deformation(self): def compute_deformation(self):
@@ -229,9 +209,6 @@ class Sensor:
self.__compute_flow() self.__compute_flow()
self.__compute_hhd() self.__compute_hhd()
# FPS calculation
self._fps = 1.0 / (time.perf_counter() - self.last_log_time)
self.last_log_time = time.perf_counter()
return True return True
@@ -322,11 +299,13 @@ class Sensor:
if any(arg in [self.info.VRAW, self.info.VNORMAL, self.info.VSHEAR, self.info.FNORMAL, self.info.FSHEARX, self.info.FSHEARY] for arg in args): if any(arg in [self.info.VRAW, self.info.VNORMAL, self.info.VSHEAR, self.info.FNORMAL, self.info.FSHEARX, self.info.FSHEARY] for arg in args):
if not self.compute_deformation_valid: if not self.compute_deformation_valid:
raise Exception("Warning: Compute deformation first") print("Warning: Compute deformation first")
# raise Exception("Warning: Compute deformation first")
if any(arg in [self.info.CENTROID, self.info.CONTOUR, self.info.DEPTH] for arg in args): if any(arg in [self.info.CENTROID, self.info.CONTOUR, self.info.DEPTH] for arg in args):
if not self.compute_contact_valid: if not self.compute_contact_valid:
raise Exception("Warning: Compute contact first") print("Warning: Compute contact first")
# raise Exception("Warning: Compute contact first")
mapping = { mapping = {
# Return copies of arrays to prevent external modification of internal state # Return copies of arrays to prevent external modification of internal state
@@ -357,11 +336,12 @@ class Sensor:
def reset(self): def reset(self):
"""Reset optical flow tracker.""" """Reset optical flow tracker."""
self.__tracker.reset() self.__tracker.reset()
self.initial_img = self.img.copy()
def disconnect(self): def disconnect(self):
"""Release resources and stop async threads.""" """Release resources and stop async threads."""
if self._async_enabled: # if self._async_enabled:
self.disable_async() # self.disable_async()
if self.cap.isOpened(): if self.cap.isOpened():
self.cap.release() self.cap.release()
if self.verbose: if self.verbose:
@@ -414,11 +394,12 @@ class Sensor:
def _load_config(self): def _load_config(self):
"""Load sensor configuration from JSON file.""" """Load sensor configuration from JSON file."""
self._config = configs.load_config(self.config_name) self._config = configs.load_config(self.config_name)
cam = self._config["camera"] self.__config_cam = self._config["camera"]
self._config_resolution_raw = cam["resolution_raw"] self._config_resolution_raw = self.__config_cam["resolution_raw"]
self._config_fps = cam["fps"] self._config_fps = self.__config_cam["fps"]
self._config_units = cam["units"] self._config_units = self.__config_cam["units"]
self._config_resolution_stitch = cam["resolution_stitch"] self._config_resolution_stitch = self.__config_cam["resolution_stitch"]
self._config_dis_preset = self._config["tracker"]["dis_preset"] self._config_dis_preset = self._config["tracker"]["dis_preset"]
self.__config_stitcher = self._config["stitcher"] self.__config_stitcher = self._config["stitcher"]
@@ -471,7 +452,13 @@ class Sensor:
else: else:
self._find_stitch_boxes(frame) self._find_stitch_boxes(frame)
self._extract_tiles(frame) self._extract_tiles(frame)
self.stitch_cal() img = self.stitch_cal()
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
self.initial_img = cv2.resize(img, tuple(self._config_resolution_stitch), interpolation=cv2.INTER_CUBIC)
if self.mirror:
self.initial_img = cv2.flip(self.initial_img, 1)
self.initial_img = self.initial_img.astype(np.uint8)
def _find_stitch_boxes(self, img_src, show=False): def _find_stitch_boxes(self, img_src, show=False):
"""Detect vision tile boxes with camera stabilization.""" """Detect vision tile boxes with camera stabilization."""