【opencv】示例-camshiftdemo.cpp CamShift算法来对选定对象进行实时跟踪

发布于:2024-04-11 ⋅ 阅读:(142) ⋅ 点赞:(0)

9b7b2225d6b416a96501c3b27d3b8e9c.png

bitwise_not(roi, roi);

3a91f776a8b64cc321640456e95f2487.png

e0a9239041e0f7b0ec6e6d5ebe00a37d.png

backprojMode

e4bed628278781819fccb5f09002f5c8.png

#include "opencv2/core/utility.hpp" // 导入OpenCV的核心工具库
#include "opencv2/video/tracking.hpp" // 导入OpenCV的视频跟踪功能库
#include "opencv2/imgproc.hpp" // 导入OpenCV的图像处理库
#include "opencv2/videoio.hpp" // 导入OpenCV的视频输入/输出库
#include "opencv2/highgui.hpp" // 导入OpenCV的高层GUI图形界面库


#include <iostream> // 导入标准输入输出流库
#include <ctype.h> // 导入字符处理库


using namespace cv; // 使用cv命名空间简化代码
using namespace std; // 使用std命名空间简化代码


Mat image; // 声明一个Mat类型的变量,用于存储图像


// 以下是一些全局变量,用于处理鼠标选择的对象及跟踪参数设置
bool backprojMode = false; // 是否使用反向投影模式
bool selectObject = false; // 是否选择了对象
int trackObject = 0; // 跟踪对象的标志
bool showHist = true; // 是否显示直方图
Point origin; // 原点位置,用作画框时的参考点
Rect selection; // 用户用鼠标选择的区域
int vmin = 10, vmax = 256, smin = 30; // HSV颜色空间中的最小值、最大值及饱和度最小值


// onMouse是回调函数,当鼠标事件发生时调用
static void onMouse( int event, int x, int y, int, void* )
{
    if( selectObject ) // 如果选择了对象
    {
        selection.x = MIN(x, origin.x); // 选择框的x坐标
        selection.y = MIN(y, origin.y); // 选择框的y坐标
        selection.width = std::abs(x - origin.x); // 选择框的宽度
        selection.height = std::abs(y - origin.y); // 选择框的高度
        //将selection矩形裁剪(clip)到图像的边界内
        selection &= Rect(0, 0, image.cols, image.rows); // 确保选择框不超出图像范围
    }


    switch( event )
    {
    case EVENT_LBUTTONDOWN: // 鼠标左键按下事件
        origin = Point(x,y); // 记录原点位置
        selection = Rect(x,y,0,0); // 初始选择框
        selectObject = true; // 设置选择对象为true
        break;
    case EVENT_LBUTTONUP: // 鼠标左键抬起事件
        selectObject = false; // 设置选择对象为false
        if( selection.width > 0 && selection.height > 0 ) // 如果选择框有效
            trackObject = -1;   // 设置trackObject为-1,表示初始化跟踪
        break;
    }
}


// 一些快捷键说明文本
string hot_keys =
    "\n\nHot keys: \n"
    "\tESC - quit the program\n" // 当按下ESC键时,程序将退出
    "\tc - stop the tracking\n"// 当按下c键时,将停止对当前选中对象的跟踪
    "\tb - switch to/from backprojection view\n"// 按下b键会在反向投影视图和正常视图之间切换
    "\th - show/hide object histogram\n" // 按下h键会显示或隐藏对象的HSV直方图
    "\tp - pause video\n" // 按下p键会暂停和播放视频
    "To initialize tracking, select the object with mouse\n";// 提示用户使用鼠标选择要跟踪的对象以初始化跟踪


// help函数用于显示帮助信息
static void help(const char** argv)
{
    cout << "\nThis is a demo that shows mean-shift based tracking\n"
            "You select a color objects such as your face and it tracks it.\n"
            "This reads from video camera (0 by default, or the camera number the user enters\n"
            "Usage: \n\t";
    cout << argv[0] << " [camera number]\n"; // 显示用法
    cout << hot_keys; // 显示快捷键
}


// 参数解析的关键字符串
const char* keys =
{
    "{help h | | show help message}{@camera_number| 0 | camera number}"
};


