// Pick only the more stable/rigid points under changes of expression
	void extract_rigid_points(Mat_<double>& source_points, Mat_<double>& destination_points)
	{
		if(source_points.rows == 68)
		{
			Mat_<double> tmp_source = source_points.clone();
			source_points = Mat_<double>();

			// Push back the rigid points (some face outline, eyes, and nose)
			source_points.push_back(tmp_source.row(1));
			source_points.push_back(tmp_source.row(2));
			source_points.push_back(tmp_source.row(3));
			source_points.push_back(tmp_source.row(4));
			source_points.push_back(tmp_source.row(12));
			source_points.push_back(tmp_source.row(13));
			source_points.push_back(tmp_source.row(14));
			source_points.push_back(tmp_source.row(15));
			source_points.push_back(tmp_source.row(27));
			source_points.push_back(tmp_source.row(28));
			source_points.push_back(tmp_source.row(29));
			source_points.push_back(tmp_source.row(31));
			source_points.push_back(tmp_source.row(32));
			source_points.push_back(tmp_source.row(33));
			source_points.push_back(tmp_source.row(34));
			source_points.push_back(tmp_source.row(35));
			source_points.push_back(tmp_source.row(36));
			source_points.push_back(tmp_source.row(39));
			source_points.push_back(tmp_source.row(40));
			source_points.push_back(tmp_source.row(41));
			source_points.push_back(tmp_source.row(42));
			source_points.push_back(tmp_source.row(45));
			source_points.push_back(tmp_source.row(46));
			source_points.push_back(tmp_source.row(47));

			Mat_<double> tmp_dest = destination_points.clone();
			destination_points = Mat_<double>();

			// Push back the rigid points
			destination_points.push_back(tmp_dest.row(1));
			destination_points.push_back(tmp_dest.row(2));
			destination_points.push_back(tmp_dest.row(3));
			destination_points.push_back(tmp_dest.row(4));
			destination_points.push_back(tmp_dest.row(12));
			destination_points.push_back(tmp_dest.row(13));
			destination_points.push_back(tmp_dest.row(14));
			destination_points.push_back(tmp_dest.row(15));
			destination_points.push_back(tmp_dest.row(27));
			destination_points.push_back(tmp_dest.row(28));
			destination_points.push_back(tmp_dest.row(29));
			destination_points.push_back(tmp_dest.row(31));
			destination_points.push_back(tmp_dest.row(32));
			destination_points.push_back(tmp_dest.row(33));
			destination_points.push_back(tmp_dest.row(34));
			destination_points.push_back(tmp_dest.row(35));
			destination_points.push_back(tmp_dest.row(36));
			destination_points.push_back(tmp_dest.row(39));
			destination_points.push_back(tmp_dest.row(40));
			destination_points.push_back(tmp_dest.row(41));
			destination_points.push_back(tmp_dest.row(42));
			destination_points.push_back(tmp_dest.row(45));
			destination_points.push_back(tmp_dest.row(46));
			destination_points.push_back(tmp_dest.row(47));
		}
	}
void collectData(int subjId,
				 CascadeClassifier &classifier,
				 ShapePredictor &predictor,
				 Mat_<float> &labels,
				 Mat_<float> &multihog,
				 Mat_<float> &landmarks)
{
	int H[] = { -15, -10, -5, 0, 5, 10, 15 };
	int V[] = { -10, 0, 10 };

	string path = to_string(subjId) + "/";
	if (subjId < 10) path = "columbia/000" + path;
	else path = "columbia/00" + path;
	ifstream fin(path + "annotation.txt");

	for (int imgId = 0; imgId < 105; imgId++) {
		int p, v, h;
		fin >> p >> v >> h;
		if (abs(p) > 15) continue;
		string imgpath = path + to_string(imgId) + ".jpg";
		Mat_<uchar> img = imread(imgpath, 0);
		BBox bbox = getTestBBox(img, classifier);
		if (EmptyBox(bbox)) continue;

		int l = 0;
		// EYE, MOUTH, NOF
		if (abs(h) <= 5 && v == 0) l = 0;
		else if (abs(h) <= 5 && v == -10) l = 1;
		else l = 2;

		if (l == 2) {
			RNG rng(getTickCount());
			double num = rng.uniform(0.0, 1.0);
			if (num > 0.5) continue;
		}

		// 上中下
		/*if (v < 0) l = 0;
		else if (v == 0) l = 1;
		else l = 2;*/

		// 9分类
		/*if (h < -5) l += 0;
		else if (h > 5) l += 2;
		else l += 1;
		if (v < 0) l += 0;
		else if (v > 0) l += 2 * 3;
		else l += 1 * 3;*/

		Mat_<float> lab = l*Mat_<float>::ones(1, 1);
		labels.push_back(lab);

		Mat_<double> shape = predictor(img, bbox);
		Geom G;	initGeom(shape, G);
		Pose P; calcPose(G, P);

		Mat_<uchar> lEye, rEye;
		regularize(img, bbox, P, shape, lEye, rEye);

		vector<float> lRlt;
		vector<float> rRlt;
		calcMultiHog(lEye, lRlt);
		calcMultiHog(rEye, rRlt);

		vector<float> _hog2nd_vec;
		for (int k = 0; k < lRlt.size(); k++)
			_hog2nd_vec.push_back(lRlt[k]);
		for (int k = 0; k < rRlt.size(); k++)
			_hog2nd_vec.push_back(rRlt[k]);
		Mat_<float> _hog2nd_row = Mat_<float>(_hog2nd_vec).reshape(1, 1);
		multihog.push_back(_hog2nd_row);

		vector<float> _ldmks;
		for (int i = 28; i < 48; i++) {
			_ldmks.push_back((shape(i, 0) - bbox.cx) / bbox.w);
			_ldmks.push_back((shape(i, 1) - bbox.cy) / bbox.h);
		}
		float mouthx = (shape(51, 0) + shape(62, 0) + shape(66, 0) + shape(57, 0)) / 4;
		float mouthy = (shape(51, 1) + shape(62, 1) + shape(66, 1) + shape(57, 1)) / 4;
		_ldmks.push_back((mouthx - bbox.cx) / bbox.w);
		_ldmks.push_back((mouthy - bbox.cy) / bbox.h);
		float maxVal = *std::max_element(_ldmks.begin(), _ldmks.end());
		for (int i = 0; i < _ldmks.size(); i++) _ldmks[i] *= 1.0 / maxVal; // scale to [-1, 1]

		Mat_<float> ldmks = Mat_<float>(_ldmks).reshape(1, 1);
		landmarks.push_back(ldmks);
	}
	fin.close();
}