16 changed files with 0 additions and 532 deletions
Before Width: | Height: | Size: 380 KiB |
Before Width: | Height: | Size: 444 KiB |
Before Width: | Height: | Size: 287 KiB |
Before Width: | Height: | Size: 350 KiB |
@ -1,19 +0,0 @@ |
|||||
import json |
|
||||
import typing |
|
||||
from dataclasses import dataclass |
|
||||
from dataclasses_json import dataclass_json |
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class Msg: |
|
||||
_from: str |
|
||||
cmd: str |
|
||||
values: typing.Any |
|
||||
|
|
||||
def to_json_(self) -> str: |
|
||||
"""将数据类序列化为 JSON 字符串""" |
|
||||
return self.to_json() |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
@ -1,43 +0,0 @@ |
|||||
import json |
|
||||
from dataclasses import dataclass |
|
||||
from typing import List |
|
||||
|
|
||||
@dataclass |
|
||||
class SensorImg: |
|
||||
base64: str |
|
||||
|
|
||||
def to_json(self) -> str: |
|
||||
"""将数据类序列化为 JSON 字符串""" |
|
||||
return json.dumps(self.__dict__, indent=4, default=lambda x: x.__dict__) |
|
||||
|
|
||||
@classmethod |
|
||||
def from_json(cls, json_str: str) -> 'SensorImg': |
|
||||
"""从 JSON 字符串反序列化为数据类""" |
|
||||
data_dict = json.loads(json_str) |
|
||||
return cls(**data_dict) |
|
||||
@dataclass |
|
||||
class SensorData: |
|
||||
pos: str |
|
||||
x: float |
|
||||
y: float |
|
||||
|
|
||||
def to_json(self) -> str: |
|
||||
"""将数据类序列化为 JSON 字符串""" |
|
||||
return json.dumps(self.__dict__, indent=4, default=lambda x: x.__dict__) |
|
||||
|
|
||||
@classmethod |
|
||||
def from_json(cls, json_str: str) -> 'SensorData': |
|
||||
"""从 JSON 字符串反序列化为数据类""" |
|
||||
data_dict = json.loads(json_str) |
|
||||
return cls(**data_dict) |
|
||||
|
|
||||
@dataclass |
|
||||
class AllSensorData: |
|
||||
data: List[SensorData] |
|
||||
time: str |
|
||||
|
|
||||
|
|
||||
@dataclass |
|
||||
class AllImg: |
|
||||
image: SensorImg |
|
||||
time: str |
|
@ -1,112 +0,0 @@ |
|||||
from dataclasses import dataclass, field |
|
||||
from typing import Optional, Dict |
|
||||
|
|
||||
import numpy as np |
|
||||
from dataclasses_json import dataclass_json, config |
|
||||
|
|
||||
import utils |
|
||||
|
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class Point: |
|
||||
x:float |
|
||||
y:float |
|
||||
def __iter__(self): # 使对象可迭代,可直接转为元组 |
|
||||
yield self.x |
|
||||
yield self.y |
|
||||
|
|
||||
@dataclass |
|
||||
class RectangleArea: |
|
||||
x: int |
|
||||
y: int |
|
||||
w: int |
|
||||
h: int |
|
||||
@classmethod |
|
||||
def from_dict(cls, data: dict): |
|
||||
return cls( |
|
||||
x=data['x'], |
|
||||
y=data['y'], |
|
||||
w=data['w'], |
|
||||
h = data['h']) |
|
||||
|
|
||||
@dataclass |
|
||||
class Threshold: |
|
||||
binary: int |
|
||||
gauss: int |
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class TargetInfo: |
|
||||
# 标靶方形区域 |
|
||||
rectangle_area:RectangleArea |
|
||||
threshold:Threshold |
|
||||
# 标靶物理半径 |
|
||||
radius:float=0.0 |
|
||||
id:int =-1 |
|
||||
desc:str="" |
|
||||
base:bool=False |
|
||||
def __init__(self,id,desc,rectangle_area:RectangleArea,radius,threshold:Threshold,base:bool,**kwargs): |
|
||||
self.id = id |
|
||||
self.desc = desc |
|
||||
self.rectangle_area=rectangle_area |
|
||||
self.radius=radius |
|
||||
self.threshold=threshold |
|
||||
self.base=base |
|
||||
|
|
||||
@classmethod |
|
||||
def from_dict(cls,data: dict): |
|
||||
return cls(data['id'],data['rectangle_area'],data['radius']) |
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class HandlerInfo: |
|
||||
# 初始话 |
|
||||
is_init=True |
|
||||
radius_pix:float= 1.0 |
|
||||
pix_length:float=0.0 |
|
||||
# 标靶中心 |
|
||||
center_point: Optional[Point]= field(default=None, metadata=config(exclude=lambda x: x is None)) |
|
||||
center_init : Optional[Point]= field(default=None, metadata=config(exclude=lambda x: x is None)) |
|
||||
# 标靶位移(像素) |
|
||||
displacement_pix: Optional[Point]= field(default=None, metadata=config(exclude=lambda x: x is None)) |
|
||||
displacement_phy: Optional[Point]= field(default=None, metadata=config(exclude=lambda x: x is None)) |
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class CircleTarget: |
|
||||
# 标靶方形区域 |
|
||||
info:TargetInfo |
|
||||
# 标靶透视矩阵 |
|
||||
perspective: np.ndarray = field( |
|
||||
metadata=config( |
|
||||
encoder=utils.encode_perspective, |
|
||||
decoder=utils.decode_perspective |
|
||||
) |
|
||||
) |
|
||||
handler_info: Optional[HandlerInfo]=None |
|
||||
|
|
||||
|
|
||||
# def __init__(self,info:TargetInfo,center_point,center_init,displacement_pix,displacement_phy): |
|
||||
# self.info=info |
|
||||
# self.center_point=center_point |
|
||||
# self.center_init=center_init |
|
||||
# self. displacement_pix=displacement_pix |
|
||||
# self.displacement_phy=displacement_phy |
|
||||
|
|
||||
|
|
||||
@classmethod |
|
||||
def init_by_info(cls,t:TargetInfo): |
|
||||
return CircleTarget(t,None,None,None,None) |
|
||||
def circle_displacement(self): |
|
||||
previous = self.handler_info.center_init |
|
||||
if previous != (): |
|
||||
self.handler_info.displacement_pix = Point(self.handler_info.center_point.x - previous.x, |
|
||||
self.handler_info.center_point.y - previous.y) |
|
||||
if self.info.radius != 0: |
|
||||
# 单位像素距离 |
|
||||
self.handler_info.pix_length = self.info.radius / self.handler_info.radius_pix |
|
||||
offset_x = round(float(self.handler_info.displacement_pix.x * self.handler_info.pix_length), 5) |
|
||||
offset_y = round(float(self.handler_info.displacement_pix.y * self.handler_info.pix_length), 5) |
|
||||
self.handler_info.displacement_phy = Point(offset_x, offset_y) |
|
||||
return self |
|
@ -1,18 +0,0 @@ |
|||||
import signal |
|
||||
import sys |
|
||||
from dataclasses import dataclass, asdict |
|
||||
from datetime import datetime |
|
||||
|
|
||||
def signal_handler(sig, frame): |
|
||||
print(f"收到退出信号 sig={sig},程序退出") |
|
||||
sys.exit(0) |
|
||||
|
|
||||
signal.signal(signal.SIGINT, signal_handler) # 捕获 Ctrl+C 信号 |
|
||||
|
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
t=datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] |
|
||||
print(t) |
|
||||
while True: |
|
||||
# 程序主循环 |
|
||||
pass |
|
@ -1,29 +0,0 @@ |
|||||
import json |
|
||||
from dataclasses import asdict |
|
||||
|
|
||||
import configSer |
|
||||
|
|
||||
def test_load_config(): |
|
||||
config_path = "../config.json" |
|
||||
# 读取配置文件 |
|
||||
config: configSer.ConfigOperate = configSer.ConfigOperate(config_path) |
|
||||
json_str2 = config.config_info.to_json(indent=4) |
|
||||
print("json=",json_str2) |
|
||||
# 测试修改相机id |
|
||||
config.config_info.capture=1 |
|
||||
config.save2json_file() |
|
||||
# 更新配置文件 |
|
||||
updates = { |
|
||||
"capture": "rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream", |
|
||||
} |
|
||||
config.update_dict_config(updates) |
|
||||
|
|
||||
# 重新读取配置文件,确认更新 |
|
||||
updated_config = configSer.ConfigOperate(config_path) |
|
||||
print(f"当前新配置capture:{updated_config.capture}") |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
if __name__ == "__main__": |
|
||||
test_load_config() |
|
@ -1,64 +0,0 @@ |
|||||
import logging |
|
||||
from time import sleep |
|
||||
|
|
||||
import cv2 |
|
||||
print(cv2.__version__) |
|
||||
def open_video(video_id): |
|
||||
cap = cv2.VideoCapture(video_id) |
|
||||
if not cap.isOpened(): |
|
||||
logging.info("无法打开摄像头") |
|
||||
return cap |
|
||||
|
|
||||
class VideoProcessor: |
|
||||
capture: cv2.VideoCapture |
|
||||
|
|
||||
rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream" |
|
||||
print("开始读取2") |
|
||||
capture=open_video(2) |
|
||||
vp = VideoProcessor() |
|
||||
vp.capture = capture |
|
||||
fps = vp.capture .get(cv2.CAP_PROP_FPS) |
|
||||
width = int(vp.capture .get(cv2.CAP_PROP_FRAME_WIDTH)) |
|
||||
height = int(vp.capture .get(cv2.CAP_PROP_FRAME_HEIGHT)) |
|
||||
print(f"fps={fps}") |
|
||||
print("width={width}, height={height}".format(width=width, height=height)) |
|
||||
|
|
||||
cv2.namedWindow('frame') |
|
||||
i = 0 |
|
||||
while True: |
|
||||
ret, frame = vp.capture .read() |
|
||||
if ret: |
|
||||
print("-->") |
|
||||
cv2.imshow("frame", frame) |
|
||||
i=i+1 |
|
||||
if i>10: |
|
||||
break |
|
||||
# 写入帧到输出文件 |
|
||||
# out.write(frame) |
|
||||
else: |
|
||||
logging.info("无法读取图像帧") |
|
||||
break |
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环 |
|
||||
break |
|
||||
print("释放2") |
|
||||
vp.capture.release() |
|
||||
cv2.destroyAllWindows() |
|
||||
print("开始读取0") |
|
||||
sleep(2) |
|
||||
vp.capture = open_video(0) |
|
||||
# # 定义视频编 = open_video(0)码器和输出文件 |
|
||||
# fourcc = cv2.VideoWriter_fourcc(*'mp4v') |
|
||||
# out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height)) |
|
||||
while True: |
|
||||
ret, frame = vp.capture .read() |
|
||||
if ret: |
|
||||
cv2.imshow("frame", frame) |
|
||||
# 写入帧到输出文件 |
|
||||
# out.write(frame) |
|
||||
else: |
|
||||
logging.info("无法读取图像帧") |
|
||||
if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环 |
|
||||
break |
|
||||
|
|
||||
vp.capture.release() |
|
||||
# out.release() |
|
@ -1,32 +0,0 @@ |
|||||
from dataclasses import dataclass, field |
|
||||
from typing import Optional |
|
||||
|
|
||||
from dataclasses_json import dataclass_json, config |
|
||||
import json |
|
||||
|
|
||||
import models.target |
|
||||
|
|
||||
@dataclass_json |
|
||||
@dataclass |
|
||||
class Rectangle: |
|
||||
x: int |
|
||||
y: int |
|
||||
width: int |
|
||||
height: int |
|
||||
p: Optional[models.target.Point]= field(default=None, metadata=config(exclude=lambda x: x is None)) |
|
||||
|
|
||||
# 使用 |
|
||||
p=models.target.Point(1,2) |
|
||||
rect = Rectangle(0, 0, 100, 100,None) |
|
||||
# 序列化 |
|
||||
json_str=rect.to_json() |
|
||||
# json_str = json.dumps(rect, default=lambda obj: obj.__dict__) |
|
||||
print(f"序列化后={json_str}") |
|
||||
|
|
||||
rect.p.x=2046 |
|
||||
print("修改后p.x=",p.x) |
|
||||
|
|
||||
new_json='{"x": 1, "y": 1, "width": 100, "height": 100, "p": {"x": 2, "y": 2}}' |
|
||||
nr=Rectangle.from_json(new_json) |
|
||||
print(nr.p) |
|
||||
|
|
@ -1,73 +0,0 @@ |
|||||
import queue |
|
||||
import threading |
|
||||
import time |
|
||||
|
|
||||
import models.msg |
|
||||
from upload.RateLimiter import RateLimiter |
|
||||
|
|
||||
|
|
||||
class DataReporter(threading.Thread): |
|
||||
call_back=None |
|
||||
def __init__(self,data_fps:int,video_fps:int): |
|
||||
super().__init__() |
|
||||
self.image_queue = queue.Queue(maxsize=video_fps) # 图片队列 |
|
||||
self.data_queue = queue.Queue(maxsize=data_fps) # 数据队列 |
|
||||
self.image_limiter = RateLimiter(max_rate=video_fps, time_window=1) # 图片限速: 5张/秒 |
|
||||
self.data_limiter = RateLimiter(max_rate=data_fps, time_window=1) # 数据限速: 20条/秒 |
|
||||
self.running = True |
|
||||
self.image_dropped = 0 # 统计丢弃的图片数量 |
|
||||
self.data_dropped = 0 # 统计丢弃的数据数量 |
|
||||
|
|
||||
def register_handler(self,handler_fun): |
|
||||
self.call_back = handler_fun |
|
||||
|
|
||||
def run(self): |
|
||||
while self.running: |
|
||||
# 优先处理图片上报 |
|
||||
if not self.image_queue.empty() and self.image_limiter.allow_request(): |
|
||||
try: |
|
||||
image_data = self.image_queue.get_nowait() |
|
||||
self._report_image(image_data) |
|
||||
except queue.Empty: |
|
||||
pass |
|
||||
|
|
||||
# 然后处理数据上报 |
|
||||
if not self.data_queue.empty() and self.data_limiter.allow_request(): |
|
||||
try: |
|
||||
data = self.data_queue.get_nowait() |
|
||||
self._report_data(data) |
|
||||
except queue.Empty: |
|
||||
pass |
|
||||
|
|
||||
time.sleep(0.02) # 避免CPU占用过高 |
|
||||
|
|
||||
def _report_image(self, data): |
|
||||
# 实现图片上报逻辑 |
|
||||
print(f"Reporting image, timestamp: {data[0]}") |
|
||||
# 这里替换为实际的上报代码 |
|
||||
msg = models.msg.Msg(_from="dev", cmd="image", values=data[1]) |
|
||||
msg_json = msg.to_json() |
|
||||
self.call_back(msg_json) |
|
||||
|
|
||||
def _report_data(self, data): |
|
||||
# 实现数据上报逻辑 |
|
||||
print(f"Reporting data: {data}") |
|
||||
# 实际的上报代码,数据结构转换 |
|
||||
msg=models.msg.Msg(_from="dev",cmd="data",values=data[1]) |
|
||||
msg_json=msg.to_json() |
|
||||
self.call_back(msg_json) |
|
||||
|
|
||||
def stop(self): |
|
||||
self.running = False |
|
||||
self.join() |
|
||||
print(f"Stats: {self.image_dropped} images dropped, {self.data_dropped} data dropped") |
|
||||
|
|
||||
def adjust_rate(self, new_rate, data_type='image'): |
|
||||
if data_type == 'image': |
|
||||
with self.image_limiter.lock: |
|
||||
self.image_limiter.max_rate = new_rate |
|
||||
self.image_queue= queue.Queue(maxsize=new_rate) |
|
||||
else: |
|
||||
with self.data_limiter.lock: |
|
||||
self.data_limiter.max_rate = new_rate |
|
||||
self.data_queue = queue.Queue(maxsize=new_rate) |
|
@ -1,12 +0,0 @@ |
|||||
import queue |
|
||||
|
|
||||
|
|
||||
class DroppingQueue(queue.Queue): |
|
||||
"""自定义队列,满时自动丢弃最旧的数据""" |
|
||||
def put(self, item, block=False, timeout=None): |
|
||||
try: |
|
||||
return super().put(item, block=block, timeout=timeout) |
|
||||
except queue.Full: |
|
||||
# 队列满时丢弃最旧的一个数据 |
|
||||
self.get_nowait() |
|
||||
return super().put(item, block=False) |
|
@ -1,23 +0,0 @@ |
|||||
import threading |
|
||||
import queue |
|
||||
import time |
|
||||
from collections import deque |
|
||||
|
|
||||
class RateLimiter: |
|
||||
def __init__(self, max_rate, time_window): |
|
||||
self.max_rate = max_rate # 最大允许的请求数 |
|
||||
self.time_window = time_window # 时间窗口(秒) |
|
||||
self.timestamps = deque() |
|
||||
self.lock = threading.Lock() |
|
||||
|
|
||||
def allow_request(self): |
|
||||
with self.lock: |
|
||||
current_time = time.time() |
|
||||
# 移除超出时间窗口的时间戳 |
|
||||
while self.timestamps and current_time - self.timestamps[0] > self.time_window: |
|
||||
self.timestamps.popleft() |
|
||||
|
|
||||
if len(self.timestamps) < self.max_rate: |
|
||||
self.timestamps.append(current_time) |
|
||||
return True |
|
||||
return False |
|
@ -1,60 +0,0 @@ |
|||||
import cv2 |
|
||||
import numpy as np |
|
||||
|
|
||||
def pingyi(x,y): |
|
||||
# 获取图像的大小 |
|
||||
height, width = image.shape[:2] |
|
||||
|
|
||||
|
|
||||
# 构造平移矩阵:将原点移到图像中心 |
|
||||
M_translate = np.float32([ |
|
||||
[1, 0, x], |
|
||||
[0, 1, y], |
|
||||
[0, 0, 1] |
|
||||
]) |
|
||||
|
|
||||
return M_translate |
|
||||
def xuanzhuan_z(z): |
|
||||
# 构造沿Z轴旋转30度的旋转矩阵 |
|
||||
theta = np.radians(z) |
|
||||
cos_theta = np.cos(theta) |
|
||||
sin_theta = np.sin(theta) |
|
||||
M_z_rotate = np.float32([ |
|
||||
[cos_theta, -sin_theta, 0], |
|
||||
[sin_theta, cos_theta, 0], |
|
||||
[0, 0, 1] |
|
||||
]) |
|
||||
return M_z_rotate |
|
||||
def xuanzhuan_y(y): |
|
||||
# 构造沿Z轴旋转30度的旋转矩阵 |
|
||||
theta = np.radians(y) |
|
||||
rotation_vector = np.array([0, theta, 0]) # 绕Y轴旋转 |
|
||||
|
|
||||
# 计算旋转矩阵 |
|
||||
rotation_matrix, _ = cv2.Rodrigues(rotation_vector) |
|
||||
return rotation_matrix |
|
||||
if __name__ == '__main__': |
|
||||
# 读取图像 |
|
||||
image = cv2.imread("images/trans/transformed_image.jpg") |
|
||||
# 获取图像的大小 |
|
||||
height, width = image.shape[:2] |
|
||||
|
|
||||
m_pinyi=pingyi(width,0) |
|
||||
# m_xuanzhuan=xuanzhuan_z(30) |
|
||||
m_xuanzhuan = xuanzhuan_y(80) |
|
||||
M_combined = m_pinyi @ m_xuanzhuan |
|
||||
rotation_3d_int = np.clip(M_combined, 0, 255) |
|
||||
# 应用透视变换 |
|
||||
warped_image = cv2.warpPerspective( |
|
||||
image, |
|
||||
M_combined, |
|
||||
(width*2, height*2) |
|
||||
) |
|
||||
|
|
||||
# 显示结果 |
|
||||
cv2.imshow('Original Image', image) |
|
||||
cv2.imshow('Warped Image', warped_image) |
|
||||
cv2.waitKey(0) |
|
||||
cv2.destroyAllWindows() |
|
||||
|
|
||||
|
|
@ -1,47 +0,0 @@ |
|||||
import cv2 |
|
||||
import numpy as np |
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
def find_line(image): |
|
||||
if image is None: |
|
||||
print("Error: Unable to load image.") |
|
||||
exit() |
|
||||
gray_frame = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
||||
blurred_image = cv2.GaussianBlur(gray_frame, (9, 9), 0) |
|
||||
ret, gray_frame = cv2.threshold(blurred_image, 50, 255, cv2.THRESH_BINARY) |
|
||||
cv2.imshow("binary", gray_frame) |
|
||||
# edges = cv2.Canny(blurred_image, 50, 150, apertureSize=3) |
|
||||
# 应用边缘检测 |
|
||||
edges = cv2.Canny(gray_frame, 200, 400, apertureSize=3) |
|
||||
|
|
||||
# 使用标准霍夫变换检测直线 |
|
||||
# 使用标准霍夫变换检测直线 |
|
||||
lines = cv2.HoughLines(edges, rho=3, theta=np.pi / 180, threshold=200) |
|
||||
|
|
||||
# 绘制检测到的直线 |
|
||||
if lines is not None: |
|
||||
for line in lines: |
|
||||
rho, theta = line[0] |
|
||||
a = np.cos(theta) |
|
||||
b = np.sin(theta) |
|
||||
x0 = a * rho |
|
||||
y0 = b * rho |
|
||||
x1 = int(x0 + 1000 * (-b)) |
|
||||
y1 = int(y0 + 1000 * (a)) |
|
||||
x2 = int(x0 - 1000 * (-b)) |
|
||||
y2 = int(y0 - 1000 * (a)) |
|
||||
cv2.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2) |
|
||||
|
|
||||
|
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
# 读取图像 |
|
||||
img = cv2.imread("images/trans/subRawImg.jpg") |
|
||||
|
|
||||
find_line(img) |
|
||||
# 显示结果 |
|
||||
cv2.imshow("Detected Lines", img) |
|
||||
cv2.waitKey(0) |
|
||||
cv2.destroyAllWindows() |
|
Loading…
Reference in new issue