# Ultralytics YOLO 🚀, AGPL-3.0 license
"""
Run prediction on images, videos, directories, globs, YouTube, webcam, streams, etc.

Usage - sources:
    $ yolo mode=predict model=yolov8n.pt source=0                               # webcam
                                                img.jpg                         # image
                                                vid.mp4                         # video
                                                screen                          # screenshot
                                                path/                           # directory
                                                list.txt                        # list of images
                                                list.streams                    # list of streams
                                                'path/*.jpg'                    # glob
                                                'https://youtu.be/Zgi9g1ksQHc'  # YouTube
                                                'rtsp://example.com/media.mp4'  # RTSP, RTMP, HTTP stream

Usage - formats:
    $ yolo mode=predict model=yolov8n.pt                 # PyTorch
                              yolov8n.torchscript        # TorchScript
                              yolov8n.onnx               # ONNX Runtime or OpenCV DNN with dnn=True
                              yolov8n_openvino_model     # OpenVINO
                              yolov8n.engine             # TensorRT
                              yolov8n.mlmodel            # CoreML (macOS-only)
                              yolov8n_saved_model        # TensorFlow SavedModel
                              yolov8n.pb                 # TensorFlow GraphDef
                              yolov8n.tflite             # TensorFlow Lite
                              yolov8n_edgetpu.tflite     # TensorFlow Edge TPU
                              yolov8n_paddle_model       # PaddlePaddle
"""
import math
import os
import platform
import time
from math import sqrt
from pathlib import Path
from random import random

import cv2
import numpy as np
import torch
from PIL import Image

from ultralytics.nn.autobackend import AutoBackend
from ultralytics.yolo.cfg import get_cfg
from ultralytics.yolo.data import load_inference_source
from ultralytics.yolo.data.augment import LetterBox, classify_transforms
from ultralytics.yolo.utils import DEFAULT_CFG, LOGGER, SETTINGS, callbacks, colorstr, ops
from ultralytics.yolo.utils.checks import check_imgsz, check_imshow
from ultralytics.yolo.utils.files import increment_path
from ultralytics.yolo.utils.torch_utils import select_device, smart_inference_mode
from ultralytics.yolo.engine.ids import check_tings


STREAM_WARNING = """
    WARNING ⚠️ stream/video/webcam/dir predict source will accumulate results in RAM unless `stream=True` is passed,
    causing potential out-of-memory errors for large sources or long-running streams/videos.

    Usage:
        results = model(source=..., stream=True)  # generator of Results objects
        for r in results:
            boxes = r.boxes  # Boxes object for bbox outputs
            masks = r.masks  # Masks object for segment masks outputs
            probs = r.probs  # Class probabilities for classification outputs
"""


