Browse Source

update 更新代码版本

master
lucas 3 days ago
parent
commit
b60af972b2
  1. 12
      app.py
  2. 76
      config.json
  3. 45
      config2.json
  4. 61
      configSer.py
  5. 6
      models/msg.py
  6. 57
      models/target.py
  7. 21
      tcp_Ser.py
  8. 6
      test/测试config.py
  9. 58
      test/测试opencv.py
  10. 16
      upload/DataReporter.py
  11. 17
      utils.py
  12. 8
      标靶识别.py
  13. 162
      标靶识别video.py

12
app.py

@ -1,3 +1,5 @@
from time import sleep
import configSer import configSer
import tcp_Ser import tcp_Ser
import upload.DataReporter import upload.DataReporter
@ -11,9 +13,9 @@ if __name__ == '__main__':
json_str = config_obj.config_info.to_json(indent=4) json_str = config_obj.config_info.to_json(indent=4)
print(f"当前配置:{json_str}") 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() 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.register_handler(tcp_service.broadcast_message)
reporter.start() reporter.start()
# 启动video # 启动video
@ -22,10 +24,10 @@ if __name__ == '__main__':
# 添加订阅者processor # 添加订阅者processor
tcp_service.add_subscribe(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.waitKey(0)
cv2.destroyAllWindows() cv2.destroyAllWindows()

76
config.json

@ -2,46 +2,66 @@
"server": { "server": {
"port": 2230 "port": 2230
}, },
"capture": 0, "fps": {
"data": 10,
"video": 0
},
"capture": "0",
"targets": { "targets": {
"0": { "0": {
"info": { "info": {
"rectangle_area": { "rectangle_area": {
"x": 50, "x": 50,
"y": 371, "y": 230,
"w": 113, "w": 130,
"h": 91 "h": 100
}, },
"threshold": { "threshold": {
"binary": 120, "binary": 180,
"gauss": 5 "gauss": 7
}, },
"radius_pix": 1,
"radius": 20.0, "radius": 20.0,
"pix_length": 0.0,
"id": 0, "id": 0,
"desc": "0_biaoba", "desc": "0_up",
"base": false "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
]
]
} }
} }

45
config2.json

@ -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
]
]
}
}

61
configSer.py

@ -2,9 +2,9 @@ import json
import os import os
from dataclasses import ( from dataclasses import (
dataclass, dataclass,
field, asdict field
) )
from typing import Dict, Optional from typing import Dict
import numpy as np import numpy as np
from dataclasses_json import dataclass_json from dataclasses_json import dataclass_json
@ -19,15 +19,20 @@ _file_path: str
class Server: class Server:
port: int = 0 port: int = 0
@dataclass_json
@dataclass
class Fps:
data: int = 0
video: int = 0
@dataclass_json @dataclass_json
@dataclass @dataclass
class ConfigInfo: class ConfigInfo:
server:Server server:Server
capture: int = 0 fps:Fps
capture: str = "0"
# 标靶配置 # 标靶配置
targets: Dict[int, models.target.CircleTarget] = field(default_factory=dict) targets: Dict[int, models.target.CircleTarget] = field(default_factory=dict)
# 标靶透视矩阵
perspective: Dict[int, np.ndarray] = field(default_factory=dict)
class ConfigOperate: class ConfigOperate:
_file_path: str _file_path: str
@ -46,29 +51,29 @@ class ConfigOperate:
config = json.load(json_file) config = json.load(json_file)
return config return config
def load2obj_sample2(self): # def load2obj_sample2(self):
""""读取配置""" # """"读取配置"""
dic=self.load2dict() # dic=self.load2dict()
ts = dic["targets"] # ts = dic["targets"]
capture = dic["capture"] # capture = dic["capture"]
# 获取矩阵数据 # # 获取矩阵数据
matrix_dict = dic.get("perspective", {}) # matrix_dict = dic.get("perspective", {})
# n0=convert_to_ndarray(self.matrix_dict["0"]) # # n0=convert_to_ndarray(self.matrix_dict["0"])
# 将矩阵转换为字符串 # # 将矩阵转换为字符串
# matrix_str = np.array2string(n0, precision=8, separator=', ', suppress_small=True) # # matrix_str = np.array2string(n0, precision=8, separator=', ', suppress_small=True)
for _,t in ts.items(): # for _,t in ts.items():
obj = models.target.TargetInfo(**t) # obj = models.target.TargetInfo(**t)
area = models.target.RectangleArea.from_dict(obj.rectangle_area) # area = models.target.RectangleArea.from_dict(obj.rectangle_area)
thres = models.target.Threshold(**obj.threshold) # thres = models.target.Threshold(**obj.threshold)
self.targets[obj.id] = models.target.CircleTarget( # self.targets[obj.id] = models.target.CircleTarget(
obj.id, # obj.id,
obj.desc, # obj.desc,
area, # area,
obj.radius, # obj.radius,
thres, # thres,
obj.base # obj.base
) # )
return self.targets # return self.targets
def load2obj_sample(self): def load2obj_sample(self):
dic=self.load2dict() dic=self.load2dict()

