์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽฏ OpenCV๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ์ฒด ์ถ”์  ์™„๋ฒฝ ๊ฐ€์ด๋“œ: ์‹ค์ „ ์†Œ์Šค์ฝ”๋“œ์™€ ํ•จ๊ป˜

๐ŸŽฏ OpenCV๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ์ฒด ์ถ”์  ์™„๋ฒฝ ๊ฐ€์ด๋“œ: ์‹ค์ „ ์†Œ์Šค์ฝ”๋“œ์™€ ํ•จ๊ป˜

์ดˆ๋ณด์ž๋„ ๋”ฐ๋ผํ•  ์ˆ˜ ์žˆ๋Š” ์‹ค์ „ ์˜ˆ์ œ๋กœ ๋ฐฐ์šฐ๋Š” ๊ฐ์ฒด ์ถ”์  ๊ธฐ์ˆ  ๐Ÿ’ป

๐Ÿ‘‹ ์•ˆ๋…•! ์˜ค๋Š˜์€ ์ปดํ“จํ„ฐ ๋น„์ „์˜ ๊ฝƒ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ์ฒด ์ถ”์ (Object Tracking)์— ๋Œ€ํ•ด ์ด์•ผ๊ธฐํ•ด๋ณผ๊ฒŒ. ์˜ํ™”์—์„œ ๋ณด๋˜ ๊ทธ ๋ฉ‹์ง„ ์ถ”์  ๊ธฐ์ˆ , ์‚ฌ์‹ค ์šฐ๋ฆฌ๋„ OpenCV๋กœ ์ถฉ๋ถ„ํžˆ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿš€

์ด ๊ธ€์—์„œ๋Š” ์‹ค์ œ๋กœ ๋™์ž‘ํ•˜๋Š” ์†Œ์Šค์ฝ”๋“œ์™€ ํ•จ๊ป˜ ๊ฐ์ฒด ์ถ”์ ์˜ ์›๋ฆฌ๋ถ€ํ„ฐ ์‹ค์ „ ํ™œ์šฉ๊นŒ์ง€ ๋ชจ๋“  ๊ฑธ ๋‹ค๋ค„๋ณผ ๊ฑฐ์•ผ. ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ด ์ฒ˜์Œ์ด๋ผ๋„ ๊ฑฑ์ • ๋งˆ! ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ์ด ์‰ฝ๊ฒŒ ํ’€์–ด์„œ ์•Œ๋ ค์ค„๊ฒŒ. ๐Ÿ˜Š

OpenCV Tracking Real-time Object Tracking System

๐ŸŽฌ ๊ฐ์ฒด ์ถ”์ ์ด ๋ญ๊ธธ๋ž˜?

๊ฐ์ฒด ์ถ”์ ์€ ๋ง ๊ทธ๋Œ€๋กœ ์˜์ƒ ์†์—์„œ ํŠน์ • ๋ฌผ์ฒด๋ฅผ ๊ณ„์† ๋”ฐ๋ผ๊ฐ€๋Š” ๊ธฐ์ˆ ์ด์•ผ. ์ƒ๊ฐํ•ด๋ด, ์œ ํŠœ๋ธŒ์—์„œ ์Šคํฌ์ธ  ๊ฒฝ๊ธฐ ๋ณผ ๋•Œ ๊ณต์ด๋‚˜ ์„ ์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ๋”ฐ๋ผ๊ฐ€๋Š” ์นด๋ฉ”๋ผ ์žˆ์ž–์•„? ๋ฐ”๋กœ ๊ทธ๊ฒŒ ๊ฐ์ฒด ์ถ”์  ๊ธฐ์ˆ ์ด์•ผ! ๐ŸŽพ

์ผ๋ฐ˜์ ์ธ ๊ฐ์ฒด ๊ฒ€์ถœ(Object Detection)์€ ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค "์ด๊ฒŒ ๋ญ์ง€?"๋ฅผ ์ƒˆ๋กœ ํŒ๋‹จํ•˜๋Š” ๊ฑฐ๋ผ๋ฉด, ๊ฐ์ฒด ์ถ”์ ์€ "์•„, ์ €๊ฑฐ ์•„๊นŒ ๊ทธ๊ฑฐ๋„ค!"ํ•˜๊ณ  ์—ฐ์†์„ฑ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ๋”ฐ๋ผ๊ฐ€๋Š” ๊ฑฐ์ง€. ํ›จ์”ฌ ํšจ์œจ์ ์ด๊ณ  ๋น ๋ฅด์ง€! โšก

๐Ÿ’ก ์•Œ์•„๋‘๋ฉด ์ข‹์•„์š”! ๊ฐ์ฒด ์ถ”์ ์€ ์ž์œจ์ฃผํ–‰์ฐจ, ๋ณด์•ˆ ์‹œ์Šคํ…œ, ์Šคํฌ์ธ  ๋ถ„์„, AR/VR, ๋“œ๋ก  ์ดฌ์˜ ๋“ฑ ์ •๋ง ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ํ™œ์šฉ๋˜๊ณ  ์žˆ์–ด. ์š”์ฆ˜ ํ•ซํ•œ ๊ธฐ์ˆ ์ด๋ผ ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ๋„ ๊ด€๋ จ ํ”„๋กœ์ ํŠธ๋‚˜ ๊ฐ•์˜๋ฅผ ๋งŽ์ด ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์ง€!

๐Ÿ› ๏ธ OpenCV ์„ค์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜์ž!

๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋”ฉํ•˜๊ธฐ ์ „์— OpenCV๋ถ€ํ„ฐ ์„ค์น˜ํ•ด์•ผ๊ฒ ์ง€? Python ํ™˜๊ฒฝ์—์„œ ์ง„ํ–‰ํ•  ๊ฑด๋ฐ, ์ •๋ง ๊ฐ„๋‹จํ•ด! ๐Ÿ˜Ž

๐Ÿ“ฆ ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜
# Python 3.7 ์ด์ƒ ๊ถŒ์žฅ
pip install opencv-python
pip install opencv-contrib-python
pip install numpy

# ์„ ํƒ์‚ฌํ•ญ (์˜์ƒ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ ํ–ฅ์ƒ)
pip install imutils
๐ŸŽฏ Pro Tip! opencv-contrib-python์€ ์ถ”๊ฐ€ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋“ค์ด ํฌํ•จ๋œ ํ™•์žฅ ๋ฒ„์ „์ด์•ผ. ๋” ๋‹ค์–‘ํ•œ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ผญ ์„ค์น˜ํ•˜๋Š” ๊ฒŒ ์ข‹์•„!

์„ค์น˜๊ฐ€ ์ž˜ ๋๋Š”์ง€ ํ™•์ธํ•ด๋ณผ๊นŒ?

import cv2
print(cv2.__version__)

# 4.5.0 ์ด์ƒ์ด๋ฉด OK!

๐ŸŽฏ OpenCV์˜ ์ฃผ์š” ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜๋“ค

OpenCV๋Š” ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ๊ฐ๊ฐ ์žฅ๋‹จ์ ์ด ์žˆ์–ด. ์ƒํ™ฉ์— ๋งž๊ฒŒ ๊ณจ๋ผ ์“ฐ๋ฉด ๋ผ! ๐ŸŽจ

์•Œ๊ณ ๋ฆฌ์ฆ˜ ์†๋„ ์ •ํ™•๋„ ํŠน์ง•
BOOSTING ๋А๋ฆผ ๋ณดํ†ต ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๋ฐฉ์‹, ์•ˆ์ •์ 
MIL ๋ณดํ†ต ๋ณดํ†ต ๋ถ€๋ถ„ ๊ฐ€๋ฆผ์— ๊ฐ•ํ•จ
KCF ๋น ๋ฆ„ ์ข‹์Œ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉ, ๊ท ํ˜•์žกํž˜
CSRT ๋А๋ฆผ ๋งค์šฐ ์ข‹์Œ ์ •ํ™•๋„ ์ตœ๊ณ , ๊ฒฝ๊ณ„ ์ •๋ฐ€
MedianFlow ๋น ๋ฆ„ ๋ณดํ†ต ์˜ˆ์ธก ๊ฐ€๋Šฅํ•œ ์›€์ง์ž„์— ์ ํ•ฉ
MOSSE ๋งค์šฐ ๋น ๋ฆ„ ๋‚ฎ์Œ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์— ์ตœ์ 
TLD ๋ณดํ†ต ์ข‹์Œ ๊ฐ€๋ฆผ ํ›„ ์žฌํƒ์ง€ ๊ฐ€๋Šฅ
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ OpenCV 4.5.1 ์ดํ›„ ๋ฒ„์ „์—์„œ๋Š” ์ผ๋ถ€ ์ถ”์ ๊ธฐ(BOOSTING, MIL, TLD)๊ฐ€ opencv-contrib-python์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ด. ๊ผญ ํ™•์ธํ•˜๊ณ  ์„ค์น˜ํ•ด์•ผ ํ•ด!

๐Ÿš€ ์ฒซ ๋ฒˆ์งธ ์˜ˆ์ œ: ๋‹จ์ผ ๊ฐ์ฒด ์ถ”์  ๊ธฐ๋ณธ

์ž, ์ด์ œ ์ง„์งœ ์ฝ”๋”ฉ ์‹œ์ž‘์ด์•ผ! ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋‹จ์ผ ๊ฐ์ฒด ์ถ”์ ๋ถ€ํ„ฐ ํ•ด๋ณด์ž. ์›น์บ ์œผ๋กœ ์‹ค์‹œ๊ฐ„ ์ถ”์ ํ•˜๋Š” ์ฝ”๋“œ์•ผ! ๐Ÿ“น

๐ŸŽฌ ์‹ค์‹œ๊ฐ„ ์›น์บ  ๊ฐ์ฒด ์ถ”์ 
import cv2
import sys

# ์ถ”์ ๊ธฐ ์ƒ์„ฑ ํ•จ์ˆ˜
def create_tracker(tracker_type):
    """
    ์›ํ•˜๋Š” ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜๋Š” ํ•จ์ˆ˜
    """
    if tracker_type == 'BOOSTING':
        tracker = cv2.legacy.TrackerBoosting_create()
    elif tracker_type == 'MIL':
        tracker = cv2.legacy.TrackerMIL_create()
    elif tracker_type == 'KCF':
        tracker = cv2.TrackerKCF_create()
    elif tracker_type == 'CSRT':
        tracker = cv2.TrackerCSRT_create()
    elif tracker_type == 'MedianFlow':
        tracker = cv2.legacy.TrackerMedianFlow_create()
    elif tracker_type == 'MOSSE':
        tracker = cv2.legacy.TrackerMOSSE_create()
    elif tracker_type == 'TLD':
        tracker = cv2.legacy.TrackerTLD_create()
    else:
        tracker = None
        print('์ž˜๋ชป๋œ ์ถ”์ ๊ธฐ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค!')
    
    return tracker