class BasePredictor:
    """
    BasePredictor

    A base class for creating predictors.

    Attributes:
        args (SimpleNamespace): Configuration for the predictor.
        save_dir (Path): Directory to save results.
        done_setup (bool): Whether the predictor has finished setup.
        model (nn.Module): Model used for prediction.
        data (dict): Data configuration.
        device (torch.device): Device used for prediction.
        dataset (Dataset): Dataset used for prediction.
        vid_path (str): Path to video file.
        vid_writer (cv2.VideoWriter): Video writer for saving video output.
        annotator (Annotator): Annotator used for prediction.
        data_path (str): Path to data.
    """

    def __init__(self, cfg=DEFAULT_CFG, overrides=None, _callbacks=None):
        """
        Initializes the BasePredictor class.

        Args:
            cfg (str, optional): Path to a configuration file. Defaults to DEFAULT_CFG.
            overrides (dict, optional): Configuration overrides. Defaults to None.
        """
        # self.real_MovBox = None
        self.args = get_cfg(cfg, overrides)
        project = self.args.project or Path(SETTINGS['runs_dir']) / self.args.task
        name = self.args.name or f'{self.args.mode}'
        self.save_dir = increment_path(Path(project) / name, exist_ok=self.args.exist_ok)
        if self.args.conf is None:
            self.args.conf = 0.25  # default conf=0.25
        self.done_warmup = False
        if self.args.show:
            self.args.show = check_imshow(warn=True)

        # Usable if setup is done
        self.model = None
        self.data = self.args.data  # data_dict
        self.imgsz = None
        self.device = None
        self.dataset = None
        self.vid_path, self.vid_writer = None, None
        self.plotted_img = None
        self.data_path = None
        self.source_type = None
        self.batch = None
        self.callbacks = _callbacks or callbacks.get_default_callbacks()
        callbacks.add_integration_callbacks(self)

    def preprocess(self, im):
        """Prepares input image before inference.

        Args:
            im (torch.Tensor | List(np.ndarray)): (N, 3, h, w) for tensor, [(h, w, 3) x N] for list.
        """
        if not isinstance(im, torch.Tensor):
            im = np.stack(self.pre_transform(im))  #size(640,XX)
            im = im[..., ::-1].transpose((0, 3, 1, 2))  # BGR to RGB, BHWC to BCHW, (n, 3, h, w)
            im = np.ascontiguousarray(im)  # contiguous
            im = torch.from_numpy(im)
        # NOTE: assuming im with (b, 3, h, w) if it's a tensor
        img = im.to(self.device)
        img = img.half() if self.model.fp16 else img.float()  # uint8 to fp16/32
        img /= 255  # 0 - 255 to 0.0 - 1.0
        return img

    def pre_transform(self, im):
        """Pre-tranform input image before inference.

        Args:
            im (List(np.ndarray)): (N, 3, h, w) for tensor, [(h, w, 3) x N] for list.

        Return: A list of transformed imgs.
        """
        same_shapes = all(x.shape == im[0].shape for x in im)
        auto = same_shapes and self.model.pt
        return [LetterBox(self.imgsz, auto=auto, stride=self.model.stride)(image=x) for x in im]

    def write_results(self, idx, results, batch):
        """Write inference results to a file or directory."""
        p, im, _ = batch
        log_string = ''
        if len(im.shape) == 3:
            im = im[None]  # expand for batch dim
        self.seen += 1
        if self.source_type.webcam or self.source_type.from_img:  # batch_size >= 1
            log_string += f'{idx}: '
            frame = self.dataset.count
        else:
            frame = getattr(self.dataset, 'frame', 0)
        self.data_path = p
        self.txt_path = str(self.save_dir / 'labels' / p.stem) + ('' if self.dataset.mode == 'image' else f'_{frame}')
        log_string += '%gx%g ' % im.shape[2:]  # print string
        result = results[idx]
        # print('result', result)
        log_string += result.verbose()

        if self.args.save or self.args.show:  # Add bbox to image
            plot_args = dict(line_width=self.args.line_width,
                             boxes=self.args.boxes,
                             conf=self.args.show_conf,
                             labels=self.args.show_labels)
            if not self.args.retina_masks:
                plot_args['im_gpu'] = im[idx]
            self.plotted_img = result.plot(**plot_args)
        # Write
        if self.args.save_txt:
            result.save_txt(f'{self.txt_path}.txt', save_conf=self.args.save_conf)
        if self.args.save_crop:
            result.save_crop(save_dir=self.save_dir / 'crops', file_name=self.data_path.stem)

        return log_string

    def postprocess(self, preds, img, orig_img):
        """Post-processes predictions for an image and returns them."""
        return preds
    def boxesMov_output(self, path, img_MovBoxes):
        return img_MovBoxes

    def __call__(self, source=None, model=None, stream=False):
        """Performs inference on an image or stream."""
        self.stream = stream
        if stream:
            return self.stream_inference(source, model)
        else:
            # print('*****************',list(self.stream_inference(source, model)))
            return list(self.stream_inference(source, model))  # merge list of Result into one

    def predict_cli(self, source=None, model=None):
        """Method used for CLI prediction. It uses always generator as outputs as not required by CLI mode."""
        gen = self.stream_inference(source, model)
        for _ in gen:  # running CLI inference without accumulating any outputs (do not modify)
            pass

    def setup_source(self, source):
        """Sets up source and inference mode."""
        self.imgsz = check_imgsz(self.args.imgsz, stride=self.model.stride, min_dim=2)  # check image size
        self.transforms = getattr(self.model.model, 'transforms', classify_transforms(
            self.imgsz[0])) if self.args.task == 'classify' else None
        self.dataset = load_inference_source(source=source, imgsz=self.imgsz, vid_stride=self.args.vid_stride)
        self.dataset0 = load_inference_source(source=source, imgsz=self.imgsz, vid_stride=self.args.vid_stride)
        self.source_type = self.dataset.source_type
        if not getattr(self, 'stream', True) and (self.dataset.mode == 'stream' or  # streams
                                                  len(self.dataset) > 1000 or  # images
                                                  any(getattr(self.dataset, 'video_flag', [False]))):  # videos
            LOGGER.warning(STREAM_WARNING)
        self.vid_path, self.vid_writer = [None] * self.dataset.bs, [None] * self.dataset.bs



    @smart_inference_mode()
    def stream_inference(self, source=None, model=None):
        """Streams real-time inference on camera feed and saves results to file."""
        if self.args.verbose:
            LOGGER.info('')
        # Setup model
        if not self.model:
            self.setup_model(model)
        # Setup source every time predict is called
        self.setup_source(source if source is not None else self.args.source)

        # Check if save_dir/ label file exists
        if self.args.save or self.args.save_txt:
            (self.save_dir / 'labels' if self.args.save_txt else self.save_dir).mkdir(parents=True, exist_ok=True)
        # Warmup model
        if not self.done_warmup:
            self.model.warmup(imgsz=(1 if self.model.pt or self.model.triton else self.dataset.bs, 3, *self.imgsz))
            self.done_warmup = True
        self.seen, self.windows, self.batch, self.batch0, profilers = 0, [], None, None, (ops.Profile(), ops.Profile(), ops.Profile())
        self.run_callbacks('on_predict_start')
        allMovBoxes = []
        boxesAll = []
        id = 0
        img_MovBoxes = []
        idx = []
        id0 = 0
        fgbg = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=16, detectShadows=True)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
        for batch0 in self.dataset0:
            path, im0, vid_cap, s = batch0
            id0 += 1
            # video_len = int(str(s).split(')')[0].split('/')[-1])
            # print('video_len', id0, video_len)
            img0 = np.array(im0).squeeze()
            frame = cv2.resize(img0, (512, 640), interpolation=cv2.INTER_CUBIC)
            frame = cv2.medianBlur(frame, ksize=3)
            # 计算前景掩码
            fgmask = fgbg.apply(frame)
            draw1 = cv2.threshold(fgmask, 230, 255, cv2.THRESH_BINARY)[1]  # 二值化
            draw1 = cv2.erode(draw1, kernel, iterations=1)
            draw1 = cv2.dilate(draw1, kernel, iterations=1)
            dics = {}
            contours_m, hierarchy_m = cv2.findContours(draw1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            for contour in contours_m:
                dics[len(contour)] = contour
            if len(dics.keys()) > 0:
                cc = sorted(dics.keys())
                iouArea = cv2.contourArea(dics[cc[-1]])
                print("***********id0", id0, iouArea)
                if iouArea>3000:
                    idx.append(id0)

            # if len(idx) != 0 and abs(id0 - idx[-1]) > 30:
            #     break
            # print('*******id0', id0, idx)
        if len(idx) != 0:
            idx.sort()
            print('idx', idx)
            for batch in self.dataset:
                t0 = time.time()
                id += 1
                if idx[0] <= id <= idx[-1]:
                    self.run_callbacks('on_predict_batch_start')
                    self.batch = batch
                    path, im0s, vid_cap, s = batch

                    visualize = increment_path(self.save_dir / Path(path[0]).stem,
                                               mkdir=True) if self.args.visualize and (not self.source_type.tensor) else False
                    # Preprocess
                    with profilers[0]:
                        im = self.preprocess(im0s)

                    # Inference
                    with profilers[1]:
                        preds = self.model(im, augment=self.args.augment, visualize=visualize)

                    # Postprocess
                    with profilers[2]:
                        self.results = self.postprocess(preds, im, im0s)

                    self.run_callbacks('on_predict_postprocess_end')

                    # Visualize, save, write results
                    n = len(im0s)
                    for i in range(n):
                        tt0 = time.time()
                        im1 = im0s[i].copy()
                        ### create class MovingBox
                        boxesMov = MovingBox(id, self.results[i].boxes.xywh, self.results[i].boxes.xyxy, self.results[i].boxes.conf, path)
                        if id == idx[0]:
                               boxesAll= boxesMov._initiate_box()
                        else:
                            ### allMovBoxes->是运动框的坐标
                            boxesAll, allMovBoxes = boxesMov.update(boxesAll, im1, self.save_dir)
                        if len(allMovBoxes) != 0:
                            mov_id = 0
                            for box in allMovBoxes:
                                box_xyxy = xywh2xyxy(box[0])
                                cropMov = im0s[i][int(box_xyxy[1]):int(box_xyxy[3]), int(box_xyxy[0]):int(box_xyxy[2]), ::-1]
                                # crop_save = os.path.abspath(im0s[i])
                                # Image.fromarray(cropMov).show()
                                ### img_MovBoxes->运动商品图片
                                img_MovBoxes.append([id, cropMov])
                                tt1 = time.time() - tt0
                                # print('运动框判断所需时间{}s'.format(tt1))
                                crop_save = self.save_dir / 'movboxes/'
                                ori_img = self.save_dir / 'oriImage/'
                                if not os.path.exists(crop_save):
                                    # crop_save.parent.mkdir(parents=True, exist_ok=True)
                                    crop_save.mkdir(parents=True, exist_ok=True)
                                if not os.path.exists(ori_img):
                                    ori_img.mkdir()
                                f = str(crop_save) + '\\frame' + str(id) + '_' + str(mov_id) + '_' + '.jpg'
                                g = str(ori_img) + '\\frame' + str(id) + '_' + '.jpg'
                                # Image.fromarray(cropMov[::-1]).save(f, quality=95, subsampling=0)
                                Image.fromarray(cropMov).save(f, quality=95, subsampling=0)
                                # Image.fromarray(im1[:,:,::-1]).save(g, quality=95, subsampling=0)
                                mov_id += 1

                        self.results[i].speed = {
                            'preprocess': profilers[0].dt * 1E3 / n,
                            'inference': profilers[1].dt * 1E3 / n,
                            'postprocess': profilers[2].dt * 1E3 / n}
                        if self.source_type.tensor:  # skip write, show and plot operations if input is raw tensor
                            continue
                        p, im0 = path[i], im0s[i].copy()
                        p = Path(p)
                        if self.args.verbose or self.args.save or self.args.save_txt or self.args.show:
                            s += self.write_results(i, self.results, (p, im, im0))
                        if self.args.show and self.plotted_img is not None:
                            self.show(p)

                        if self.args.save and self.plotted_img is not None:
                            self.save_preds(vid_cap, i, str(self.save_dir / p.name))
                elif id < idx[0]:
                    # self.results = []
                    continue
                else:
                    break
            else:
                for batch in self.dataset:
                    t0 = time.time()
                    id += 1
                    self.run_callbacks('on_predict_batch_start')
                    self.batch = batch
                    path, im0s, vid_cap, s = batch

                    visualize = increment_path(self.save_dir / Path(path[0]).stem,
                                               mkdir=True) if self.args.visualize and (
                        not self.source_type.tensor) else False
                    # Preprocess
                    with profilers[0]:
                        im = self.preprocess(im0s)

                    # Inference
                    with profilers[1]:
                        preds = self.model(im, augment=self.args.augment, visualize=visualize)

                    # Postprocess
                    with profilers[2]:
                        self.results = self.postprocess(preds, im, im0s)

                    self.run_callbacks('on_predict_postprocess_end')

                    # Visualize, save, write results
                    n = len(im0s)
                    for i in range(n):
                        tt0 = time.time()
                        im1 = im0s[i].copy()
                        ### create class MovingBox
                        boxesMov = MovingBox(id, self.results[i].boxes.xywh, self.results[i].boxes.xyxy,
                                             self.results[i].boxes.conf, path)
                        if id == 1:
                            boxesAll = boxesMov._initiate_box()
                        else:
                            ### allMovBoxes->是运动框的坐标
                            boxesAll, allMovBoxes = boxesMov.update(boxesAll, im1, self.save_dir)
                        if len(allMovBoxes) != 0:
                            mov_id = 0
                            for box in allMovBoxes:
                                box_xyxy = xywh2xyxy(box[0])
                                cropMov = im0s[i][int(box_xyxy[1]):int(box_xyxy[3]), int(box_xyxy[0]):int(box_xyxy[2]),
                                          ::-1]
                                # crop_save = os.path.abspath(im0s[i])
                                # Image.fromarray(cropMov).show()
                                ### img_MovBoxes->运动商品图片
                                img_MovBoxes.append([id, cropMov])
                                tt1 = time.time() - tt0
                                # print('运动框判断所需时间{}s'.format(tt1))
                                crop_save = self.save_dir / 'movboxes/'
                                ori_img = self.save_dir / 'oriImage/'
                                if not os.path.exists(crop_save):
                                    # crop_save.parent.mkdir(parents=True, exist_ok=True)
                                    crop_save.mkdir(parents=True, exist_ok=True)
                                if not os.path.exists(ori_img):
                                    ori_img.mkdir()
                                f = str(crop_save) + '\\frame' + str(id) + '_' + str(mov_id) + '_' + '.jpg'
                                g = str(ori_img) + '\\frame' + str(id) + '_' + '.jpg'
                                # Image.fromarray(cropMov[::-1]).save(f, quality=95, subsampling=0)
                                Image.fromarray(cropMov).save(f, quality=95, subsampling=0)
                                # Image.fromarray(im1[:,:,::-1]).save(g, quality=95, subsampling=0)
                                mov_id += 1

                        self.results[i].speed = {
                            'preprocess': profilers[0].dt * 1E3 / n,
                            'inference': profilers[1].dt * 1E3 / n,
                            'postprocess': profilers[2].dt * 1E3 / n}
                        if self.source_type.tensor:  # skip write, show and plot operations if input is raw tensor
                            continue
                        p, im0 = path[i], im0s[i].copy()
                        p = Path(p)
                        if self.args.verbose or self.args.save or self.args.save_txt or self.args.show:
                            s += self.write_results(i, self.results, (p, im, im0))
                        if self.args.show and self.plotted_img is not None:
                            self.show(p)

                        if self.args.save and self.plotted_img is not None:
                            self.save_preds(vid_cap, i, str(self.save_dir / p.name))

                self.run_callbacks('on_predict_batch_end')
                yield from self.results
                # Print time (inference-only)
                if self.args.verbose:
                    LOGGER.info(f'{s}{profilers[1].dt * 1E3:.1f}ms')
                t1 = time.time() - t0
                # print('推理所需时间：{}s'.format(t1))

        t2 = time.time()
        self.real_MovBox = self.boxesMov_output(path, img_MovBoxes)
        for i in range(len(self.results)):
            self.results[i].movBoxes = self.real_MovBox
            # print("self.results11111111111", self.results[i])
        t3 = time.time() - t2
        print('保留最后十帧所有运动框所需时间：{}s'.format(t3))

        # Release assets
        if isinstance(self.vid_writer[-1], cv2.VideoWriter):
            self.vid_writer[-1].release()  # release final video writer

        # Print results
        if self.args.verbose and self.seen:
            t = tuple(x.t / self.seen * 1E3 for x in profilers)  # speeds per image
            LOGGER.info(f'Speed: %.1fms preprocess, %.1fms inference, %.1fms postprocess per image at shape '
                        f'{(1, 3, *self.imgsz)}' % t)
        if self.args.save or self.args.save_txt or self.args.save_crop:
            nl = len(list(self.save_dir.glob('labels/*.txt')))  # number of labels
            s = f"\n{nl} label{'s' * (nl > 1)} saved to {self.save_dir / 'labels'}" if self.args.save_txt else ''
            LOGGER.info(f"Results saved to {colorstr('bold', self.save_dir)}{s}")

        self.run_callbacks('on_predict_end')



    def setup_model(self, model, verbose=True):
        """Initialize YOLO model with given parameters and set it to evaluation mode."""
        device = select_device(self.args.device, verbose=verbose)
        model = model or self.args.model
        self.args.half &= device.type != 'cpu'  # half precision only supported on CUDA
        self.model = AutoBackend(model,
                                 device=device,
                                 dnn=self.args.dnn,
                                 data=self.args.data,
                                 fp16=self.args.half,
                                 fuse=True,
                                 verbose=verbose)
        self.device = device
        self.model.eval()

    def show(self, p):
        """Display an image in a window using OpenCV imshow()."""
        im0 = self.plotted_img
        if platform.system() == 'Linux' and p not in self.windows:
            self.windows.append(p)
            cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)  # allow window resize (Linux)
            cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
        cv2.imshow(str(p), im0)
        cv2.waitKey(500 if self.batch[3].startswith('image') else 1)  # 1 millisecond

    def save_preds(self, vid_cap, idx, save_path):
        """Save video predictions as mp4 at specified path."""
        im0 = self.plotted_img
        # Save imgs
        if self.dataset.mode == 'image':
            cv2.imwrite(save_path, im0)
        else:  # 'video' or 'stream'
            if self.vid_path[idx] != save_path:  # new video
                self.vid_path[idx] = save_path
                if isinstance(self.vid_writer[idx], cv2.VideoWriter):
                    self.vid_writer[idx].release()  # release previous video writer
                if vid_cap:  # video
                    fps = int(vid_cap.get(cv2.CAP_PROP_FPS))  # integer required, floats produce error in MP4 codec
                    w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
                    h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
                else:  # stream
                    fps, w, h = 30, im0.shape[1], im0.shape[0]
                save_path = str(Path(save_path).with_suffix('.mp4'))  # force *.mp4 suffix on results videos
                self.vid_writer[idx] = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h))
            self.vid_writer[idx].write(im0)

    def run_callbacks(self, event: str):
        """Runs all registered callbacks for a specific event."""
        for callback in self.callbacks.get(event, []):
            callback(self)

    def add_callback(self, event: str, func):
        """
        Add callback
        """
        self.callbacks[event].append(func)

