ultralytics 8.3.11__py3-none-any.whl → 8.3.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ultralytics might be problematic. Click here for more details.

tests/test_cli.py CHANGED
@@ -97,9 +97,12 @@ def test_mobilesam():
97
97
  # Source
98
98
  source = ASSETS / "zidane.jpg"
99
99
 
100
- # Predict a segment based on a point prompt
100
+ # Predict a segment based on a 1D point prompt and 1D labels.
101
101
  model.predict(source, points=[900, 370], labels=[1])
102
102
 
103
+ # Predict a segment based on 3D points and 2D labels (multiple points per object).
104
+ model.predict(source, points=[[[900, 370], [1000, 100]]], labels=[[1, 1]])
105
+
103
106
  # Predict a segment based on a box prompt
104
107
  model.predict(source, bboxes=[439, 437, 524, 709], save=True)
105
108
 
tests/test_cuda.py CHANGED
@@ -127,9 +127,21 @@ def test_predict_sam():
127
127
  # Run inference with bboxes prompt
128
128
  model(SOURCE, bboxes=[439, 437, 524, 709], device=0)
129
129
 
130
- # Run inference with points prompt
130
+ # Run inference with no labels
131
+ model(ASSETS / "zidane.jpg", points=[900, 370], device=0)
132
+
133
+ # Run inference with 1D points and 1D labels
131
134
  model(ASSETS / "zidane.jpg", points=[900, 370], labels=[1], device=0)
132
135
 
136
+ # Run inference with 2D points and 1D labels
137
+ model(ASSETS / "zidane.jpg", points=[[900, 370]], labels=[1], device=0)
138
+
139
+ # Run inference with multiple 2D points and 1D labels
140
+ model(ASSETS / "zidane.jpg", points=[[400, 370], [900, 370]], labels=[1, 1], device=0)
141
+
142
+ # Run inference with 3D points and 2D labels (multiple points per object)
143
+ model(ASSETS / "zidane.jpg", points=[[[900, 370], [1000, 100]]], labels=[[1, 1]], device=0)
144
+
133
145
  # Create SAMPredictor
134
146
  overrides = dict(conf=0.25, task="segment", mode="predict", imgsz=1024, model=WEIGHTS_DIR / "mobile_sam.pt")
135
147
  predictor = SAMPredictor(overrides=overrides)
ultralytics/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # Ultralytics YOLO 🚀, AGPL-3.0 license
2
2
 
3
- __version__ = "8.3.11"
3
+ __version__ = "8.3.12"
4
4
 
5
5
  import os
6
6
 
@@ -8,7 +8,6 @@ import os
8
8
  if not os.environ.get("OMP_NUM_THREADS"):
9
9
  os.environ["OMP_NUM_THREADS"] = "1" # default for reduced CPU utilization during training
10
10
 
11
- from ultralytics.data.explorer.explorer import Explorer
12
11
  from ultralytics.models import NAS, RTDETR, SAM, YOLO, FastSAM, YOLOWorld
13
12
  from ultralytics.utils import ASSETS, SETTINGS
14
13
  from ultralytics.utils.checks import check_yolo as checks
@@ -27,5 +26,4 @@ __all__ = (
27
26
  "checks",
28
27
  "download",
29
28
  "settings",
30
- "Explorer",
31
29
  )