6
models/msg.py

@ -12,14 +12,8 @@ class Msg:
def to_json_(self) -> str: def to_json_(self) -> str:
"""将数据类序列化为 JSON 字符串""" """将数据类序列化为 JSON 字符串"""
# return json.dumps(self.__dict__, indent=4, default=lambda x: x.__dict__)
return self.to_json() return self.to_json()
# @classmethod
# def from_json(cls, json_str: str) -> 'Msg':
# """从 JSON 字符串反序列化为数据类"""
# data_dict = json.loads(json_str)
# return cls(**data_dict)

57
models/target.py

@ -1,8 +1,11 @@
from dataclasses import dataclass, field 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 from dataclasses_json import dataclass_json, config
import utils
@dataclass_json @dataclass_json
@dataclass @dataclass
@ -38,10 +41,8 @@ class TargetInfo:
# 标靶方形区域 # 标靶方形区域
rectangle_area:RectangleArea rectangle_area:RectangleArea
threshold:Threshold threshold:Threshold
radius_pix:float= 1
# 标靶物理半径 # 标靶物理半径
radius:float=0.0 radius:float=0.0
pix_length:float=0.0
id:int =-1 id:int =-1
desc:str="" desc:str=""
base:bool=False base:bool=False
@ -57,15 +58,13 @@ class TargetInfo:
def from_dict(cls,data: dict): def from_dict(cls,data: dict):
return cls(data['id'],data['rectangle_area'],data['radius']) return cls(data['id'],data['rectangle_area'],data['radius'])
@dataclass_json @dataclass_json
@dataclass @dataclass
class CircleTarget: class HandlerInfo:
# 标靶方形区域 # 初始话
info:TargetInfo
# 初始标靶中心
is_init=True 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_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)) 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_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)) 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): @dataclass_json
self.info=info @dataclass
self.center_point=center_point class CircleTarget:
self.center_init=center_init # 标靶方形区域
self. displacement_pix=displacement_pix info:TargetInfo
self.displacement_phy=displacement_phy # 标靶透视矩阵
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 @classmethod
def init_by_info(cls,t:TargetInfo): def init_by_info(cls,t:TargetInfo):
return CircleTarget(t,None,None,None,None) return CircleTarget(t,None,None,None,None)
def circle_displacement(self): def circle_displacement(self):
previous = self.center_init previous = self.handler_info.center_init
if previous != (): 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: if self.info.radius != 0:
# 单位像素距离 # 单位像素距离
self.info.pix_length = self.info.radius / self.info.radius_pix self.handler_info.pix_length = self.info.radius / self.handler_info.radius_pix
offset_x = round(float(self.displacement_pix.x * self.info.pix_length), 5) offset_x = round(float(self.handler_info.displacement_pix.x * self.handler_info.pix_length), 5)
offset_y = round(float(self.displacement_pix.y * self.info.pix_length), 5) offset_y = round(float(self.handler_info.displacement_pix.y * self.handler_info.pix_length), 5)
self.displacement_phy = Point(offset_x, offset_y) self.handler_info.displacement_phy = Point(offset_x, offset_y)
return self return self

21
tcp_Ser.py