class BoxState:
    Static = 0
    Moving = 1
class allBoxes:
    def __init__(self, fame_id, tr_id, xywh):
        self.fame_id = fame_id
        self.state = BoxState.Static
        self.track_id = tr_id
        self.xywh = xywh
    def is_static(self):
        return self.state == BoxState.Static
    def is_moving(self):
        return self.state == BoxState.Moving
def xywh2xyxy(x):
    # Convert nx4 boxes from [x, y, w, h] to [x1, y1, x2, y2] where xy1=top-left, xy2=bottom-right
    y = x.clone() if isinstance(x, torch.Tensor) else np.copy(x)
    y[0] = x[0] - x[2] / 2  # top left x
    y[1] = x[1] - x[3] / 2  # top left y
    y[2] = x[0] + x[2] / 2  # bottom right x
    y[3] = x[1] + x[3] / 2  # bottom right y
    return y
def plot_one_box(x, img,  trid, line_thickness=3,color=None):
    # Plots one bounding box on image img
    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1]) / 2) + 1  # line/font thickness
    # print('x',x)
    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))
    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
    cv2.putText(img, str(trid), (c1[0], c1[1] - 2), 0, tl / 3, [0, 0, 255], thickness=6, lineType=cv2.LINE_AA)
def compute_iou(box1, box2):
    xx1 = max(box1[0], box2[0])
    yy1 = max(box1[1], box2[1])
    xx2 = min(box1[2], box2[2])
    yy2 = min(box1[3], box2[3])
    w = max(0., xx2 - xx1)
    h = max(0., yy2 - yy1)
    wh = w * h
    o = wh / ((box1[2] - box1[0]) * (box1[3] - box1[1])
              + (box2[2] - box2[0]) * (box2[3] - box2[1]) - wh)
    return o

