Meanshift Tracking

Eden.Yang·2023년 10월 31일
0

Computer VIsion

목록 보기
19/22

▪Example code

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

struct CallbackParam{
    Mat frame;  // 프레임을 저장할 구조체
    Point pt1, pt2;  // 마우스로 클릭한 좌표
    Rect roi;  // 관심 영역을 나타내는 직사각형
    bool drag;  // 드래그 중인지 나타내는 플래그
    bool updated;  // 업데이트가 필요한지 나타내는 플래그
};

// 마우스 이벤트 처리 함수
void onMouse(int event, int x, int y, int flags, void* param){
    CallbackParam* p = (CallbackParam*)param;

    // 마우스 왼쪽 버튼 클릭 시
    if (event == EVENT_LBUTTONDOWN){
        p->pt1.x = x;
        p->pt1.y = y;
        p->pt2 = p->pt1;
        p->drag = true;
    }

    // 마우스 왼쪽 버튼 떼기 시
    if (event == EVENT_LBUTTONUP){
        int w = x - p->pt1.x;
        int h = y - p->pt1.y;
        p->roi.x = p->pt1.x;
        p->roi.y = p->pt1.y;
        p->roi.width = w;
        p->roi.height = h;
        p->drag = false;

        // 너비와 높이가 일정 크기 이상인 경우에만 업데이트
        if (w >= 10 && h >= 10){
            p->updated = true;
        }
    }

    // 마우스 드래그 중일 때
    if (p->drag && event == EVENT_MOUSEMOVE){
        if (p->pt2.x != x || p->pt2.y != y){
            Mat img = p->frame.clone();  // 정지된 이미지 복사
            p->pt2.x = x;  // 업데이트
            p->pt2.y = y;  // 목적 좌표
            rectangle(img, p->pt1, p->pt2, Scalar(0, 255, 0), 1);
            imshow("트래커", img);
        }
    }
}

int main(int argc, char *argv[]){
    // 웹캠 열기
    VideoCapture cap(0);
    CallbackParam param;
    Mat frame, m_backproj, hsv;
    Mat m_model3d;
    Rect m_rc;

    // HSV 색 공간을 위한 범위 값 설정
    float hrange[] = { 0,180 }; // 색상
    float srange[] = { 0,255 }; // 채도
    float vrange[] = { 0,255 }; // 명도
    const float* ranges[] = { hrange, srange, vrange }; // 색상, 채도, 명도
    int channels[] = { 0, 1, 2 };
    int hist_sizes[] = { 16, 16, 16 };

    // 영상 불러오기가 성공했는지 확인
    if (!cap.isOpened()){
        cout << "비디오 파일을 열 수 없습니다." << endl;
        return 0;
    }

    // 이미지에서 클릭하여 관심 영역 설정
    cap >> frame;
    imshow("트래커", frame);
    param.frame = frame;
    param.drag = false;
    param.updated = false;

    // 마우스 이벤트가 발생하면 onMouse 함수 호출
    setMouseCallback("트래커", onMouse, &param);

    bool tracking = false;
    while (true){
        // 이미지 취득 및 대상 초기화
        if (param.drag){
            if (waitKey(33) == 27) break; // ESC 키
            continue;
        }

        // RGB에서 HSV로 이미지 변환
        cvtColor(frame, hsv, COLOR_BGR2HSV);

        if (param.updated){
            m_rc = param.roi;
            Mat mask = Mat::zeros(m_rc.height, m_rc.width, CV_8U);
            ellipse(mask, Point(m_rc.width/2, m_rc.height/2), Size(m_rc.width/2, m_rc.height/2), 0, 0, 360, 255, CV_FILLED);
            Mat roi(hsv, m_rc);

            // 히스토그램 계산
            calcHist(&roi, 1, channels, mask, m_model3d, 3, hist_sizes, ranges);
        }

        m_rc = param.roi;
        param.updated = false;
        tracking = true;

        // 웹캠에서 새로운 프레임 얻기
        cap >> frame;
        if (frame.empty()) break; // 읽을 프레임이 없으면 종료

        // 이미지 처리
        if (tracking){
            // 히스토그램 역투영
            // 사용된 인자들은 히스토그램을 계산한 것과 동일함
            // 추가로 역투영 매트릭스(m_backproj)가 있음
            calcBackProject(&hsv, 1, channels, m_model3d, m_backproj, ranges);

            // 트래킹 (meanShift)
            // 픽셀 분포가 최대인 창을 얻음
            meanShift(m_backproj, m_rc, TermCriteria(TermCriteria::EPS | TermCriteria::COUNT, 10, 1)); // 종료 조건
            rectangle(frame, m_rc, Scalar(0, 0, 255), 3);
        }

        // 이미지 표시
        imshow("트래커", frame);

        // 사용자 입력
        char ch = waitKey(33);
        if (ch == 27) break; // ESC 키 (종료)
        else if (ch == 32){ // 스페이스바 키 (비디오 일시정지)
            // 사용자 입력이 스페이스바 또는 ESC 키가 아니면 프로그램 종료
            while ((ch = waitKey(33)) != 32 && ch != 27);
            if (ch == 27) break;
        }
    }

    return 0;
}
profile
손끝에서 땅끝으로, 골방에서 열방으로

0개의 댓글