这里有几个函数:
mixChannels()
- Mat rgba( 3, 4, CV_8UC4, Scalar(1,2,3,4) );
- Mat bgr( rgba.rows, rgba.cols, CV_8UC3 );
- Mat alpha( rgba.rows, rgba.cols, CV_8UC1 );
- // forming an array of matrices is a quite efficient operation,
- // because the matrix data is not copied, only the headers
- Mat out[] = { bgr, alpha };
- // rgba[0] -> bgr[2], rgba[1] -> bgr[1],
- // rgba[2] -> bgr[0], rgba[3] -> alpha[0]
- int from_to[] = { 0,2, 1,1, 2,0, 3,3 };
- mixChannels( &rgba, 1, out, 2, from_to, 4 );
inRange()
inRange用来检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵mask(0-1矩阵)
calcHist()
calcHist()函数第一个参数为输入矩阵序列,第2个参数表示输入的矩阵数目,第3个参数表示将被计算直方图维数通道的列表,第4个参数表示可选的掩码函数
第5个参数表示输出直方图,第6个参数表示直方图的维数,第7个参数为每一维直方图数组的大小,第8个参数为每一维直方图bin的边界
以下为demo代码:
// camshift.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <OpenCV245.h>
using namespace std;
using namespace cv;
Mat image;
bool backprojMode = false; //表示是否要进入反向投影模式,ture表示准备进入反向投影模式
bool selectObject = false; //用来判断是否选中,当鼠标左键按下时为ture,左键松开时为false
int trackObject = 0; //代表跟踪目标数目
bool showHist = true; //是否显示直方图
Point origin; //选中的起点
Rect selection; //选中的区域
int vmin = 10, vmax = 256, smin = 30;
//鼠标事件响应函数,这个函数从按下左键时开始响应直到左键释放
static void onMouse(int event, int x, int y, int, void* )
{
if( selectObject )
{
selection.x = MIN(x, origin.x);
selection.y = MIN(y, origin.y);
selection.width = abs(x - origin.x);
selection.height = abs(y - origin.y);
}
switch (event)
{
case CV_EVENT_LBUTTONDOWN: //按下鼠标时,捕获点origin
origin = Point(x, y);
selection = Rect(x, y, 0, 0);
selectObject = true;
break;
case CV_EVENT_LBUTTONUP:
selectObject = false;
if ( selection.width > 0 && selection.height > 0)
{
trackObject = -1;
}
break;
}
}
const char* keys =
{
"{1| | 0 | camera number}"
};
//const char* keys = { "{1| | 0 | camera number}" };
int _tmain(int argc, _TCHAR* argv[])
{
VideoCapture cap;
Rect trackWindow; //跟踪的窗口
int hsize = 16; //创建直方图时要用的常量
float hranges[] = {0, 180};
const float* phranges = hranges;
CommandLineParser parser(argc, argv, keys);//命令解析器函数
//CommandLineParser parser(argc, argv, keys);
int camNum = parser.get<int>("0");
cap.open(camNum);
if ( !cap.isOpened())
{
cout << "***Could not initialize capturing...***\n";
cout << "Current parameter's value: \n";
parser.printParams();
return -1;
}
//关于显示窗口的一些设置
namedWindow("Histogram", 0);
namedWindow("CamShift Demo", 0);
//设置鼠标事件,把鼠标响应与onMouse函数关联起来
setMouseCallback("CamShift Demo", onMouse, 0);
//创建三个滑块条,特定条件用滑块条选择不同参数能获得较好的跟踪效果
createTrackbar( "Vmin", "CamShift Demo", &vmin, 256, 0 );
createTrackbar( "Vmax", "CamShift Demo", &vmax, 256, 0 );
createTrackbar( "Smin", "CamShift Demo", &smin, 256, 0 );
Mat frame, hsv, hue, mask, hist, histimg = Mat::zeros(200, 320, CV_8UC3), backproj;
bool paused = false;
for(;;)
{
if ( !paused ) //没有暂停
{
cap >> frame; //从摄像头输入frame
if(frame.empty())
break;
}
frame.copyTo(image);
if( !paused )
{
cvtColor(image, hsv, CV_BGR2HSV);
if( trackObject ) //松开鼠标左键时,trackObject为-1,执行核心部分
{
int _vmin = vmin, _vmax = vmax; //int vmin = 10, vmax = 256, smin = 30;
//inRange用来检查元素的取值范围是否在另两个矩阵的元素取值之间,返回验证矩阵mask(0-1矩阵)
//这里用于制作掩膜板,只处理像素值为H:0~180,S:smin~256, V:vmin~vmax之间的部分。mask是要求的,单通道
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());
mixChannels(&hsv, 1, &hue, 1, ch, 1);//将H分量拷贝到hue中,其他分量不拷贝
if ( trackObject < 0 )
{
Mat roi(hue, selection), maskroi(mask, selection);
//calcHist()函数第一个参数为输入矩阵序列,第2个参数表示输入的矩阵数目,第3个参数表示将被计算直方图维数通道的列表,第4个参数表示可选的掩码函数
//第5个参数表示输出直方图,第6个参数表示直方图的维数,第7个参数为每一维直方图数组的大小,第8个参数为每一维直方图bin的边界
calcHist(&roi, 1, 0, maskroi, hist, 1, &hsize, &phranges);
normalize(hist, hist, 0, 255, CV_MINMAX);
trackWindow = selection;
trackObject = 1;//只要鼠标选完区域松开后,且没有按键盘清0键'c',则trackObject一直保持为1,因此该if函数只能执行一次,除非重新选择跟踪区域
histimg = Scalar::all(0); //与按下'c'键是一样的,这里的all(0)表示的是标量全部清0
int binw = histimg.cols / hsize;//histing是一个200*300的矩阵,hsize应该是每一个bin的宽度,也就是histing矩阵能分出几个bin出来
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);
cvtColor(buf, buf, CV_HSV2BGR);
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);
}
}
calcBackProject(&hue, 1, 0, hist, backproj, &phranges);
//计算两个矩阵backproj、mask的每个元素的按位与,返回backproj
backproj &= mask;
RotatedRect trackBox = CamShift(backproj, trackWindow, TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER, 10, 1));
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, CV_GRAY2BGR);
ellipse(image, trackBox, Scalar(0, 0, 255), 3, CV_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 );
imshow( "Histogram", histimg );
//每轮都要等待用户的按键控制
char c = (char)waitKey(10);
if( c == 27 )//"Esc"键,直接退出
break;
switch(c)
{
case 'b'://转换显示方式
backprojMode = !backprojMode;
break;
case 'c'://停止追踪
trackObject = 0;
histimg = Scalar::all(0);
break;
case 'h'://隐藏或显示直方图
showHist = !showHist;
if( !showHist )
destroyWindow( "Histogram" );
else
namedWindow( "Histogram", 1 );
break;
case 'p'://暂停
paused = !paused;//frame停止从摄像头获取图像,只显示旧的图像
break;
default:
;
}
}
return 0;
}
参考链接:http://blog.csdn.net/jkhere/article/details/8696347