// 主函数
int main( int argc, const char** argv )
{
    VideoCapture cap; // 视频捕获对象
    Rect trackWindow; // 跟踪窗口
    int hsize = 16; // 直方图大小
    float hranges[] = {0,180}; // 直方图范围
    const float* phranges = hranges; // 指向直方图范围的指针
    CommandLineParser parser(argc, argv, keys); // 命令行解析
    if (parser.has("help")) // 如果有帮助选项
    {
        help(argv); // 显示帮助信息
        return 0; // 退出程序
    }
    int camNum = parser.get<int>(0); // 获取摄像头编号
    cap.open(camNum); // 打开摄像头


    if( !cap.isOpened() ) // 如果摄像头打不开
    {
        help(argv); // 显示帮助信息
        cout << "***Could not initialize capturing...***\n"; // 输出错误信息
        cout << "Current parameter's value: \n"; // 输出当前参数值
        parser.printMessage(); // 打印参数信息
        return -1; // 退出程序
    }
    cout << hot_keys; // 显示快捷键信息
    namedWindow( "Histogram", 0 ); // 创建直方图窗口
    namedWindow( "CamShift Demo", 0 ); // 创建CamShift演示窗口
    //namedWindow("backproj image", 0);
    setMouseCallback( "CamShift Demo", onMouse, 0 ); // 设置鼠标回调函数
    createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 ); // 创建Vmin滑动条
    createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 ); // 创建Vmax滑动条
    createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 ); // 创建Smin滑动条


    Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj; // 声明一些Mat变量用于处理图像和直方图
    bool paused = false; // 是否暂停


    for(;;) // 无限循环,直到用户退出
    {
        if( !paused ) // 如果没有暂停
        {
            cap >> frame; // 从摄像头读取一帧图像
            if( frame.empty() ) // 如果图像为空
                break; // 跳出循环
        }


        frame.copyTo(image); // 复制一帧图像到image变量


        if( !paused )
        {
            cvtColor(image, hsv, COLOR_BGR2HSV); // 将BGR图像转换为HSV图像


            if( trackObject ) // 如果开始跟踪对象
            {
                int _vmin = vmin, _vmax = vmax;


                // 根据Vmin和Vmax值在HSV空间中创建掩码
                inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
                        Scalar(180, 256, MAX(_vmin, _vmax)), mask);
                int ch[] = {0, 0};
                hue.create(hsv.size(), hsv.depth()); // 创建hue图像
                //从hsv图像的第0个通道(Hue)复制到hue图像的第0个通道。
                mixChannels(&hsv, 1, &hue, 1, ch, 1); // 从hsv图像中分离出色调(hue)通道


                if( trackObject < 0 ) // 如果是新选择的对象
                {
                    // 使用用户选择的区域(selection)设置直方图并归一化
                    Mat roi(hue, selection), maskroi(mask, selection);
                    calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges); // 计算直方图 16 bins
                    normalize(hist, hist, 0, 255, NORM_MINMAX); // 归一化直方图


                    trackWindow = selection; // 初始化跟踪窗口为用户选择的区域
                    trackObject = 1; // 设置trackObject为1,表示已经配置好了跟踪对象


                    histimg = Scalar::all(0); // 直方图图像初始化
                    int binW = histimg.cols / hsize; // 计算直方图的宽度
                    Mat buf(1, hsize, CV_8UC3); // 创建缓冲区以绘制直方图
                    for( int i = 0; i < hsize; i++ )
                        buf.at<Vec3b>(i) = Vec3b(saturate_cast<uchar>(i*180./hsize), 255, 255); // 为每个直方图bin分配一个颜色
                    cvtColor(buf, buf, COLOR_HSV2BGR); // 将HSV颜色转换为BGR颜色


                    // 绘制直方图到histimg上
                    for( int i = 0; i < hsize; i++ )
                    {
                        int val = saturate_cast<int>(hist.at<float>(i)*histimg.rows/255); // 计算直方图的高度
                        rectangle( histimg, Point(i*binW,histimg.rows),
                                   Point((i+1)*binW,histimg.rows - val),
                                   Scalar(buf.at<Vec3b>(i)), -1, 8 ); // 绘制直方图
                    }
                }


                // 执行CAMShift算法
                calcBackProject(&hue, 1, 0, hist, backproj, &phranges); // 计算反向投影
                backproj &= mask; // 应用掩码
                //imshow("backproj image", backproj);
                RotatedRect trackBox = CamShift(backproj, trackWindow,
                                    TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 )); // 进行CamShift跟踪
                if( trackWindow.area() <= 1 ) // 如果跟踪窗口太小
                {
                    int cols = backproj.cols, rows = backproj.rows, r = (MIN(cols, rows) + 5)/6;
                    trackWindow = Rect(trackWindow.x - r, trackWindow.y - r,
                                       trackWindow.x + r, trackWindow.y + r) &
                                  Rect(0, 0, cols, rows); // 扩大跟踪窗口
                }


                if( backprojMode ) // 如果是反向投影模式
                    cvtColor( backproj, image, COLOR_GRAY2BGR ); // 将反向投影转换为BGR图像
                ellipse( image, trackBox, Scalar(0,0,255), 3, LINE_AA ); // 在图像上绘制跟踪的椭圆
            }
        }
        else if( trackObject < 0 ) // 如果是新选择的对象但是暂停了
            paused = false; // 取消暂停


        if( selectObject && selection.width > 0 && selection.height > 0 ) // 如果选择了对象且选择框有效
        {
            Mat roi(image, selection); // 创建图像上的感兴趣区域
            bitwise_not(roi, roi); // 应用位反转以高亮显示选择区域
        }


        imshow( "CamShift Demo", image ); // 显示CamShift窗口
        if(showHist) imshow( "Histogram", histimg ); // 根据参数是否显示直方图


        char c = (char)waitKey(10); // 捕捉按键,用于控制程序
        if( c == 27 ) // 如果按下ESC则退出程序
            break;
        switch(c) // 根据按键做出相应的操作
        {
            case 'b': // 按下b键切换反向投影模式
                backprojMode = !backprojMode;
                break;
            case 'c': // 按下c键停止跟踪
                trackObject = 0;
                histimg = Scalar::all(0); // 清空直方图图像
                break;
            case 'h': // 按下h键显示或隐藏直方图
                showHist = !showHist;
                if( !showHist )
                    destroyWindow( "Histogram" ); // 关闭直方图窗口
                else
                    namedWindow( "Histogram", 1 ); // 创建直方图窗口
                break;
            case 'p': // 按下p键暂停或继续
                paused = !paused;
                break;
            default:
                ;
        }
    }


    return 0; // 正常退出程序
}