@@ -79,14 +79,11 @@ CLI_HELP_MSG = f"""
79
79
 
80
80
  4. Export a YOLO11n classification model to ONNX format at image size 224 by 128 (no TASK required)
81
81
  yolo export model=yolo11n-cls.pt format=onnx imgsz=224,128
82
-
83
- 5. Explore your datasets using semantic search and SQL with a simple GUI powered by Ultralytics Explorer API
84
- yolo explorer data=data.yaml model=yolo11n.pt
85
82
 
86
- 6. Streamlit real-time webcam inference GUI
83
+ 5. Streamlit real-time webcam inference GUI
87
84
  yolo streamlit-predict
88
85
 
89
- 7. Run special commands:
86
+ 6. Run special commands:
90
87
  yolo help
91
88
  yolo checks
92
89
  yolo version
@@ -546,35 +543,6 @@ def handle_yolo_settings(args: List[str]) -> None:
546
543
  LOGGER.warning(f"WARNING ⚠️ settings error: '{e}'. Please see {url} for help.")
547
544
 
548
545
 
549
- def handle_explorer(args: List[str]):
550
- """
551
- Launches a graphical user interface that provides tools for interacting with and analyzing datasets using the
552
- Ultralytics Explorer API. It checks for the required 'streamlit' package and informs the user that the Explorer
553
- dashboard is loading.
554
-
555
- Args:
556
- args (List[str]): A list of optional command line arguments.
557
-
558
- Examples:
559
- ```bash
560
- yolo explorer data=data.yaml model=yolo11n.pt
561
- ```
562
-
563
- Notes:
564
- - Requires 'streamlit' package version 1.29.0 or higher.
565
- - The function does not take any arguments or return any values.
566
- - It is typically called from the command line interface using the 'yolo explorer' command.
567
- """
568
- checks.check_requirements("streamlit>=1.29.0")
569
- LOGGER.info("💡 Loading Explorer dashboard...")
570
- cmd = ["streamlit", "run", ROOT / "data/explorer/gui/dash.py", "--server.maxMessageSize", "2048"]
571
- new = dict(parse_key_value_pair(a) for a in args)
572
- check_dict_alignment(base={k: DEFAULT_CFG_DICT[k] for k in ["model", "data"]}, custom=new)
573
- for k, v in new.items():
574
- cmd += [k, v]
575
- subprocess.run(cmd)
576
-
577
-
578
546
  def handle_streamlit_inference():
579
547
  """
580
548
  Open the Ultralytics Live Inference Streamlit app for real-time object detection.
@@ -715,7 +683,6 @@ def entrypoint(debug=""):
715
683
  "login": lambda: handle_yolo_hub(args),
716
684
  "logout": lambda: handle_yolo_hub(args),
717
685
  "copy-cfg": copy_default_cfg,
718
- "explorer": lambda: handle_explorer(args[1:]),
719
686
  "streamlit-predict": lambda: handle_streamlit_inference(),
720
687
  }
721
688
  full_args_dict = {**DEFAULT_CFG_DICT, **{k: None for k in TASKS}, **{k: None for k in MODES}, **special}
@@ -14,3 +14,4 @@ up_angle: 145.0 # Workouts up_angle for counts, 145.0 is default value. You can
14
14
  down_angle: 90 # Workouts down_angle for counts, 90 is default value. You can change it for different workouts, based on position of keypoints.
15
15
  kpts: [6, 8, 10] # Keypoints for workouts monitoring, i.e. If you want to consider keypoints for pushups that have mostly values of [6, 8, 10].
16
16
  colormap: # Colormap for heatmap, Only OPENCV supported colormaps can be used. By default COLORMAP_PARULA will be used for visualization.
17
+ analytics_type: "line" # Analytics type i.e "line", "pie", "bar" or "area" charts. By default, "line" analytics will be used for processing.
@@ -960,7 +960,15 @@ class Exporter:
960
960
  LOGGER.info(f"\n{prefix} starting export with Edge TPU compiler {ver}...")
961
961
  f = str(tflite_model).replace(".tflite", "_edgetpu.tflite") # Edge TPU model
962
962
 
963
- cmd = f'edgetpu_compiler -s -d -k 10 --out_dir "{Path(f).parent}" "{tflite_model}"'
963
+ cmd = (
964
+ "edgetpu_compiler "
965
+ f'--out_dir "{Path(f).parent}" '
966
+ "--show_operations "
967
+ "--search_delegate "
968
+ "--delegate_search_step 3 "
969
+ "--timeout_sec 180 "
970
+ f'"{tflite_model}"'
971
+ )
964
972
  LOGGER.info(f"{prefix} running '{cmd}'")