@ -28,20 +28,30 @@ class TcpSer(threading.Thread):
# 保持连接,直到客户端断开 # 保持连接,直到客户端断开
while True: while True:
# 接收客户端数据(如果需要) # 接收客户端数据(如果需要)
data = client_socket.recv(1024) data = client_socket.recv(4096)
msg_str=data.decode('utf-8') msg_str=data.decode('utf-8')
if not data: if not data:
break # 如果没有数据,退出循环 break # 如果没有数据,退出循环
print(f"{client_socket.getpeername()} 收到: {msg_str}") print(f"{client_socket.getpeername()} 收到: {msg_str}")
# 反序列化为 实例 # 反序列化为 实例
s_cmd = Msg.from_json(msg_str) s_cmd = Msg.from_json(msg_str)
valid_msg:bool=True
match s_cmd.cmd: match s_cmd.cmd:
case "getPoints" | "setPoints": 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 添加处理 # todo 添加处理
case "xxxxx": case _:
self.on_data(s_cmd) 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: except Exception as e:
print(f"处理客户端时出错: {e}") print(f"处理客户端时出错: {e}")
finally: finally:
@ -58,7 +68,7 @@ class TcpSer(threading.Thread):
self.consumers.append(consumer) self.consumers.append(consumer)
else: else:
print("consumer 缺少on_data函数,订阅无效 ") print("consumer 缺少on_data函数,订阅无效 ")
def on_data(self,msg): def on_data(self,msg,valid):
for consumer in self.consumers: for consumer in self.consumers:
try: try:
resp=consumer.on_data(msg) resp=consumer.on_data(msg)
@ -72,6 +82,7 @@ class TcpSer(threading.Thread):
if len(message)==0: if len(message)==0:
return return
message+="\n\n"
for client in self.connected_clients: for client in self.connected_clients:
try: try:
client.sendall(message.encode()) client.sendall(message.encode())

6
test/测试config.py

@ -6,11 +6,11 @@ import configSer
def test_load_config(): def test_load_config():
config_path = "../config.json" 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) json_str2 = config.config_info.to_json(indent=4)
print("json=",json_str2) print("json=",json_str2)
config_dict = asdict(config) # 测试修改相机id
config.capture=1 config.config_info.capture=1
config.save2json_file() config.save2json_file()
# 更新配置文件 # 更新配置文件
updates = { updates = {

58
test/测试opencv.py

@ -1,4 +1,5 @@
import logging import logging
from time import sleep
import cv2 import cv2
print(cv2.__version__) print(cv2.__version__)
@ -6,33 +7,58 @@ def open_video(video_id):
cap = cv2.VideoCapture(video_id) cap = cv2.VideoCapture(video_id)
if not cap.isOpened(): if not cap.isOpened():
logging.info("无法打开摄像头") logging.info("无法打开摄像头")
exit()
return cap return cap
class VideoProcessor:
capture: cv2.VideoCapture
rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream" rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream"
capture = open_video(rtsp_url) print("开始读取2")
fps = capture.get(cv2.CAP_PROP_FPS) capture=open_video(2)
width = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)) vp = VideoProcessor()
height = int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) 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(f"fps={fps}")
print("width={width}, height={height}".format(width=width, height=height)) print("width={width}, height={height}".format(width=width, height=height))
# 定义视频编码器和输出文件 cv2.namedWindow('frame')
fourcc = cv2.VideoWriter_fourcc(*'mp4v') i = 0
out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))
# 读取一帧图像
while True: while True:
ret, frame = capture.read() ret, frame = vp.capture .read()
print("-->") 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: if ret:
cv2.imshow("frame", frame)
# 写入帧到输出文件 # 写入帧到输出文件
out.write(frame) # out.write(frame)
else: else:
logging.info("无法读取帧") logging.info("无法读取图像")
if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环 if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环
break break
capture.release() vp.capture.release()
out.release() # out.release()

16
upload/DataReporter.py

