From b60af972b26e263bc981e8bc17e975393ee89972 Mon Sep 17 00:00:00 2001 From: lucas Date: Mon, 16 Jun 2025 16:05:27 +0800 Subject: [PATCH] =?UTF-8?q?update=20=20=E6=9B=B4=E6=96=B0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 12 +-- config.json | 76 ++++++++++++------- config2.json | 45 ------------ configSer.py | 61 +++++++++------- models/msg.py | 6 -- models/target.py | 57 +++++++++------ tcp_Ser.py | 21 ++++-- test/测试config.py | 6 +- test/测试opencv.py | 58 +++++++++++---- upload/DataReporter.py | 16 ++-- utils.py | 17 ++++- 标靶识别.py | 8 +- 标靶识别video.py | 162 +++++++++++++++++++++++++++-------------- 13 files changed, 321 insertions(+), 224 deletions(-) delete mode 100644 config2.json diff --git a/app.py b/app.py index 61598e3..42224e5 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,5 @@ +from time import sleep + import configSer import tcp_Ser import upload.DataReporter @@ -11,9 +13,9 @@ if __name__ == '__main__': json_str = config_obj.config_info.to_json(indent=4) print(f"当前配置:{json_str}") - tcp_service = tcp_Ser.TcpSer("127.0.0.1", config_obj.config_info.server.port) + tcp_service = tcp_Ser.TcpSer("0.0.0.0", config_obj.config_info.server.port) tcp_service.start() - reporter = upload.DataReporter.DataReporter() + reporter = upload.DataReporter.DataReporter(data_fps=config_obj.config_info.fps.data,video_fps=config_obj.config_info.fps.video) reporter.register_handler(tcp_service.broadcast_message) reporter.start() # 启动video @@ -22,10 +24,10 @@ if __name__ == '__main__': # 添加订阅者processor tcp_service.add_subscribe(processor) # 启动 - processor.video_mode(0) - - + processor.video_mode(config_obj.config_info.capture) + while True: + sleep(10) cv2.waitKey(0) cv2.destroyAllWindows() \ No newline at end of file diff --git a/config.json b/config.json index b8d0abe..4d97f33 100644 --- a/config.json +++ b/config.json @@ -2,46 +2,66 @@ "server": { "port": 2230 }, - "capture": 0, + "fps": { + "data": 10, + "video": 0 + }, + "capture": "0", "targets": { "0": { "info": { "rectangle_area": { "x": 50, - "y": 371, - "w": 113, - "h": 91 + "y": 230, + "w": 130, + "h": 100 }, "threshold": { - "binary": 120, - "gauss": 5 + "binary": 180, + "gauss": 7 }, - "radius_pix": 1, "radius": 20.0, - "pix_length": 0.0, "id": 0, - "desc": "0_biaoba", + "desc": "0_up", "base": false + }, + "perspective": [ + [ + 1.02, + -0.277155559, + 109.207622 + ], + [ + 0.0802663708, + 1.00426514, + -34.8436318 + ], + [ + 3.80200276e-05, + 2.664279e-06, + 1.0 + ] + ], + "handler_info": { + "radius_pix": 20.0, + "pix_length": 1.0, + "center_point": { + "x": 125.0, + "y": 272.0 + }, + "center_init": { + "x": 123.5, + "y": 272.0 + }, + "displacement_pix": { + "x": 1.5, + "y": 0.0 + }, + "displacement_phy": { + "x": 1.5, + "y": 0.0 + } } } - }, - "perspective": { - "0": [ - [ - 1.02161644, - -0.277155559, - 109.207622 - ], - [ - 0.0802663708, - 1.00426514, - -34.8436318 - ], - [ - 3.80200276e-05, - 2.664279e-06, - 1.0 - ] - ] } } \ No newline at end of file diff --git a/config2.json b/config2.json deleted file mode 100644 index 60b1feb..0000000 --- a/config2.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "server": { - "port": 2230 - }, - "capture": 0, - "targets": { - "0": { - "info": { - "rectangle_area": { - "x": 75, - "y": 310, - "w": 59, - "h": 55 - }, - "threshold": { - "binary": 128, - "gauss": 9 - }, - "radius": 20.0, - "id": 0, - "desc": "", - "base": false - } - } - }, - "perspective2": { - "0": [ - [ - 1.02161644, - -0.277155559, - 109.207622 - ], - [ - 0.0802663708, - 1.00426514, - -34.8436318 - ], - [ - 3.80200276e-05, - 2.664279e-06, - 1.0 - ] - ] - } -} \ No newline at end of file diff --git a/configSer.py b/configSer.py index bc11bde..1e3bb19 100644 --- a/configSer.py +++ b/configSer.py @@ -2,9 +2,9 @@ import json import os from dataclasses import ( dataclass, - field, asdict + field ) -from typing import Dict, Optional +from typing import Dict import numpy as np from dataclasses_json import dataclass_json @@ -19,15 +19,20 @@ _file_path: str class Server: port: int = 0 +@dataclass_json +@dataclass +class Fps: + data: int = 0 + video: int = 0 + @dataclass_json @dataclass class ConfigInfo: server:Server - capture: int = 0 + fps:Fps + capture: str = "0" # 标靶配置 targets: Dict[int, models.target.CircleTarget] = field(default_factory=dict) - # 标靶透视矩阵 - perspective: Dict[int, np.ndarray] = field(default_factory=dict) class ConfigOperate: _file_path: str @@ -46,29 +51,29 @@ class ConfigOperate: config = json.load(json_file) return config - def load2obj_sample2(self): - """"读取配置""" - dic=self.load2dict() - ts = dic["targets"] - capture = dic["capture"] - # 获取矩阵数据 - matrix_dict = dic.get("perspective", {}) - # n0=convert_to_ndarray(self.matrix_dict["0"]) - # 将矩阵转换为字符串 - # matrix_str = np.array2string(n0, precision=8, separator=', ', suppress_small=True) - for _,t in ts.items(): - obj = models.target.TargetInfo(**t) - area = models.target.RectangleArea.from_dict(obj.rectangle_area) - thres = models.target.Threshold(**obj.threshold) - self.targets[obj.id] = models.target.CircleTarget( - obj.id, - obj.desc, - area, - obj.radius, - thres, - obj.base - ) - return self.targets + # def load2obj_sample2(self): + # """"读取配置""" + # dic=self.load2dict() + # ts = dic["targets"] + # capture = dic["capture"] + # # 获取矩阵数据 + # matrix_dict = dic.get("perspective", {}) + # # n0=convert_to_ndarray(self.matrix_dict["0"]) + # # 将矩阵转换为字符串 + # # matrix_str = np.array2string(n0, precision=8, separator=', ', suppress_small=True) + # for _,t in ts.items(): + # obj = models.target.TargetInfo(**t) + # area = models.target.RectangleArea.from_dict(obj.rectangle_area) + # thres = models.target.Threshold(**obj.threshold) + # self.targets[obj.id] = models.target.CircleTarget( + # obj.id, + # obj.desc, + # area, + # obj.radius, + # thres, + # obj.base + # ) + # return self.targets def load2obj_sample(self): dic=self.load2dict() diff --git a/models/msg.py b/models/msg.py index 103ab68..c59606d 100644 --- a/models/msg.py +++ b/models/msg.py @@ -12,14 +12,8 @@ class Msg: def to_json_(self) -> str: """将数据类序列化为 JSON 字符串""" - # return json.dumps(self.__dict__, indent=4, default=lambda x: x.__dict__) return self.to_json() - # @classmethod - # def from_json(cls, json_str: str) -> 'Msg': - # """从 JSON 字符串反序列化为数据类""" - # data_dict = json.loads(json_str) - # return cls(**data_dict) diff --git a/models/target.py b/models/target.py index e55915e..4b62e00 100644 --- a/models/target.py +++ b/models/target.py @@ -1,8 +1,11 @@ from dataclasses import dataclass, field -from typing import Optional +from typing import Optional, Dict +import numpy as np from dataclasses_json import dataclass_json, config +import utils + @dataclass_json @dataclass @@ -38,10 +41,8 @@ class TargetInfo: # 标靶方形区域 rectangle_area:RectangleArea threshold:Threshold - radius_pix:float= 1 # 标靶物理半径 radius:float=0.0 - pix_length:float=0.0 id:int =-1 desc:str="" base:bool=False @@ -57,15 +58,13 @@ class TargetInfo: def from_dict(cls,data: dict): return cls(data['id'],data['rectangle_area'],data['radius']) - @dataclass_json @dataclass -class CircleTarget: - # 标靶方形区域 - info:TargetInfo - - # 初始标靶中心 +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)) @@ -73,25 +72,41 @@ class CircleTarget: 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)) - 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 +@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.center_init + previous = self.handler_info.center_init if previous != (): - self.displacement_pix = Point(self.center_point.x - previous.x, self.center_point.y - previous.y) + 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.info.pix_length = self.info.radius / self.info.radius_pix - offset_x = round(float(self.displacement_pix.x * self.info.pix_length), 5) - offset_y = round(float(self.displacement_pix.y * self.info.pix_length), 5) - self.displacement_phy = Point(offset_x, offset_y) + 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 \ No newline at end of file diff --git a/tcp_Ser.py b/tcp_Ser.py index 6972d4b..c9f678a 100644 --- a/tcp_Ser.py +++ b/tcp_Ser.py @@ -28,20 +28,30 @@ class TcpSer(threading.Thread): # 保持连接,直到客户端断开 while True: # 接收客户端数据(如果需要) - data = client_socket.recv(1024) + data = client_socket.recv(4096) msg_str=data.decode('utf-8') if not data: break # 如果没有数据,退出循环 print(f"从 {client_socket.getpeername()} 收到: {msg_str}") # 反序列化为 实例 s_cmd = Msg.from_json(msg_str) + valid_msg:bool=True match s_cmd.cmd: case "getPoints" | "setPoints": - self.on_data(s_cmd) + self.on_data(s_cmd,valid_msg) + case "videoFps"| "dataFps": + self.on_data(s_cmd,valid_msg) + case "setCap": + self.on_data(s_cmd,valid_msg) # todo 添加处理 - case "xxxxx": - self.on_data(s_cmd) + case _: + valid_msg = False + err_msg=f"valid cmd={s_cmd.cmd}" + resp=f"""{{"_from": "dev","cmd": "{s_cmd.cmd}","values": {{"operate": false,"err": "{err_msg}"}}}}""" + client_socket.sendall(resp.encode()) + print("非法命令",resp) + print("通讯完成") except Exception as e: print(f"处理客户端时出错: {e}") finally: @@ -58,7 +68,7 @@ class TcpSer(threading.Thread): self.consumers.append(consumer) else: print("consumer 缺少on_data函数,订阅无效 ") - def on_data(self,msg): + def on_data(self,msg,valid): for consumer in self.consumers: try: resp=consumer.on_data(msg) @@ -72,6 +82,7 @@ class TcpSer(threading.Thread): if len(message)==0: return + message+="\n\n" for client in self.connected_clients: try: client.sendall(message.encode()) diff --git a/test/测试config.py b/test/测试config.py index c90c190..7be1498 100644 --- a/test/测试config.py +++ b/test/测试config.py @@ -6,11 +6,11 @@ import configSer def test_load_config(): config_path = "../config.json" # 读取配置文件 - config = configSer.ConfigOperate(config_path) + config: configSer.ConfigOperate = configSer.ConfigOperate(config_path) json_str2 = config.config_info.to_json(indent=4) print("json=",json_str2) - config_dict = asdict(config) - config.capture=1 + # 测试修改相机id + config.config_info.capture=1 config.save2json_file() # 更新配置文件 updates = { diff --git a/test/测试opencv.py b/test/测试opencv.py index 9709023..86a2949 100644 --- a/test/测试opencv.py +++ b/test/测试opencv.py @@ -1,4 +1,5 @@ import logging +from time import sleep import cv2 print(cv2.__version__) @@ -6,33 +7,58 @@ def open_video(video_id): cap = cv2.VideoCapture(video_id) if not cap.isOpened(): logging.info("无法打开摄像头") - exit() return cap +class VideoProcessor: + capture: cv2.VideoCapture rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream" -capture = open_video(rtsp_url) -fps = capture.get(cv2.CAP_PROP_FPS) -width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) -height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) +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)) -# 定义视频编码器和输出文件 -fourcc = cv2.VideoWriter_fourcc(*'mp4v') -out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height)) -# 读取一帧图像 +cv2.namedWindow('frame') +i = 0 while True: - ret, frame = capture.read() - print("-->") - + 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) + # out.write(frame) else: - logging.info("无法读取帧") + logging.info("无法读取图像帧") if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环 break -capture.release() -out.release() +vp.capture.release() +# out.release() diff --git a/upload/DataReporter.py b/upload/DataReporter.py index a717da6..49746a1 100644 --- a/upload/DataReporter.py +++ b/upload/DataReporter.py @@ -8,12 +8,12 @@ from upload.RateLimiter import RateLimiter class DataReporter(threading.Thread): call_back=None - def __init__(self,): + def __init__(self,data_fps:int,video_fps:int): super().__init__() - self.image_queue = queue.Queue(maxsize=10) # 图片队列 - self.data_queue = queue.Queue(maxsize=50) # 数据队列 - self.image_limiter = RateLimiter(max_rate=1, time_window=1) # 图片限速: 5张/秒 - self.data_limiter = RateLimiter(max_rate=1, time_window=1) # 数据限速: 20条/秒 + 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 # 统计丢弃的数据数量 @@ -27,7 +27,7 @@ class DataReporter(threading.Thread): 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) + self._report_image(image_data) except queue.Empty: pass @@ -66,6 +66,8 @@ class DataReporter(threading.Thread): 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 \ No newline at end of file + self.data_limiter.max_rate = new_rate + self.data_queue = queue.Queue(maxsize=new_rate) \ No newline at end of file diff --git a/utils.py b/utils.py index 988239c..e1f5537 100644 --- a/utils.py +++ b/utils.py @@ -1,5 +1,11 @@ +from typing import Dict, List + import cv2 import base64 + +import numpy as np + + def frame_to_base64(frame, format="JPEG"): """将 OpenCV 读取的图片帧转换为 Base64 编码的字符串""" # 将图片帧编码为 JPEG 或 PNG 格式 @@ -16,4 +22,13 @@ def frame_to_base64(frame, format="JPEG"): # 将编码后的字节流转换为 Base64 字符串 base64_string = base64.b64encode(encoded_frame).decode("utf-8") - return base64_string \ No newline at end of file + return base64_string + + + +# 自定义编码器和解码器 +def encode_perspective(value: np.ndarray) -> List[List[float]]: + return value.tolist() + +def decode_perspective(value: List[List[float]]) -> np.ndarray: + return np.array(value) \ No newline at end of file diff --git a/标靶识别.py b/标靶识别.py index 1d94279..a292278 100644 --- a/标靶识别.py +++ b/标靶识别.py @@ -230,7 +230,9 @@ def image_mode(): show_image(img_raw) def rtsp_mode(): - rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554" + # rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554" + rtsp_url ="rtsp://localhost:8554/rtsp" + capture = open_video(rtsp_url) fps = capture.get(cv2.CAP_PROP_FPS) print(f"fps={fps}") @@ -242,8 +244,8 @@ def rtsp_mode(): if __name__ == '__main__': signal.signal(signal.SIGINT, check_exit) - # rtsp_mode() - video_mode(0) + rtsp_mode() + # video_mode(0) # image_mode() cv2.waitKey(0) diff --git a/标靶识别video.py b/标靶识别video.py index e3d4879..7e08770 100644 --- a/标靶识别video.py +++ b/标靶识别video.py @@ -1,20 +1,17 @@ +import gc from datetime import datetime import json import queue import time from time import sleep -from dataclasses import asdict import cv2 import numpy as np -import signal import sys -import threading import logging import configSer import models.target import models.sampleMsg -import tcp_Ser import upload.DataReporter import utils from models.msg import Msg @@ -59,9 +56,9 @@ def add_rectangle(event, x, y, flags, param): "test add", area, radius, - models.target.Threshold(128,9), + models.target.Threshold(190,9), False) - new_target = models.target.CircleTarget(t_info,None,None,None,None) + new_target = models.target.CircleTarget(t_info,None,None) logging.info(f"新增区域[{target_id}] => {start_point, end_point}") configObj.config_info.targets[target_id] = new_target @@ -69,7 +66,8 @@ def read_target_rectangle(): return configObj.config_info.targets class VideoProcessor: reporter: upload.DataReporter.DataReporter - + capture: cv2.VideoCapture + is_opened: bool= False def __init__(self, reporter:upload.DataReporter.DataReporter): self.reporter = reporter @@ -78,33 +76,56 @@ class VideoProcessor: logging.info(f"msg={msg}") match msg.cmd: case "getPoints": - data_dict = {k: asdict(v.info) for k, v in configObj.config_info.targets.items()} - resp_msg = models.msg.Msg(_from="dev", cmd="getPoints", values={"targets": data_dict}) + targets=configObj.config_info.targets.copy() + for k,v in targets.items(): + targets[k].handler_info=None + + resp_msg = models.msg.Msg(_from="dev", cmd="getPoints", values={"targets": targets}) resp_json = resp_msg.to_json_() return resp_json case "setPoints": v=msg.values ts=v["targets"] - # 清空原配置 - configObj.config_info.targets={} + # # 清空原配置 + # configObj.config_info.targets={} for _,t in ts.items(): t_str=json.dumps(t) - t_info = models.target.TargetInfo.from_json(t_str) - c_target=models.target.CircleTarget.init_by_info(t_info) - configObj.config_info.targets[c_target.info.id] =c_target + new_c_target = models.target.CircleTarget.from_json(t_str) + configObj.config_info.targets[new_c_target.info.id] =new_c_target configObj.save2json_file() resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True}) resp_json = resp_msg.to_json() return resp_json + case "videoFps": + v = msg.values + fps = v["fps"] + self.reporter.adjust_rate(fps,"image") + configObj.config_info.fps.video = fps + configObj.save2json_file() + resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True}) + resp_json = resp_msg.to_json() + return resp_json + case "dataFps": + v = msg.values + fps = v["fps"] + self.reporter.adjust_rate(fps,"data") + configObj.config_info.fps.data=fps + configObj.save2json_file() + resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True}) + resp_json = resp_msg.to_json() + return resp_json + case "setCap": + v = msg.values + cap = v["cap"] + self.switch_video(cap) + resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True}) + resp_json = resp_msg.to_json() + return resp_json print("==") - def update_thresh_binary(self,v:int): - self.thresh_binary = v - - def pre_handler_img(self,gray_frame,now_str:str): # 将灰度图压缩为 JPEG 格式,并存储到内存缓冲区 img_base64 = utils.frame_to_base64(gray_frame, format="JPEG") @@ -139,7 +160,7 @@ class VideoProcessor: ret, sub_binary_frame = cv2.threshold(sub_image, tr.info.threshold.binary, 255, cv2.THRESH_BINARY) # 高斯滤波 - sub_binary_frame = cv2.GaussianBlur(sub_binary_frame, (tr.info.threshold.gauss, tr.info.threshold.gauss), 1) + sub_binary_frame = cv2.GaussianBlur(sub_binary_frame, (tr.info.threshold.gauss, tr.info.threshold.gauss), 10,borderType=cv2.BORDER_REPLICATE) cv2.imshow(f'{tr.info.id}_binaryImg', sub_binary_frame) # 覆盖原图 @@ -149,30 +170,29 @@ class VideoProcessor: circles = self.circle2_detect(sub_binary_frame) if len(circles) == 0: continue - center,radius=self.circle_show(img,circles,_start_point) + center,radius_pix=self.circle_show(img,circles,_start_point) # 纪录圆心位置 - tr.center_point=center - tr.radius_pix=radius - if tr.is_init: - tr.center_init=tr.center_point - tr.is_init=False + if tr.handler_info is None: + tr.handler_info= models.target.HandlerInfo() + if tr.handler_info.is_init: + tr.handler_info.is_init=False + tr.handler_info.center_init = center + + tr.handler_info.center_point=center + tr.handler_info.radius_pix=radius_pix tr.circle_displacement() all_upload_data.data.append( models.sampleMsg.SensorData( str(tr.info.id), - tr.displacement_phy.x, - tr.displacement_phy.y) + tr.handler_info.displacement_phy.x, + tr.handler_info.displacement_phy.y) ) #过滤无效空数据 if len(all_upload_data.data)==0: return - # json_str = json.dumps( - # {k:asdict(v) for k, v in once_upload.items() if v.is_init==False} - # ) - # print(f"标靶数据={json_str}",json_str) self.enqueue_data(all_upload_data) @@ -235,30 +255,58 @@ class VideoProcessor: def open_video(self,video_id): - cap = cv2.VideoCapture(video_id) - # cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1600) # 宽度 - # cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 900) # 高度 - if not cap.isOpened(): - logging.info("无法打开摄像头") - exit() - return cap + print(f"打开摄像头 -> {video_id}") + self.capture = cv2.VideoCapture(video_id) + frame_width = int(self.capture.get(cv2.CAP_PROP_FRAME_WIDTH)) + frame_height = int(self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) + print(f"默认分辨率= {frame_width}*{frame_height}") + logging.info(f"{video_id}地址->{self.capture}") + fps = self.capture.get(cv2.CAP_PROP_FPS) + print(f"fps={fps},video_id={video_id},") + # self.capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1600) # 宽度 + # self.capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 900) # 高度 + if not self.capture.isOpened(): + self.capture.release() + logging.info(f"无法打开摄像头{video_id},release 地址 -> {self.capture}") + return + self.is_opened=True + + def switch_video(self,video_id:str): + print(f"切换摄像头 -> {video_id}") + self.is_opened = False + self.capture.release() + cv2.destroyAllWindows() + if str.isdigit(video_id): + video_id=int(video_id) + self.open_video(video_id) - def show_video(self,cap): + + def show_video(self): global sigExit,start_point, end_point, drawing cv2.namedWindow('Frame') cv2.setMouseCallback('Frame', add_rectangle) # 读取一帧图像 while True: - ret, frame = cap.read() + if not self.is_opened: + print(f"摄像头 标记is_opened={self.is_opened}") + sleep(5) + continue + ret, frame = self.capture.read() if ret: self.frame_handle(frame) else: - logging.info("无法读取帧") + logging.info(f"无法读取帧,cap地址- >{self.capture}") + sleep(1) + # self.capture.release() + # self.capture= cv2.VideoCapture(0) # 再次尝试 if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环 break if sigExit: break + # 显示图像 + if frame is not None: + cv2.imshow('Frame', frame) def show_image(self,frame): global start_point, end_point, drawing cv2.namedWindow('Frame') @@ -279,8 +327,6 @@ class VideoProcessor: if drawing: cv2.rectangle(frame, tuple(start_point), tuple(end_point), (0, 200, 200), 4) # print(f"鼠标位置 {start_point} -> {end_point}") - # 显示图像 - cv2.imshow('Frame', frame) # 读取图像 #img_copy = img.copy() # 复制图像用于还原 @@ -292,24 +338,26 @@ class VideoProcessor: self.show_image(img_raw) # 支持 - def video_mode(self,video_id): - capture = self.open_video(video_id) - fps = capture.get(cv2.CAP_PROP_FPS) - print(f"fps={fps}") - self.show_video(capture) + def video_mode(self,video_id:str): + if str.isdigit(video_id): + video_id=int(video_id) + self.open_video(video_id) + # if self.is_opened: + self.show_video() # 释放摄像头资源并关闭所有窗口 - capture.release() + print("退出 video") + self.capture.release() cv2.destroyAllWindows() def rtsp_mode(self,rtsp_url:str): # rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554" # rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream" - capture = self.open_video(rtsp_url) - fps = capture.get(cv2.CAP_PROP_FPS) - print(f"fps={fps}") - self.show_video(capture) + self.open_video(rtsp_url) + fps = self.capture.get(cv2.CAP_PROP_FPS) + print(f"rtsp fps={fps}") + self.show_video() # 释放摄像头资源并关闭所有窗口 - capture.release() + self.capture.release() cv2.destroyAllWindows() def enqueue_data(self,data): @@ -321,7 +369,8 @@ class VideoProcessor: try: self.reporter.data_queue.put((dt, data), block=False) except queue.Full: - self.reporter.data_dropped += 1 + # self.reporter.data_dropped += 1 + pass def enqueue_image(self,data): # 获取当前时间戳 timestamp = time.time() @@ -331,7 +380,8 @@ class VideoProcessor: try: self.reporter.image_queue.put((dt, data), block=False) except queue.Full: - self.reporter.image_dropped += 1 + pass + #self.reporter.image_dropped += 1 #数据广播 def check_exit(sig, frame):