965
973
  subprocess.run(cmd, shell=True)
966
974
  self._add_tflite_metadata(f)
@@ -213,11 +213,14 @@ class Predictor(BasePredictor):
213
213
  Args:
214
214
  im (torch.Tensor): Preprocessed input image tensor with shape (N, C, H, W).
215
215
  bboxes (np.ndarray | List | None): Bounding boxes in XYXY format with shape (N, 4).
216
- points (np.ndarray | List | None): Points indicating object locations with shape (N, 2), in pixels.
217
- labels (np.ndarray | List | None): Point prompt labels with shape (N,). 1 for foreground, 0 for background.
216
+ points (np.ndarray | List | None): Points indicating object locations with shape (N, 2) or (N, num_points, 2), in pixels.
217
+ labels (np.ndarray | List | None): Point prompt labels with shape (N,) or (N, num_points). 1 for foreground, 0 for background.
218
218
  masks (np.ndarray | None): Low-res masks from previous predictions with shape (N, H, W). For SAM, H=W=256.
219
219
  multimask_output (bool): Flag to return multiple masks for ambiguous prompts.
220
220
 
221
+ Raises:
222
+ AssertionError: If the number of points don't match the number of labels, in case labels were passed.
223
+
221
224
  Returns:
222
225
  (tuple): Tuple containing:
223
226
  - np.ndarray: Output masks with shape (C, H, W), where C is the number of generated masks.
@@ -240,11 +243,15 @@ class Predictor(BasePredictor):
240
243
  points = points[None] if points.ndim == 1 else points
241
244
  # Assuming labels are all positive if users don't pass labels.
242
245
  if labels is None:
243
- labels = np.ones(points.shape[0])
246
+ labels = np.ones(points.shape[:-1])
244
247
  labels = torch.as_tensor(labels, dtype=torch.int32, device=self.device)
248
+ assert (
249
+ points.shape[-2] == labels.shape[-1]
250
+ ), f"Number of points {points.shape[-2]} should match number of labels {labels.shape[-1]}."
245
251
  points *= r
246
- # (N, 2) --> (N, 1, 2), (N, ) --> (N, 1)
247
- points, labels = points[:, None, :], labels[:, None]
252
+ if points.ndim == 2:
253
+ # (N, 2) --> (N, 1, 2), (N, ) --> (N, 1)
254
+ points, labels = points[:, None, :], labels[:, None]
248
255
  if bboxes is not None:
249
256
  bboxes = torch.as_tensor(bboxes, dtype=torch.float32, device=self.device)
250
257
  bboxes = bboxes[None] if bboxes.ndim == 1 else bboxes
@@ -1,6 +1,5 @@
1
1
  # Ultralytics YOLO 🚀, AGPL-3.0 license
2
2
 
3
- import warnings
4
3
  from itertools import cycle
5
4
 
6
5
  import cv2
@@ -9,299 +8,187 @@ import numpy as np
9
8
  from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
10
9
  from matplotlib.figure import Figure
11
10
 
11
+ from ultralytics.solutions.solutions import BaseSolution # Import a parent class
12
12
 
13
- class Analytics:
13
+
14
+ class Analytics(BaseSolution):
14
15
  """A class to create and update various types of charts (line, bar, pie, area) for visual analytics."""
15
16
 
