前言
以下为camshift目标跟踪的代码
代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
//HSV
int smin = 30;
int smax = 255;
int vmin = 40;
int vmax = 255;
//calcHist参数
int histSize = 16;//区间的个数
float hue_ranges[] = { 0,180 };
const float* ranges = hue_ranges;
int main(int argc, char** argv)
{
VideoCapture capture;
//capture.open(0);
capture.open(0);
if (!capture.isOpened())
{
cout << "could not load video..." << endl;
return -1;
}
bool firstRead = true;//用于判断是否第一次读取视频
Rect selection;//selectROI用鼠标选择的ROI区域
Mat frame,blur,hsv, mask, hue, temp_histogram, backprojection;
Mat Histogram = Mat::zeros(300, 300, CV_8UC3);
namedWindow("CAMShift Tracking", WINDOW_AUTOSIZE);
while (capture.read(frame))
{
if (firstRead)
{
//获取第一帧的ROI区域
Rect2d first_img = selectROI("CAMShift Tracking", frame);//在"CAMShift Tracking"窗口的frame图像上用鼠标选择ROI区域
selection.x = first_img.x;
selection.y = first_img.y;
selection.width = first_img.width;
selection.height = first_img.height;
cout << "ROI的x值为: " << selection.x << endl;
cout << "ROI的y值为: " << selection.y << endl;
cout << "ROI的width值为: " << selection.width << endl;
cout << "ROI的height值为: " << selection.height << endl;
}
GaussianBlur(frame,blur,Size(5,5),3,3);//使用高斯滤波进行去噪
cvtColor(blur, hsv, COLOR_BGR2HSV);//转换到HSV空间
//测试矩阵frame的元素是否在其他两个矩阵的值之间//提取黄色
inRange(hsv, Scalar(0, smin, vmin), Scalar(180, smax, vmax), mask);
hue = Mat(hsv.size(), hsv.depth());
int channels[] = { 0,0 };//fromto映射//指定被复制通道与要复制到的位置组成的索引对
size_t npairs = 1; //指定被复制通道与要复制到的位置channels[]组成的索引对
mixChannels(&hsv, 1, &hue, 1, channels, npairs); //输入矩阵通道 重新排列到 输出矩阵通道(HSV中的H值)
if (firstRead)
{
//计算ROI直方图
Mat ROI(hue, selection);//Mat& image , Rect& roi
Mat MaskROI(mask, selection);
calcHist(&ROI, 1, 0, MaskROI, temp_histogram, 1, &histSize, &ranges);//计算直方图
normalize(temp_histogram, temp_histogram, 0, 255, NORM_MINMAX);//归一化
//显示直方图图像
int bins = Histogram.cols / histSize; //300/16 = 18.5 单个区间宽度
//定义一个缓冲单bins矩阵,1行16列,用于存放颜色数据,用于直方图histSize个bin的“染色”
Mat ColorIndex = Mat(1, histSize, CV_8UC3);//1行histSize=16列
for (int i = 0; i < histSize; i++)
{
ColorIndex.at<Vec3b>(0, i) = Vec3b(saturate_cast<uchar>(i * 180 / histSize), 255, 255);//只是为了好看
}
cvtColor(ColorIndex, ColorIndex, COLOR_HSV2BGR);
for (int j = 0; j < histSize; j++)
{
//value是直方图temp_histogram的相对Histogram的高度
//temp_histogram.at(i)获取了第i个bin直方图数据,除以255后得到百分比
//再乘以Histogram的行数就得到了相对高度,最后进行int的强制类型转换,转换为整数。
int value = saturate_cast<int>(temp_histogram.at<float>(j) * Histogram.rows / 255);
//之后使用rectangle()函数进行16个bin的绘制
//值得注意的是矩阵的坐标系以左上角为原点,y轴是向下的
//而需要展示给人看的直方图图案是左下角为原点,y轴向上的
//因此rectangle的两个标定点的纵坐标是Histogram.rows和(Histogram.rows - val)而不是0和val
rectangle(Histogram,
Point(j * bins, Histogram.rows), //矩阵对角点:左下角
Point((j + 1) * bins, Histogram.rows - value), //矩阵对角点:右上角
Scalar(ColorIndex.at<Vec3b>(0, j)), -1, 8, 0);
}
firstRead = false;//需要绘制第一帧的直方图
}
//直方图反向投影
/*void calcBackProject(
const Mat* images, //输入的数组
int nimages, //输入数组的个数
const int* channels, //需要统计的通道索引
InputArray hist, //输入的直方图
OutputArray backProject,//目标的反向投影
const float** ranges, //每一位数值的取值范围
double scale=1, //输出方向投影的缩放因子
bool uniform=true //指示直方图是否均匀的标识符
)
*/
//用来计算像素和直方图模型中像素吻合度的方法
calcBackProject(&hue, 1, 0, temp_histogram, backprojection, &ranges);
backprojection &= mask;//a&=b 即 a=a&b 其中&为位与运算//给b取了一个别名叫a,所有对b的操作都是直接作用于a
//CAMShift
RotatedRect trackBox = CamShift(
backprojection, //反向投影
selection,//矩形搜索框
TermCriteria((TermCriteria::COUNT | TermCriteria::EPS), 10, 1));//迭代中止条件
//绘制位置更新显示在frame上
//---------------------------------获取CamShift的返回值,是一个旋转矩形,以下为绘制跟踪结果的部分
ellipse(frame, trackBox, Scalar(255, 0, 255), 3, 8);//根据旋转矩形绘制一个椭圆形显示在图像上作为追踪结果。
Point2f vertices[4];//定义存储4个x,y变量的数组
trackBox.points(vertices);//将旋转矩形的四个顶点值传入数组
for (int i = 0; i < 4; i++)//绘制旋转矩形的四条边
line(frame, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0));
Rect brect = trackBox.boundingRect();//返回最小旋转角度为0的外接矩形
rectangle(frame, brect, Scalar(255, 0, 0));//绘制外接矩形
//----------------------------------
if (firstRead)
{
firstRead = false;
}
imshow("CAMShift Tracking", frame);
imshow("Histogram", Histogram);
char c = waitKey(1);
if (c == 27)
{
break;
}
}
capture.release();
//waitKey(0);
destroyAllWindows();
return 0;
}