bitwise_not(roi, roi);
backprojMode
#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算法会在后续的视频帧中识别并跟踪该对象。程序提供了一些交互操作,如开始/停止跟踪、切换反向投影模式、显示/隐藏直方图和暂停/继续视频等。
在计算机视觉和图像处理中,为什么常用HSV颜色空间而不是RGB颜色空间?
inRange(hsv, Scalar(0, smin, MIN(_vmin,_vmax)),
Scalar(180, 256, MAX(_vmin, _vmax)), mask);
mixChannels(&hsv, 1, &hue, 1, ch, 1);
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
RotatedRect trackBox = CamShift(backproj, trackWindow,
TermCriteria( TermCriteria::EPS | TermCriteria::COUNT, 10, 1 ));
The end