16
- def __init__(
17
- self,
18
- type,
19
- writer,
20
- im0_shape,
21
- title="ultralytics",
22
- x_label="x",
23
- y_label="y",
24
- bg_color="white",
25
- fg_color="black",
26
- line_color="yellow",
27
- line_width=2,
28
- points_width=10,
29
- fontsize=13,
30
- view_img=False,
31
- save_img=True,
32
- max_points=50,
33
- ):
34
- """
35
- Initialize the Analytics class with various chart types.
17
+ def __init__(self, **kwargs):
18
+ """Initialize the Analytics class with various chart types."""
19
+ super().__init__(**kwargs)
36
20
 
37
- Args:
38
- type (str): Type of chart to initialize ('line', 'bar', 'pie', or 'area').
39
- writer (object): Video writer object to save the frames.
40
- im0_shape (tuple): Shape of the input image (width, height).
41
- title (str): Title of the chart.
42
- x_label (str): Label for the x-axis.
43
- y_label (str): Label for the y-axis.
44
- bg_color (str): Background color of the chart.
45
- fg_color (str): Foreground (text) color of the chart.
46
- line_color (str): Line color for line charts.
47
- line_width (int): Width of the lines in line charts.
48
- points_width (int): Width of line points highlighter
49
- fontsize (int): Font size for chart text.
50
- view_img (bool): Whether to display the image.
51
- save_img (bool): Whether to save the image.
52
- max_points (int): Specifies when to remove the oldest points in a graph for multiple lines.
53
- """
54
- self.bg_color = bg_color
55
- self.fg_color = fg_color
56
- self.view_img = view_img
57
- self.save_img = save_img
58
- self.title = title
59
- self.writer = writer
60
- self.max_points = max_points
61
- self.line_color = line_color
62
- self.x_label = x_label
63
- self.y_label = y_label
64
- self.points_width = points_width
65
- self.line_width = line_width
66
- self.fontsize = fontsize
21
+ self.type = self.CFG["analytics_type"] # extract type of analytics
22
+ self.x_label = "Classes" if self.type in {"bar", "pie"} else "Frame#"
23
+ self.y_label = "Total Counts"
24
+
25
+ # Predefined data
26
+ self.bg_color = "#00F344" # background color of frame
27
+ self.fg_color = "#111E68" # foreground color of frame
28
+ self.title = "Ultralytics Solutions" # window name
29
+ self.max_points = 45 # maximum points to be drawn on window
30
+ self.fontsize = 25 # text font size for display
31
+ figsize = (19.2, 10.8) # Set output image size 1920 * 1080
32
+ self.color_cycle = cycle(["#DD00BA", "#042AFF", "#FF4447", "#7D24FF", "#BD00FF"])
67
33
 
68
- # Set figure size based on image shape
69
- figsize = (im0_shape[0] / 100, im0_shape[1] / 100)
34
+ self.total_counts = 0 # count variable for storing total counts i.e for line
35
+ self.clswise_count = {} # dictionary for classwise counts
70
36
 
71
- if type in {"line", "area"}:
72
- # Initialize line or area plot
37
+ # Ensure line and area chart
38
+ if self.type in {"line", "area"}:
73
39
  self.lines = {}
74
40
  self.fig = Figure(facecolor=self.bg_color, figsize=figsize)
75
- self.canvas = FigureCanvas(self.fig)
41
+ self.canvas = FigureCanvas(self.fig) # Set common axis properties
76
42
  self.ax = self.fig.add_subplot(111, facecolor=self.bg_color)
77
- if type == "line":
78
- (self.line,) = self.ax.plot([], [], color=self.line_color, linewidth=self.line_width)
79
-
80
- elif type in {"bar", "pie"}:
43
+ if self.type == "line":
44
+ (self.line,) = self.ax.plot([], [], color="cyan", linewidth=self.line_width)
45
+ elif self.type in {"bar", "pie"}:
81
46
  # Initialize bar or pie plot
82
47
  self.fig, self.ax = plt.subplots(figsize=figsize, facecolor=self.bg_color)
48
+ self.canvas = FigureCanvas(self.fig) # Set common axis properties
83
49
  self.ax.set_facecolor(self.bg_color)
