import logging from dataclasses import dataclass, field from datetime import datetime from numbers import Number from typing import Optional, Dict, Deque from collections import deque 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 gradient: int=100 anchor: int=80 @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:bool=True last_detected_time:datetime=datetime.now() radius_pix:float= 1.0 pix_length:float=0.0 # 标靶中心 center_point_queue:Deque[Point] = field(default_factory=lambda: deque(maxlen=10)) 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)) def calculate_mean(self)-> Point: """计算队列中所有数据的均值""" if self.center_point_queue: length=len(self.center_point_queue) mean_x = sum(p.x for p in self.center_point_queue) / length mean_y = sum(p.y for p in self.center_point_queue) / length return Point(mean_x, mean_y) else: return None def enqueue_center_point(self, data) -> Point: """入队操作""" self.center_point_queue.append(data) return self.calculate_mean() @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 @classmethod def init_by_info(cls,t:TargetInfo): return CircleTarget(t,None,None,None,None) def circle_displacement_pix(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) return self def circle_displacement_phy(self): if (self.info.radius != 0 and self.handler_info.displacement_pix is not None and self.handler_info.radius_pix !=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), 3) offset_y = round(float(self.handler_info.displacement_pix.y * self.handler_info.pix_length), 3) # logging.info(f"[{self.info.desc}] phy=> x={self.handler_info.displacement_pix.x}*{self.handler_info.pix_length} ={offset_x}") # logging.info(f"[{self.info.desc}] phy=> y={self.handler_info.displacement_pix.y}*{self.handler_info.pix_length} ={offset_y}") self.handler_info.displacement_phy = Point(offset_x, offset_y) return self