Tiện ích đơn giản#
Gói ultralytics cung cấp nhiều tiện ích hỗ trợ, cải thiện và tăng tốc quy trình làm việc của bạn. Dù còn nhiều tiện ích khác, hướng dẫn này giới thiệu một số tiện ích hữu ích nhất cho developer, đóng vai trò như tài liệu tham khảo thực tế khi lập trình với các công cụ Ultralytics.
Xem: Tiện ích Ultralytics | Tự động gán nhãn, Explorer API và chuyển đổi dataset
Dữ liệu#
Gán nhãn tự động / Chú thích#
Chú thích dataset là quy trình tiêu tốn nhiều tài nguyên và thời gian. Nếu bạn có model Ultralytics YOLO phát hiện đối tượng được huấn luyện trên một lượng dữ liệu hợp lý, bạn có thể dùng model đó cùng với SAM để tự động chú thích dữ liệu bổ sung theo định dạng segmentation.
from ultralytics.data.annotator import auto_annotate
auto_annotate(
data="path/to/new/data",
det_model="yolo26n.pt",
sam_model="mobile_sam.pt",
device="cuda",
output_dir="path/to/save_labels",
)Hàm này không trả về giá trị nào. Để biết thêm chi tiết:
- Xem phần tham chiếu về
annotator.auto_annotateđể hiểu rõ hơn cách hàm hoạt động. - Kết hợp với hàm
segments2boxesđể tạo thêm bounding box phát hiện đối tượng.
Trực quan hóa chú thích dataset#
Hàm này trực quan hóa chú thích YOLO trên ảnh trước khi huấn luyện, giúp xác định và sửa các chú thích sai có thể dẫn đến kết quả phát hiện không chính xác. Hàm vẽ bounding box, gắn nhãn đối tượng bằng tên lớp và điều chỉnh màu văn bản dựa trên độ sáng của nền để dễ đọc hơn.
from ultralytics.data.utils import visualize_image_annotations
label_map = { # Định nghĩa ánh xạ nhãn với tất cả nhãn lớp đã được chú thích.
0: "person",
1: "car",
}
# Visualize
visualize_image_annotations(
"path/to/image.jpg", # Đường dẫn ảnh đầu vào.
"path/to/annotations.txt", # Đường dẫn tệp chú thích của ảnh.
label_map,
)Chuyển đổi mask segmentation sang định dạng YOLO#

Dùng hàm này để chuyển đổi dataset gồm các ảnh mask segmentation sang định dạng segmentation của Ultralytics YOLO. Hàm nhận thư mục chứa các ảnh mask ở định dạng nhị phân và chuyển đổi chúng sang định dạng segmentation của YOLO.
Các mask đã chuyển đổi sẽ được lưu trong thư mục đầu ra được chỉ định.
from ultralytics.data.converter import convert_segment_masks_to_yolo_seg
# `classes` là tổng số lớp trong dataset.
# Dataset COCO có 80 lớp.
convert_segment_masks_to_yolo_seg(masks_dir="path/to/masks_dir", output_dir="path/to/output_dir", classes=80)Chuyển đổi COCO sang định dạng YOLO#
Dùng hàm này để chuyển đổi chú thích JSON của COCO sang định dạng YOLO. Với dataset phát hiện đối tượng (bounding box), đặt cả use_segments và use_keypoints thành False.
from ultralytics.data.converter import convert_coco
convert_coco(
"coco/annotations/",
use_segments=False,
use_keypoints=False,
cls91to80=True,
)Để biết thêm thông tin về hàm convert_coco, truy cập trang tham chiếu.
Lấy kích thước bounding box#
import cv2
from ultralytics import YOLO
from ultralytics.utils.plotting import Annotator
model = YOLO("yolo26n.pt") # Load pretrain or fine-tune model
# Process the image
source = cv2.imread("path/to/image.jpg")
results = model(source)
# Extract results
annotator = Annotator(source, example=model.names)
for box in results[0].boxes.xyxy.cpu():
width, height, area = annotator.get_bbox_dimension(box)
print(f"Bounding Box Width {width.item()}, Height {height.item()}, Area {area.item()}")Chuyển đổi bounding box thành segment#
Với dữ liệu bounding box x y w h hiện có, hãy chuyển đổi thành segment bằng hàm yolo_bbox2segment. Sắp xếp các tệp ảnh và chú thích như sau:
data
|__ images
├─ 001.jpg
├─ 002.jpg
├─ ..
└─ NNN.jpg
|__ labels
├─ 001.txt
├─ 002.txt
├─ ..
└─ NNN.txtfrom ultralytics.data.converter import yolo_bbox2segment
yolo_bbox2segment(
im_dir="path/to/images",
save_dir=None, # được lưu trong "labels-segment" bên cạnh thư mục ảnh
sam_model="sam_b.pt",
)Truy cập trang tham chiếu yolo_bbox2segment để biết thêm thông tin về hàm này.
Chuyển đổi segment thành bounding box#
Nếu bạn có dataset sử dụng định dạng dataset segmentation, bạn có thể dễ dàng chuyển đổi các segment thành bounding box thẳng đứng (hoặc nằm ngang) (định dạng x y w h) bằng hàm này.
import numpy as np
from ultralytics.utils.ops import segments2boxes
segments = np.array(
[
[805, 392, 797, 400, 812, 402, 808, 714, 808, 392],
[115, 398, 113, 400, 150, 410, 150, 400, 149, 298],
[267, 412, 265, 413, 300, 420, 300, 413, 299, 412],
],
dtype=np.float32,
)
segments2boxes([s.reshape(-1, 2) for s in segments])
# >>> array([[804.5, 553. , 15. , 322. ],
# [131.5, 354. , 37. , 112. ],
# [282.5, 416. , 35. , 8. ]],
# dtype=float32) # bounding box xywhĐể hiểu cách hàm này hoạt động, hãy truy cập trang tham chiếu.
Tiện ích#
Nén ảnh#
Nén một tệp ảnh đơn lẻ để giảm dung lượng trong khi vẫn giữ nguyên tỷ lệ khung hình và chất lượng. Nếu ảnh đầu vào nhỏ hơn kích thước tối đa, ảnh sẽ không được thay đổi kích thước.
from pathlib import Path
from ultralytics.data.utils import compress_one_image
for f in Path("path/to/dataset").rglob("*.jpg"):
compress_one_image(f)Tự động chia dataset#
Tự động chia dataset thành các tập train/val/test và lưu các tập kết quả vào các tệp autosplit_*.txt. Tiện ích này tạo các tệp chia tập được lưu liên tục; trong khi đó, đối số huấn luyện fraction chọn một tập con có thể tái lập cho một lần chạy.
from ultralytics.data.split import autosplit
autosplit(
path="path/to/images",
weights=(0.9, 0.1, 0.0), # (các tỷ lệ phân chia train, validation, test)
annotated_only=False, # chỉ chia các ảnh có tệp chú thích khi giá trị là True
)Xem trang tham chiếu để biết thêm chi tiết về hàm này.
Chuyển polygon segmentation thành mask nhị phân#
Chuyển đổi một polygon (dưới dạng danh sách) thành mask nhị phân có kích thước ảnh được chỉ định. Polygon phải là một mảng 1D phẳng gồm các tọa độ N, liệt kê xen kẽ các giá trị x, y để xác định đường bao polygon.
N luôn phải là số chẵn.
import numpy as np
from ultralytics.data.utils import polygon2mask
imgsz = (1080, 810)
polygon = np.array([805, 392, 797, 400, ..., 808, 714, 808, 392]) # (N,) tọa độ x, y dạng mảng phẳng
mask = polygon2mask(
imgsz, # tuple
[polygon], # đầu vào dưới dạng danh sách
color=255, # nhị phân 8-bit
downsample_ratio=1,
)Bounding box#
Các đối tượng bounding box (nằm ngang)#
Để quản lý dữ liệu bounding box, lớp Bboxes hỗ trợ chuyển đổi giữa các định dạng tọa độ box, thay đổi tỷ lệ kích thước box, tính diện tích, thêm độ lệch và nhiều thao tác khác.
import numpy as np
from ultralytics.utils.instance import Bboxes
boxes = Bboxes(
bboxes=np.array(
[
[22.878, 231.27, 804.98, 756.83],
[48.552, 398.56, 245.35, 902.71],
[669.47, 392.19, 809.72, 877.04],
[221.52, 405.8, 344.98, 857.54],
[0, 550.53, 63.01, 873.44],
[0.0584, 254.46, 32.561, 324.87],
]
),
format="xyxy",
)
boxes.areas()
# >>> array([ 4.1104e+05, 99216, 68000, 55772, 20347, 2288.5])
boxes.convert("xywh")
print(boxes.bboxes)
# >>> array(
# [[ 413.93, 494.05, 782.1, 525.56],
# [ 146.95, 650.63, 196.8, 504.15],
# [ 739.6, 634.62, 140.25, 484.85],
# [ 283.25, 631.67, 123.46, 451.74],
# [ 31.505, 711.99, 63.01, 322.91],
# [ 16.31, 289.67, 32.503, 70.41]]
# )Xem phần tham chiếu về Bboxes để biết thêm thuộc tính và phương thức.
Có thể truy cập nhiều hàm sau đây (và các hàm khác) thông qua lớp Bboxes, nhưng nếu muốn làm việc trực tiếp với các hàm, hãy xem các tiểu mục tiếp theo để biết cách import từng hàm riêng.
Thay đổi tỷ lệ box#
Khi phóng to hoặc thu nhỏ ảnh, bạn có thể thay đổi tỷ lệ tọa độ bounding box tương ứng cho phù hợp bằng ultralytics.utils.ops.scale_boxes.
import cv2 as cv
import numpy as np
from ultralytics.utils.ops import scale_boxes
image = cv.imread("ultralytics/assets/bus.jpg")
h, w, c = image.shape
resized = cv.resize(image, None, fx=1.2, fy=1.2)
new_h, new_w, _ = resized.shape
xyxy_boxes = np.array(
[
[22.878, 231.27, 804.98, 756.83],
[48.552, 398.56, 245.35, 902.71],
[669.47, 392.19, 809.72, 877.04],
[221.52, 405.8, 344.98, 857.54],
[0, 550.53, 63.01, 873.44],
[0.0584, 254.46, 32.561, 324.87],
]
)
new_boxes = scale_boxes(
img1_shape=(h, w), # kích thước ảnh ban đầu
boxes=xyxy_boxes, # các box từ ảnh ban đầu
img0_shape=(new_h, new_w), # kích thước ảnh đã đổi kích thước (tỷ lệ cần áp dụng)
ratio_pad=None,
padding=False,
xywh=False,
)
print(new_boxes)
# >>> array(
# [[ 27.454, 277.52, 965.98, 908.2],
# [ 58.262, 478.27, 294.42, 1083.3],
# [ 803.36, 470.63, 971.66, 1052.4],
# [ 265.82, 486.96, 413.98, 1029],
# [ 0, 660.64, 75.612, 1048.1],
# [ 0.0701, 305.35, 39.073, 389.84]]
# )Chuyển đổi định dạng bounding box#
XYXY → XYWH#
Chuyển đổi tọa độ bounding box từ định dạng (x1, y1, x2, y2) sang định dạng (x, y, width, height), trong đó (x1, y1) là góc trên bên trái và (x2, y2) là góc dưới bên phải.
import numpy as np
from ultralytics.utils.ops import xyxy2xywh
xyxy_boxes = np.array(
[
[22.878, 231.27, 804.98, 756.83],
[48.552, 398.56, 245.35, 902.71],
[669.47, 392.19, 809.72, 877.04],
[221.52, 405.8, 344.98, 857.54],
[0, 550.53, 63.01, 873.44],
[0.0584, 254.46, 32.561, 324.87],
]
)
xywh = xyxy2xywh(xyxy_boxes)
print(xywh)
# >>> array(
# [[ 413.93, 494.05, 782.1, 525.56],
# [ 146.95, 650.63, 196.8, 504.15],
# [ 739.6, 634.62, 140.25, 484.85],
# [ 283.25, 631.67, 123.46, 451.74],
# [ 31.505, 711.99, 63.01, 322.91],
# [ 16.31, 289.67, 32.503, 70.41]]
# )Tất cả chuyển đổi bounding box#
from ultralytics.utils.ops import (
ltwh2xywh,
ltwh2xyxy,
xywh2ltwh, # xywh → góc trên bên trái, w, h
xywh2xyxy,
xywhn2xyxy, # chuẩn hóa → pixel
xyxy2ltwh, # xyxy → góc trên bên trái, w, h
xyxy2xywhn, # pixel → chuẩn hóa
)
for func in (ltwh2xywh, ltwh2xyxy, xywh2ltwh, xywh2xyxy, xywhn2xyxy, xyxy2ltwh, xyxy2xywhn):
print(help(func)) # in docstring của hàmXem docstring của từng hàm hoặc truy cập ultralytics.utils.ops trang tham chiếu để tìm hiểu thêm.
Vẽ biểu đồ#
Tiện ích chú thích#
Ultralytics có lớp Annotator để chú thích nhiều loại dữ liệu. Lớp này phù hợp nhất khi dùng với bounding box phát hiện đối tượng, keypoint pose và bounding box định hướng.
Chú thích box#
import cv2 as cv
import numpy as np
from ultralytics.utils.plotting import Annotator, colors
names = {
0: "person",
5: "bus",
11: "stop sign",
}
image = cv.imread("ultralytics/assets/bus.jpg")
ann = Annotator(
image,
line_width=None, # default auto-size
font_size=None, # default auto-size
font="Arial.ttf", # must be ImageFont compatible
pil=False, # use PIL, otherwise uses OpenCV
)
xyxy_boxes = np.array(
[
[5, 22.878, 231.27, 804.98, 756.83], # class-idx x1 y1 x2 y2
[0, 48.552, 398.56, 245.35, 902.71],
[0, 669.47, 392.19, 809.72, 877.04],
[0, 221.52, 405.8, 344.98, 857.54],
[0, 0, 550.53, 63.01, 873.44],
[11, 0.0584, 254.46, 32.561, 324.87],
]
)
for nb, box in enumerate(xyxy_boxes):
c_idx, *box = box
label = f"{str(nb).zfill(2)}:{names.get(int(c_idx))}"
ann.box_label(box, label, color=colors(c_idx, bgr=True))
image_with_bboxes = ann.result()Có thể sử dụng tên từ model.names khi làm việc với kết quả phát hiện. Ngoài ra, hãy xem Trang tham chiếu Annotator để biết thêm thông tin.
Chú thích quét Ultralytics#
import sys
import cv2
import numpy as np
from ultralytics import YOLO
from ultralytics.solutions.solutions import SolutionAnnotator
from ultralytics.utils.plotting import colors
# User defined video path and model file
cap = cv2.VideoCapture("path/to/video.mp4")
model = YOLO(model="yolo26s-seg.pt") # Model file, e.g., yolo26s.pt or yolo26m-seg.pt
if not cap.isOpened():
print("Error: Could not open video.")
sys.exit()
# Initialize the video writer object.
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
video_writer = cv2.VideoWriter("ultralytics.avi", cv2.VideoWriter_fourcc(*"mp4v"), fps, (w, h))
masks = None # Initialize variable to store masks data
f = 0 # Initialize frame count variable for enabling mouse event.
line_x = w # Store width of line.
dragging = False # Initialize bool variable for line dragging.
classes = model.names # Store model classes names for plotting.
window_name = "Ultralytics Sweep Annotator"
def drag_line(event, x, _, flags, param):
"""Mouse callback function to enable dragging a vertical sweep line across the video frame."""
global line_x, dragging
if event == cv2.EVENT_LBUTTONDOWN or (flags & cv2.EVENT_FLAG_LBUTTON):
line_x = max(0, min(x, w))
dragging = True
while cap.isOpened(): # Loop over the video capture object.
ret, im0 = cap.read()
if not ret:
break
f = f + 1 # Increment frame count.
count = 0 # Re-initialize count variable on every frame for precise counts.
results = model.track(im0, persist=True)[0]
if f == 1:
cv2.namedWindow(window_name)
cv2.setMouseCallback(window_name, drag_line)
annotator = SolutionAnnotator(im0)
if results.boxes.is_track:
if results.masks is not None:
masks = [np.array(m, dtype=np.int32) for m in results.masks.xy]
boxes = results.boxes.xyxy.tolist()
track_ids = results.boxes.id.int().cpu().tolist()
clss = results.boxes.cls.cpu().tolist()
for mask, box, cls, t_id in zip(masks or [None] * len(boxes), boxes, clss, track_ids):
color = colors(t_id, True) # Assign different color to each tracked object.
label = f"{classes[cls]}:{t_id}"
if mask is not None and mask.size > 0:
if box[0] > line_x:
count += 1
cv2.polylines(im0, [mask], True, color, 2)
x, y = mask.min(axis=0)
(w_m, _), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)
cv2.rectangle(im0, (x, y - 20), (x + w_m, y), color, -1)
cv2.putText(im0, label, (x, y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
else:
if box[0] > line_x:
count += 1
annotator.box_label(box=box, color=color, label=label)
# Generate draggable sweep line
annotator.sweep_annotator(line_x=line_x, line_y=h, label=f"COUNT:{count}")
cv2.imshow(window_name, im0)
video_writer.write(im0)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
# Release the resources
cap.release()
video_writer.release()
cv2.destroyAllWindows()Tìm thêm chi tiết về phương thức sweep_annotator trong phần tham chiếu của chúng tôi tại đây.
Chú thích nhãn thích ứng#
SolutionAnnotator.adaptive_label vẽ nhãn có hình dạng được chọn bằng đối số shape (đối số này thay thế các phương thức circle_label và text_label cũ hơn):
- Hình chữ nhật:
annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="rect") - Hình tròn:
annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="circle")
Xem: Hướng dẫn chuyên sâu về chú thích văn bản và hình tròn kèm bản demo trực tiếp bằng Python | Chú thích Ultralytics 🚀
import cv2
from ultralytics import YOLO
from ultralytics.solutions.solutions import SolutionAnnotator
from ultralytics.utils.plotting import colors
model = YOLO("yolo26s.pt")
names = model.names
cap = cv2.VideoCapture("path/to/video.mp4")
w, h, fps = (int(cap.get(x)) for x in (cv2.CAP_PROP_FRAME_WIDTH, cv2.CAP_PROP_FRAME_HEIGHT, cv2.CAP_PROP_FPS))
writer = cv2.VideoWriter("Ultralytics circle annotation.avi", cv2.VideoWriter_fourcc(*"MJPG"), fps, (w, h))
while True:
ret, im0 = cap.read()
if not ret:
break
annotator = SolutionAnnotator(im0)
results = model.predict(im0)[0]
boxes = results.boxes.xyxy.cpu()
clss = results.boxes.cls.cpu().tolist()
for box, cls in zip(boxes, clss):
annotator.adaptive_label(box, label=names[int(cls)], color=colors(cls, True), shape="circle")
writer.write(im0)
cv2.imshow("Ultralytics circle annotation", im0)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
writer.release()
cap.release()
cv2.destroyAllWindows()Xem Trang tham khảo SolutionAnnotator để biết thêm thông tin.
Khác#
Phân tích hiệu năng mã#
Kiểm tra thời gian chạy/xử lý mã bằng with hoặc dưới dạng decorator.
from ultralytics.utils.ops import Profile
with Profile(device="cuda:0") as dt:
pass # thao tác cần đo
print(dt)
# >>> "Elapsed time is 9.5367431640625e-07 s"Các định dạng được Ultralytics hỗ trợ#
Bạn cần sử dụng theo cách lập trình các định dạng hình ảnh hoặc video được Ultralytics hỗ trợ? Hãy dùng các hằng số này nếu cần:
from ultralytics.data.utils import IMG_FORMATS, VID_FORMATS
print(IMG_FORMATS)
# {'avif', 'bmp', 'dng', 'heic', 'heif', 'jp2', 'jpeg', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp'}
print(VID_FORMATS)
# {'asf', 'avi', 'gif', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg', 'mpg', 'ts', 'wmv', 'webm'}Làm tròn lên bội số#
Tính số nguyên nhỏ nhất lớn hơn hoặc bằng x và chia hết cho y.
from ultralytics.utils.ops import make_divisible
make_divisible(7, 3)
# >>> 9
make_divisible(7, 2)
# >>> 8Câu hỏi thường gặp#
Gói Ultralytics bao gồm các tiện ích được thiết kế để tinh giản và tối ưu hóa quy trình machine learning. Các tiện ích chính gồm tự động gán nhãn cho dataset, chuyển đổi COCO sang định dạng YOLO bằng convert_coco, nén hình ảnh và tự động chia dataset. Những công cụ này giảm thiểu công việc thủ công, đảm bảo tính nhất quán và nâng cao hiệu quả xử lý dữ liệu.
Nếu có model phát hiện đối tượng Ultralytics YOLO đã pretrained, bạn có thể dùng model đó cùng với model SAM để tự động gán nhãn dataset ở định dạng segmentation. Dưới đây là ví dụ:
from ultralytics.data.annotator import auto_annotate auto_annotate( data="path/to/new/data", det_model="yolo26n.pt", sam_model="mobile_sam.pt", device="cuda", output_dir="path/to/save_labels", )Để biết thêm chi tiết, hãy xem phần tham khảo auto_annotate, hoặc dùng Ultralytics Platform làm giải pháp thay thế được host, không cần viết mã, với tính năng tạo mask bằng thao tác nhấp qua SAM 2.1, SAM 3 hoặc SAM 3.1, hoặc dự đoán bằng model YOLO pretrained và fine-tuned cho các tác vụ detect, segment và OBB.
Để chuyển annotation COCO JSON sang định dạng YOLO cho tác vụ phát hiện đối tượng, bạn có thể dùng tiện ích
convert_coco. Dưới đây là đoạn mã mẫu:from ultralytics.data.converter import convert_coco convert_coco( "coco/annotations/", use_segments=False, use_keypoints=False, cls91to80=True, )Để biết thêm thông tin, hãy truy cập trang tham khảo convert_coco.
Ultralytics Platform cung cấp tính năng phân tích dataset tự động: tab
Chartshiển thị phân bố các tập dữ liệu, số lượng của những class phổ biến nhất, histogram kích thước hình ảnh và heatmap 2D về vị trí annotation, giúp bạn phát hiện mất cân bằng và giá trị ngoại lệ trước khi training.Để chuyển dữ liệu bounding box hiện có (ở định dạng
x y w h) thành segment, bạn có thể dùng hàmyolo_bbox2segment. Đảm bảo các file được sắp xếp trong những thư mục riêng biệt dành cho hình ảnh và nhãn.from ultralytics.data.converter import yolo_bbox2segment yolo_bbox2segment( im_dir="path/to/images", save_dir=None, # được lưu trong "labels-segment" bên cạnh thư mục ảnh sam_model="sam_b.pt", )Để biết thêm thông tin, hãy truy cập trang tham khảo yolo_bbox2segment.