84
- color_palette = [
85
- (31, 119, 180),
86
- (255, 127, 14),
87
- (44, 160, 44),
88
- (214, 39, 40),
89
- (148, 103, 189),
90
- (140, 86, 75),
91
- (227, 119, 194),
92
- (127, 127, 127),
93
- (188, 189, 34),
94
- (23, 190, 207),
95
- ]
96
- self.color_palette = [(r / 255, g / 255, b / 255, 1) for r, g, b in color_palette]
97
- self.color_cycle = cycle(self.color_palette)
98
50
  self.color_mapping = {}
51
+ self.ax.axis("equal") if type == "pie" else None # Ensure pie chart is circular
99
52
 
100
- # Ensure pie chart is circular
101
- self.ax.axis("equal") if type == "pie" else None
102
-
103
- # Set common axis properties
104
- self.ax.set_title(self.title, color=self.fg_color, fontsize=self.fontsize)
105
- self.ax.set_xlabel(x_label, color=self.fg_color, fontsize=self.fontsize - 3)
106
- self.ax.set_ylabel(y_label, color=self.fg_color, fontsize=self.fontsize - 3)
107
- self.ax.tick_params(axis="both", colors=self.fg_color)
53
+ def process_data(self, im0, frame_number):
54
+ """
55
+ Process the image data, run object tracking.
108
56
 
109
- def update_area(self, frame_number, counts_dict):
57
+ Args:
58
+ im0 (ndarray): Input image for processing.
59
+ frame_number (int): Video frame # for plotting the data.
60
+ """
61
+ self.extract_tracks(im0) # Extract tracks
62
+
63
+ if self.type == "line":
64
+ for box in self.boxes:
65
+ self.total_counts += 1
66
+ im0 = self.update_graph(frame_number=frame_number)
67
+ self.total_counts = 0
68
+ elif self.type == "pie" or self.type == "bar" or self.type == "area":
69
+ self.clswise_count = {}
70
+ for box, cls in zip(self.boxes, self.clss):
71
+ if self.names[int(cls)] in self.clswise_count:
72
+ self.clswise_count[self.names[int(cls)]] += 1
73
+ else:
74
+ self.clswise_count[self.names[int(cls)]] = 1
75
+ im0 = self.update_graph(frame_number=frame_number, count_dict=self.clswise_count, plot=self.type)
76
+ else:
77
+ raise ModuleNotFoundError(f"{self.type} chart is not supported ❌")
78
+ return im0
79
+
80
+ def update_graph(self, frame_number, count_dict=None, plot="line"):
110
81
  """
111
- Update the area graph with new data for multiple classes.
82
+ Update the graph (line or area) with new data for single or multiple classes.
112
83
 
113
84
  Args:
114
85
  frame_number (int): The current frame number.
115
- counts_dict (dict): Dictionary with class names as keys and counts as values.
86
+ count_dict (dict, optional): Dictionary with class names as keys and counts as values for multiple classes.
87
+ If None, updates a single line graph.
88
+ plot (str): Type of the plot i.e. line, bar or area.
116
89
  """