# ๋ฉ”์ธ ์‹คํ–‰ ์ฝ”๋“œ
def main():
    # ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ ํƒ (KCF ์ถ”์ฒœ!)
    tracker_type = 'KCF'
    
    # ๋น„๋””์˜ค ์บก์ฒ˜ ๊ฐ์ฒด ์ƒ์„ฑ (0์€ ๊ธฐ๋ณธ ์›น์บ )
    video = cv2.VideoCapture(0)
    
    # ์›น์บ ์ด ์ œ๋Œ€๋กœ ์—ด๋ ธ๋Š”์ง€ ํ™•์ธ
    if not video.isOpened():
        print("์›น์บ ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        sys.exit()
    
    # ์ฒซ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
    ok, frame = video.read()
    if not ok:
        print('ํ”„๋ ˆ์ž„์„ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!')
        sys.exit()
    
    # ์ถ”์ ํ•  ๊ฐ์ฒด ์˜์—ญ ์„ ํƒ (๋งˆ์šฐ์Šค๋กœ ๋“œ๋ž˜๊ทธ)
    bbox = cv2.selectROI("๊ฐ์ฒด ์„ ํƒ", frame, False)
    print(f"์„ ํƒ๋œ ์˜์—ญ: {bbox}")
    
    # ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”
    tracker = create_tracker(tracker_type)
    ok = tracker.init(frame, bbox)
    
    print("์ถ”์  ์‹œ์ž‘! ESC๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.")
    
    # ๋ฉ”์ธ ๋ฃจํ”„
    while True:
        # ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
        ok, frame = video.read()
        if not ok:
            break
        
        # ์ถ”์  ์—…๋ฐ์ดํŠธ
        ok, bbox = tracker.update(frame)
        
        # ์ถ”์  ์„ฑ๊ณต ์—ฌ๋ถ€์— ๋”ฐ๋ผ ํ‘œ์‹œ
        if ok:
            # ์ถ”์  ์„ฑ๊ณต - ์ดˆ๋ก์ƒ‰ ๋ฐ•์Šค
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, (0, 255, 0), 2, 1)
            
            # ์ƒํƒœ ํ…์ŠคํŠธ
            cv2.putText(frame, "Tracking", (10, 30), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        else:
            # ์ถ”์  ์‹คํŒจ - ๋นจ๊ฐ„์ƒ‰ ๊ฒฝ๊ณ 
            cv2.putText(frame, "Tracking Failed!", (10, 30), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        
        # ์ถ”์ ๊ธฐ ์ •๋ณด ํ‘œ์‹œ
        cv2.putText(frame, f"Tracker: {tracker_type}", (10, 60), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
        
        # FPS ๊ณ„์‚ฐ ๋ฐ ํ‘œ์‹œ (์„ ํƒ์‚ฌํ•ญ)
        cv2.putText(frame, "Press ESC to exit", (10, frame.shape[0] - 10), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow("Object Tracking", frame)
        
        # ESC ํ‚ค๋กœ ์ข…๋ฃŒ
        k = cv2.waitKey(1) & 0xff
        if k == 27:
            break
    
    # ์ •๋ฆฌ
    video.release()
    cv2.destroyAllWindows()
    print("์ถ”์  ์ข…๋ฃŒ!")

if __name__ == '__main__':
    main()
๐Ÿ’ก ์ฝ”๋“œ ์„ค๋ช… 1๏ธโƒฃ create_tracker(): ์›ํ•˜๋Š” ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์„ ํƒํ•˜๋Š” ํ•จ์ˆ˜์•ผ. tracker_type๋งŒ ๋ฐ”๊พธ๋ฉด ๋‹ค๋ฅธ ์•Œ๊ณ ๋ฆฌ์ฆ˜๋„ ์‰ฝ๊ฒŒ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์–ด!

2๏ธโƒฃ cv2.selectROI(): ๋งˆ์šฐ์Šค๋กœ ๋“œ๋ž˜๊ทธํ•ด์„œ ์ถ”์ ํ•  ์˜์—ญ์„ ์„ ํƒํ•˜๋Š” ํ•จ์ˆ˜. ์ •๋ง ์ง๊ด€์ ์ด์ง€?

3๏ธโƒฃ tracker.update(): ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค ๊ฐ์ฒด์˜ ์ƒˆ๋กœ์šด ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•ด์ค˜. ๋ฐ˜ํ™˜๊ฐ’์€ (์„ฑ๊ณต์—ฌ๋ถ€, ๋ฐ”์šด๋”ฉ๋ฐ•์Šค)์•ผ.

๐ŸŽจ ๋‘ ๋ฒˆ์งธ ์˜ˆ์ œ: ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ ๊ฐ์ฒด ์ถ”์ 

์›น์บ  ๋ง๊ณ  ์ €์žฅ๋œ ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ๋„ ์ถ”์ ํ•  ์ˆ˜ ์žˆ์–ด! ์ด๋ฒˆ์—” ์ข€ ๋” ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ. ๐ŸŽฅ

import cv2
import time

def track_video_file(video_path, tracker_type='CSRT'):
    """
    ๋น„๋””์˜ค ํŒŒ์ผ์—์„œ ๊ฐ์ฒด๋ฅผ ์ถ”์ ํ•˜๋Š” ํ•จ์ˆ˜
    
    Args:
        video_path: ๋น„๋””์˜ค ํŒŒ์ผ ๊ฒฝ๋กœ
        tracker_type: ์‚ฌ์šฉํ•  ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜
    """
    
    # ๋น„๋””์˜ค ํŒŒ์ผ ์—ด๊ธฐ
    video = cv2.VideoCapture(video_path)
    
    if not video.isOpened():
        print(f"๋น„๋””์˜ค ํŒŒ์ผ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {video_path}")
        return
    
    # ๋น„๋””์˜ค ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
    fps = int(video.get(cv2.CAP_PROP_FPS))
    width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
    total_frames = int(video.get(cv2.CAP_PROP_FRAME_COUNT))
    
    print(f"๋น„๋””์˜ค ์ •๋ณด:")
    print(f"  - ํ•ด์ƒ๋„: {width}x{height}")
    print(f"  - FPS: {fps}")
    print(f"  - ์ด ํ”„๋ ˆ์ž„: {total_frames}")
    
    # ์ฒซ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
    ok, frame = video.read()
    if not ok:
        print("์ฒซ ํ”„๋ ˆ์ž„์„ ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        return
    
    # ์ถ”์  ์˜์—ญ ์„ ํƒ
    print("\n์ถ”์ ํ•  ๊ฐ์ฒด๋ฅผ ๋งˆ์šฐ์Šค๋กœ ์„ ํƒํ•˜์„ธ์š”!")
    bbox = cv2.selectROI("๊ฐ์ฒด ์„ ํƒ (SPACE: ํ™•์ธ, ESC: ์ทจ์†Œ)", frame, False)
    cv2.destroyWindow("๊ฐ์ฒด ์„ ํƒ (SPACE: ํ™•์ธ, ESC: ์ทจ์†Œ)")
    
    # ์ถ”์ ๊ธฐ ์ƒ์„ฑ ๋ฐ ์ดˆ๊ธฐํ™”
    if tracker_type == 'KCF':
        tracker = cv2.TrackerKCF_create()
    elif tracker_type == 'CSRT':
        tracker = cv2.TrackerCSRT_create()
    elif tracker_type == 'MOSSE':
        tracker = cv2.legacy.TrackerMOSSE_create()
    else:
        print(f"์ง€์›ํ•˜์ง€ ์•Š๋Š” ์ถ”์ ๊ธฐ: {tracker_type}")
        return
    
    tracker.init(frame, bbox)
    
    # ๊ฒฐ๊ณผ ์ €์žฅ์„ ์œ„ํ•œ VideoWriter (์„ ํƒ์‚ฌํ•ญ)
    output_path = 'tracked_output.mp4'
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
    
    # ์ถ”์  ํ†ต๊ณ„
    frame_count = 0
    success_count = 0
    start_time = time.time()
    
    print(f"\n์ถ”์  ์‹œ์ž‘! (์•Œ๊ณ ๋ฆฌ์ฆ˜: {tracker_type})")
    print("Q ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.\n")
    
    # ๋ฉ”์ธ ์ถ”์  ๋ฃจํ”„
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        frame_count += 1
        
        # ์ถ”์  ์—…๋ฐ์ดํŠธ
        timer = cv2.getTickCount()
        ok, bbox = tracker.update(frame)
        fps_current = cv2.getTickFrequency() / (cv2.getTickCount() - timer)
        
        # ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ
        if ok:
            success_count += 1
            # ์ถ”์  ์„ฑ๊ณต - ์ดˆ๋ก์ƒ‰ ๋ฐ•์Šค
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, (0, 255, 0), 3)
            
            # ์ค‘์‹ฌ์  ํ‘œ์‹œ
            center_x = int(bbox[0] + bbox[2]/2)
            center_y = int(bbox[1] + bbox[3]/2)
            cv2.circle(frame, (center_x, center_y), 5, (0, 255, 0), -1)
            
            # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ (์„ ํƒ์‚ฌํ•ญ)
            cv2.putText(frame, "TRACKING", (p1[0], p1[1]-10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
        else:
            # ์ถ”์  ์‹คํŒจ
            cv2.putText(frame, "LOST", (50, 80), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
        
        # ์ •๋ณด ์˜ค๋ฒ„๋ ˆ์ด
        info_text = [
            f"Tracker: {tracker_type}",
            f"Frame: {frame_count}/{total_frames}",
            f"FPS: {int(fps_current)}",
            f"Success Rate: {success_count/frame_count*100:.1f}%"
        ]
        
        y_offset = 30
        for i, text in enumerate(info_text):
            cv2.putText(frame, text, (10, y_offset + i*30), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
        
        # ์ง„ํ–‰๋ฅ  ๋ฐ”
        progress = frame_count / total_frames
        bar_width = int(width * 0.8)
        bar_height = 20
        bar_x = int(width * 0.1)
        bar_y = height - 40
        
        cv2.rectangle(frame, (bar_x, bar_y), 
                     (bar_x + bar_width, bar_y + bar_height), 
                     (50, 50, 50), -1)
        cv2.rectangle(frame, (bar_x, bar_y), 
                     (bar_x + int(bar_width * progress), bar_y + bar_height), 
                     (0, 255, 0), -1)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow("Video Tracking", frame)
        
        # ๊ฒฐ๊ณผ ์ €์žฅ
        out.write(frame)
        
        # ํ‚ค ์ž…๋ ฅ ์ฒ˜๋ฆฌ
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q') or key == ord('Q'):
            print("\n์‚ฌ์šฉ์ž๊ฐ€ ์ข…๋ฃŒํ–ˆ์Šต๋‹ˆ๋‹ค.")
            break
        elif key == ord('p'):  # ์ผ์‹œ์ •์ง€
            cv2.waitKey(-1)
    
    # ์ข…๋ฃŒ ์ฒ˜๋ฆฌ
    elapsed_time = time.time() - start_time
    
    print(f"\n=== ์ถ”์  ์™„๋ฃŒ ===")
    print(f"์ด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„: {elapsed_time:.2f}์ดˆ")
    print(f"ํ‰๊ท  FPS: {frame_count/elapsed_time:.2f}")
    print(f"์ถ”์  ์„ฑ๊ณต๋ฅ : {success_count/frame_count*100:.1f}%")
    print(f"๊ฒฐ๊ณผ ์ €์žฅ: {output_path}")
    
    video.release()
    out.release()
    cv2.destroyAllWindows()

# ์‹คํ–‰ ์˜ˆ์ œ
if __name__ == '__main__':
    # ๋น„๋””์˜ค ํŒŒ์ผ ๊ฒฝ๋กœ ์ง€์ •
    video_file = 'sample_video.mp4'  # ์—ฌ๊ธฐ์— ์‹ค์ œ ํŒŒ์ผ ๊ฒฝ๋กœ ์ž…๋ ฅ
    
    # CSRT๋Š” ์ •ํ™•๋„๊ฐ€ ๋†’์ง€๋งŒ ๋А๋ฆผ
    # KCF๋Š” ์†๋„์™€ ์ •ํ™•๋„์˜ ๊ท ํ˜•์ด ์ข‹์Œ
    # MOSSE๋Š” ๋งค์šฐ ๋น ๋ฅด์ง€๋งŒ ์ •ํ™•๋„๋Š” ๋‚ฎ์Œ
    
    track_video_file(video_file, tracker_type='CSRT')
๐ŸŽฏ ์‹ค์ „ ํŒ! ์ด ์ฝ”๋“œ๋Š” ์ถ”์  ๊ฒฐ๊ณผ๋ฅผ ์ƒˆ๋กœ์šด ๋น„๋””์˜ค ํŒŒ์ผ๋กœ ์ €์žฅํ•ด์ค˜. ๋‚˜์ค‘์— ๋ถ„์„ํ•˜๊ฑฐ๋‚˜ ๊ณต์œ ํ•  ๋•Œ ์ •๋ง ์œ ์šฉํ•˜์ง€! ์ง„ํ–‰๋ฅ  ๋ฐ”์™€ ํ†ต๊ณ„ ์ •๋ณด๋„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ณด์—ฌ์ฃผ๋‹ˆ๊นŒ ์ถ”์  ์„ฑ๋Šฅ์„ ํ•œ๋ˆˆ์— ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด. ๐Ÿ“Š

๐ŸŽฏ ์„ธ ๋ฒˆ์งธ ์˜ˆ์ œ: ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  (Multi-Object Tracking)

ํ•˜๋‚˜๋งŒ ์ถ”์ ํ•˜๋Š” ๊ฑด ์žฌ๋ฏธ์—†์ง€! ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ๋™์‹œ์— ์ถ”์ ํ•ด๋ณด์ž. ์ด๊ฒŒ ์ง„์งœ ์‹ค์ „์ด์•ผ! ๐ŸŽช

import cv2
import numpy as np

class MultiObjectTracker:
    """
    ์—ฌ๋Ÿฌ ๊ฐ์ฒด๋ฅผ ๋™์‹œ์— ์ถ”์ ํ•˜๋Š” ํด๋ž˜์Šค
    """
    
    def __init__(self, tracker_type='KCF'):
        self.tracker_type = tracker_type
        self.trackers = []
        self.colors = []
        self.object_ids = []
        self.next_id = 1
    
    def create_tracker(self):
        """์ƒˆ๋กœ์šด ์ถ”์ ๊ธฐ ์ƒ์„ฑ"""
        if self.tracker_type == 'KCF':
            return cv2.TrackerKCF_create()
        elif self.tracker_type == 'CSRT':
            return cv2.TrackerCSRT_create()
        elif self.tracker_type == 'MOSSE':
            return cv2.legacy.TrackerMOSSE_create()
        else:
            return cv2.TrackerKCF_create()
    
    def add_tracker(self, frame, bbox):
        """
        ์ƒˆ๋กœ์šด ์ถ”์  ๊ฐ์ฒด ์ถ”๊ฐ€
        
        Args:
            frame: ํ˜„์žฌ ํ”„๋ ˆ์ž„
            bbox: ์ถ”์ ํ•  ์˜์—ญ (x, y, w, h)
        """
        tracker = self.create_tracker()
        ok = tracker.init(frame, bbox)
        
        if ok:
            self.trackers.append(tracker)
            # ๋žœ๋ค ์ƒ‰์ƒ ์ƒ์„ฑ (๊ฐ ๊ฐ์ฒด๋งˆ๋‹ค ๋‹ค๋ฅธ ์ƒ‰)
            color = tuple(np.random.randint(0, 255, 3).tolist())
            self.colors.append(color)
            self.object_ids.append(self.next_id)
            self.next_id += 1
            print(f"๊ฐ์ฒด #{self.object_ids[-1]} ์ถ”๊ฐ€๋จ!")
            return True
        return False
    
    def update(self, frame):
        """
        ๋ชจ๋“  ์ถ”์ ๊ธฐ ์—…๋ฐ์ดํŠธ
        
        Returns:
            ์„ฑ๊ณตํ•œ ์ถ”์  ๊ฒฐ๊ณผ ๋ฆฌ์ŠคํŠธ
        """
        results = []
        
        for i in range(len(self.trackers)):
            ok, bbox = self.trackers[i].update(frame)
            
            if ok:
                results.append({
                    'id': self.object_ids[i],
                    'bbox': bbox,
                    'color': self.colors[i],
                    'success': True
                })
            else:
                results.append({
                    'id': self.object_ids[i],
                    'bbox': None,
                    'color': self.colors[i],
                    'success': False
                })
        
        return results
    
    def remove_failed_trackers(self, results):
        """์‹คํŒจํ•œ ์ถ”์ ๊ธฐ ์ œ๊ฑฐ"""
        indices_to_remove = []
        
        for i, result in enumerate(results):
            if not result['success']:
                indices_to_remove.append(i)
        
        # ์—ญ์ˆœ์œผ๋กœ ์ œ๊ฑฐ (์ธ๋ฑ์Šค ๊ผฌ์ž„ ๋ฐฉ์ง€)
        for i in sorted(indices_to_remove, reverse=True):
            print(f"๊ฐ์ฒด #{self.object_ids[i]} ์ถ”์  ์‹คํŒจ๋กœ ์ œ๊ฑฐ๋จ")
            del self.trackers[i]
            del self.colors[i]
            del self.object_ids[i]
    
    def get_count(self):
        """ํ˜„์žฌ ์ถ”์  ์ค‘์ธ ๊ฐ์ฒด ์ˆ˜"""
        return len(self.trackers)


def multi_object_tracking_demo():
    """
    ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  ๋ฐ๋ชจ
    """
    
    # ์›น์บ  ์—ด๊ธฐ
    video = cv2.VideoCapture(0)
    
    if not video.isOpened():
        print("์›น์บ ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        return
    
    # ๋‹ค์ค‘ ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”
    mot = MultiObjectTracker(tracker_type='KCF')
    
    print("\n=== ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  ์‹œ์ž‘ ===")
    print("์‚ฌ์šฉ๋ฒ•:")
    print("  S ํ‚ค: ์ƒˆ๋กœ์šด ๊ฐ์ฒด ์ถ”๊ฐ€")
    print("  D ํ‚ค: ์‹คํŒจํ•œ ์ถ”์ ๊ธฐ ์ œ๊ฑฐ")
    print("  C ํ‚ค: ๋ชจ๋“  ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”")
    print("  Q ํ‚ค: ์ข…๋ฃŒ")
    print("=" * 40)
    
    # ์ถ”์  ๊ถค์  ์ €์žฅ (์„ ํƒ์‚ฌํ•ญ)
    trajectories = {}
    
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        # ๋ชจ๋“  ์ถ”์ ๊ธฐ ์—…๋ฐ์ดํŠธ
        if mot.get_count() > 0:
            results = mot.update(frame)
            
            # ๊ฒฐ๊ณผ ๊ทธ๋ฆฌ๊ธฐ
            for result in results:
                if result['success']:
                    bbox = result['bbox']
                    obj_id = result['id']
                    color = result['color']
                    
                    # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค
                    p1 = (int(bbox[0]), int(bbox[1]))
                    p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
                    cv2.rectangle(frame, p1, p2, color, 3)
                    
                    # ๊ฐ์ฒด ID ํ‘œ์‹œ
                    label = f"ID: {obj_id}"
                    cv2.putText(frame, label, (p1[0], p1[1]-10), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
                    
                    # ์ค‘์‹ฌ์ 
                    center_x = int(bbox[0] + bbox[2]/2)
                    center_y = int(bbox[1] + bbox[3]/2)
                    cv2.circle(frame, (center_x, center_y), 4, color, -1)
                    
                    # ๊ถค์  ์ €์žฅ
                    if obj_id not in trajectories:
                        trajectories[obj_id] = []
                    trajectories[obj_id].append((center_x, center_y))
                    
                    # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ (์ตœ๊ทผ 30๊ฐœ ์ )
                    if len(trajectories[obj_id]) > 1:
                        points = trajectories[obj_id][-30:]
                        for i in range(1, len(points)):
                            cv2.line(frame, points[i-1], points[i], color, 2)
        
        # ์ •๋ณด ํŒจ๋„
        info_panel_height = 120
        panel = np.zeros((info_panel_height, frame.shape[1], 3), dtype=np.uint8)
        panel[:] = (40, 40, 40)
        
        cv2.putText(panel, f"Tracking Objects: {mot.get_count()}", 
                   (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        cv2.putText(panel, "Press 'S' to add new object", 
                   (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 200), 1)
        cv2.putText(panel, "Press 'D' to remove failed | 'C' to clear all | 'Q' to quit", 
                   (10, 85), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 200), 1)
        
        # ํŒจ๋„๊ณผ ํ”„๋ ˆ์ž„ ํ•ฉ์น˜๊ธฐ
        display = np.vstack([panel, frame])
        
        cv2.imshow("Multi-Object Tracking", display)
        
        # ํ‚ค ์ž…๋ ฅ ์ฒ˜๋ฆฌ
        key = cv2.waitKey(1) & 0xFF
        
        if key == ord('s') or key == ord('S'):
            # ์ƒˆ ๊ฐ์ฒด ์ถ”๊ฐ€
            print("\n์ถ”์ ํ•  ๊ฐ์ฒด๋ฅผ ์„ ํƒํ•˜์„ธ์š”!")
            bbox = cv2.selectROI("๊ฐ์ฒด ์„ ํƒ", frame, False)
            cv2.destroyWindow("๊ฐ์ฒด ์„ ํƒ")
            
            if bbox[2] > 0 and bbox[3] > 0:  # ์œ ํšจํ•œ ์„ ํƒ์ธ์ง€ ํ™•์ธ
                mot.add_tracker(frame, bbox)
        
        elif key == ord('d') or key == ord('D'):
            # ์‹คํŒจํ•œ ์ถ”์ ๊ธฐ ์ œ๊ฑฐ
            if mot.get_count() > 0:
                results = mot.update(frame)
                mot.remove_failed_trackers(results)
        
        elif key == ord('c') or key == ord('C'):
            # ๋ชจ๋“  ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”
            mot = MultiObjectTracker(tracker_type='KCF')
            trajectories = {}
            print("\n๋ชจ๋“  ์ถ”์ ๊ธฐ๊ฐ€ ์ดˆ๊ธฐํ™”๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
        
        elif key == ord('q') or key == ord('Q'):
            break
    
    video.release()
    cv2.destroyAllWindows()
    print("\n์ถ”์  ์ข…๋ฃŒ!")


if __name__ == '__main__':
    multi_object_tracking_demo()
๐Ÿ’ก ๋‹ค์ค‘ ์ถ”์ ์˜ ํ•ต์‹ฌ ํฌ์ธํŠธ 1๏ธโƒฃ ๊ฐ์ฒด๋ณ„ ID ๊ด€๋ฆฌ: ๊ฐ ๊ฐ์ฒด์— ๊ณ ์œ  ID๋ฅผ ๋ถ€์—ฌํ•ด์„œ ๊ตฌ๋ถ„ํ•ด

2๏ธโƒฃ ์ƒ‰์ƒ ๊ตฌ๋ถ„: ๋žœ๋ค ์ƒ‰์ƒ์œผ๋กœ ๊ฐ ๊ฐ์ฒด๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์—ˆ์–ด

3๏ธโƒฃ ๊ถค์  ์ถ”์ : ๊ฐ์ฒด์˜ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ์„ ์œผ๋กœ ๊ทธ๋ ค์„œ ์›€์ง์ž„ ํŒจํ„ด์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์–ด

4๏ธโƒฃ ๋™์  ๊ด€๋ฆฌ: ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์–ด์„œ ์œ ์—ฐํ•ด!

๐Ÿ”ฅ ๋„ค ๋ฒˆ์งธ ์˜ˆ์ œ: ์ƒ‰์ƒ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด ์ถ”์ 

์ด๋ฒˆ์—” ์ข€ ๋‹ค๋ฅธ ์ ‘๊ทผ! ํŠน์ • ์ƒ‰์ƒ์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ์•„์„œ ์ถ”์ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ. ๋นจ๊ฐ„ ๊ณต์ด๋‚˜ ๋…ธ๋ž€ ์ฐจ ๊ฐ™์€ ๊ฑธ ์ถ”์ ํ•  ๋•Œ ์œ ์šฉํ•ด! ๐ŸŽจ

import cv2
import numpy as np

class ColorBasedTracker:
    """
    ์ƒ‰์ƒ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด ์ถ”์  ํด๋ž˜์Šค
    """
    
    def __init__(self, color_name='red'):
        self.color_name = color_name
        self.lower_bound, self.upper_bound = self.get_color_range(color_name)
        self.kernel = np.ones((5, 5), np.uint8)
    
    def get_color_range(self, color_name):
        """
        ์ƒ‰์ƒ๋ณ„ HSV ๋ฒ”์œ„ ์ •์˜
        """
        color_ranges = {
            'red': ([0, 120, 70], [10, 255, 255]),  # ๋นจ๊ฐ•
            'red2': ([170, 120, 70], [180, 255, 255]),  # ๋นจ๊ฐ• (์ถ”๊ฐ€ ๋ฒ”์œ„)
            'blue': ([100, 150, 0], [140, 255, 255]),  # ํŒŒ๋ž‘
            'green': ([40, 40, 40], [80, 255, 255]),  # ์ดˆ๋ก
            'yellow': ([20, 100, 100], [30, 255, 255]),  # ๋…ธ๋ž‘
            'orange': ([10, 100, 100], [20, 255, 255]),  # ์ฃผํ™ฉ
            'purple': ([140, 50, 50], [160, 255, 255]),  # ๋ณด๋ผ
        }
        
        if color_name in color_ranges:
            lower, upper = color_ranges[color_name]
            return np.array(lower), np.array(upper)
        else:
            # ๊ธฐ๋ณธ๊ฐ’ (๋นจ๊ฐ•)
            return np.array([0, 120, 70]), np.array([10, 255, 255])
    
    def detect_color(self, frame):
        """
        ํ”„๋ ˆ์ž„์—์„œ ํŠน์ • ์ƒ‰์ƒ ์˜์—ญ ๊ฒ€์ถœ
        
        Returns:
            mask: ์ด์ง„ ๋งˆ์Šคํฌ
            contours: ๊ฒ€์ถœ๋œ ์œค๊ณฝ์„ ๋“ค
        """
        # BGR์„ HSV๋กœ ๋ณ€ํ™˜
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        
        # ์ƒ‰์ƒ ๋ฒ”์œ„๋กœ ๋งˆ์Šคํฌ ์ƒ์„ฑ
        mask = cv2.inRange(hsv, self.lower_bound, self.upper_bound)
        
        # ๋นจ๊ฐ•์€ HSV์—์„œ ๋‘ ๋ฒ”์œ„๋ฅผ ์‚ฌ์šฉ
        if self.color_name == 'red':
            lower2 = np.array([170, 120, 70])
            upper2 = np.array([180, 255, 255])
            mask2 = cv2.inRange(hsv, lower2, upper2)
            mask = cv2.bitwise_or(mask, mask2)
        
        # ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ (๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ)
        mask = cv2.erode(mask, self.kernel, iterations=2)
        mask = cv2.dilate(mask, self.kernel, iterations=2)
        
        # ์œค๊ณฝ์„  ์ฐพ๊ธฐ
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, 
                                       cv2.CHAIN_APPROX_SIMPLE)
        
        return mask, contours
    
    def get_largest_contour(self, contours, min_area=500):
        """
        ๊ฐ€์žฅ ํฐ ์œค๊ณฝ์„  ์ฐพ๊ธฐ
        
        Args:
            contours: ์œค๊ณฝ์„  ๋ฆฌ์ŠคํŠธ
            min_area: ์ตœ์†Œ ๋ฉด์  (์ž‘์€ ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ)
        
        Returns:
            ๊ฐ€์žฅ ํฐ ์œค๊ณฝ์„ ์˜ ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค (x, y, w, h)
        """
        if len(contours) == 0:
            return None
        
        # ๋ฉด์  ๊ธฐ์ค€์œผ๋กœ ์ •๋ ฌ
        valid_contours = [c for c in contours if cv2.contourArea(c) > min_area]
        
        if len(valid_contours) == 0:
            return None
        
        largest_contour = max(valid_contours, key=cv2.contourArea)
        x, y, w, h = cv2.boundingRect(largest_contour)
        
        return (x, y, w, h), largest_contour


def color_tracking_demo():
    """
    ์ƒ‰์ƒ ๊ธฐ๋ฐ˜ ์ถ”์  ๋ฐ๋ชจ
    """
    
    # ์ถ”์ ํ•  ์ƒ‰์ƒ ์„ ํƒ
    print("์ถ”์ ํ•  ์ƒ‰์ƒ์„ ์„ ํƒํ•˜์„ธ์š”:")
    print("1: ๋นจ๊ฐ•(red)")
    print("2: ํŒŒ๋ž‘(blue)")
    print("3: ์ดˆ๋ก(green)")
    print("4: ๋…ธ๋ž‘(yellow)")
    
    choice = input("์„ ํƒ (1-4): ").strip()
    
    color_map = {'1': 'red', '2': 'blue', '3': 'green', '4': 'yellow'}
    color_name = color_map.get(choice, 'red')
    
    # ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”
    tracker = ColorBasedTracker(color_name)
    
    # ์›น์บ  ์—ด๊ธฐ
    video = cv2.VideoCapture(0)
    
    if not video.isOpened():
        print("์›น์บ ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        return
    
    print(f"\n{color_name.upper()} ์ƒ‰์ƒ ์ถ”์  ์‹œ์ž‘!")
    print("Q ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.\n")
    
    # ๊ถค์  ์ €์žฅ
    trajectory = []
    max_trajectory_length = 50
    
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        # ์ƒ‰์ƒ ๊ฒ€์ถœ
        mask, contours = tracker.detect_color(frame)
        
        # ๊ฐ€์žฅ ํฐ ๊ฐ์ฒด ์ฐพ๊ธฐ
        result = tracker.get_largest_contour(contours)
        
        if result is not None:
            bbox, contour = result
            x, y, w, h = bbox
            
            # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค ๊ทธ๋ฆฌ๊ธฐ
            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 3)
            
            # ์œค๊ณฝ์„  ๊ทธ๋ฆฌ๊ธฐ
            cv2.drawContours(frame, [contour], -1, (0, 255, 255), 2)
            
            # ์ค‘์‹ฌ์ 
            center_x = x + w // 2
            center_y = y + h // 2
            cv2.circle(frame, (center_x, center_y), 5, (0, 0, 255), -1)
            
            # ๊ถค์  ์ €์žฅ
            trajectory.append((center_x, center_y))
            if len(trajectory) > max_trajectory_length:
                trajectory.pop(0)
            
            # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ
            for i in range(1, len(trajectory)):
                thickness = int(np.sqrt(max_trajectory_length / float(i + 1)) * 2.5)
                cv2.line(frame, trajectory[i-1], trajectory[i], 
                        (0, 255, 0), thickness)
            
            # ์ •๋ณด ํ‘œ์‹œ
            area = cv2.contourArea(contour)
            cv2.putText(frame, f"Tracking: {color_name.upper()}", 
                       (x, y-30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
            cv2.putText(frame, f"Area: {int(area)}", 
                       (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        else:
            cv2.putText(frame, f"No {color_name.upper()} object detected", 
                       (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        
        # ๋งˆ์Šคํฌ ์‹œ๊ฐํ™” (์ž‘์€ ์ฐฝ์œผ๋กœ)
        mask_display = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
        mask_small = cv2.resize(mask_display, (320, 240))
        
        # ํ”„๋ ˆ์ž„์— ๋งˆ์Šคํฌ ์˜ค๋ฒ„๋ ˆ์ด
        frame[10:250, 10:330] = mask_small
        cv2.rectangle(frame, (10, 10), (330, 250), (255, 255, 255), 2)
        cv2.putText(frame, "Color Mask", (15, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow("Color-Based Tracking", frame)
        
        # ์ข…๋ฃŒ ํ‚ค
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    video.release()
    cv2.destroyAllWindows()


def interactive_color_picker():
    """
    ๋Œ€ํ™”ํ˜• ์ƒ‰์ƒ ๋ฒ”์œ„ ์„ ํƒ ๋„๊ตฌ
    HSV ๊ฐ’์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์กฐ์ •ํ•˜๋ฉด์„œ ์ตœ์ ์˜ ๋ฒ”์œ„๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์–ด!
    """
    
    def nothing(x):
        pass
    
    video = cv2.VideoCapture(0)
    
    # ํŠธ๋ž™๋ฐ” ์œˆ๋„์šฐ ์ƒ์„ฑ
    cv2.namedWindow('Color Picker')
    
    # HSV ๋ฒ”์œ„ ํŠธ๋ž™๋ฐ”
    cv2.createTrackbar('H Low', 'Color Picker', 0, 179, nothing)
    cv2.createTrackbar('H High', 'Color Picker', 179, 179, nothing)
    cv2.createTrackbar('S Low', 'Color Picker', 0, 255, nothing)
    cv2.createTrackbar('S High', 'Color Picker', 255, 255, nothing)
    cv2.createTrackbar('V Low', 'Color Picker', 0, 255, nothing)
    cv2.createTrackbar('V High', 'Color Picker', 255, 255, nothing)
    
    print("\n=== ๋Œ€ํ™”ํ˜• ์ƒ‰์ƒ ์„ ํƒ๊ธฐ ===")
    print("ํŠธ๋ž™๋ฐ”๋ฅผ ์กฐ์ •ํ•ด์„œ ์›ํ•˜๋Š” ์ƒ‰์ƒ ๋ฒ”์œ„๋ฅผ ์ฐพ์œผ์„ธ์š”!")
    print("Q ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ํ˜„์žฌ ๊ฐ’์ด ์ถœ๋ ฅ๋˜๊ณ  ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.\n")
    
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        # ํŠธ๋ž™๋ฐ” ๊ฐ’ ์ฝ๊ธฐ
        h_low = cv2.getTrackbarPos('H Low', 'Color Picker')
        h_high = cv2.getTrackbarPos('H High', 'Color Picker')
        s_low = cv2.getTrackbarPos('S Low', 'Color Picker')
        s_high = cv2.getTrackbarPos('S High', 'Color Picker')
        v_low = cv2.getTrackbarPos('V Low', 'Color Picker')
        v_high = cv2.getTrackbarPos('V High', 'Color Picker')
        
        # HSV ๋ณ€ํ™˜
        hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
        
        # ๋งˆ์Šคํฌ ์ƒ์„ฑ
        lower = np.array([h_low, s_low, v_low])
        upper = np.array([h_high, s_high, v_high])
        mask = cv2.inRange(hsv, lower, upper)
        
        # ๊ฒฐ๊ณผ ์ ์šฉ
        result = cv2.bitwise_and(frame, frame, mask=mask)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow('Original', frame)
        cv2.imshow('Mask', mask)
        cv2.imshow('Result', result)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            print("\n=== ์„ ํƒ๋œ HSV ๋ฒ”์œ„ ===")
            print(f"Lower: [{h_low}, {s_low}, {v_low}]")
            print(f"Upper: [{h_high}, {s_high}, {v_high}]")
            break
    
    video.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    # ์ผ๋ฐ˜ ์ƒ‰์ƒ ์ถ”์  ์‹คํ–‰
    color_tracking_demo()
    
    # ๋˜๋Š” ์ƒ‰์ƒ ๋ฒ”์œ„ ์ฐพ๊ธฐ ๋„๊ตฌ ์‹คํ–‰
    # interactive_color_picker()
๐ŸŽจ ์ƒ‰์ƒ ์ถ”์ ์˜ ์žฅ์  โ€ข ๋น ๋ฅธ ์†๋„: ๋ณต์žกํ•œ ๋จธ์‹ ๋Ÿฌ๋‹ ์—†์ด๋„ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ!
โ€ข ๊ฐ„๋‹จํ•œ ๊ตฌํ˜„: HSV ์ƒ‰๊ณต๊ฐ„๋งŒ ์ดํ•ดํ•˜๋ฉด ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด
โ€ข ํŠน์ • ์ƒํ™ฉ์— ์ตœ์ : ์ƒ‰์ƒ์ด ๋šœ๋ ทํ•œ ๊ฐ์ฒด(๊ณต, ์ฐจ๋Ÿ‰, ์˜ท ๋“ฑ) ์ถ”์ ์— ์™„๋ฒฝํ•ด
โ€ข ์กฐ๋ช… ๋ณ€ํ™” ๋Œ€์‘: HSV๋Š” RGB๋ณด๋‹ค ์กฐ๋ช… ๋ณ€ํ™”์— ๊ฐ•ํ•ด!

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

์ถ”์  ์ฝ”๋“œ๋ฅผ ๋” ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•๋“ค์„ ์•Œ๋ ค์ค„๊ฒŒ! ์‹ค์ „์—์„œ ์ •๋ง ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด์•ผ. ๐Ÿš€

1๏ธโƒฃ ํ”„๋ ˆ์ž„ ํฌ๊ธฐ ์กฐ์ •

# ์›๋ณธ ํ”„๋ ˆ์ž„์„ ์ž‘๊ฒŒ ๋ฆฌ์‚ฌ์ด์ฆˆํ•˜๋ฉด ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ํฌ๊ฒŒ ํ–ฅ์ƒ๋ผ!
def resize_frame(frame, width=640):
    """
    ํ”„๋ ˆ์ž„ ํฌ๊ธฐ๋ฅผ ์กฐ์ •ํ•˜๋ฉด์„œ ๋น„์œจ ์œ ์ง€
    """
    height = int(frame.shape[0] * (width / frame.shape[1]))
    return cv2.resize(frame, (width, height))

# ์‚ฌ์šฉ ์˜ˆ
frame = resize_frame(frame, width=640)  # 640px๋กœ ์ถ•์†Œ

2๏ธโƒฃ ROI (Region of Interest) ํ™œ์šฉ

# ์ „์ฒด ํ”„๋ ˆ์ž„์ด ์•„๋‹Œ ๊ด€์‹ฌ ์˜์—ญ๋งŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ
def expand_roi(bbox, frame_shape, expand_ratio=1.5):
    """
    ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค๋ฅผ ํ™•์žฅํ•ด์„œ ROI ์ƒ์„ฑ
    ๊ฐ์ฒด๊ฐ€ ๋น ๋ฅด๊ฒŒ ์›€์ง์—ฌ๋„ ๋†“์น˜์ง€ ์•Š์•„!
    """
    x, y, w, h = bbox
    
    # ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
    center_x = x + w // 2
    center_y = y + h // 2
    
    # ํ™•์žฅ๋œ ํฌ๊ธฐ
    new_w = int(w * expand_ratio)
    new_h = int(h * expand_ratio)
    
    # ์ƒˆ๋กœ์šด ์ขŒํ‘œ (ํ”„๋ ˆ์ž„ ๋ฒ”์œ„ ๋‚ด๋กœ ์ œํ•œ)
    new_x = max(0, center_x - new_w // 2)
    new_y = max(0, center_y - new_h // 2)
    new_x2 = min(frame_shape[1], new_x + new_w)
    new_y2 = min(frame_shape[0], new_y + new_h)
    
    return (new_x, new_y, new_x2 - new_x, new_y2 - new_y)

# ์‚ฌ์šฉ ์˜ˆ
roi = expand_roi(bbox, frame.shape, expand_ratio=1.5)
roi_frame = frame[roi[1]:roi[1]+roi[3], roi[0]:roi[0]+roi[2]]

3๏ธโƒฃ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์œผ๋กœ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ ์ตœ์ ํ™”

import threading
import queue

class VideoStream:
    """
    ๋ณ„๋„ ์Šค๋ ˆ๋“œ์—์„œ ํ”„๋ ˆ์ž„์„ ์ฝ์–ด์„œ ๋ฒ„ํผ๋ง
    ๋ฉ”์ธ ๋ฃจํ”„์˜ ์ฒ˜๋ฆฌ ์†๋„๊ฐ€ ํ–ฅ์ƒ๋ผ!
    """
    
    def __init__(self, src=0, queue_size=128):
        self.stream = cv2.VideoCapture(src)
        self.stopped = False
        self.queue = queue.Queue(maxsize=queue_size)
        
        # ์Šค๋ ˆ๋“œ ์‹œ์ž‘
        self.thread = threading.Thread(target=self.update, args=())
        self.thread.daemon = True
        self.thread.start()
    
    def update(self):
        """๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ"""
        while True:
            if self.stopped:
                return
            
            if not self.queue.full():
                ok, frame = self.stream.read()
                if not ok:
                    self.stop()
                    return
                self.queue.put(frame)
    
    def read(self):
        """ํ์—์„œ ํ”„๋ ˆ์ž„ ๊ฐ€์ ธ์˜ค๊ธฐ"""
        return self.queue.get()
    
    def stop(self):
        """์ŠคํŠธ๋ฆผ ์ค‘์ง€"""
        self.stopped = True
        self.thread.join()
    
    def __del__(self):
        self.stream.release()

# ์‚ฌ์šฉ ์˜ˆ
vs = VideoStream(src=0)
time.sleep(1.0)  # ์นด๋ฉ”๋ผ ์›Œ๋ฐ์—…

while True:
    frame = vs.read()
    # ํ”„๋ ˆ์ž„ ์ฒ˜๋ฆฌ...
    
vs.stop()

4๏ธโƒฃ ํ”„๋ ˆ์ž„ ์Šคํ‚ต์œผ๋กœ ์†๋„ ํ–ฅ์ƒ

# ๋ชจ๋“  ํ”„๋ ˆ์ž„์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  N๊ฐœ๋งˆ๋‹ค ์ฒ˜๋ฆฌ
frame_skip = 2  # 2ํ”„๋ ˆ์ž„๋งˆ๋‹ค 1๋ฒˆ ์ฒ˜๋ฆฌ
frame_count = 0

while True:
    ok, frame = video.read()
    frame_count += 1
    
    # ํ”„๋ ˆ์ž„ ์Šคํ‚ต
    if frame_count % frame_skip != 0:
        continue
    
    # ์ถ”์  ์—…๋ฐ์ดํŠธ
    ok, bbox = tracker.update(frame)
    # ...
โšก ์„ฑ๋Šฅ ๋น„๊ต โ€ข ์›๋ณธ (1920x1080): ~15 FPS
โ€ข ๋ฆฌ์‚ฌ์ด์ฆˆ (640x360): ~45 FPS (3๋ฐฐ ํ–ฅ์ƒ!)
โ€ข ROI + ๋ฆฌ์‚ฌ์ด์ฆˆ: ~60 FPS (4๋ฐฐ ํ–ฅ์ƒ!)
โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ถ”๊ฐ€: ~75 FPS (5๋ฐฐ ํ–ฅ์ƒ!)

์ƒํ™ฉ์— ๋งž๊ฒŒ ์กฐํ•ฉํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๊ฐ€ ํ›จ์”ฌ ์ˆ˜์›”ํ•ด์ ธ! ๐Ÿ’ช

๐ŸŽ“ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์˜ˆ์ œ

์ด๋ก ๋งŒ ๋ฐฐ์šฐ๋ฉด ์žฌ๋ฏธ์—†์ง€! ์‹ค์ œ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž. ๐ŸŽฏ

๐Ÿ“น ํ”„๋กœ์ ํŠธ 1: ์นจ์ž… ๊ฐ์ง€ ์‹œ์Šคํ…œ
import cv2
import numpy as np
from datetime import datetime
import os

class IntrusionDetectionSystem:
    """
    ํŠน์ • ์˜์—ญ์— ๊ฐ์ฒด๊ฐ€ ์นจ์ž…ํ•˜๋ฉด ๊ฒฝ๊ณ ํ•˜๊ณ  ๋…นํ™”ํ•˜๋Š” ์‹œ์Šคํ…œ
    """
    
    def __init__(self, detection_zone):
        """
        Args:
            detection_zone: ๊ฐ์ง€ ์˜์—ญ [(x1,y1), (x2,y2), ...]
        """
        self.detection_zone = np.array(detection_zone, dtype=np.int32)
        self.is_recording = False
        self.video_writer = None
        self.alert_count = 0
        
        # ๋…นํ™” ํด๋” ์ƒ์„ฑ
        self.output_dir = 'intrusion_recordings'
        if not os.path.exists(self.output_dir):
            os.makedirs(self.output_dir)
    
    def is_inside_zone(self, point):
        """์ ์ด ๊ฐ์ง€ ์˜์—ญ ์•ˆ์— ์žˆ๋Š”์ง€ ํ™•์ธ"""
        result = cv2.pointPolygonTest(self.detection_zone, point, False)
        return result >= 0
    
    def start_recording(self, frame_size, fps=20):
        """์นจ์ž… ๊ฐ์ง€์‹œ ๋…นํ™” ์‹œ์ž‘"""
        if not self.is_recording:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            filename = f"{self.output_dir}/intrusion_{timestamp}.mp4"
            
            fourcc = cv2.VideoWriter_fourcc(*'mp4v')
            self.video_writer = cv2.VideoWriter(
                filename, fourcc, fps, frame_size
            )
            self.is_recording = True
            print(f"๐Ÿšจ ์นจ์ž… ๊ฐ์ง€! ๋…นํ™” ์‹œ์ž‘: {filename}")
    
    def stop_recording(self):
        """๋…นํ™” ์ค‘์ง€"""
        if self.is_recording and self.video_writer is not None:
            self.video_writer.release()
            self.is_recording = False
            print("โœ… ๋…นํ™” ์ข…๋ฃŒ")
    
    def draw_zone(self, frame, color=(0, 255, 255), thickness=2):
        """๊ฐ์ง€ ์˜์—ญ ๊ทธ๋ฆฌ๊ธฐ"""
        cv2.polylines(frame, [self.detection_zone], True, color, thickness)
        
        # ์˜์—ญ ๋‚ด๋ถ€๋ฅผ ๋ฐ˜ํˆฌ๋ช…ํ•˜๊ฒŒ
        overlay = frame.copy()
        cv2.fillPoly(overlay, [self.detection_zone], color)
        cv2.addWeighted(overlay, 0.2, frame, 0.8, 0, frame)
        
        # ํ…์ŠคํŠธ
        cv2.putText(frame, "DETECTION ZONE", 
                   tuple(self.detection_zone[0]), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, color, 2)


def intrusion_detection_demo():
    """์นจ์ž… ๊ฐ์ง€ ์‹œ์Šคํ…œ ๋ฐ๋ชจ"""
    
    video = cv2.VideoCapture(0)
    
    if not video.isOpened():
        print("์›น์บ ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        return
    
    # ์ฒซ ํ”„๋ ˆ์ž„์œผ๋กœ ๊ฐ์ง€ ์˜์—ญ ์„ค์ •
    ok, frame = video.read()
    height, width = frame.shape[:2]
    
    # ํ™”๋ฉด ์ค‘์•™์— ์‚ฌ๊ฐํ˜• ๊ฐ์ง€ ์˜์—ญ ์„ค์ •
    margin = 100
    detection_zone = [
        (margin, margin),
        (width - margin, margin),
        (width - margin, height - margin),
        (margin, height - margin)
    ]
    
    # ์‹œ์Šคํ…œ ์ดˆ๊ธฐํ™”
    ids = IntrusionDetectionSystem(detection_zone)
    
    # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๊ธฐ (์›€์ง์ž„ ๊ฐ์ง€์šฉ)
    bg_subtractor = cv2.createBackgroundSubtractorMOG2(
        history=500, varThreshold=50, detectShadows=True
    )
    
    print("\n=== ์นจ์ž… ๊ฐ์ง€ ์‹œ์Šคํ…œ ์‹œ์ž‘ ===")
    print("๊ฐ์ง€ ์˜์—ญ ๋‚ด์— ์›€์ง์ž„์ด ๊ฐ์ง€๋˜๋ฉด ์ž๋™์œผ๋กœ ๋…นํ™”๋ฉ๋‹ˆ๋‹ค!")
    print("Q ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.\n")
    
    frame_count = 0
    no_intrusion_frames = 0
    recording_timeout = 100  # ์นจ์ž…์ด ์—†์œผ๋ฉด 100ํ”„๋ ˆ์ž„ ํ›„ ๋…นํ™” ์ค‘์ง€
    
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        frame_count += 1
        
        # ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ๋กœ ์›€์ง์ž„ ๊ฐ์ง€
        fg_mask = bg_subtractor.apply(frame)
        
        # ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ
        kernel = np.ones((5, 5), np.uint8)
        fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_OPEN, kernel)
        fg_mask = cv2.morphologyEx(fg_mask, cv2.MORPH_CLOSE, kernel)
        
        # ์œค๊ณฝ์„  ์ฐพ๊ธฐ
        contours, _ = cv2.findContours(fg_mask, cv2.RETR_EXTERNAL, 
                                       cv2.CHAIN_APPROX_SIMPLE)
        
        intrusion_detected = False
        
        # ๊ฐ ์œค๊ณฝ์„  ๊ฒ€์‚ฌ
        for contour in contours:
            area = cv2.contourArea(contour)
            
            # ์ž‘์€ ๋…ธ์ด์ฆˆ ๋ฌด์‹œ
            if area < 1000:
                continue
            
            # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค
            x, y, w, h = cv2.boundingRect(contour)
            center = (x + w//2, y + h//2)
            
            # ๊ฐ์ง€ ์˜์—ญ ๋‚ด๋ถ€์ธ์ง€ ํ™•์ธ
            if ids.is_inside_zone(center):
                intrusion_detected = True
                
                # ์นจ์ž… ๊ฐ์ฒด ํ‘œ์‹œ
                cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 0, 255), 3)
                cv2.circle(frame, center, 5, (0, 0, 255), -1)
                cv2.putText(frame, "INTRUDER!", (x, y-10), 
                           cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
        
        # ์นจ์ž… ๊ฐ์ง€ ์ฒ˜๋ฆฌ
        if intrusion_detected:
            no_intrusion_frames = 0
            ids.alert_count += 1
            
            # ๋…นํ™” ์‹œ์ž‘
            if not ids.is_recording:
                ids.start_recording((width, height))
            
            # ๊ฒฝ๊ณ  ํ‘œ์‹œ
            cv2.putText(frame, "!!! INTRUSION DETECTED !!!", 
                       (width//2 - 200, 50), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 3)
        else:
            no_intrusion_frames += 1
            
            # ์ผ์ • ์‹œ๊ฐ„ ์นจ์ž…์ด ์—†์œผ๋ฉด ๋…นํ™” ์ค‘์ง€
            if no_intrusion_frames > recording_timeout and ids.is_recording:
                ids.stop_recording()
        
        # ๊ฐ์ง€ ์˜์—ญ ๊ทธ๋ฆฌ๊ธฐ
        zone_color = (0, 0, 255) if intrusion_detected else (0, 255, 255)
        ids.draw_zone(frame, color=zone_color)
        
        # ์ƒํƒœ ์ •๋ณด
        status_text = "RECORDING" if ids.is_recording else "MONITORING"
        status_color = (0, 0, 255) if ids.is_recording else (0, 255, 0)
        cv2.putText(frame, f"Status: {status_text}", (10, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, status_color, 2)
        cv2.putText(frame, f"Alerts: {ids.alert_count}", (10, 60), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        
        # ๋…นํ™” ์ค‘์ด๋ฉด ํŒŒ์ผ์— ์ €์žฅ
        if ids.is_recording:
            ids.video_writer.write(frame)
            # ๋…นํ™” ํ‘œ์‹œ
            cv2.circle(frame, (width - 30, 30), 10, (0, 0, 255), -1)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow("Intrusion Detection System", frame)
        cv2.imshow("Motion Mask", fg_mask)
        
        # ์ข…๋ฃŒ ํ‚ค
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    # ์ •๋ฆฌ
    ids.stop_recording()
    video.release()
    cv2.destroyAllWindows()
    
    print(f"\n์ด ์นจ์ž… ๊ฐ์ง€ ํšŸ์ˆ˜: {ids.alert_count}")


if __name__ == '__main__':
    intrusion_detection_demo()
๐Ÿ  ์‹ค์ „ ํ™œ์šฉ ์•„์ด๋””์–ด โ€ข ํ™ˆ ๋ณด์•ˆ: ์ง‘์ด๋‚˜ ์‚ฌ๋ฌด์‹ค ์ถœ์ž…๊ตฌ ๊ฐ์‹œ
โ€ข ์ฃผ์ฐจ์žฅ ๊ด€๋ฆฌ: ๋ถˆ๋ฒ• ์ฃผ์ฐจ ์ฐจ๋Ÿ‰ ์ž๋™ ๊ฐ์ง€
โ€ข ๊ณต์žฅ ์•ˆ์ „: ์œ„ํ—˜ ๊ตฌ์—ญ ์นจ์ž… ๊ฒฝ๊ณ 
โ€ข ์•ผ์ƒ๋™๋ฌผ ๋ชจ๋‹ˆํ„ฐ๋ง: ํŠน์ • ๊ตฌ์—ญ ๋™๋ฌผ ์ถœํ˜„ ๊ธฐ๋ก

์ด๋Ÿฐ ํ”„๋กœ์ ํŠธ๋ฅผ ์žฌ๋Šฅ๋„ท์—์„œ ์˜๋ขฐ๋ฐ›๊ฑฐ๋‚˜ ํฌํŠธํด๋ฆฌ์˜ค๋กœ ํ™œ์šฉํ•˜๋ฉด ์ •๋ง ์ข‹์•„! ๐Ÿ’ผ
๐Ÿƒ ํ”„๋กœ์ ํŠธ 2: ์šด๋™ ์ž์„ธ ์ถ”์  ๋ฐ ์นด์šดํ„ฐ
import cv2
import numpy as np
from collections import deque

class ExerciseCounter:
    """
    ์šด๋™ ๋™์ž‘์„ ์ถ”์ ํ•˜๊ณ  ํšŸ์ˆ˜๋ฅผ ์„ธ๋Š” ํด๋ž˜์Šค
    ํ‘ธ์‹œ์—…, ์Šค์ฟผํŠธ ๋“ฑ์— ํ™œ์šฉ ๊ฐ€๋Šฅ!
    """
    
    def __init__(self, exercise_type='pushup'):
        self.exercise_type = exercise_type
        self.count = 0
        self.state = 'up'  # 'up' ๋˜๋Š” 'down'
        self.position_history = deque(maxlen=30)
        
        # ์šด๋™๋ณ„ ์ž„๊ณ„๊ฐ’ ์„ค์ •
        self.thresholds = {
            'pushup': {'down': 0.3, 'up': 0.7},
            'squat': {'down': 0.4, 'up': 0.8}
        }
    
    def update(self, y_position, frame_height):
        """
        ๊ฐ์ฒด์˜ Y ์œ„์น˜๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์šด๋™ ํšŸ์ˆ˜ ์นด์šดํŠธ
        
        Args:
            y_position: ์ถ”์  ๊ฐ์ฒด์˜ Y ์ขŒํ‘œ
            frame_height: ํ”„๋ ˆ์ž„ ๋†’์ด
        """
        # ์ •๊ทœํ™”๋œ ์œ„์น˜ (0~1)
        normalized_pos = y_position / frame_height
        self.position_history.append(normalized_pos)
        
        # ์ถฉ๋ถ„ํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์Œ“์ด๋ฉด ๋ถ„์„
        if len(self.position_history) < 10:
            return
        
        # ํ‰๊ท  ์œ„์น˜ ๊ณ„์‚ฐ (๋…ธ์ด์ฆˆ ์ œ๊ฑฐ)
        avg_pos = np.mean(list(self.position_history)[-10:])
        
        threshold = self.thresholds.get(self.exercise_type, 
                                       {'down': 0.3, 'up': 0.7})
        
        # ์ƒํƒœ ์ „ํ™˜ ๊ฐ์ง€
        if self.state == 'up' and avg_pos < threshold['down']:
            self.state = 'down'
        elif self.state == 'down' and avg_pos > threshold['up']:
            self.state = 'up'
            self.count += 1
            print(f"โœ… {self.exercise_type.upper()}: {self.count}ํšŒ")
    
    def get_state_color(self):
        """ํ˜„์žฌ ์ƒํƒœ์— ๋”ฐ๋ฅธ ์ƒ‰์ƒ ๋ฐ˜ํ™˜"""
        return (0, 255, 0) if self.state == 'up' else (0, 165, 255)
    
    def draw_info(self, frame):
        """์šด๋™ ์ •๋ณด๋ฅผ ํ”„๋ ˆ์ž„์— ๊ทธ๋ฆฌ๊ธฐ"""
        height, width = frame.shape[:2]
        
        # ๋ฐ˜ํˆฌ๋ช… ํŒจ๋„
        overlay = frame.copy()
        cv2.rectangle(overlay, (10, 10), (300, 150), (0, 0, 0), -1)
        cv2.addWeighted(overlay, 0.6, frame, 0.4, 0, frame)
        
        # ์šด๋™ ์ •๋ณด
        cv2.putText(frame, f"Exercise: {self.exercise_type.upper()}", 
                   (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
        cv2.putText(frame, f"Count: {self.count}", 
                   (20, 75), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
        cv2.putText(frame, f"State: {self.state.upper()}", 
                   (20, 110), cv2.FONT_HERSHEY_SIMPLEX, 0.7, 
                   self.get_state_color(), 2)
        
        # ์œ„์น˜ ๊ทธ๋ž˜ํ”„ (์„ ํƒ์‚ฌํ•ญ)
        if len(self.position_history) > 1:
            graph_x = width - 220
            graph_y = 20
            graph_w = 200
            graph_h = 100
            
            # ๊ทธ๋ž˜ํ”„ ๋ฐฐ๊ฒฝ
            cv2.rectangle(frame, (graph_x, graph_y), 
                         (graph_x + graph_w, graph_y + graph_h), 
                         (50, 50, 50), -1)
            
            # ๋ฐ์ดํ„ฐ ํ”Œ๋กฏ
            positions = list(self.position_history)
            for i in range(1, len(positions)):
                x1 = graph_x + int((i-1) / len(positions) * graph_w)
                y1 = graph_y + int((1 - positions[i-1]) * graph_h)
                x2 = graph_x + int(i / len(positions) * graph_w)
                y2 = graph_y + int((1 - positions[i]) * graph_h)
                cv2.line(frame, (x1, y1), (x2, y2), (0, 255, 255), 2)
            
            cv2.putText(frame, "Position", (graph_x, graph_y - 5), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)


def exercise_tracking_demo():
    """์šด๋™ ์ถ”์  ๋ฐ๋ชจ"""
    
    video = cv2.VideoCapture(0)
    
    if not video.isOpened():
        print("์›น์บ ์„ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค!")
        return
    
    # ์ฒซ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
    ok, frame = video.read()
    if not ok:
        return
    
    print("\n=== ์šด๋™ ์ถ”์  ์‹œ์Šคํ…œ ===")
    print("์ถ”์ ํ•  ์‹ ์ฒด ๋ถ€์œ„(์˜ˆ: ๋จธ๋ฆฌ, ์†)๋ฅผ ์„ ํƒํ•˜์„ธ์š”!")
    
    # ์ถ”์  ์˜์—ญ ์„ ํƒ
    bbox = cv2.selectROI("๊ฐ์ฒด ์„ ํƒ", frame, False)
    cv2.destroyWindow("๊ฐ์ฒด ์„ ํƒ")
    
    # ์ถ”์ ๊ธฐ ์ดˆ๊ธฐํ™”
    tracker = cv2.TrackerCSRT_create()
    tracker.init(frame, bbox)
    
    # ์šด๋™ ์นด์šดํ„ฐ ์ดˆ๊ธฐํ™”
    counter = ExerciseCounter(exercise_type='pushup')
    
    print("\n์ถ”์  ์‹œ์ž‘! Q ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ข…๋ฃŒ๋ฉ๋‹ˆ๋‹ค.")
    print("R ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์นด์šดํŠธ๋ฅผ ๋ฆฌ์…‹ํ•ฉ๋‹ˆ๋‹ค.\n")
    
    while True:
        ok, frame = video.read()
        if not ok:
            break
        
        # ์ถ”์  ์—…๋ฐ์ดํŠธ
        ok, bbox = tracker.update(frame)
        
        if ok:
            # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค ๊ทธ๋ฆฌ๊ธฐ
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, counter.get_state_color(), 3)
            
            # ์ค‘์‹ฌ์ 
            center_y = int(bbox[1] + bbox[3]/2)
            center_x = int(bbox[0] + bbox[2]/2)
            cv2.circle(frame, (center_x, center_y), 5, (0, 0, 255), -1)
            
            # ์šด๋™ ํšŸ์ˆ˜ ์—…๋ฐ์ดํŠธ
            counter.update(center_y, frame.shape[0])
        else:
            cv2.putText(frame, "Tracking Lost!", (50, 50), 
                       cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
        
        # ์ •๋ณด ํ‘œ์‹œ
        counter.draw_info(frame)
        
        # ํ™”๋ฉด ์ถœ๋ ฅ
        cv2.imshow("Exercise Tracker", frame)
        
        # ํ‚ค ์ž…๋ ฅ
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q'):
            break
        elif key == ord('r'):
            counter.count = 0
            counter.state = 'up'
            print("์นด์šดํŠธ ๋ฆฌ์…‹!")
    
    video.release()
    cv2.destroyAllWindows()
    
    print(f"\n์ด ์šด๋™ ํšŸ์ˆ˜: {counter.count}ํšŒ")
    print("์ˆ˜๊ณ ํ•˜์…จ์Šต๋‹ˆ๋‹ค! ๐Ÿ’ช")


if __name__ == '__main__':
    exercise_tracking_demo()
๐Ÿ’ช ์šด๋™ ์ถ”์  ํ™œ์šฉ ํŒ โ€ข ๋จธ๋ฆฌ ์ถ”์ : ํ‘ธ์‹œ์—…, ํ„ฑ๊ฑธ์ด ๋“ฑ ์ƒํ•˜ ์šด๋™
โ€ข ์—‰๋ฉ์ด ์ถ”์ : ์Šค์ฟผํŠธ, ๋Ÿฐ์ง€ ๋“ฑ
โ€ข ์† ์ถ”์ : ๋ค๋ฒจ ์ปฌ, ์ˆ„๋” ํ”„๋ ˆ์Šค ๋“ฑ

์ž„๊ณ„๊ฐ’(threshold)์„ ์กฐ์ •ํ•˜๋ฉด ๋‹ค์–‘ํ•œ ์šด๋™์— ๋งž์ถฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด! ํ—ฌ์Šค ํŠธ๋ ˆ์ด๋„ˆ๋‚˜ ํ”ผํŠธ๋‹ˆ์Šค ์•ฑ ๊ฐœ๋ฐœ์— ํ™œ์šฉํ•˜๋ฉด ์ข‹๊ฒ ์ง€? ๐Ÿ‹๏ธ

๐Ÿ› ๋ฌธ์ œ ํ•ด๊ฒฐ ๊ฐ€์ด๋“œ

์ฝ”๋”ฉํ•˜๋‹ค ๋ณด๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ธฐ ๋งˆ๋ จ์ด์•ผ. ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•ด๋ดค์–ด! ๐Ÿ”ง

โŒ ๋ฌธ์ œ 1: "Tracker creation failed" ์—๋Ÿฌ

์›์ธ: opencv-contrib-python์ด ์„ค์น˜๋˜์ง€ ์•Š์•˜๊ฑฐ๋‚˜ ๋ฒ„์ „์ด ๋งž์ง€ ์•Š์Œ

ํ•ด๊ฒฐ:
# ๊ธฐ์กด OpenCV ์ œ๊ฑฐ
pip uninstall opencv-python opencv-contrib-python

# ์ตœ์‹  ๋ฒ„์ „ ์žฌ์„ค์น˜
pip install opencv-contrib-python

# ๋˜๋Š” ํŠน์ • ๋ฒ„์ „ ์„ค์น˜
pip install opencv-contrib-python==4.5.5.64
โŒ ๋ฌธ์ œ 2: ์ถ”์ ์ด ์ž๊พธ ์‹คํŒจํ•ด์š” ์›์ธ: ์กฐ๋ช… ๋ณ€ํ™”, ๋น ๋ฅธ ์›€์ง์ž„, ๊ฐ€๋ฆผ ํ˜„์ƒ ๋“ฑ

ํ•ด๊ฒฐ:
1๏ธโƒฃ ๋‹ค๋ฅธ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‹œ๋„: CSRT๋Š” ์ •ํ™•ํ•˜์ง€๋งŒ ๋А๋ฆฌ๊ณ , KCF๋Š” ๋น ๋ฅด์ง€๋งŒ ๋œ ์ •ํ™•ํ•ด. ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒ!
2๏ธโƒฃ ํ”„๋ ˆ์ž„ ํ’ˆ์งˆ ๊ฐœ์„ : ์กฐ๋ช…์„ ๋ฐ๊ฒŒ ํ•˜๊ณ  ๋ฐฐ๊ฒฝ์„ ๋‹จ์ˆœํ•˜๊ฒŒ
3๏ธโƒฃ ROI ํ™•์žฅ: ์ถ”์  ์˜์—ญ์„ ๋„“๊ฒŒ ์žก์•„์„œ ๊ฐ์ฒด๊ฐ€ ๋ฒ—์–ด๋‚˜์ง€ ์•Š๋„๋ก
4๏ธโƒฃ ์žฌ์ดˆ๊ธฐํ™” ๋กœ์ง ์ถ”๊ฐ€: ์ถ”์  ์‹คํŒจ์‹œ ์ž๋™์œผ๋กœ ์žฌํƒ์ง€
โŒ ๋ฌธ์ œ 3: ์›น์บ ์ด ์•ˆ ์—ด๋ ค์š”

ํ•ด๊ฒฐ:
# ๋‹ค๋ฅธ ์นด๋ฉ”๋ผ ์ธ๋ฑ์Šค ์‹œ๋„
for i in range(5):
    cap = cv2.VideoCapture(i)
    if cap.isOpened():
        print(f"์นด๋ฉ”๋ผ {i}๋ฒˆ์ด ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค!")
        cap.release()
    else:
        print(f"์นด๋ฉ”๋ผ {i}๋ฒˆ ์—†์Œ")

# DirectShow ๋ฐฑ์—”๋“œ ์‚ฌ์šฉ (Windows)
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

# V4L2 ๋ฐฑ์—”๋“œ ์‚ฌ์šฉ (Linux)
cap = cv2.VideoCapture(0, cv2.CAP_V4L2)
โŒ ๋ฌธ์ œ 4: FPS๊ฐ€ ๋„ˆ๋ฌด ๋‚ฎ์•„์š” ํ•ด๊ฒฐ:
1๏ธโƒฃ ํ”„๋ ˆ์ž„ ํฌ๊ธฐ ์ค„์ด๊ธฐ (640x480 ๊ถŒ์žฅ)
2๏ธโƒฃ MOSSE๋‚˜ KCF ๊ฐ™์€ ๋น ๋ฅธ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‚ฌ์šฉ
3๏ธโƒฃ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ ์šฉ
4๏ธโƒฃ GPU ๊ฐ€์† ํ™œ์šฉ (CUDA ์ง€์› OpenCV ๋นŒ๋“œ)
โŒ ๋ฌธ์ œ 5: ์ƒ‰์ƒ ์ถ”์ ์ด ์ž˜ ์•ˆ๋ผ์š”

ํ•ด๊ฒฐ:
1๏ธโƒฃ HSV ๋ฒ”์œ„ ์กฐ์ •: interactive_color_picker() ํ•จ์ˆ˜๋กœ ์ตœ์ ๊ฐ’ ์ฐพ๊ธฐ
2๏ธโƒฃ ์กฐ๋ช… ๊ฐœ์„ : ๊ท ์ผํ•œ ์กฐ๋ช…์ด ์ค‘์š”ํ•ด
3๏ธโƒฃ ๋ชจํด๋กœ์ง€ ์—ฐ์‚ฐ: ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ ๊ฐ•ํ™”
# ๋” ๊ฐ•๋ ฅํ•œ ๋…ธ์ด์ฆˆ ์ œ๊ฑฐ
kernel = np.ones((7, 7), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=2)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ ๋ฐ ์‹ฌํ™” ์ฃผ์ œ

์—ฌ๊ธฐ๊นŒ์ง€ ๋”ฐ๋ผ์™”๋‹ค๋ฉด ์ด๋ฏธ ๊ฐ์ฒด ์ถ”์ ์˜ ๊ธฐ๋ณธ์€ ๋งˆ์Šคํ„ฐํ–ˆ์–ด! ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์ฃผ์ œ๋“ค์„ ์‚ดํŽด๋ด. ๐Ÿš€

๐ŸŽฏ ์‹ฌํ™” ์ฃผ์ œ 1: ๋”ฅ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ ์ถ”์ 

OpenCV์˜ ์ „ํ†ต์ ์ธ ์ถ”์  ์•Œ๊ณ ๋ฆฌ์ฆ˜๋ณด๋‹ค ๋” ๊ฐ•๋ ฅํ•œ ๋”ฅ๋Ÿฌ๋‹ ๊ธฐ๋ฐ˜ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ์–ด:

โ€ข SORT (Simple Online and Realtime Tracking)
- ์นผ๋งŒ ํ•„ํ„ฐ์™€ ํ—๊ฐ€๋ฆฌ์•ˆ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์กฐํ•ฉ
- ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ ์— ์ตœ์ ํ™”
- ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

โ€ข DeepSORT
- SORT์— ๋”ฅ๋Ÿฌ๋‹ ํŠน์ง• ์ถ”์ถœ ์ถ”๊ฐ€
- ๊ฐ€๋ฆผ ํ˜„์ƒ์— ๊ฐ•ํ•จ
- ์žฌ์‹๋ณ„(Re-identification) ๊ฐ€๋Šฅ

โ€ข SiamMask
- ์‹œ์•” ๋„คํŠธ์›Œํฌ ๊ธฐ๋ฐ˜
- ํ”ฝ์…€ ๋‹จ์œ„ ์ •๋ฐ€ ์ถ”์ 
- ์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ ๋งˆ์Šคํฌ ์ œ๊ณต

# DeepSORT ์˜ˆ์ œ (๊ฐ„๋‹จ ๋ฒ„์ „)
# pip install deep-sort-realtime

from deep_sort_realtime.deepsort_tracker import DeepSort

tracker = DeepSort(max_age=30)

# ๋งค ํ”„๋ ˆ์ž„๋งˆ๋‹ค
detections = []  # [[x, y, w, h, confidence], ...]
tracks = tracker.update_tracks(detections, frame=frame)

for track in tracks:
    if not track.is_confirmed():
        continue
    track_id = track.track_id
    bbox = track.to_ltrb()  # left, top, right, bottom
    # ์ถ”์  ๊ฒฐ๊ณผ ์‚ฌ์šฉ...

๐ŸŽฏ ์‹ฌํ™” ์ฃผ์ œ 2: ๊ด‘ํ•™ ํ๋ฆ„(Optical Flow)

ํ”ฝ์…€ ๋‹จ์œ„๋กœ ์›€์ง์ž„์„ ์ถ”์ ํ•˜๋Š” ๊ธฐ๋ฒ•์ด์•ผ. ๊ฐ์ฒด ์ถ”์ ๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ •ํ™•ํ•ด์ ธ!

import cv2
import numpy as np

def optical_flow_tracking():
    """
    Lucas-Kanade ๊ด‘ํ•™ ํ๋ฆ„์„ ์ด์šฉํ•œ ํŠน์ง•์  ์ถ”์ 
    """
    video = cv2.VideoCapture(0)
    
    # ์ฒซ ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
    ret, old_frame = video.read()
    old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
    
    # ์ถ”์ ํ•  ํŠน์ง•์  ์ฐพ๊ธฐ
    feature_params = dict(
        maxCorners=100,
        qualityLevel=0.3,
        minDistance=7,
        blockSize=7
    )
    p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
    
    # ๊ด‘ํ•™ ํ๋ฆ„ ํŒŒ๋ผ๋ฏธํ„ฐ
    lk_params = dict(
        winSize=(15, 15),
        maxLevel=2,
        criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)
    )
    
    # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ์šฉ
    mask = np.zeros_like(old_frame)
    
    while True:
        ret, frame = video.read()
        if not ret:
            break
        
        frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # ๊ด‘ํ•™ ํ๋ฆ„ ๊ณ„์‚ฐ
        p1, st, err = cv2.calcOpticalFlowPyrLK(
            old_gray, frame_gray, p0, None, **lk_params
        )
        
        # ์ข‹์€ ์ ๋“ค๋งŒ ์„ ํƒ
        if p1 is not None:
            good_new = p1[st == 1]
            good_old = p0[st == 1]
        
        # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ
        for i, (new, old) in enumerate(zip(good_new, good_old)):
            a, b = new.ravel()
            c, d = old.ravel()
            mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), 
                           (0, 255, 0), 2)
            frame = cv2.circle(frame, (int(a), int(b)), 5, (0, 0, 255), -1)
        
        img = cv2.add(frame, mask)
        cv2.imshow('Optical Flow Tracking', img)
        
        # ๋‹ค์Œ ํ”„๋ ˆ์ž„ ์ค€๋น„
        old_gray = frame_gray.copy()
        p0 = good_new.reshape(-1, 1, 2)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    video.release()
    cv2.destroyAllWindows()

๐ŸŽฏ ์‹ฌํ™” ์ฃผ์ œ 3: ์นผ๋งŒ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•œ ์˜ˆ์ธก

๊ฐ์ฒด์˜ ๋‹ค์Œ ์œ„์น˜๋ฅผ ์˜ˆ์ธกํ•ด์„œ ์ถ”์  ์ •ํ™•๋„๋ฅผ ๋†’์ผ ์ˆ˜ ์žˆ์–ด!

import cv2
import numpy as np

class KalmanTracker:
    """
    ์นผ๋งŒ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•œ ๊ฐ์ฒด ์œ„์น˜ ์˜ˆ์ธก ๋ฐ ์ถ”์ 
    """
    
    def __init__(self):
        # ์นผ๋งŒ ํ•„ํ„ฐ ์ดˆ๊ธฐํ™” (4์ฐจ์› ์ƒํƒœ: x, y, dx, dy)
        self.kalman = cv2.KalmanFilter(4, 2)
        self.kalman.measurementMatrix = np.array([
            [1, 0, 0, 0],
            [0, 1, 0, 0]
        ], np.float32)
        
        self.kalman.transitionMatrix = np.array([
            [1, 0, 1, 0],
            [0, 1, 0, 1],
            [0, 0, 1, 0],
            [0, 0, 0, 1]
        ], np.float32)
        
        self.kalman.processNoiseCov = np.eye(4, dtype=np.float32) * 0.03
        self.kalman.measurementNoiseCov = np.eye(2, dtype=np.float32) * 0.1
    
    def predict(self):
        """๋‹ค์Œ ์œ„์น˜ ์˜ˆ์ธก"""
        prediction = self.kalman.predict()
        return int(prediction[0]), int(prediction[1])
    
    def update(self, x, y):
        """์ธก์ •๊ฐ’์œผ๋กœ ํ•„ํ„ฐ ์—…๋ฐ์ดํŠธ"""
        measurement = np.array([[np.float32(x)], [np.float32(y)]])
        self.kalman.correct(measurement)
    
    def get_state(self):
        """ํ˜„์žฌ ์ƒํƒœ ๋ฐ˜ํ™˜"""
        state = self.kalman.statePost
        return int(state[0]), int(state[1])


def kalman_tracking_demo():
    """์นผ๋งŒ ํ•„ํ„ฐ ์ถ”์  ๋ฐ๋ชจ"""
    
    video = cv2.VideoCapture(0)
    ret, frame = video.read()
    
    # ์ถ”์  ์˜์—ญ ์„ ํƒ
    bbox = cv2.selectROI("๊ฐ์ฒด ์„ ํƒ", frame, False)
    cv2.destroyWindow("๊ฐ์ฒด ์„ ํƒ")
    
    # OpenCV ์ถ”์ ๊ธฐ
    tracker = cv2.TrackerKCF_create()
    tracker.init(frame, bbox)
    
    # ์นผ๋งŒ ํ•„ํ„ฐ ์ถ”์ ๊ธฐ
    kalman_tracker = KalmanTracker()
    
    # ์ดˆ๊ธฐ ์œ„์น˜
    center_x = int(bbox[0] + bbox[2]/2)
    center_y = int(bbox[1] + bbox[3]/2)
    kalman_tracker.update(center_x, center_y)
    
    trajectory_measured = []
    trajectory_predicted = []
    
    while True:
        ret, frame = video.read()
        if not ret:
            break
        
        # ์˜ˆ์ธก (์ธก์ • ์ „)
        pred_x, pred_y = kalman_tracker.predict()
        trajectory_predicted.append((pred_x, pred_y))
        
        # ์‹ค์ œ ์ถ”์ 
        ok, bbox = tracker.update(frame)
        
        if ok:
            # ์ธก์ •๊ฐ’
            center_x = int(bbox[0] + bbox[2]/2)
            center_y = int(bbox[1] + bbox[3]/2)
            trajectory_measured.append((center_x, center_y))
            
            # ์นผ๋งŒ ํ•„ํ„ฐ ์—…๋ฐ์ดํŠธ
            kalman_tracker.update(center_x, center_y)
            
            # ๋ฐ”์šด๋”ฉ ๋ฐ•์Šค
            p1 = (int(bbox[0]), int(bbox[1]))
            p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
            cv2.rectangle(frame, p1, p2, (0, 255, 0), 2)
            
            # ์ธก์ • ์œ„์น˜ (์ดˆ๋ก)
            cv2.circle(frame, (center_x, center_y), 5, (0, 255, 0), -1)
            cv2.putText(frame, "Measured", (center_x + 10, center_y), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
        
        # ์˜ˆ์ธก ์œ„์น˜ (๋นจ๊ฐ•)
        cv2.circle(frame, (pred_x, pred_y), 5, (0, 0, 255), -1)
        cv2.putText(frame, "Predicted", (pred_x + 10, pred_y), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
        
        # ๊ถค์  ๊ทธ๋ฆฌ๊ธฐ
        for i in range(1, len(trajectory_measured)):
            cv2.line(frame, trajectory_measured[i-1], trajectory_measured[i], 
                    (0, 255, 0), 2)
        
        for i in range(1, len(trajectory_predicted)):
            cv2.line(frame, trajectory_predicted[i-1], trajectory_predicted[i], 
                    (0, 0, 255), 1)
        
        cv2.imshow("Kalman Filter Tracking", frame)
        
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
    
    video.release()
    cv2.destroyAllWindows()
๐ŸŽ“ ์นผ๋งŒ ํ•„ํ„ฐ์˜ ์žฅ์ 
โ€ข ๋…ธ์ด์ฆˆ๊ฐ€ ์žˆ๋Š” ์ธก์ •๊ฐ’์„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๋งŒ๋“ค์–ด์ค˜
โ€ข ๊ฐ์ฒด๊ฐ€ ์ผ์‹œ์ ์œผ๋กœ ๊ฐ€๋ ค์ ธ๋„ ์œ„์น˜๋ฅผ ์˜ˆ์ธกํ•  ์ˆ˜ ์žˆ์–ด
โ€ข ๋น ๋ฅธ ์›€์ง์ž„์—๋„ ์•ˆ์ •์ ์œผ๋กœ ์ถ”์  ๊ฐ€๋Šฅ
โ€ข ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•œ ๊ฒฝ๋Ÿ‰ ์•Œ๊ณ ๋ฆฌ์ฆ˜!

๐ŸŒŸ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด

๋ฐฐ์šด ๊ฑธ ํ™œ์šฉํ•ด์„œ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์žฌ๋ฏธ์žˆ๋Š” ํ”„๋กœ์ ํŠธ๋“ค์„ ์†Œ๊ฐœํ• ๊ฒŒ! ํฌํŠธํด๋ฆฌ์˜ค๋‚˜ ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ๋กœ ๋”ฑ์ด์•ผ. ๐Ÿ’ก

1๏ธโƒฃ ์Šค๋งˆํŠธ ์ฃผ์ฐจ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ โ€ข ์ฃผ์ฐจ์žฅ ์ž…๊ตฌ์—์„œ ์ฐจ๋Ÿ‰ ๋ฒˆํ˜ธํŒ ์ถ”์ 
โ€ข ์ฃผ์ฐจ ๊ณต๊ฐ„ ์ ์œ  ์—ฌ๋ถ€ ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
โ€ข ๋ถˆ๋ฒ• ์ฃผ์ฐจ ์ž๋™ ๊ฐ์ง€ ๋ฐ ์•Œ๋ฆผ
โ€ข ์ฃผ์ฐจ ์‹œ๊ฐ„ ์ž๋™ ๊ณ„์‚ฐ
2๏ธโƒฃ AI ํ”ผํŠธ๋‹ˆ์Šค ์ฝ”์น˜ โ€ข ์šด๋™ ์ž์„ธ ์‹ค์‹œ๊ฐ„ ๋ถ„์„
โ€ข ๋ฐ˜๋ณต ํšŸ์ˆ˜ ์ž๋™ ์นด์šดํŒ…
โ€ข ์ž˜๋ชป๋œ ์ž์„ธ ๊ต์ • ํ”ผ๋“œ๋ฐฑ
โ€ข ์šด๋™ ๊ธฐ๋ก ๋ฐ ํ†ต๊ณ„ ์ œ๊ณต
3๏ธโƒฃ ์Šคํฌ์ธ  ๊ฒฝ๊ธฐ ๋ถ„์„ ๋„๊ตฌ โ€ข ์ถ•๊ตฌ๊ณต/๋†๊ตฌ๊ณต ์ž๋™ ์ถ”์ 
โ€ข ์„ ์ˆ˜ ์›€์ง์ž„ ํŒจํ„ด ๋ถ„์„
โ€ข ํžˆํŠธ๋งต ์ƒ์„ฑ
โ€ข ํ•˜์ด๋ผ์ดํŠธ ์ž๋™ ํŽธ์ง‘
4๏ธโƒฃ ๋ฐ˜๋ ค๋™๋ฌผ ๋ชจ๋‹ˆํ„ฐ๋ง โ€ข ์ง‘์— ํ˜ผ์ž ์žˆ๋Š” ๋ฐ˜๋ ค๋™๋ฌผ ์ถ”์ 
โ€ข ์ด์ƒ ํ–‰๋™ ๊ฐ์ง€ ๋ฐ ์•Œ๋ฆผ
โ€ข ํ™œ๋™๋Ÿ‰ ์ธก์ •
โ€ข ์ž๋™ ์˜์ƒ ๋…นํ™”
5๏ธโƒฃ ๊ตํ†ต ํ๋ฆ„ ๋ถ„์„ ์‹œ์Šคํ…œ โ€ข ์ฐจ๋Ÿ‰ ๋Œ€์ˆ˜ ์ž๋™ ์นด์šดํŒ…
โ€ข ํ‰๊ท  ์†๋„ ์ธก์ •
โ€ข ๊ตํ†ต ํ˜ผ์žก๋„ ๋ถ„์„
โ€ข ์‚ฌ๊ณ  ์ž๋™ ๊ฐ์ง€
๐Ÿ’ผ ํ”„๋กœ์ ํŠธ ์ˆ˜์ตํ™” ํŒ ์ด๋Ÿฐ ํ”„๋กœ์ ํŠธ๋“ค์„ ์™„์„ฑํ•˜๋ฉด ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์„œ๋น„์Šค๋กœ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ์–ด! ์˜ˆ๋ฅผ ๋“ค์–ด:

โ€ข "CCTV ์˜์ƒ ๋ถ„์„ ๋ฐ ๊ฐ์ฒด ์ถ”์  ์†”๋ฃจ์…˜ ๊ฐœ๋ฐœ"
โ€ข "์šด๋™ ์ž์„ธ ๋ถ„์„ ์•ฑ ๊ฐœ๋ฐœ"
โ€ข "๋งž์ถคํ˜• ๋น„์ „ AI ์‹œ์Šคํ…œ ๊ตฌ์ถ•"

์‹ค๋ ฅ์„ ์Œ“์œผ๋ฉด์„œ ์ˆ˜์ต๋„ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋Š” ์ผ์„์ด์กฐ! ๐ŸŽฏ
Your Journey with OpenCV Basic ๋‹จ์ผ ๊ฐ์ฒด ์ถ”์  Advanced ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์  Expert ๋”ฅ๋Ÿฌ๋‹ ํ†ตํ•ฉ Master ์‹ค์ „ ํ”„๋กœ์ ํŠธ ๐Ÿ† ํ”„๋กœ ๊ฐœ๋ฐœ์ž

๐ŸŽ“ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์—ฌ๊ธฐ๊นŒ์ง€ ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์–ด. ๊ณ ์ƒ ๋งŽ์•˜์–ด! ๐Ÿ‘

์šฐ๋ฆฌ๋Š” OpenCV๋ฅผ ์ด์šฉํ•œ ๊ฐ์ฒด ์ถ”์ ์˜ ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๊นŒ์ง€ ๋ชจ๋“  ๊ฑธ ๋‹ค๋ค„๋ดค์–ด. ๋‹จ์ผ ๊ฐ์ฒด ์ถ”์ , ๋‹ค์ค‘ ๊ฐ์ฒด ์ถ”์ , ์ƒ‰์ƒ ๊ธฐ๋ฐ˜ ์ถ”์ , ๊ทธ๋ฆฌ๊ณ  ์นผ๋งŒ ํ•„ํ„ฐ๋ฅผ ์ด์šฉํ•œ ์˜ˆ์ธก๊นŒ์ง€! ์ด์ œ ๋„ˆ๋„ ์ถฉ๋ถ„ํžˆ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. ๐Ÿ’ช

๊ธฐ์–ตํ•ด์•ผ ํ•  ํ•ต์‹ฌ ํฌ์ธํŠธ:

1. ์ƒํ™ฉ์— ๋งž๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์„ ํƒ
โ€ข ์†๋„๊ฐ€ ์ค‘์š”ํ•˜๋ฉด: MOSSE, KCF
โ€ข ์ •ํ™•๋„๊ฐ€ ์ค‘์š”ํ•˜๋ฉด: CSRT
โ€ข ๊ท ํ˜•์ด ํ•„์š”ํ•˜๋ฉด: KCF

2. ์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ํ•„์ˆ˜
โ€ข ํ”„๋ ˆ์ž„ ํฌ๊ธฐ ์กฐ์ •
โ€ข ROI ํ™œ์šฉ
โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ
โ€ข ํ”„๋ ˆ์ž„ ์Šคํ‚ต

3. ์‹คํŒจ ๋Œ€์‘ ์ „๋žต
โ€ข ์ถ”์  ์‹คํŒจ ๊ฐ์ง€
โ€ข ์ž๋™ ์žฌ์ดˆ๊ธฐํ™”
โ€ข ์˜ˆ์ธก ์•Œ๊ณ ๋ฆฌ์ฆ˜ ํ™œ์šฉ

4. ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋กœ ์—ฐ์Šต
โ€ข ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘
โ€ข ์ ์ง„์ ์œผ๋กœ ๊ธฐ๋Šฅ ์ถ”๊ฐ€
โ€ข ํฌํŠธํด๋ฆฌ์˜ค ๊ตฌ์ถ•

์ปดํ“จํ„ฐ ๋น„์ „์€ ์ •๋ง ๋น ๋ฅด๊ฒŒ ๋ฐœ์ „ํ•˜๋Š” ๋ถ„์•ผ์•ผ. ์˜ค๋Š˜ ๋ฐฐ์šด ๋‚ด์šฉ์ด ๊ธฐ์ดˆ๊ฐ€ ๋˜์–ด์„œ ๋” ๊ณ ๊ธ‰ ๊ธฐ์ˆ ๋“ค์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ. YOLO, Mask R-CNN, Transformer ๊ธฐ๋ฐ˜ ์ถ”์  ๋“ฑ ํƒํ—˜ํ•  ๊ฒŒ ์ •๋ง ๋งŽ์•„! ๐Ÿš€

ํ˜น์‹œ ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๊ฑฐ๋‚˜ ๋” ๋ฐฐ์šฐ๊ณ  ์‹ถ์€ ๊ฒŒ ์žˆ๋‹ค๋ฉด, OpenCV ๊ณต์‹ ๋ฌธ์„œ๋‚˜ GitHub์˜ ์˜คํ”ˆ์†Œ์Šค ํ”„๋กœ์ ํŠธ๋“ค์„ ์ฐธ๊ณ ํ•ด๋ด. ๊ทธ๋ฆฌ๊ณ  ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ฒฝํ—˜ํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ •๋ง ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

์ž, ์ด์ œ ๋„ˆ์˜ ์ฐจ๋ก€์•ผ! ์˜ค๋Š˜ ๋ฐฐ์šด ๊ฑธ ๋ฐ”ํƒ•์œผ๋กœ ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ด. ์‹œ์ž‘์ด ๋ฐ˜์ด๋ผ๊ณ , ์ผ๋‹จ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ด๋ณด๋Š” ๊ฒŒ ๊ฐ€์žฅ ์ค‘์š”ํ•ด. ์‹คํŒจํ•ด๋„ ๊ดœ์ฐฎ์•„, ๊ทธ๊ฒŒ ๋ฐ”๋กœ ๋ฐฐ์›€์˜ ๊ณผ์ •์ด๋‹ˆ๊นŒ! ๐Ÿ˜Š

ํ™”์ดํŒ…! ๋„ˆ๋ผ๋ฉด ์ถฉ๋ถ„ํžˆ ํ•  ์ˆ˜ ์žˆ์–ด! ๐ŸŽ‰

๐ŸŽฏ ๋‹ค์Œ ๋‹จ๊ณ„ ์ถ”์ฒœ

1. ์ด ๊ธ€์˜ ๋ชจ๋“  ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•ด๋ณด๊ธฐ
2. ์ž์‹ ๋งŒ์˜ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด ๊ตฌ์ƒํ•˜๊ธฐ
3. GitHub์— ์ฝ”๋“œ ์˜ฌ๋ฆฌ๊ณ  ํฌํŠธํด๋ฆฌ์˜ค ๋งŒ๋“ค๊ธฐ
4. ์ปค๋ฎค๋‹ˆํ‹ฐ์—์„œ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค๊ณผ ๊ต๋ฅ˜ํ•˜๊ธฐ
5. ์‹ค์ „ ํ”„๋กœ์ ํŠธ์— ๋„์ „ํ•˜๊ธฐ!

Happy Coding! ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉ ๋˜์„ธ์š”! ๐Ÿš€โœจ

์งˆ๋ฌธ์ด๋‚˜ ํ”ผ๋“œ๋ฐฑ์€ ์–ธ์ œ๋“  ํ™˜์˜์ด์—์š”!
ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์–ด์š”! ๐Ÿ’ป๐ŸŒŸ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.