@ -8,12 +8,12 @@ from upload.RateLimiter import RateLimiter
class DataReporter(threading.Thread): class DataReporter(threading.Thread):
call_back=None call_back=None
def __init__(self,): def __init__(self,data_fps:int,video_fps:int):
super().__init__() super().__init__()
self.image_queue = queue.Queue(maxsize=10) # 图片队列 self.image_queue = queue.Queue(maxsize=video_fps) # 图片队列
self.data_queue = queue.Queue(maxsize=50) # 数据队列 self.data_queue = queue.Queue(maxsize=data_fps) # 数据队列
self.image_limiter = RateLimiter(max_rate=1, time_window=1) # 图片限速: 5张/秒 self.image_limiter = RateLimiter(max_rate=video_fps, time_window=1) # 图片限速: 5张/秒
self.data_limiter = RateLimiter(max_rate=1, time_window=1) # 数据限速: 20条/秒 self.data_limiter = RateLimiter(max_rate=data_fps, time_window=1) # 数据限速: 20条/秒
self.running = True self.running = True
self.image_dropped = 0 # 统计丢弃的图片数量 self.image_dropped = 0 # 统计丢弃的图片数量
self.data_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(): if not self.image_queue.empty() and self.image_limiter.allow_request():
try: try:
image_data = self.image_queue.get_nowait() image_data = self.image_queue.get_nowait()
# self._report_image(image_data) self._report_image(image_data)
except queue.Empty: except queue.Empty:
pass pass
@ -66,6 +66,8 @@ class DataReporter(threading.Thread):
if data_type == 'image': if data_type == 'image':
with self.image_limiter.lock: with self.image_limiter.lock:
self.image_limiter.max_rate = new_rate self.image_limiter.max_rate = new_rate
self.image_queue= queue.Queue(maxsize=new_rate)
else: else:
with self.data_limiter.lock: with self.data_limiter.lock:
self.data_limiter.max_rate = new_rate self.data_limiter.max_rate = new_rate
self.data_queue = queue.Queue(maxsize=new_rate)

17
utils.py

@ -1,5 +1,11 @@
from typing import Dict, List
import cv2 import cv2
import base64 import base64
import numpy as np
def frame_to_base64(frame, format="JPEG"): def frame_to_base64(frame, format="JPEG"):
"""将 OpenCV 读取的图片帧转换为 Base64 编码的字符串""" """将 OpenCV 读取的图片帧转换为 Base64 编码的字符串"""
# 将图片帧编码为 JPEG 或 PNG 格式 # 将图片帧编码为 JPEG 或 PNG 格式
@ -16,4 +22,13 @@ def frame_to_base64(frame, format="JPEG"):
# 将编码后的字节流转换为 Base64 字符串 # 将编码后的字节流转换为 Base64 字符串
base64_string = base64.b64encode(encoded_frame).decode("utf-8") base64_string = base64.b64encode(encoded_frame).decode("utf-8")
return base64_string 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)

8
标靶识别.py

@ -230,7 +230,9 @@ def image_mode():
show_image(img_raw) show_image(img_raw)
def rtsp_mode(): 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) capture = open_video(rtsp_url)
fps = capture.get(cv2.CAP_PROP_FPS) fps = capture.get(cv2.CAP_PROP_FPS)
print(f"fps={fps}") print(f"fps={fps}")
@ -242,8 +244,8 @@ def rtsp_mode():
if __name__ == '__main__': if __name__ == '__main__':
signal.signal(signal.SIGINT, check_exit) signal.signal(signal.SIGINT, check_exit)
# rtsp_mode() rtsp_mode()
video_mode(0) # video_mode(0)
# image_mode() # image_mode()
cv2.waitKey(0) cv2.waitKey(0)

162
标靶识别video.py