117
- x_data = np.array([])
118
- y_data_dict = {key: np.array([]) for key in counts_dict.keys()}
119
-
120
- if self.ax.lines:
121
- x_data = self.ax.lines[0].get_xdata()
122
- for line, key in zip(self.ax.lines, counts_dict.keys()):
123
- y_data_dict[key] = line.get_ydata()
124
-
125
- x_data = np.append(x_data, float(frame_number))
126
- max_length = len(x_data)
127
-
128
- for key in counts_dict.keys():
129
- y_data_dict[key] = np.append(y_data_dict[key], float(counts_dict[key]))
130
- if len(y_data_dict[key]) < max_length:
131
- y_data_dict[key] = np.pad(y_data_dict[key], (0, max_length - len(y_data_dict[key])), "constant")
132
-
133
- # Remove the oldest points if the number of points exceeds max_points
134
- if len(x_data) > self.max_points:
135
- x_data = x_data[1:]
136
- for key in counts_dict.keys():
137
- y_data_dict[key] = y_data_dict[key][1:]
138
-
139
- self.ax.clear()
140
-
141
- colors = ["#E1FF25", "#0BDBEB", "#FF64DA", "#111F68", "#042AFF"]
142
- color_cycle = cycle(colors)
143
-
144
- for key, y_data in y_data_dict.items():
145
- color = next(color_cycle)
146
- self.ax.fill_between(x_data, y_data, color=color, alpha=0.6)
147
- self.ax.plot(
148
- x_data,
149
- y_data,
150
- color=color,
151
- linewidth=self.line_width,
152
- marker="o",
153
- markersize=self.points_width,
154
- label=f"{key} Data Points",
155
- )
156
-
90
+ if count_dict is None:
91
+ # Single line update
92
+ x_data = np.append(self.line.get_xdata(), float(frame_number))
93
+ y_data = np.append(self.line.get_ydata(), float(self.total_counts))
94
+
95
+ if len(x_data) > self.max_points:
96
+ x_data, y_data = x_data[-self.max_points :], y_data[-self.max_points :]
97
+
98
+ self.line.set_data(x_data, y_data)
99
+ self.line.set_label("Counts")
100
+ self.line.set_color("#7b0068") # Pink color
101
+ self.line.set_marker("*")
102
+ self.line.set_markersize(self.line_width * 5)
103
+ else:
104
+ labels = list(count_dict.keys())
105
+ counts = list(count_dict.values())
106
+ if plot == "area":
107
+ color_cycle = cycle(["#DD00BA", "#042AFF", "#FF4447", "#7D24FF", "#BD00FF"])
108
+ # Multiple lines or area update
109
+ x_data = self.ax.lines[0].get_xdata() if self.ax.lines else np.array([])
110
+ y_data_dict = {key: np.array([]) for key in count_dict.keys()}
111
+ if self.ax.lines:
112
+ for line, key in zip(self.ax.lines, count_dict.keys()):
113
+ y_data_dict[key] = line.get_ydata()
114
+
115
+ x_data = np.append(x_data, float(frame_number))
116
+ max_length = len(x_data)
117
+ for key in count_dict.keys():
118
+ y_data_dict[key] = np.append(y_data_dict[key], float(count_dict[key]))
119
+ if len(y_data_dict[key]) < max_length:
120
+ y_data_dict[key] = np.pad(y_data_dict[key], (0, max_length - len(y_data_dict[key])), "constant")
121
+ if len(x_data) > self.max_points:
122
+ x_data = x_data[1:]
123
+ for key in count_dict.keys():
124
+ y_data_dict[key] = y_data_dict[key][1:]
125
+
126
+ self.ax.clear()
127
+ for key, y_data in y_data_dict.items():
128
+ color = next(color_cycle)
129
+ self.ax.fill_between(x_data, y_data, color=color, alpha=0.7)
130
+ self.ax.plot(
131
+ x_data,
132
+ y_data,
133
+ color=color,
134
+ linewidth=self.line_width,
135
+ marker="o",
136
+ markersize=self.line_width * 5,
137
+ label=f"{key} Data Points",
138
+ )
139
+ if plot == "bar":
140
+ self.ax.clear() # clear bar data
141
+ for label in labels: # Map labels to colors
142
+ if label not in self.color_mapping:
143
+ self.color_mapping[label] = next(self.color_cycle)
144
+ colors = [self.color_mapping[label] for label in labels]
145
+ bars = self.ax.bar(labels, counts, color=colors)
146
+ for bar, count in zip(bars, counts):
147
+ self.ax.text(
148
+ bar.get_x() + bar.get_width() / 2,
149
+ bar.get_height(),
150
+ str(count),
151
+ ha="center",
152
+ va="bottom",
153
+ color=self.fg_color,
154
+ )
155
+ # Create the legend using labels from the bars
156
+ for bar, label in zip(bars, labels):
157
+ bar.set_label(label) # Assign label to each bar
158
+ self.ax.legend(loc="upper left", fontsize=13, facecolor=self.fg_color, edgecolor=self.fg_color)
159
+ if plot == "pie":
160
+ total = sum(counts)
161
+ percentages = [size / total * 100 for size in counts]
162
+ start_angle = 90
163
+ self.ax.clear()
164
+
165
+ # Create pie chart and create legend labels with percentages
166
+ wedges, autotexts = self.ax.pie(
167
+ counts, labels=labels, startangle=start_angle, textprops={"color": self.fg_color}, autopct=None
168
+ )
169
+ legend_labels = [f"{label} ({percentage:.1f}%)" for label, percentage in zip(labels, percentages)]
170
+
171
+ # Assign the legend using the wedges and manually created labels
172
+ self.ax.legend(wedges, legend_labels, title="Classes", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))
173
+ self.fig.subplots_adjust(left=0.1, right=0.75) # Adjust layout to fit the legend
174
+
175
+ # Common plot settings
176
+ self.ax.set_facecolor("#f0f0f0") # Set to light gray or any other color you like
157
177
  self.ax.set_title(self.title, color=self.fg_color, fontsize=self.fontsize)