这段代码实现了使用OpenCV库的CamShift算法来对选定对象进行实时跟踪的功能。程序首先通过摄像头获取视频帧,用户可以用鼠标在图像上选择要跟踪的对象。被选定对象的颜色信息将被用于创建HSV直方图并进行归一化,之后CamShift算法会在后续的视频帧中识别并跟踪该对象。程序提供了一些交互操作,如开始/停止跟踪、切换反向投影模式、显示/隐藏直方图和暂停/继续视频等。

b8e1d65a05aab2354356b8ea870a4e0b.png

在计算机视觉和图像处理中,为什么常用HSV颜色空间而不是RGB颜色空间?c20affe0379173d97605ebc9a16507fd.png

22ebe985efd29e482c30d2426d6beef8.png

inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
        Scalar(180, 256, MAX(_vmin, _vmax)), mask);

89714cf72b750d86d49a2f0aa08b4538.png

mixChannels(&hsv, 1, &hue, 1, ch, 1);

ccf8d6e655880c03baf0a79164315db5.png

calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);

52139939fffa6a294f8421e77af76277.png

calcBackProject(&hue, 1, 0, hist, backproj, &phranges);

faa7183394770385b1b23cb8e2cf0568.png

RotatedRect trackBox = CamShift(backproj, trackWindow,
TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 ));

83bac6333785175c0349eb630a96a24c.png

The end


网站公告

今日签到

点亮在社区的每一天
去签到