# def sort_(elem):
#     return elem[0]
class MovingBox(object):
    def __init__(self, frame_id, xywh, xyxy, conf, path):
        # self.track_id = 0
        self.frame_id = frame_id
        # self.state = 1    # 0 static, 1 moving
        self.xywh = xywh
        self.conf = conf
        self.xyxy = xyxy
        self.min_thr = 0.8
        self.max_thr = 1.2
        self.iou_thr = 0.9
        self.path = path

    def _initiate_box(self):
        boxes = []
        state = 0
        for i in range(len(self.xywh)):
            boxes.append([self.xywh[i], state, self.xyxy[i]])
        # print('moving_box', boxes)
        return boxes

    def update(self, boxes, img, save_dir):
        index = []
        allBoxes = []
        state = 1
        print(">>>>>>>>>>>>id", self.frame_id)
        # if len(self.xywh) != 0:   ##detect box 不为空
        if len(boxes) == 0:
            for i in range(len(self.xywh)):
                allBoxes.append([self.xywh[i], state, self.xyxy[i]])

        else:
            for i in range(len(self.xywh)):  ## self.xywh为当前帧的检测框
                flag0 = 0  # 0 长宽比未匹配上, 1 匹配上
                for j in range(len(boxes)):  ## boxes为前一帧的检测框
                    rw = self.xywh[i][2] / boxes[j][0][2]
                    rh = self.xywh[i][3] / boxes[j][0][3]
                    area0 = boxes[j][0][2] * boxes[j][0][3]
                    area1 = self.xywh[i][2] * self.xywh[i][3]
                    area_r = area1 / area0
                    # print(f'detect_{i} track_{j}')
                    # print("rw,rh,area_r", rw, rh, area_r)
                    ##可能短边比长边，可能长边比短边
                    if (rw < self.max_thr and rw > self.min_thr) and (rh < self.max_thr and rh > self.min_thr) \
                            and (area_r < self.max_thr and area_r > self.min_thr):
                        flag0 = 1
                        iou = compute_iou(self.xyxy[i], boxes[j][2])
                        print('iou',iou)
                        if iou > self.iou_thr:
                            index.append(i)
                            break
            print("=====last_box", boxes)
            print('index', index)

            for i in range(len(self.xywh)):
                if i in index:
                    state = 0
                    allBoxes.append([self.xywh[i], state, self.xyxy[i]])
                else:
                    state = 1
                    allBoxes.append([self.xywh[i], state, self.xyxy[i]])
                    # track_id += 1
        moving_boxes = [box for box in allBoxes if box[1]==1]
        if len(self.xywh) != 0:
            for i, x in enumerate(allBoxes):
                xyxy = xywh2xyxy(x[0])
                if x[1] == 1:
                    # flag = str(i) + '_moving_'
                    flag = 'moving_'
                    box_color = (0,255,0)
                else:
                    # flag = str(i) + '_static_'
                    flag = 'static_'
                    box_color = (0,0,255)
                plot_one_box(xyxy, img, f'{flag}{self.conf[i].cpu().numpy():.2f}', color=box_color)
                path2 = save_dir / 'detect_result/'
                if not os.path.exists(path2):
                    path2.mkdir(parents=True, exist_ok=True)
                cv2.imwrite(os.sep.join([str(path2), str(self.path).split('.mp4')[0].split('\\')[-1]+ "_" + str(self.frame_id) + '.jpg']), img)
        else:
            path2 = save_dir / 'detect_result/'
            if not os.path.exists(path2):
                path2.mkdir(parents=True, exist_ok=True)
            cv2.imwrite(os.sep.join([str(path2), str(self.path).split('.mp4')[0].split('\\')[-1] + "_" + str(self.frame_id) + '.jpg']), img)
        return allBoxes, moving_boxes