158
178
  self.ax.set_xlabel(self.x_label, color=self.fg_color, fontsize=self.fontsize - 3)
159
179
  self.ax.set_ylabel(self.y_label, color=self.fg_color, fontsize=self.fontsize - 3)
160
- legend = self.ax.legend(loc="upper left", fontsize=13, facecolor=self.bg_color, edgecolor=self.fg_color)
161
180
 
162
- # Set legend text color
181
+ # Add and format legend
182
+ legend = self.ax.legend(loc="upper left", fontsize=13, facecolor=self.bg_color, edgecolor=self.bg_color)
163
183
  for text in legend.get_texts():
164
184
  text.set_color(self.fg_color)
165
185
 
166
- self.canvas.draw()
167
- im0 = np.array(self.canvas.renderer.buffer_rgba())
168
- self.write_and_display(im0)
169
-
170
- def update_line(self, frame_number, total_counts):
171
- """
172
- Update the line graph with new data.
173
-
174
- Args:
175
- frame_number (int): The current frame number.
176
- total_counts (int): The total counts to plot.
177
- """
178
- # Update line graph data
179
- x_data = self.line.get_xdata()
180
- y_data = self.line.get_ydata()
181
- x_data = np.append(x_data, float(frame_number))
182
- y_data = np.append(y_data, float(total_counts))
183
- self.line.set_data(x_data, y_data)
186
+ # Redraw graph, update view, capture, and display the updated plot
184
187
  self.ax.relim()
185
188
  self.ax.autoscale_view()
186
189
  self.canvas.draw()
187
190
  im0 = np.array(self.canvas.renderer.buffer_rgba())
188
- self.write_and_display(im0)
189
-
190
- def update_multiple_lines(self, counts_dict, labels_list, frame_number):
191
- """
192
- Update the line graph with multiple classes.
193
-
194
- Args:
195
- counts_dict (int): Dictionary include each class counts.
196
- labels_list (int): list include each classes names.
197
- frame_number (int): The current frame number.
198
- """
199
- warnings.warn("Display is not supported for multiple lines, output will be stored normally!")
200
- for obj in labels_list:
201
- if obj not in self.lines:
202
- (line,) = self.ax.plot([], [], label=obj, marker="o", markersize=self.points_width)
203
- self.lines[obj] = line
204
-
205
- x_data = self.lines[obj].get_xdata()
206
- y_data = self.lines[obj].get_ydata()
207
-
208
- # Remove the initial point if the number of points exceeds max_points
209
- if len(x_data) >= self.max_points:
210
- x_data = np.delete(x_data, 0)
211
- y_data = np.delete(y_data, 0)
212
-
213
- x_data = np.append(x_data, float(frame_number)) # Ensure frame_number is converted to float
214
- y_data = np.append(y_data, float(counts_dict.get(obj, 0))) # Ensure total_count is converted to float
215
- self.lines[obj].set_data(x_data, y_data)
216
-
217
- self.ax.relim()
218
- self.ax.autoscale_view()
219
- self.ax.legend()
220
- self.canvas.draw()
221
-
222
- im0 = np.array(self.canvas.renderer.buffer_rgba())
223
- self.view_img = False # for multiple line view_img not supported yet, coming soon!
224
- self.write_and_display(im0)
225
-
226
- def write_and_display(self, im0):
227
- """
228
- Write and display the line graph
229
- Args:
230
- im0 (ndarray): Image for processing.
231
- """
232
191
  im0 = cv2.cvtColor(im0[:, :, :3], cv2.COLOR_RGBA2BGR)
