-
Notifications
You must be signed in to change notification settings - Fork 0
/
facetrack.cpp
217 lines (183 loc) · 6.79 KB
/
facetrack.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#include "facetrack.h"
FaceTrack::FaceTrack(QObject *parent) :
QObject(parent),
stateNum(4),
measureNum(2),
smin(30),
vmin(10),
vmax(255),
histSize(30),
showHist(false),
globalwidth(256),
globalheight(256)
{
//卡尔曼滤波器初始化
statePost = (cv::Mat_<float>(stateNum,1) <<0,0,0,0);
this->klFilter = new cv::KalmanFilter(stateNum,measureNum,0);
this->klFilter->transitionMatrix= *(cv::Mat_<float>(4, 4) << 1,0,1,0, 0,1,0,1, 0,0,1,0, 0,0,0,1);
cv::setIdentity(klFilter->measurementMatrix);
cv::setIdentity(klFilter->processNoiseCov,cv::Scalar::all(1e-1));
cv::setIdentity(klFilter->measurementNoiseCov,cv::Scalar::all(1e-3));
//预测估计协方差矩阵;
cv::setIdentity(klFilter->errorCovPost,cv::Scalar::all(0.1));
measurement = cv::Mat::zeros(measureNum,1,CV_32F);
// this->measurement = cv::Mat_<float>(2,1);
// this->measurement.setTo(cv::Scalar::all(0));
}
//camshift处理过程实现
cv::RotatedRect FaceTrack::camshiftProcess(cv::Mat &frame,cv::Rect &ROI)
{
// //保存上次的结果
// this->lastROI = ROI;
cv::Size fsize = frame.size();
hsv = cv::Mat(fsize,CV_8UC3);
mask = cv::Mat(fsize,CV_8UC1);
//颜色空间转换
cv::cvtColor(frame,hsv,CV_RGB2HSV);
/*得到二值的 MASK
inRange函数的功能是检查输入数组每个元素大小是否在2个给定数值之间,可以有多通道,mask保存0通道的最小值,也就是h分量
这里利用了hsv的3个通道,比较h,0~180,s,smin~256,v,min(vmin,vmax),max(vmin,vmax)。如果3个通道都在对应的范围内,则
mask对应的那个点的值全为1(0xff),否则为0(0x00)
*/
cv::inRange(hsv,cv::Scalar(0,smin,MIN(vmin,vmax),0),
cv::Scalar(180,256,MAX(vmin,vmax)),mask);
//只提取 H 分量
int ch[] = {0,0};
hue.create(hsv.size(),hsv.depth());
//将hsv第一个通道(也就是色调)的数复制到hue中,0索引数组
cv::mixChannels(&hsv,1,&hue,1,ch,1);
//用于camshift计算直方图
float range[] = {0,180};
const float* histRange = {range};
cv::Mat roi(hue,ROI);
cv::Mat maskroi(mask,ROI);
//计算直方图
cv::calcHist(&roi,1,0,maskroi,hist,1,&histSize,&histRange);
//进行归一化
cv::normalize(hist, hist, 0, 255, CV_MINMAX);
if(this->showHist)
{//画出直方图
histImg = cv::Mat::zeros(frame.size(),CV_8UC3);
histImg = cv::Scalar::all(0);
int binW = histImg.cols / histSize;
cv::Mat buf(1,histSize,CV_8UC3);
//saturate_case函数为从一个初始类型准确变换到另一个初始类型
for( int i = 0; i < histSize; i++ )
buf.at<cv::Vec3b>(i) = cv::Vec3b(cv::saturate_cast<uchar>(i*180./histSize), 255, 255);
cvtColor(buf, buf, CV_HSV2RGB);
for( int i = 0; i < histSize; i++ )
{
int val = cv::saturate_cast<int>(hist.at<float>(i)*histImg.rows/255);
cv::rectangle( histImg, cv::Point(i*binW,histImg.rows),
cv::Point((i+1)*binW,histImg.rows - val),
cv::Scalar(buf.at<cv::Vec3b>(i)), -1, 8 );
}
}
//计算直方图的反向投影,计算hue图像0通道直方图hist的反向投影
cv::calcBackProject(&hue,1,0,hist,backProject,&histRange);
backProject &= mask;
//调用camshift模块
trackBox = cv::CamShift(backProject,ROI,
cv::TermCriteria(CV_TERMCRIT_EPS | CV_TERMCRIT_ITER,10,1));
camCenter = cv::Point2f(trackBox.center.x,trackBox.center.y);
return trackBox;
}
//依据给出区域的中心坐标,进行卡尔曼滤波,只预测中心
void FaceTrack::kalmanProcess()
{
//do kalman prediction
klFilter->predict();
KFPredictCenter = cv::Point2f(klFilter->statePost.at<float>(0),
klFilter->statePost.at<float>(1));
this->measurement.at<float>(0) = camCenter.x;
this->measurement.at<float>(1) = camCenter.y;
//进行 kalman 预测,可以得到预测坐标
klFilter->correct(this->measurement);
KFCorrectCenter = cv::Point2f(klFilter->statePost.at<float>(0),
klFilter->statePost.at<float>(1));
}
//画出跟踪到的区域
void FaceTrack::plotRect(cv::Mat &frame, cv::RotatedRect &trackBox)
{
cv::Point2f vertices[4];
trackBox.points(vertices);
for(int i=0;i<4;i++)
{
cv::line(frame,vertices[i],vertices[(i+1)%4],cv::Scalar(0,255,0),2);
}
cv::circle(frame,camCenter,2,cv::Scalar(255,0,0),2,CV_AA);
cv::circle(frame, KFPredictCenter, 2, cv::Scalar(0,255,0), 4, CV_AA); // draw kalman predict result
cv::circle(frame, KFCorrectCenter, 2, cv::Scalar(0,0,255), 2, CV_AA); // draw kalman correct result
cv::Rect brect = trackBox.boundingRect();
cv::rectangle(frame,brect,cv::Scalar(255,0,0),2);
}
cv::Mat FaceTrack::rotatedProcess(cv::Mat &frame, cv::RotatedRect &rotRect)
{
float angle = rotRect.angle;
angle = int(angle)%360;
if( angle>135.0 )
angle = 180 - angle;
std::cout<<angle<<std::endl;
cv::Mat warp,rotAfter;
warp = cv::getRotationMatrix2D(rotRect.center,(double)angle,1.0);
cv::warpAffine(frame,rotAfter,warp,frame.size());
cv::getRectSubPix(rotAfter,rotRect.size,rotRect.center,rotAfter);
cv::cvtColor(rotAfter,rotAfter,CV_BGR2RGB);
// std::cout<<rotAfter.cols<<","<<rotAfter.rows<<std::endl;
// cv::imshow("Rotated Window",rotAfter);
// cv::waitKey(10);
// if( c == 27 )
// break;
return rotAfter;
}
void FaceTrack::setVmin(int min)
{
this->vmin = min;
}
void FaceTrack::setVmax(int max)
{
this->vmax = max;
}
//期望的宽度设置
void FaceTrack::setWidth(int width)
{
this->globalwidth = width;
}
//期望的长度设置
void FaceTrack::setHeight(int height)
{
this->globalheight = height;
}
//输出结果尺度调整
cv::Mat FaceTrack::uniformSize(cv::Mat &input)
{
cv::Mat output;
if(input.cols > this->globalwidth && input.rows > this->globalheight)
{
double x = (input.cols - globalwidth)/2.0;
double y = (input.rows - globalheight)/2.0;
cv::Rect rect(x,y,globalwidth,globalheight);
output = input(rect);
}else
{
cv::resize(input,output,cv::Size(globalwidth,globalheight),0,0,CV_INTER_CUBIC);
}
return output;
}
cv::Mat FaceTrack::getTrackBox()
{
return this->uniMat;
}
//通过信号和槽机制完成主要的函数调用
void FaceTrack::run(cv::Mat &frameIn, cv::Rect &faceRect)
{
this->trackBox = this->camshiftProcess(frameIn,faceRect);
this->kalmanProcess();
cv::Mat rotMat = this->rotatedProcess(frameIn,trackBox);
uniMat = this->uniformSize(rotMat);
// cv::namedWindow("Uniform Window");
// cv::imshow("Uniform Window",uniMat);
// cv::waitKey(10);
//画出rotatedRect
this->plotRect(frameIn,trackBox);
}