@ -1,20 +1,17 @@
import gc
from datetime import datetime from datetime import datetime
import json import json
import queue import queue
import time import time
from time import sleep from time import sleep
from dataclasses import asdict
import cv2 import cv2
import numpy as np import numpy as np
import signal
import sys import sys
import threading
import logging import logging
import configSer import configSer
import models.target import models.target
import models.sampleMsg import models.sampleMsg
import tcp_Ser
import upload.DataReporter import upload.DataReporter
import utils import utils
from models.msg import Msg from models.msg import Msg
@ -59,9 +56,9 @@ def add_rectangle(event, x, y, flags, param):
"test add", "test add",
area, area,
radius, radius,
models.target.Threshold(128,9), models.target.Threshold(190,9),
False) 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}") logging.info(f"新增区域[{target_id}] => {start_point, end_point}")
configObj.config_info.targets[target_id] = new_target configObj.config_info.targets[target_id] = new_target
@ -69,7 +66,8 @@ def read_target_rectangle():
return configObj.config_info.targets return configObj.config_info.targets
class VideoProcessor: class VideoProcessor:
reporter: upload.DataReporter.DataReporter reporter: upload.DataReporter.DataReporter
capture: cv2.VideoCapture
is_opened: bool= False
def __init__(self, reporter:upload.DataReporter.DataReporter): def __init__(self, reporter:upload.DataReporter.DataReporter):
self.reporter = reporter self.reporter = reporter
@ -78,33 +76,56 @@ class VideoProcessor:
logging.info(f"msg={msg}") logging.info(f"msg={msg}")
match msg.cmd: match msg.cmd:
case "getPoints": case "getPoints":
data_dict = {k: asdict(v.info) for k, v in configObj.config_info.targets.items()} targets=configObj.config_info.targets.copy()
resp_msg = models.msg.Msg(_from="dev", cmd="getPoints", values={"targets": data_dict}) 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_() resp_json = resp_msg.to_json_()
return resp_json return resp_json
case "setPoints": case "setPoints":
v=msg.values v=msg.values
ts=v["targets"] ts=v["targets"]
# 清空原配置 # # 清空原配置
configObj.config_info.targets={} # configObj.config_info.targets={}
for _,t in ts.items(): for _,t in ts.items():
t_str=json.dumps(t) t_str=json.dumps(t)
t_info = models.target.TargetInfo.from_json(t_str) new_c_target = models.target.CircleTarget.from_json(t_str)
c_target=models.target.CircleTarget.init_by_info(t_info) configObj.config_info.targets[new_c_target.info.id] =new_c_target
configObj.config_info.targets[c_target.info.id] =c_target
configObj.save2json_file() configObj.save2json_file()
resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True}) resp_msg = models.msg.Msg(_from="dev", cmd="setPoints", values={"operate": True})
resp_json = resp_msg.to_json() resp_json = resp_msg.to_json()
return resp_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("==") print("==")
def update_thresh_binary(self,v:int):
self.thresh_binary = v
def pre_handler_img(self,gray_frame,now_str:str): def pre_handler_img(self,gray_frame,now_str:str):
# 将灰度图压缩为 JPEG 格式,并存储到内存缓冲区 # 将灰度图压缩为 JPEG 格式,并存储到内存缓冲区
img_base64 = utils.frame_to_base64(gray_frame, format="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) 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) cv2.imshow(f'{tr.info.id}_binaryImg', sub_binary_frame)
# 覆盖原图 # 覆盖原图
@ -149,30 +170,29 @@ class VideoProcessor:
circles = self.circle2_detect(sub_binary_frame) circles = self.circle2_detect(sub_binary_frame)
if len(circles) == 0: if len(circles) == 0:
continue continue
center,radius=self.circle_show(img,circles,_start_point) center,radius_pix=self.circle_show(img,circles,_start_point)
# 纪录圆心位置 # 纪录圆心位置
tr.center_point=center if tr.handler_info is None:
tr.radius_pix=radius tr.handler_info= models.target.HandlerInfo()
if tr.is_init: if tr.handler_info.is_init:
tr.center_init=tr.center_point tr.handler_info.is_init=False
tr.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() tr.circle_displacement()
all_upload_data.data.append( all_upload_data.data.append(
models.sampleMsg.SensorData( models.sampleMsg.SensorData(
str(tr.info.id), str(tr.info.id),
tr.displacement_phy.x, tr.handler_info.displacement_phy.x,
tr.displacement_phy.y) tr.handler_info.displacement_phy.y)
) )
#过滤无效空数据 #过滤无效空数据
if len(all_upload_data.data)==0: if len(all_upload_data.data)==0:
return 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) self.enqueue_data(all_upload_data)
@ -235,30 +255,58 @@ class VideoProcessor:
def open_video(self,video_id): def open_video(self,video_id):
cap = cv2.VideoCapture(video_id) print(f"打开摄像头 -> {video_id}")
# cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1600) # 宽度 self.capture = cv2.VideoCapture(video_id)
# cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 900) # 高度 frame_width = int(self.capture.get(cv2.CAP_PROP_FRAME_WIDTH))
if not cap.isOpened(): frame_height = int(self.capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
logging.info("无法打开摄像头") print(f"默认分辨率= {frame_width}*{frame_height}")
exit() logging.info(f"{video_id}地址->{self.capture}")
return cap 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 global sigExit,start_point, end_point, drawing
cv2.namedWindow('Frame') cv2.namedWindow('Frame')
cv2.setMouseCallback('Frame', add_rectangle) cv2.setMouseCallback('Frame', add_rectangle)
# 读取一帧图像 # 读取一帧图像
while True: 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: if ret:
self.frame_handle(frame) self.frame_handle(frame)
else: 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'退出循环 if cv2.waitKey(1) & 0xFF == ord('q'): # 按'q'退出循环
break break
if sigExit: if sigExit:
break break
# 显示图像
if frame is not None:
cv2.imshow('Frame', frame)
def show_image(self,frame): def show_image(self,frame):
global start_point, end_point, drawing global start_point, end_point, drawing
cv2.namedWindow('Frame') cv2.namedWindow('Frame')
@ -279,8 +327,6 @@ class VideoProcessor:
if drawing: if drawing:
cv2.rectangle(frame, tuple(start_point), tuple(end_point), (0, 200, 200), 4) cv2.rectangle(frame, tuple(start_point), tuple(end_point), (0, 200, 200), 4)
# print(f"鼠标位置 {start_point} -> {end_point}") # print(f"鼠标位置 {start_point} -> {end_point}")
# 显示图像
cv2.imshow('Frame', frame)
# 读取图像 # 读取图像
#img_copy = img.copy() # 复制图像用于还原 #img_copy = img.copy() # 复制图像用于还原
@ -292,24 +338,26 @@ class VideoProcessor:
self.show_image(img_raw) self.show_image(img_raw)
# 支持 # 支持
def video_mode(self,video_id): def video_mode(self,video_id:str):
capture = self.open_video(video_id) if str.isdigit(video_id):
fps = capture.get(cv2.CAP_PROP_FPS) video_id=int(video_id)
print(f"fps={fps}") self.open_video(video_id)
self.show_video(capture) # if self.is_opened:
self.show_video()
# 释放摄像头资源并关闭所有窗口 # 释放摄像头资源并关闭所有窗口
capture.release() print("退出 video")
self.capture.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()
def rtsp_mode(self,rtsp_url:str): 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"
# rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream" # rtsp_url ="rtsp://admin:123456abc@192.168.1.64:554/h264/ch1/main/av_stream"
capture = self.open_video(rtsp_url) self.open_video(rtsp_url)
fps = capture.get(cv2.CAP_PROP_FPS) fps = self.capture.get(cv2.CAP_PROP_FPS)
print(f"fps={fps}") print(f"rtsp fps={fps}")
self.show_video(capture) self.show_video()
# 释放摄像头资源并关闭所有窗口 # 释放摄像头资源并关闭所有窗口
capture.release() self.capture.release()
cv2.destroyAllWindows() cv2.destroyAllWindows()
def enqueue_data(self,data): def enqueue_data(self,data):
@ -321,7 +369,8 @@ class VideoProcessor:
try: try:
self.reporter.data_queue.put((dt, data), block=False) self.reporter.data_queue.put((dt, data), block=False)
except queue.Full: except queue.Full:
self.reporter.data_dropped += 1 # self.reporter.data_dropped += 1
pass
def enqueue_image(self,data): def enqueue_image(self,data):
# 获取当前时间戳 # 获取当前时间戳
timestamp = time.time() timestamp = time.time()
@ -331,7 +380,8 @@ class VideoProcessor:
try: try:
self.reporter.image_queue.put((dt, data), block=False) self.reporter.image_queue.put((dt, data), block=False)
except queue.Full: except queue.Full:
self.reporter.image_dropped += 1 pass
#self.reporter.image_dropped += 1
#数据广播 #数据广播
def check_exit(sig, frame): def check_exit(sig, frame):

Loading…
Cancel
Save