233
- cv2.imshow(self.title, im0) if self.view_img else None
234
- self.writer.write(im0) if self.save_img else None
235
-
236
- def update_bar(self, count_dict):
237
- """
238
- Update the bar graph with new data.
239
-
240
- Args:
241
- count_dict (dict): Dictionary containing the count data to plot.
242
- """
243
- # Update bar graph data
244
- self.ax.clear()
245
- self.ax.set_facecolor(self.bg_color)
246
- labels = list(count_dict.keys())
247
- counts = list(count_dict.values())
248
-
249
- # Map labels to colors
250
- for label in labels:
251
- if label not in self.color_mapping:
252
- self.color_mapping[label] = next(self.color_cycle)
253
-
254
- colors = [self.color_mapping[label] for label in labels]
255
-
256
- bars = self.ax.bar(labels, counts, color=colors)
257
- for bar, count in zip(bars, counts):
258
- self.ax.text(
259
- bar.get_x() + bar.get_width() / 2,
260
- bar.get_height(),
261
- str(count),
262
- ha="center",
263
- va="bottom",
264
- color=self.fg_color,
265
- )
266
-
267
- # Display and save the updated graph
268
- canvas = FigureCanvas(self.fig)
269
- canvas.draw()
270
- buf = canvas.buffer_rgba()
271
- im0 = np.asarray(buf)
272
- self.write_and_display(im0)
273
-
274
- def update_pie(self, classes_dict):
275
- """
276
- Update the pie chart with new data.
277
-
278
- Args:
279
- classes_dict (dict): Dictionary containing the class data to plot.
280
- """
281
- # Update pie chart data
282
- labels = list(classes_dict.keys())
283
- sizes = list(classes_dict.values())
284
- total = sum(sizes)
285
- percentages = [size / total * 100 for size in sizes]
286
- start_angle = 90
287
- self.ax.clear()
288
-
289
- # Create pie chart without labels inside the slices
290
- wedges, autotexts = self.ax.pie(sizes, autopct=None, startangle=start_angle, textprops={"color": self.fg_color})
291
-
292
- # Construct legend labels with percentages
293
- legend_labels = [f"{label} ({percentage:.1f}%)" for label, percentage in zip(labels, percentages)]
294
- self.ax.legend(wedges, legend_labels, title="Classes", loc="center left", bbox_to_anchor=(1, 0, 0.5, 1))
295
-
296
- # Adjust layout to fit the legend
297
- self.fig.tight_layout()
298
- self.fig.subplots_adjust(left=0.1, right=0.75)
299
-
300
- # Display and save the updated chart
301
- im0 = self.fig.canvas.draw()
302
- im0 = np.array(self.fig.canvas.renderer.buffer_rgba())
303
- self.write_and_display(im0)
304
-
192
+ self.display_output(im0)
305
193
 
306
- if __name__ == "__main__":
307
- Analytics("line", writer=None, im0_shape=None)
194
+ return im0 # Return the image