23 changed files with 913 additions and 216 deletions
@ -0,0 +1,149 @@ |
|||
{ |
|||
"mac": "mac123", |
|||
"server": { |
|||
"port": 2230 |
|||
}, |
|||
"fps": { |
|||
"data": 5, |
|||
"video": 0 |
|||
}, |
|||
"alert": { |
|||
"enable": false, |
|||
"intervalSec": 6 |
|||
}, |
|||
"win": { |
|||
"enable": true, |
|||
"method": "median", |
|||
"size": 5, |
|||
"threshold": -0.1, |
|||
"imgThreshold": 10.0 |
|||
}, |
|||
"upload": { |
|||
"mqtt": { |
|||
"broker": "218.3.126.49", |
|||
"port": 1883, |
|||
"topic": "wybb/zj/mqtt179_debug", |
|||
"username": "", |
|||
"password": "", |
|||
"client_id": "wybb_zj_123_debug" |
|||
}, |
|||
"enable": true |
|||
}, |
|||
"capture": "rtsp://192.168.1.10:554/user=admin&password=&channel=1&stream=0.sdp?", |
|||
"targets": { |
|||
"101": { |
|||
"info": { |
|||
"rectangle_area": { |
|||
"x": 758, |
|||
"y": 1267, |
|||
"w": 111, |
|||
"h": 108 |
|||
}, |
|||
"threshold": { |
|||
"binary": 100, |
|||
"gauss": 1, |
|||
"gradient": 100, |
|||
"anchor": 80 |
|||
}, |
|||
"radius": 40.0, |
|||
"id": 101, |
|||
"desc": "T_1", |
|||
"base": false |
|||
}, |
|||
"perspective": [ |
|||
[ |
|||
1.0, |
|||
0.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
1.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
0.0, |
|||
1.0 |
|||
] |
|||
], |
|||
"handler_info": null |
|||
}, |
|||
"102": { |
|||
"info": { |
|||
"rectangle_area": { |
|||
"x": 2043, |
|||
"y": 1379, |
|||
"w": 140, |
|||
"h": 129 |
|||
}, |
|||
"threshold": { |
|||
"binary": 100, |
|||
"gauss": 1, |
|||
"gradient": 100, |
|||
"anchor": 80 |
|||
}, |
|||
"radius": 40.0, |
|||
"id": 102, |
|||
"desc": "T_2", |
|||
"base": false |
|||
}, |
|||
"perspective": [ |
|||
[ |
|||
1.0, |
|||
0.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
1.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
0.0, |
|||
1.0 |
|||
] |
|||
], |
|||
"handler_info": null |
|||
}, |
|||
"103": { |
|||
"info": { |
|||
"rectangle_area": { |
|||
"x": 2789, |
|||
"y": 1410, |
|||
"w": 152, |
|||
"h": 142 |
|||
}, |
|||
"threshold": { |
|||
"binary": 100, |
|||
"gauss": 1, |
|||
"gradient": 100, |
|||
"anchor": 80 |
|||
}, |
|||
"radius": 40.0, |
|||
"id": 103, |
|||
"desc": "T_3", |
|||
"base": false |
|||
}, |
|||
"perspective": [ |
|||
[ |
|||
1.0, |
|||
0.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
1.0, |
|||
0.0 |
|||
], |
|||
[ |
|||
0.0, |
|||
0.0, |
|||
1.0 |
|||
] |
|||
], |
|||
"handler_info": null |
|||
} |
|||
} |
|||
} |
|||
Binary file not shown.
@ -1,25 +1,105 @@ |
|||
{ |
|||
"mac": "", |
|||
"server": { |
|||
"port": 2230 |
|||
}, |
|||
"fps": { |
|||
"data": 30, |
|||
"data": 1, |
|||
"video": 0 |
|||
}, |
|||
"alert": { |
|||
"enable": false, |
|||
"intervalSec": 60 |
|||
"enable": true, |
|||
"intervalSec": 30 |
|||
}, |
|||
"upload": { |
|||
"win": { |
|||
"enable": true, |
|||
"method": "median", |
|||
"size": 20, |
|||
"threshold": -0.1, |
|||
"imgThreshold": 10.0 |
|||
}, |
|||
"upload": { |
|||
"mqtt": { |
|||
"broker": "218.3.126.49", |
|||
"port": 1883, |
|||
"topic": "wybb/mqtt", |
|||
"client_id": "wybb_lk" |
|||
} |
|||
"topic": "wybb/zj/mqtt110_debug", |
|||
"username": "", |
|||
"password": "", |
|||
"client_id": "wybb_debug" |
|||
}, |
|||
"enable": true |
|||
}, |
|||
"capture": "C:/Users/Administrator/Videos/1k.mp4", |
|||
"targets": { |
|||
"0": { |
|||
"info": { |
|||
"rectangle_area": { |
|||
"x": 524, |
|||
"y": 521, |
|||
"w": 50, |
|||
"h": 48 |
|||
}, |
|||
"threshold": { |
|||
"binary": 120, |
|||
"gauss": 1, |
|||
"gradient": 100, |
|||
"anchor": 80 |
|||
}, |
|||
"radius": 40.0, |
|||
"id": 0, |
|||
"desc": "bb_0", |
|||
"base": false |
|||
}, |
|||
"perspective": [], |
|||
"handler_info": { |
|||
"is_init": true, |
|||
"last_detected_time": 1766046547.513531, |
|||
"radius_pix": 16.498, |
|||
"pix_length": 2.4245363074312034, |
|||
"center_point": { |
|||
"x": 548.511, |
|||
"y": 543.337 |
|||
}, |
|||
"center_init": { |
|||
"x": 548.515, |
|||
"y": 543.271 |
|||
} |
|||
} |
|||
}, |
|||
"1": { |
|||
"info": { |
|||
"rectangle_area": { |
|||
"x": 729, |
|||
"y": 484, |
|||
"w": 68, |
|||
"h": 64 |
|||
}, |
|||
"threshold": { |
|||
"binary": 120, |
|||
"gauss": 1, |
|||
"gradient": 100, |
|||
"anchor": 80 |
|||
}, |
|||
"radius": 40.0, |
|||
"id": 1, |
|||
"desc": "bb_1", |
|||
"base": false |
|||
}, |
|||
"perspective": [], |
|||
"handler_info": { |
|||
"is_init": true, |
|||
"last_detected_time": 1766046547.513531, |
|||
"radius_pix": 17.024, |
|||
"pix_length": 2.3496240601503757, |
|||
"center_point": { |
|||
"x": 771.218, |
|||
"y": 515.245 |
|||
}, |
|||
"center_init": { |
|||
"x": 771.233, |
|||
"y": 515.17 |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,23 +1,9 @@ |
|||
from datetime import datetime |
|||
import sched |
|||
import time |
|||
from time import sleep |
|||
|
|||
|
|||
# 定义任务 |
|||
def print_event(name): |
|||
print(f"EVENT: {time.time()} - {name}") |
|||
def periodic_task(interval): |
|||
print(f"Task executed at: {datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-2]}") |
|||
scheduler.enter(interval, 1, periodic_task, (interval,)) |
|||
|
|||
def print_hi(name): |
|||
# 在下面的代码行中使用断点来调试脚本。 |
|||
print(f'Hi, {name}') # 按 Ctrl+F8 切换断点。 |
|||
|
|||
|
|||
# 按装订区域中的绿色按钮以运行脚本。 |
|||
if __name__ == '__main__': |
|||
# 初始化调度器 |
|||
scheduler = sched.scheduler(time.time) |
|||
scheduler.enter(0, 1, periodic_task, (0.33,)) # 每 5 秒执行一次 |
|||
scheduler.run() |
|||
print("===============") |
|||
print_hi('PyCharm') |
|||
|
|||
|
|||
Binary file not shown.
Binary file not shown.
@ -1,7 +1,8 @@ |
|||
numpy~=2.2.2 |
|||
matplotlib~=3.10.3 |
|||
dataclasses-json~=0.6.7 |
|||
flask~=3.1.0 |
|||
matplotlib~=3.10.3 |
|||
requests~=2.32.3 |
|||
opencv-python~=4.10 |
|||
scipy~=1.16.1 |
|||
paho-mqtt~=2.1.0 |
|||
requests~=2.32.3 |
|||
opencv-python~=4.10.0 |
|||
@ -0,0 +1,120 @@ |
|||
# smoothing_window_improved.py |
|||
import threading |
|||
from collections import deque |
|||
from typing import Dict, List, Tuple |
|||
import numpy as np |
|||
|
|||
from models.sampleMsg import AllSensorData, SensorData |
|||
|
|||
|
|||
class AdaptiveSmoothingWindow: |
|||
""" |
|||
一个自适应的数据平滑窗口。 |
|||
当数据波动较小时,使用原始数据;当波动较大时,使用移动平均值进行平滑。 |
|||
""" |
|||
|
|||
def __init__(self,method:str ="median", window_size: int = 11, volatility_threshold: float = 2.0,img_threshold: float = 20.0): |
|||
""" |
|||
初始化自适应平滑窗口。 |
|||
|
|||
:param window_size: 移动平均窗口的大小,默认为 11。 |
|||
:param volatility_threshold: 波动阈值。当新数据与上一个平滑值的差异超过此值时, |
|||
将触发平滑。默认为 2.0。 |
|||
""" |
|||
if window_size <= 0: |
|||
raise ValueError(f"窗口大小 'window_size={window_size}' 必须是正数。") |
|||
|
|||
self.method = method |
|||
self.window_size = window_size |
|||
self.volatility_threshold = volatility_threshold |
|||
self.img_Threshold = img_threshold |
|||
# 内部存储结构: {sensor_pos: deque of (x, y) tuples} |
|||
self.sensor_history: Dict[str, deque[Tuple[float, float]]] = {} |
|||
|
|||
# 存储每个传感器上一次输出的平滑值: {sensor_pos: (last_smoothed_x, last_smoothed_y)} |
|||
self._last_smoothed_values: Dict[str, Tuple[float, float]] = {} |
|||
self._lock = threading.Lock() |
|||
|
|||
def resize(self,method:str ="median", window_size: int = 11, volatility_threshold: float = 2.0,img_threshold: float = 20.0): |
|||
with self._lock: |
|||
self.method = method |
|||
self.window_size = window_size |
|||
self.volatility_threshold = volatility_threshold |
|||
self.img_Threshold = img_threshold |
|||
# 内部存储结构: {sensor_pos: deque of (x, y) tuples} |
|||
self.sensor_history: Dict[str, deque[Tuple[float, float]]] = {} |
|||
|
|||
# 存储每个传感器上一次输出的平滑值: {sensor_pos: (last_smoothed_x, last_smoothed_y)} |
|||
self._last_smoothed_values: Dict[str, Tuple[float, float]] = {} |
|||
|
|||
def process(self, new_sensor_data: AllSensorData) -> (AllSensorData,bool): |
|||
""" |
|||
处理一帧新的传感器数据,根据波动大小自适应地应用平滑。 |
|||
|
|||
:param new_sensor_data: 包含所有传感器最新数据的 AllSensorData 对象。 |
|||
:return: 一个新的 AllSensorData 对象,其中包含了自适应平滑后的数据。 |
|||
""" |
|||
smoothed_sensor_list: List[SensorData] = [] |
|||
is_abnormal=False |
|||
with self._lock: |
|||
for sensor in new_sensor_data.data: |
|||
pos = sensor.pos |
|||
|
|||
# 如果是新传感器,初始化其历史队列和上一个平滑值 |
|||
if pos not in self.sensor_history: |
|||
self.sensor_history[pos] = deque(maxlen=self.window_size) |
|||
# 首次出现,将当前值作为“上一个平滑值” |
|||
self._last_smoothed_values[pos] = (sensor.x, sensor.y) |
|||
|
|||
history_queue = self.sensor_history[pos] |
|||
history_queue.append((sensor.x, sensor.y)) |
|||
# print(f"====> {pos} 设备 滑窗数据 {len(history_queue)}/{self.window_size}") |
|||
last_x, last_y = self._last_smoothed_values[pos] |
|||
|
|||
# 决定当前使用哪个值作为输出 |
|||
current_output_x, current_output_y = sensor.x, sensor.y # 默认使用原始值 |
|||
|
|||
# 仅当窗口填满后,才考虑进行平滑 |
|||
if len(history_queue) == self.window_size: |
|||
# 计算窗口内的值 |
|||
xs, ys = zip(*history_queue) |
|||
# 初始化平滑值 |
|||
if self.method == "median": |
|||
sma_x = float(np.median(xs)) |
|||
sma_y = float(np.median(ys)) |
|||
else: # 默认使用均值 |
|||
sma_x = float(np.mean(xs)) |
|||
sma_y = float(np.mean(ys)) |
|||
|
|||
# 计算新原始数据与上一个平滑值之间的波动 |
|||
delta_x = abs(sensor.x - last_x) |
|||
delta_y = abs(sensor.y - last_y) |
|||
|
|||
# 如果 x 或 y 的波动超过阈值,则使用平滑值 |
|||
if delta_x >= self.volatility_threshold: |
|||
current_output_x = sma_x |
|||
if delta_y >= self.volatility_threshold: |
|||
current_output_y = sma_y |
|||
|
|||
#巨大变化 存储图片 |
|||
if delta_x >= self.img_Threshold: |
|||
is_abnormal = True |
|||
if delta_y >= self.img_Threshold: |
|||
is_abnormal = True |
|||
|
|||
# 创建新的 SensorData 对象 |
|||
smoothed_sensor = SensorData( |
|||
pos=pos, |
|||
desc=sensor.desc, |
|||
x=current_output_x, |
|||
y=current_output_y |
|||
) |
|||
smoothed_sensor_list.append(smoothed_sensor) |
|||
|
|||
# 更新“上一个平滑值”为本次的输出值 |
|||
self._last_smoothed_values[pos] = (current_output_x, current_output_y) |
|||
|
|||
return AllSensorData( |
|||
data=smoothed_sensor_list, |
|||
time=new_sensor_data.time |
|||
), is_abnormal |
|||
@ -0,0 +1,78 @@ |
|||
# smoothing_window.py |
|||
|
|||
from collections import deque |
|||
from typing import Dict, List, Tuple |
|||
import numpy as np # 使用 numpy 可以更高效地进行平均值计算 |
|||
|
|||
from models.sampleMsg import AllSensorData, SensorData |
|||
|
|||
|
|||
class SmoothingWindow: |
|||
""" |
|||
一个用于对传感器数据进行简单移动平均 (SMA) 平滑处理的窗口。 |
|||
""" |
|||
def __init__(self, window_size: int = 10): |
|||
""" |
|||
初始化平滑窗口。 |
|||
|
|||
:param window_size: 移动平均窗口的大小,默认为 10。 |
|||
""" |
|||
if window_size <= 0: |
|||
raise ValueError("窗口大小 'window_size' 必须是正数。") |
|||
|
|||
self.window_size = window_size |
|||
|
|||
# 内部存储结构: {sensor_pos: deque of (x, y) tuples} |
|||
# deque 会自动维护固定大小,当新元素加入而队列已满时,最老的元素会被移除 |
|||
self.sensor_history: Dict[str, deque[Tuple[float, float]]] = {} |
|||
|
|||
def process(self, all_sensor_data: AllSensorData) -> AllSensorData: |
|||
""" |
|||
处理一帧新的传感器数据,对每个传感器的 x, y 坐标应用移动平均平滑。 |
|||
|
|||
:param all_sensor_data: 包含所有传感器最新数据的 AllSensorData 对象。 |
|||
:return: 一个新的 AllSensorData 对象,其中包含了平滑后的数据。 |
|||
如果某个传感器的数据点少于窗口大小,则其值不会被平滑(保持原始值)。 |
|||
""" |
|||
smoothed_sensor_list: List[SensorData] = [] |
|||
|
|||
# 遍历当前帧中的每一个传感器数据 |
|||
for sensor in all_sensor_data.data: |
|||
# 如果是新传感器,为其创建一个新的 deque |
|||
if sensor.pos not in self.sensor_history: |
|||
self.sensor_history[sensor.pos] = deque(maxlen=self.window_size) |
|||
|
|||
# 获取该传感器的历史数据队列 |
|||
history_queue = self.sensor_history[sensor.pos] |
|||
|
|||
# 将新的 (x, y) 数据添加到队列末尾 |
|||
history_queue.append((sensor.x, sensor.y)) |
|||
|
|||
# 检查队列中的数据点数量是否达到了窗口大小 |
|||
if len(history_queue) == self.window_size: |
|||
# 如果达到窗口大小,则进行平滑计算 |
|||
|
|||
# 使用 numpy 将队列中的 x 和 y 值分别转换为数组,便于计算 |
|||
# history_queue 是一个 (x,y) 元组的列表,我们用 zip(*...) 来解包 |
|||
xs, ys = zip(*history_queue) |
|||
smoothed_x = np.mean(xs) |
|||
smoothed_y = np.mean(ys) |
|||
|
|||
# 创建一个新的 SensorData 对象,使用平滑后的值 |
|||
# 注意:desc 等其他字段保持不变 |
|||
smoothed_sensor = SensorData( |
|||
pos=sensor.pos, |
|||
desc=sensor.desc, |
|||
x=smoothed_x, |
|||
y=smoothed_y |
|||
) |
|||
smoothed_sensor_list.append(smoothed_sensor) |
|||
else: |
|||
# 如果未达到窗口大小,不进行平滑,直接返回原始数据 |
|||
smoothed_sensor_list.append(sensor) |
|||
|
|||
# 创建并返回一个包含所有平滑后数据的新 AllSensorData 对象 |
|||
return AllSensorData( |
|||
data=smoothed_sensor_list, |
|||
time=all_sensor_data.time # 时间戳保持不变 |
|||
) |
|||
@ -0,0 +1,10 @@ |
|||
from functools import cached_property |
|||
|
|||
from stabilize.algorithm.adaptiveSmoothingWindow import AdaptiveSmoothingWindow |
|||
|
|||
|
|||
class Win: |
|||
@staticmethod |
|||
def create_window(method,size, threshold,img_threshold): |
|||
print(f"开启稳定窗口method={method},size={size}, threshold={threshold},img_threshold={img_threshold}") |
|||
return AdaptiveSmoothingWindow(method,size, threshold,img_threshold) |
|||
@ -0,0 +1,23 @@ |
|||
import platform |
|||
import socket |
|||
import struct |
|||
|
|||
def get_mac_address(interface='eth0') -> str: |
|||
os_name = platform.system() |
|||
if os_name=="Windows": |
|||
return "mac_win_123" |
|||
else: |
|||
import fcntl |
|||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
|||
mac = fcntl.ioctl( |
|||
sock.fileno(), |
|||
0x8927, |
|||
struct.pack('256s', interface[:15].encode('utf-8')) |
|||
) |
|||
mac_address = ':'.join(['%02x' % b for b in mac[18:24]]) |
|||
return mac_address |
|||
|
|||
|
|||
if __name__ == '__main__': |
|||
mac_str=get_mac_address() |
|||
print(mac_str) |
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue