์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  Ultralytics YOLO

๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  ์˜ˆ์‹œ

๋น„๋””์˜ค ๋ถ„์„ ์˜์—ญ์—์„œ ๊ฐ์ฒด ์ถ”์ ์€ ํ”„๋ ˆ์ž„ ๋‚ด ๊ฐ์ฒด์˜ ์œ„์น˜์™€ ํด๋ž˜์Šค๋ฅผ ์‹๋ณ„ํ•  ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋น„๋””์˜ค๊ฐ€ ์ง„ํ–‰๋จ์— ๋”ฐ๋ผ ๊ฐ์ง€๋œ ๊ฐ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๊ณ ์œ  ID๋ฅผ ์œ ์ง€ํ•˜๋Š” ์ค‘์š”ํ•œ ์ž‘์—…์ž…๋‹ˆ๋‹ค. ๊ฐ์‹œ ๋ฐ ๋ณด์•ˆ๋ถ€ํ„ฐ ์‹ค์‹œ๊ฐ„ ์Šคํฌ์ธ  ๋ถ„์„์— ์ด๋ฅด๊ธฐ๊นŒ์ง€ ๊ทธ ํ™œ์šฉ ๋ถ„์•ผ๋Š” ๋ฌด๊ถ๋ฌด์ง„ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ์ฒด ์ถ”์ ์— Ultralytics YOLO ์„ ์„ ํƒํ•˜๋Š” ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?

Ultralytics ํŠธ๋ž˜์ปค์˜ ์ถœ๋ ฅ์€ ํ‘œ์ค€ ๊ฐ์ฒด ๊ฐ์ง€์™€ ์ผ์น˜ํ•˜์ง€๋งŒ ๊ฐ์ฒด ID๋ผ๋Š” ๋ถ€๊ฐ€ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋™์˜์ƒ ์ŠคํŠธ๋ฆผ์—์„œ ๊ฐ์ฒด๋ฅผ ์‰ฝ๊ฒŒ ์ถ”์ ํ•˜๊ณ  ํ›„์† ๋ถ„์„์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ์ฒด ์ถ”์ ์— Ultralytics YOLO ์‚ฌ์šฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•˜๋Š” ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  • ํšจ์œจ์„ฑ: ์ •ํ™•๋„ ์ €ํ•˜ ์—†์ด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์—ฐ์„ฑ: ๋‹ค์–‘ํ•œ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜๊ณผ ๊ตฌ์„ฑ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ํŽธ์˜์„ฑ: ๊ฐ„๋‹จํ•œ Python API ๋ฐ CLI ์˜ต์…˜์œผ๋กœ ๋น ๋ฅธ ํ†ตํ•ฉ ๋ฐ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ง€์ • ๊ธฐ๋Šฅ: ์‚ฌ์šฉ์ž ์ •์˜ ํ›ˆ๋ จ๋œ YOLO ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋„๋ฉ”์ธ๋ณ„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‰ฝ๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.



Watch: ๋ฌผ์ฒด ๊ฐ์ง€ ๋ฐ ์ถ”์  Ultralytics YOLO .

์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜

๊ตํ†ตํŽธ ๋ฆฌํ…Œ์ผ ์–‘์‹์—…
์ฐจ๋Ÿ‰ ์ถ”์  ์‚ฌ๋žŒ ์ถ”์  ๋ฌผ๊ณ ๊ธฐ ์ถ”์ 
์ฐจ๋Ÿ‰ ์ถ”์  ์‚ฌ๋žŒ ์ถ”์  ๋ฌผ๊ณ ๊ธฐ ์ถ”์ 

๊ธฐ๋Šฅ ์‚ดํŽด๋ณด๊ธฐ

Ultralytics YOLO ๋Š” ๋ฌผ์ฒด ๊ฐ์ง€ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜์—ฌ ๊ฐ•๋ ฅํ•˜๊ณ  ๋‹ค์–‘ํ•œ ๋ฌผ์ฒด ์ถ”์ ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค:

  • ์‹ค์‹œ๊ฐ„ ์ถ”์ : ๊ณ ํ”„๋ ˆ์ž„๋ฅ  ๋™์˜์ƒ์—์„œ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์›ํ™œํ•˜๊ฒŒ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.
  • ์—ฌ๋Ÿฌ ํŠธ๋ž˜์ปค ์ง€์›: ์ด๋ฏธ ํ™•๋ฆฝ๋œ ๋‹ค์–‘ํ•œ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ค‘์—์„œ ์„ ํƒํ•˜์„ธ์š”.
  • ์‚ฌ์šฉ์ž ์ง€์ • ๊ฐ€๋Šฅํ•œ ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ: ๋‹ค์–‘ํ•œ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์กฐ์ •ํ•˜์—ฌ ํŠน์ • ์š”๊ตฌ์‚ฌํ•ญ์„ ์ถฉ์กฑํ•˜๋„๋ก ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ๋งž์ถค ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํŠธ๋ž˜์ปค

Ultralytics YOLO ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ด€๋ จ YAML ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์ „๋‹ฌํ•˜์—ฌ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. tracker=tracker_type.yaml:

  • BoT-SORT - ์‚ฌ์šฉ botsort.yaml ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ด ํŠธ๋ž˜์ปค๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ByteTrack - ์‚ฌ์šฉ bytetrack.yaml ๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ด ํŠธ๋ž˜์ปค๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค.

๊ธฐ๋ณธ ํŠธ๋ž˜์ปค๋Š” BoT-SORT์ž…๋‹ˆ๋‹ค.

์ถ”์ 

ํŠธ๋ž˜์ปค ์ž„๊ณ„๊ฐ’ ์ •๋ณด

๊ฐ์ฒด ์‹ ๋ขฐ๋„ ์ ์ˆ˜๊ฐ€ ๋‚ฎ์„ ๊ฒฝ์šฐ(์ฆ‰, ๋‹ค์Œ๋ณด๋‹ค ๋‚ฎ์€ ๊ฒฝ์šฐ) track_high_thresh๋ฅผ ๋ˆ„๋ฅด๋ฉด ์„ฑ๊ณต์ ์œผ๋กœ ๋ฐ˜ํ™˜ ๋ฐ ์—…๋ฐ์ดํŠธ๋œ ํŠธ๋ž™์ด ์—†์Šต๋‹ˆ๋‹ค.

๋™์˜์ƒ ์ŠคํŠธ๋ฆผ์—์„œ ํŠธ๋ž˜์ปค๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด, ํ›ˆ๋ จ๋œ ๊ฐ์ง€, ์„ธ๊ทธ๋จผํŠธ ๋˜๋Š” ํฌ์ฆˆ ๋ชจ๋ธ(์˜ˆ: YOLO11n, YOLO11n-seg, YOLO11n-pose)์„ ์‚ฌ์šฉํ•˜์„ธ์š”.

์˜ˆ

from ultralytics import YOLO

# Load an official or custom model
model = YOLO("yolo11n.pt")  # Load an official Detect model
model = YOLO("yolo11n-seg.pt")  # Load an official Segment model
model = YOLO("yolo11n-pose.pt")  # Load an official Pose model
model = YOLO("path/to/best.pt")  # Load a custom trained model

# Perform tracking with the model
results = model.track("https://youtu.be/LNwODJXcvt4", show=True)  # Tracking with default tracker
results = model.track("https://youtu.be/LNwODJXcvt4", show=True, tracker="bytetrack.yaml")  # with ByteTrack
# Perform tracking with various models using the command line interface
yolo track model=yolo11n.pt source="https://youtu.be/LNwODJXcvt4"  # Official Detect model
yolo track model=yolo11n-seg.pt source="https://youtu.be/LNwODJXcvt4"  # Official Segment model
yolo track model=yolo11n-pose.pt source="https://youtu.be/LNwODJXcvt4"  # Official Pose model
yolo track model=path/to/best.pt source="https://youtu.be/LNwODJXcvt4"  # Custom trained model

# Track using ByteTrack tracker
yolo track model=path/to/best.pt tracker="bytetrack.yaml"

์œ„์˜ ์‚ฌ์šฉ ์˜ˆ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด ๋™์˜์ƒ ๋˜๋Š” ์ŠคํŠธ๋ฆฌ๋ฐ ์†Œ์Šค์—์„œ ์‹คํ–‰๋˜๋Š” ๋ชจ๋“  ๊ฐ์ง€, ์„ธ๊ทธ๋จผํŠธ ๋ฐ ํฌ์ฆˆ ๋ชจ๋ธ์—์„œ ์ถ”์ ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ตฌ์„ฑ

ํŠธ๋ž˜์ปค ์ž„๊ณ„๊ฐ’ ์ •๋ณด

๊ฐ์ฒด ์‹ ๋ขฐ๋„ ์ ์ˆ˜๊ฐ€ ๋‚ฎ์„ ๊ฒฝ์šฐ(์ฆ‰, ๋‹ค์Œ๋ณด๋‹ค ๋‚ฎ์€ ๊ฒฝ์šฐ) track_high_thresh๋ฅผ ๋ˆ„๋ฅด๋ฉด ์„ฑ๊ณต์ ์œผ๋กœ ๋ฐ˜ํ™˜ ๋ฐ ์—…๋ฐ์ดํŠธ๋œ ํŠธ๋ž™์ด ์—†์Šต๋‹ˆ๋‹ค.

์ถ”์  ์ธ์ˆ˜

์ถ”์  ๊ตฌ์„ฑ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์˜ˆ์ธก ๋ชจ๋“œ์™€ ์†์„ฑ์„ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. conf, iou๋ฐ show. ์ถ”๊ฐ€ ๊ตฌ์„ฑ์— ๋Œ€ํ•ด์„œ๋Š” ์˜ˆ์ธก ๋ชจ๋ธ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ

from ultralytics import YOLO

# Configure the tracking parameters and run the tracker
model = YOLO("yolo11n.pt")
results = model.track(source="https://youtu.be/LNwODJXcvt4", conf=0.3, iou=0.5, show=True)
# Configure tracking parameters and run the tracker using the command line interface
yolo track model=yolo11n.pt source="https://youtu.be/LNwODJXcvt4" conf=0.3, iou=0.5 show

ํŠธ๋ž˜์ปค ์„ ํƒ

Ultralytics ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ˆ˜์ •๋œ ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ ํŒŒ์ผ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ ํŒŒ์ผ์˜ ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค๊ธฐ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค(์˜ˆ custom_tracker.yaml)์—์„œ ultralytics/cfg/trackers ๋ฅผ ํด๋ฆญํ•˜๊ณ  ๋ชจ๋“  ๊ตฌ์„ฑ์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค( tracker_type) ํ•„์š”์— ๋”ฐ๋ผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ

from ultralytics import YOLO

# Load the model and run the tracker with a custom configuration file
model = YOLO("yolo11n.pt")
results = model.track(source="https://youtu.be/LNwODJXcvt4", tracker="custom_tracker.yaml")
# Load the model and run the tracker with a custom configuration file using the command line interface
yolo track model=yolo11n.pt source="https://youtu.be/LNwODJXcvt4" tracker='custom_tracker.yaml'

์ถ”์  ์ธ์ˆ˜์˜ ์ „์ฒด ๋ชฉ๋ก์€ ultralytics/cfg/trackers ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

Python ์˜ˆ์ œ

ํŠธ๋ž™ ๋ฃจํ”„ ์ง€์†

๋‹ค์Œ์€ ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜๋Š” Python ์Šคํฌ๋ฆฝํŠธ์ž…๋‹ˆ๋‹ค. OpenCV (cv2) ๋ฐ YOLO11 ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์—์„œ ๊ฐœ์ฒด ์ถ”์ ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์ด๋ฏธ ์„ค์น˜ํ–ˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค(opencv-python ๊ทธ๋ฆฌ๊ณ  ultralytics). ๊ทธ๋ฆฌ๊ณ  persist=True ์ธ์ˆ˜๋Š” ํŠธ๋ž˜์ปค์—๊ฒŒ ํ˜„์žฌ ์ด๋ฏธ์ง€ ๋˜๋Š” ํ”„๋ ˆ์ž„์ด ์‹œํ€€์Šค์˜ ๋‹ค์Œ ์ด๋ฏธ์ง€์ด๋ฉฐ ํ˜„์žฌ ์ด๋ฏธ์ง€์—์„œ ์ด์ „ ์ด๋ฏธ์ง€์˜ ํŠธ๋ž™์„ ์˜ˆ์ƒํ•˜๋„๋ก ์ง€์‹œํ•ฉ๋‹ˆ๋‹ค.

์ถ”์  ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ํฌ-๋ฃจํ”„ ์ŠคํŠธ๋ฆฌ๋ฐ

import cv2

from ultralytics import YOLO

# Load the YOLO11 model
model = YOLO("yolo11n.pt")

# Open the video file
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)

# Loop through the video frames
while cap.isOpened():
    # Read a frame from the video
    success, frame = cap.read()

    if success:
        # Run YOLO11 tracking on the frame, persisting tracks between frames
        results = model.track(frame, persist=True)

        # Visualize the results on the frame
        annotated_frame = results[0].plot()

        # Display the annotated frame
        cv2.imshow("YOLO11 Tracking", annotated_frame)

        # Break the loop if 'q' is pressed
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    else:
        # Break the loop if the end of the video is reached
        break

# Release the video capture object and close the display window
cap.release()
cv2.destroyAllWindows()

๋‹ค์Œ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ์œ ์˜ํ•˜์„ธ์š”. model(frame) ์— model.track(frame)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹จ์ˆœ ๊ฐ์ง€ ๋Œ€์‹  ๊ฐ์ฒด ์ถ”์ ์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์ˆ˜์ •๋œ ์Šคํฌ๋ฆฝํŠธ๋Š” ๋™์˜์ƒ์˜ ๊ฐ ํ”„๋ ˆ์ž„์—์„œ ํŠธ๋ž˜์ปค๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๊ฒฐ๊ณผ๋ฅผ ์‹œ๊ฐํ™”ํ•˜์—ฌ ์ฐฝ์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. 'Q'๋ฅผ ๋ˆŒ๋Ÿฌ ๋ฃจํ”„๋ฅผ ์ข…๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹œ๊ฐ„ ๊ฒฝ๊ณผ์— ๋”ฐ๋ฅธ ํŠธ๋ž™ ํ”Œ๋กœํŒ…

์—ฐ์†๋œ ํ”„๋ ˆ์ž„์— ๊ฑธ์ณ ๊ฐ์ฒด ํŠธ๋ž™์„ ์‹œ๊ฐํ™”ํ•˜๋ฉด ๋™์˜์ƒ ๋‚ด์—์„œ ๊ฐ์ง€๋œ ๊ฐ์ฒด์˜ ์›€์ง์ž„ ํŒจํ„ด๊ณผ ๋™์ž‘์— ๋Œ€ํ•œ ๊ท€์ค‘ํ•œ ์ธ์‚ฌ์ดํŠธ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Ultralytics YOLO11 ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ํŠธ๋ž™์„ ์›ํ™œํ•˜๊ณ  ํšจ์œจ์ ์œผ๋กœ ํ”Œ๋กœํŒ…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” YOLO11 ์˜ ์ถ”์  ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์—์„œ ๊ฐ์ง€๋œ ๊ฐ์ฒด์˜ ์›€์ง์ž„์„ ํ”Œ๋กœํŒ…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด ์Šคํฌ๋ฆฝํŠธ์—์„œ๋Š” ๋น„๋””์˜ค ํŒŒ์ผ์„ ์—ด๊ณ  ํ”„๋ ˆ์ž„๋ณ„๋กœ ์ฝ์€ ๋‹ค์Œ YOLO ๋ชจ๋ธ์„ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ฐ์ฒด๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ์ง€๋œ ๊ฒฝ๊ณ„ ์ƒ์ž์˜ ์ค‘์‹ฌ์ ์„ ์œ ์ง€ํ•˜๊ณ  ์—ฐ๊ฒฐํ•˜์—ฌ ์ถ”์ ๋œ ๊ฐ์ฒด๊ฐ€ ๋”ฐ๋ผ๊ฐ„ ๊ฒฝ๋กœ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์„ ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์— ๊ฑธ์ณ ํŠธ๋ž™ ํ”Œ๋กœํŒ…ํ•˜๊ธฐ

from collections import defaultdict

import cv2
import numpy as np

from ultralytics import YOLO

# Load the YOLO11 model
model = YOLO("yolo11n.pt")

# Open the video file
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)

# Store the track history
track_history = defaultdict(lambda: [])

# Loop through the video frames
while cap.isOpened():
    # Read a frame from the video
    success, frame = cap.read()

    if success:
        # Run YOLO11 tracking on the frame, persisting tracks between frames
        results = model.track(frame, persist=True)

        # Get the boxes and track IDs
        boxes = results[0].boxes.xywh.cpu()
        track_ids = results[0].boxes.id.int().cpu().tolist()

        # Visualize the results on the frame
        annotated_frame = results[0].plot()

        # Plot the tracks
        for box, track_id in zip(boxes, track_ids):
            x, y, w, h = box
            track = track_history[track_id]
            track.append((float(x), float(y)))  # x, y center point
            if len(track) > 30:  # retain 90 tracks for 90 frames
                track.pop(0)

            # Draw the tracking lines
            points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
            cv2.polylines(annotated_frame, [points], isClosed=False, color=(230, 230, 230), thickness=10)

        # Display the annotated frame
        cv2.imshow("YOLO11 Tracking", annotated_frame)

        # Break the loop if 'q' is pressed
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    else:
        # Break the loop if the end of the video is reached
        break

# Release the video capture object and close the display window
cap.release()
cv2.destroyAllWindows()

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ์ถ”์ 

๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ์ถ”์ ์€ ์—ฌ๋Ÿฌ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์—์„œ ๋™์‹œ์— ๊ฐ์ฒด ์ถ”์ ์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ์—ฌ๋Ÿฌ ๋Œ€์˜ ๊ฐ์‹œ ์นด๋ฉ”๋ผ์—์„œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋น„๋””์˜ค ์ž…๋ ฅ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ํŠนํžˆ ์œ ์šฉํ•˜๋ฉฐ, ๋™์‹œ ์ฒ˜๋ฆฌ๋ฅผ ํ†ตํ•ด ํšจ์œจ์„ฑ๊ณผ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ œ๊ณต๋œ Python ์Šคํฌ๋ฆฝํŠธ์—์„œ Python ์˜ threading ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠธ๋ž˜์ปค์˜ ์—ฌ๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ํ•˜๋‚˜์˜ ๋™์˜์ƒ ํŒŒ์ผ์—์„œ ํŠธ๋ž˜์ปค๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ, ๋ชจ๋“  ์Šค๋ ˆ๋“œ๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋™์‹œ์— ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

๊ฐ ์Šค๋ ˆ๋“œ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ ๋งค๊ฐœ๋ณ€์ˆ˜(๋น„๋””์˜ค ํŒŒ์ผ, ์‚ฌ์šฉํ•  ๋ชจ๋ธ ๋ฐ ํŒŒ์ผ ์ธ๋ฑ์Šค)๋ฅผ ์ˆ˜์‹ ํ•˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. run_tracker_in_thread ์ด ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ํ—ˆ์šฉํ•˜๊ณ  ๊ธฐ๋ณธ ์ถ”์  ๋ฃจํ”„๋ฅผ ํฌํ•จํ•˜๋Š” ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋น„๋””์˜ค๋ฅผ ํ”„๋ ˆ์ž„ ๋‹จ์œ„๋กœ ์ฝ๊ณ  ํŠธ๋ž˜์ปค๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ด ์˜ˆ์—์„œ๋Š” ๋‘ ๊ฐ€์ง€ ๋‹ค๋ฅธ ๋ชจ๋ธ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค: yolo11n.pt ๊ทธ๋ฆฌ๊ณ  yolo11n-seg.pt๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ๊ฐ ๋‹ค๋ฅธ ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ ๊ฐœ์ฒด๋ฅผ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„๋””์˜ค ํŒŒ์ผ์€ video_file1 ๊ทธ๋ฆฌ๊ณ  video_file2.

๊ทธ๋ฆฌ๊ณ  daemon=True ๋งค๊ฐœ๋ณ€์ˆ˜์˜ threading.Thread ๋Š” ๋ฉ”์ธ ํ”„๋กœ๊ทธ๋žจ์ด ์™„๋ฃŒ๋˜๋Š” ์ฆ‰์‹œ ์ด๋Ÿฌํ•œ ์Šค๋ ˆ๋“œ๊ฐ€ ๋‹ซํžŒ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์Šค๋ ˆ๋“œ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. start() ๋ฐ ์‚ฌ์šฉ join() ๋ฅผ ์„ค์ •ํ•˜์—ฌ ๋‘ ํŠธ๋ž˜์ปค ์Šค๋ ˆ๋“œ๊ฐ€ ๋ชจ๋‘ ์™„๋ฃŒ๋  ๋•Œ๊นŒ์ง€ ๋ฉ”์ธ ์Šค๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ์ž‘์—…์„ ์™„๋ฃŒํ•˜๋ฉด ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ์ฐฝ์ด ๋‹ค์Œ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ซํž™๋‹ˆ๋‹ค. cv2.destroyAllWindows().

์ถ”์  ๊ธฐ๋Šฅ์ด ์žˆ๋Š” ํฌ-๋ฃจํ”„ ์ŠคํŠธ๋ฆฌ๋ฐ

import threading

import cv2

from ultralytics import YOLO

# Define model names and video sources
MODEL_NAMES = ["yolo11n.pt", "yolo11n-seg.pt"]
SOURCES = ["path/to/video.mp4", "0"]  # local video, 0 for webcam


def run_tracker_in_thread(model_name, filename):
    """
    Run YOLO tracker in its own thread for concurrent processing.

    Args:
        model_name (str): The YOLO11 model object.
        filename (str): The path to the video file or the identifier for the webcam/external camera source.
    """
    model = YOLO(model_name)
    results = model.track(filename, save=True, stream=True)
    for r in results:
        pass


# Create and start tracker threads using a for loop
tracker_threads = []
for video_file, model_name in zip(SOURCES, MODEL_NAMES):
    thread = threading.Thread(target=run_tracker_in_thread, args=(model_name, video_file), daemon=True)
    tracker_threads.append(thread)
    thread.start()

# Wait for all tracker threads to finish
for thread in tracker_threads:
    thread.join()

# Clean up and close windows
cv2.destroyAllWindows()

์ด ์˜ˆ์ œ๋Š” ๋” ๋งŽ์€ ์Šค๋ ˆ๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋™์ผํ•œ ๋ฐฉ๋ฒ•๋ก ์„ ์ ์šฉํ•˜์—ฌ ๋” ๋งŽ์€ ๋น„๋””์˜ค ํŒŒ์ผ๊ณผ ๋ชจ๋ธ์„ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์‰ฝ๊ฒŒ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ํŠธ๋ž˜์ปค ๊ธฐ์—ฌํ•˜๊ธฐ

๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ ์— ๋Šฅ์ˆ™ํ•˜๊ณ  ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ฑ๊ณต์ ์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๊ฑฐ๋‚˜ ์ ์šฉํ–ˆ๋‚˜์š” Ultralytics YOLO ? ultralytics /cfg/trackers์˜ ํŠธ๋ž˜์ปค ์„น์…˜์— ๊ธฐ์—ฌํ•ด ์ฃผ์„ธ์š”! ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹ค์ œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์†”๋ฃจ์…˜์€ ์ถ”์  ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ๋งค์šฐ ์œ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์„น์…˜์— ๊ธฐ์—ฌํ•จ์œผ๋กœ์จ Ultralytics YOLO ํ”„๋ ˆ์ž„์›Œํฌ ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ถ”์  ์†”๋ฃจ์…˜์˜ ๋ฒ”์œ„๋ฅผ ํ™•์žฅํ•˜์—ฌ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ๋˜ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๊ณผ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ฃผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์—ฌ๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด ํ’€ ๋ฆฌํ€˜์ŠคํŠธ(PR) ์ œ์ถœ์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ์ง€์นจ์ด ๋‹ด๊ธด ๊ธฐ์—ฌ ๊ฐ€์ด๋“œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”(๐Ÿ› ๏ธ). ์—ฌ๋Ÿฌ๋ถ„์˜ ์˜๊ฒฌ์„ ๊ธฐ๋‹ค๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค!

ํ•จ๊ป˜ Ultralytics YOLO ์—์ฝ”์‹œ์Šคํ…œ์˜ ์ถ”์  ๊ธฐ๋Šฅ์„ ๊ฐ•ํ™”ํ•ด ๋ด…์‹œ๋‹ค ๐Ÿ™!

์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ

๋‹ค์ค‘ ๊ฐœ์ฒด ์ถ”์ ์ด๋ž€ ๋ฌด์—‡์ด๋ฉฐ Ultralytics YOLO ์–ด๋–ป๊ฒŒ ์ง€์›ํ•˜๋‚˜์š”?

๋น„๋””์˜ค ๋ถ„์„์—์„œ ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ ์—๋Š” ๊ฐ์ฒด๋ฅผ ์‹๋ณ„ํ•˜๊ณ  ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์—์„œ ๊ฐ์ง€๋œ ๊ฐ ๊ฐ์ฒด์˜ ๊ณ ์œ  ID๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ๋ชจ๋‘ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. Ultralytics YOLO ๊ฐ์ฒด ID์™€ ํ•จ๊ป˜ ์‹ค์‹œ๊ฐ„ ์ถ”์ ์„ ์ œ๊ณตํ•˜์—ฌ ๋ณด์•ˆ ๊ฐ์‹œ ๋ฐ ์Šคํฌ์ธ  ๋ถ„์„๊ณผ ๊ฐ™์€ ์ž‘์—…์„ ์šฉ์ดํ•˜๊ฒŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด ์‹œ์Šคํ…œ์€ YAML ํŒŒ์ผ์„ ํ†ตํ•ด ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” BoT-SORT ๋ฐ ByteTrack๊ณผ ๊ฐ™์€ ํŠธ๋ž˜์ปค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Ultralytics YOLO ์— ๋Œ€ํ•œ ์‚ฌ์šฉ์ž ์ง€์ • ํŠธ๋ž˜์ปค๋ฅผ ๊ตฌ์„ฑํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?

๊ธฐ์กด ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ ํŒŒ์ผ์„ ๋ณต์‚ฌํ•˜์—ฌ ์‚ฌ์šฉ์ž ์ง€์ • ํŠธ๋ž˜์ปค๋ฅผ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(์˜ˆ, custom_tracker.yaml)์—์„œ Ultralytics ํŠธ๋ž˜์ปค ๊ตฌ์„ฑ ๋””๋ ‰ํ† ๋ฆฌ ๋กœ ์„ค์ •ํ•˜๊ณ  ํ•„์š”์— ๋”ฐ๋ผ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค. tracker_type. ์ถ”์  ๋ชจ๋ธ์—์„œ ์ด ํŒŒ์ผ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•˜์„ธ์š”:

์˜ˆ

from ultralytics import YOLO

model = YOLO("yolo11n.pt")
results = model.track(source="https://youtu.be/LNwODJXcvt4", tracker="custom_tracker.yaml")
yolo track model=yolo11n.pt source="https://youtu.be/LNwODJXcvt4" tracker='custom_tracker.yaml'

์—ฌ๋Ÿฌ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์—์„œ ๋™์‹œ์— ์˜ค๋ธŒ์ ํŠธ ์ถ”์ ์„ ์‹คํ–‰ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?

์—ฌ๋Ÿฌ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์—์„œ ์˜ค๋ธŒ์ ํŠธ ์ถ”์ ์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๋ ค๋ฉด Python ์˜ threading ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ์Šค๋ ˆ๋“œ๋Š” ๋ณ„๋„์˜ ๋™์˜์ƒ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ด๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

๋ฉ€ํ‹ฐ ์Šค๋ ˆ๋“œ ์ถ”์ 

import threading

import cv2

from ultralytics import YOLO

# Define model names and video sources
MODEL_NAMES = ["yolo11n.pt", "yolo11n-seg.pt"]
SOURCES = ["path/to/video.mp4", "0"]  # local video, 0 for webcam


def run_tracker_in_thread(model_name, filename):
    """
    Run YOLO tracker in its own thread for concurrent processing.

    Args:
        model_name (str): The YOLO11 model object.
        filename (str): The path to the video file or the identifier for the webcam/external camera source.
    """
    model = YOLO(model_name)
    results = model.track(filename, save=True, stream=True)
    for r in results:
        pass


# Create and start tracker threads using a for loop
tracker_threads = []
for video_file, model_name in zip(SOURCES, MODEL_NAMES):
    thread = threading.Thread(target=run_tracker_in_thread, args=(model_name, video_file), daemon=True)
    tracker_threads.append(thread)
    thread.start()

# Wait for all tracker threads to finish
for thread in tracker_threads:
    thread.join()

# Clean up and close windows
cv2.destroyAllWindows()

๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ ์˜ ์‹ค์ œ ์‘์šฉ ๋ถ„์•ผ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ Ultralytics YOLO ?

๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ ( Ultralytics YOLO )์€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค:

  • ๊ตํ†ต: ๊ตํ†ต ๊ด€๋ฆฌ ๋ฐ ์ž์œจ ์ฃผํ–‰์„ ์œ„ํ•œ ์ฐจ๋Ÿ‰ ์ถ”์ .
  • ๋ฆฌํ…Œ์ผ: ๋งค์žฅ ๋‚ด ๋ถ„์„ ๋ฐ ๋ณด์•ˆ์„ ์œ„ํ•œ ์‚ฌ๋žŒ ์ถ”์ .
  • ์–‘์‹์—…: ์ˆ˜์ค‘ ํ™˜๊ฒฝ ๋ชจ๋‹ˆํ„ฐ๋ง์„ ์œ„ํ•œ ๋ฌผ๊ณ ๊ธฐ ์ถ”์ .

์ด๋Ÿฌํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๊ณ ํ”„๋ ˆ์ž„๋ฅ  ๋™์˜์ƒ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” Ultralytics YOLO ์˜ ๊ธฐ๋Šฅ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Ultralytics YOLO ์„ ์‚ฌ์šฉํ•˜์—ฌ ์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์— ๊ฑธ์ณ ์˜ค๋ธŒ์ ํŠธ ํŠธ๋ž™์„ ์‹œ๊ฐํ™”ํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?

์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์— ๊ฑธ์ณ ์˜ค๋ธŒ์ ํŠธ ํŠธ๋ž™์„ ์‹œ๊ฐํ™”ํ•˜๋ ค๋ฉด YOLO ๋ชจ๋ธ์˜ ์ถ”์  ๊ธฐ๋Šฅ์„ OpenCV์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ง€๋œ ์˜ค๋ธŒ์ ํŠธ์˜ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ์ด๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์Šคํฌ๋ฆฝํŠธ ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค:

์—ฌ๋Ÿฌ ๋น„๋””์˜ค ํ”„๋ ˆ์ž„์— ๊ฑธ์ณ ํŠธ๋ž™ ํ”Œ๋กœํŒ…ํ•˜๊ธฐ

from collections import defaultdict

import cv2
import numpy as np

from ultralytics import YOLO

model = YOLO("yolo11n.pt")
video_path = "path/to/video.mp4"
cap = cv2.VideoCapture(video_path)
track_history = defaultdict(lambda: [])

while cap.isOpened():
    success, frame = cap.read()
    if success:
        results = model.track(frame, persist=True)
        boxes = results[0].boxes.xywh.cpu()
        track_ids = results[0].boxes.id.int().cpu().tolist()
        annotated_frame = results[0].plot()
        for box, track_id in zip(boxes, track_ids):
            x, y, w, h = box
            track = track_history[track_id]
            track.append((float(x), float(y)))
            if len(track) > 30:
                track.pop(0)
            points = np.hstack(track).astype(np.int32).reshape((-1, 1, 2))
            cv2.polylines(annotated_frame, [points], isClosed=False, color=(230, 230, 230), thickness=10)
        cv2.imshow("YOLO11 Tracking", annotated_frame)
        if cv2.waitKey(1) & 0xFF == ord("q"):
            break
    else:
        break
cap.release()
cv2.destroyAllWindows()

์ด ์Šคํฌ๋ฆฝํŠธ๋Š” ์ถ”์ ๋œ ๊ฐœ์ฒด์˜ ์‹œ๊ฐ„ ๊ฒฝ๊ณผ์— ๋”ฐ๋ฅธ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์ถ”์ ์„ ์„ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

๐Ÿ“…1 ๋…„ ์ „ ์ƒ์„ฑ๋จ โœ๏ธ 2๊ฐœ์›” ์ „ ์—…๋ฐ์ดํŠธ๋จ

๋Œ“๊ธ€