opencv_c++学习(二十八)

一、单目相机位姿估计

在这里插入图片描述
如上图所示,根据图像的情况反推相机的运动情况。
如实现上述功能则需要拍摄当前物体的图像,然后拍摄一段时间之后物体的图像,然后联合两张图像则可以获取两个时刻的相机位姿关系。
位姿估计函数:

bool cv:solvePnP( InputArray objectPoints, InputArray imagePoints, InputArray cameraMatrix, InputArray distCoeffs, OutputArray rvec, OutputArray tvec, useExtrinsicGuess =, bool false, int flags = 5OLVEPNP_ITERATIVE)

objectPoints:前一时刻世界坐标系中的3D点的三维坐标。
imagePoints: 3D点在图像中对应的像素点的二维坐标。
cameraMatrix:相机的内参矩阵。
distCoeffs:相机的畸变系数矩阵。
rvec:世界坐标系变换到相机坐标系的旋转向量。
tvec:世界坐标系变换到相机坐标系的平移向量。
uscExtrinsicGuess:是否使用旋转向量初值和平移向量初值的标志。
flags:选择解算PnP问题方法的标志。
本节应用案例如下:

int main() {
    
    

	//读取图片
	Mat src = imread("left1.jpg");
	Mat gray;
	if (src.empty())
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	cvtColor(src, gray, COLOR_BGR2GRAY);
	vector<Point2f> imgpoints;
	Size boardSize = Size(9,6);
	//计算标定板的角点
	findChessboardCorners(gray, boardSize, imgpoints);
	//细化方格标定板角点坐标
	find4QuadCornerSubpix(gray, imgpoints, Size(5, 5));

	//棋盘格每个方格的真实尺寸
	Size squareSize = Size(10, 10);

	vector<Point3f> PointSets;
	for (int j = 0; j < boardSize.height; j++)
	{
    
    
		for (int k = 0; k < boardSize.width; k++)
		{
    
    
			Point3f realPoint;

			//假设标定板为世界坐标系的Z平面,及z=0
			realPoint.x = j * squareSize.width;
			realPoint.y = k * squareSize.height;
			realPoint.z = 0;
			PointSets.push_back(realPoint);
		}
	}

	//输入计算的内参矩阵和畸变矩阵
	Mat cameraMatrix = (Mat_<float>(3, 3) << 120.8643306554273, 0, 94.55565247064737,
		0, 119.979406894919, 55.48571212317609,
		0, 0, 1);

	Mat distCoeffs = (Mat_<float>(1, 5) << -0.5559208449775317, 3.15840209023594, -0.001816753642197531, -0.01817901488786, -7.629569308066959);

	//用PnP算法计算旋转和平移量
	Mat rvec, tvec;
	solvePnP(PointSets, imgpoints, cameraMatrix, distCoeffs, rvec, tvec);
	cout << "旋转向量为: " << rvec << endl;

	//旋转向量转换旋转矩阵
	Mat R;
	Rodrigues(rvec, R);
	cout << "旋转矩阵为:" << R << endl;

	//用PnP + Ransac算法计算旋转向量和平移向量
	Mat rvecRansac, tvecRansac;

	solvePnPRansac(PointSets, imgpoints, cameraMatrix, distCoeffs, rvecRansac, tvecRansac);
	Mat RRansac;
	Rodrigues(rvecRansac, RRansac);
	cout << "旋转矩阵" << RRansac << endl;
	waitKey(0);
	return 0;	 
}

二、插值法从视频中跟踪移动的物体

在这里插入图片描述
计算差值绝对值函数:

void cv::absdiff ( InputArray src1, InputArray src2, OutputArray dst)

srcl:第一个数组或者Mat类矩阵。
src2:第二个数组或者Mat类矩阵,需要与第一个参数具有相同的尺寸和数据类型。
dst:两个数据差值的绝对值,与输入数据具有相同的尺寸和数据类型。
本节应用案例如下:

int main() {
    
    

	//读取视频
	VideoCapture capture("1.mp4");
	if (!capture.isOpened())
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	//获取视频相关信息
	//帧率
	int fps = capture.get(CAP_PROP_FPS);
	//宽度
	int wideth = capture.get(CAP_PROP_FRAME_WIDTH);
	//高度
	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	//总帧数
	int num_of_frames = capture.get(CAP_PROP_FRAME_COUNT);

	//读取视频中第一幅图像作为前一帧图像,并进行灰度化
	Mat preFrame, preGray;
	capture.read(preFrame);
	cvtColor(preFrame, preGray, COLOR_BGR2GRAY);
	//对图像进行高斯滤波
	GaussianBlur(preGray, preGray, Size(0, 0), 15);

	Mat binary;
	Mat frame, gray;
	//形态学操作的矩形模板
	Mat k = getStructuringElement(MORPH_RECT, Size(7, 7), Point(-1, -1));

	while (true)
	{
    
    
		//视频中所有图像处理完成后退出循环
		if (!capture.read(frame))
		{
    
    
			break;
		}

		//对当前帧进行灰度化
		cvtColor(frame, gray, COLOR_BGR2GRAY);
		GaussianBlur(gray, gray, Size(0, 0), 15);

		//计算当前帧与前一帧差值的绝对值
		absdiff(gray, preGray, binary);

		//对计算结果二值化进行开运算,减少噪声的干扰
		threshold(binary, binary, 10, 255, THRESH_BINARY | THRESH_OTSU);
		morphologyEx(binary, binary, MORPH_OPEN, k);

		imshow("input", frame);
		imshow("result", binary);

		//延迟5毫秒延迟判断是否退出程序,按ESC退出
		char c = waitKey(5);
		//if (c = 27)
		//{
    
    
		//	break;
		//}
	}
	waitKey(0);
	return 0;
}

三、稠密光流法实现物体跟踪

在这里插入图片描述
在这里插入图片描述

void cv::calcOpticalFlowFarneback ( InputArray prev, InputArray  next, InputOutputArray flow,double pyr_scale, int levels, int winsize, int iterations, int poly_n. double poly_sigma, int flags)

prev:前一帧图像,必须是CV_8UC1类型。
next:当前帧图像,与前一帧图像具有相同的尺寸和数据类型。
flow:输出的光流图像,图像与前一帧图像具有相同的尺寸,为CV_32F双通道图像。
pyr_scale:图像金字塔两层之间尺寸缩放的比例。
levels:构建图像金字塔的层数。
winsize:均值窗口的尺寸。
iterations:算法在每个金字塔图层中迭代的次数。
poly_n:在每个像素中找到多项式展开的像素邻域的大小。
poly_sigma:高斯标准差。
flags:计算方法标志。
本节应用案例如下:

int main() {
    
    

	//读取视频
	VideoCapture capture("1.mp4");
	if (!capture.isOpened())
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	//读取视频中第一幅图像作为前一帧图像,并进行灰度化
	Mat preFrame, preGray;
	capture.read(preFrame);
	cvtColor(preFrame, preGray, COLOR_BGR2GRAY);

	while (true)
	{
    
    
		Mat nextFrame, nextGray;
		//视频中所有图像处理完成后退出循环
		if (!capture.read(nextFrame))
		{
    
    
			break;
		}

		imshow("input", nextFrame);

		//计算稠密光流
		cvtColor(nextFrame, nextGray, COLOR_BGR2GRAY);
		//两个方向的运动速度
		Mat_<Point2f> flow;
		calcOpticalFlowFarneback(preGray, nextGray, flow, 0.5, 3, 15, 3, 5, 1.2, 0);

		//x方向移动速度
		Mat xV = Mat::zeros(preFrame.size(), CV_32FC1);
		//y方向移动速度
		Mat yV = Mat::zeros(preFrame.size(), CV_32FC1);

		//获取两个方向的速度
		for (int row = 0; row < flow.rows; row++)
		{
    
    
			for (int col = 0; col < flow.cols; col++)
			{
    
    
				const Point2f& flow_xy = flow.at<Point2f>(row, col);
				xV.at<float>(row, col) = flow_xy.x;
				yV.at<float>(row, col) = flow_xy.y;
			}
		}

		//计算向量角度和幅度
		Mat magnitude, angle;
		cartToPolar(xV, yV, magnitude, angle);

		//将角度转换为角度制
		angle = angle * 180.0 / CV_PI / 2.0;

		//将幅值归一化到0-255
		normalize(magnitude, magnitude, 0, 255, NORM_MINMAX);

		//计算角度和幅值的绝对值
		convertScaleAbs(magnitude, magnitude);
		convertScaleAbs(angle, angle);

		//将运动的赋值和角度生成HSV颜色的空间图像
		Mat HSV = Mat::zeros(preFrame.size(), preFrame.type());
		vector<Mat> result;
		split(HSV, result);
		//颜色
		result[0] = angle;

		result[1] = Scalar(255);

		//形态
		result[2] = magnitude;

		merge(result, HSV);

		//将HSV颜色转换为RGB
		Mat rgbImg;
		cvtColor(HSV, rgbImg, COLOR_HSV2BGR);

		imshow("result", rgbImg);

		//延迟5毫秒延迟判断是否退出程序,按ESC退出
		char c = waitKey(5);
		//if (c = 27)
		//{
    
    
		//	break;
		//}
	}
	waitKey(0);
	return 0;
}

四、稀疏光流法实现物体跟踪

与稀疏光流法的计算方式相同,但唯一不同的地方就是稀疏光流法并不是计算整个图像的像素点,而是选取图像中有代表性的点来实现物体的跟踪。

void cv::calcOpticalFlowPyrLK ( InputArray previmg, InputArray nextlmg, InputArray prevPts, InputOutputArray nextPts, OutputArray status, OutputArray err, Size winSize = Size(21,21), int maxLevel = 3, criteria =, TermCriteria Termcriteria(TermCriteria: :COUNT+TermCriteria: : EPS,308.81), int flags =0, double minEligThreshold = 1e-4)

prevPts:前一帧图像的稀疏光流点坐标,必须是单精度浮点数。
nextPts:当前帧中与前一帧图像稀疏光流点匹配成功的稀疏光流点坐标,同样必须是单精度浮点数。
status:输出状态向量,如果在两帧图像中寻找到相对应的稀疏光流点,那么向量值为1,否则向量值为0。
err:输出误差向量,向量每个元素都设置为对应点的误差,度量误差的
标准可以在flags参数中设置。
winSize:每层金字塔中搜索窗口的大小,默认Size(21,21)。
maxLevel:构建图像金字塔层数,参数值为从0开始的整数。
criteria:迭代搜索的终止条件。
flags:寻找匹配光流点的操作标志。
minEig Threshold:响应的最小特征值。
本节应用案例如下:

//颜色查找表
vector<Scalar>color_lut;
void draw_lines(Mat &image, vector<Point2f> pt1, vector<Point2f> pt2)
{
    
    
	RNG rng(10000);
	if (color_lut.size() < pt1.size())
	{
    
    
		for (size_t t = 0; t < pt1.size(); t++)
		{
    
    
			color_lut.push_back(Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)));
		}
	}
	for (size_t t = 0; t < pt1.size(); t++)
	{
    
    
		line(image, pt1[t], pt2[t], color_lut[t], 2, 8, 0);
	}
}

int main() {
    
    

	//读取视频
	VideoCapture capture("1.mp4");
	Mat preframe, preImg;
	if (!capture.read(preframe))
	{
    
    
		printf("不能打开空图片");
		return -1;
	}

	//读取视频中第一幅图像作为前一帧图像,并进行灰度化
	cvtColor(preframe, preImg, COLOR_BGR2GRAY);

	//角点检测相关参数设置
	vector<Point2f> Points;
	double qualityLevel = 0.01;
	int minDistance = 10;
	int blockSize = 3;
	bool useHarrisDetector = false;
	double k = 0.04;
	int Corners = 5000;

	//开始角点检测
	vector<Point2f> prevPts;  //前一幅图像的角点坐标
	vector<Point2f> nextPts;  //当前帧图像的角点坐标
	vector<uchar> status; //检点检测到的状态
	vector<float> err;
	TermCriteria criteria = TermCriteria(TermCriteria::COUNT + TermCriteria::EPS, 30, 0.01);
	double derivlambda = 0.5;
	int flags = 0;

	//初始状态的角点
	vector<Point2f>initPoints;
	initPoints.insert(initPoints.end(), Points.begin(), Points.end());

	//前一帧图像中角点坐标
	prevPts.insert(prevPts.end(), Points.begin(), Points.end());

	while (true)
	{
    
    
		//读取视频
		Mat nextFrame, nextImg;
		if (!capture.read(nextFrame))
		{
    
    
			break;
		}


		//光流法跟踪
		cvtColor(nextFrame, nextImg, COLOR_BGR2GRAY);
		imshow("nextFrame", preImg);
		calcOpticalFlowPyrLK(preImg, nextImg, prevPts, nextPts, status, err, 
			Size(31, 31), 3, criteria, derivlambda, flags);

		//判断角点是否移动,如果不移动就删除
		size_t i, k;
		for (i = k = 0; i < nextPts.size(); i++)
		{
    
    
			//距离与状态测量
			double dist = abs(prevPts[i].x - nextPts[i].x + abs(prevPts[i].y - nextPts[i].y));
			if (status[i] && dist > 2)
			{
    
    
				prevPts[k] = prevPts[i];
				initPoints[k] = initPoints[i];
				nextPts[k++] = nextPts[i];
				circle(nextFrame, nextPts[i], 3, Scalar(0, 255, 0), -1, 8);
			}
		}

		//更新移动角点数目
		nextPts.resize(k);
		prevPts.resize(k);
		initPoints.resize(k);

		//绘制跟踪轨迹
		draw_lines(nextFrame, initPoints, nextPts);
		imshow("result", nextFrame);

		//延迟5毫秒延迟判断是否退出程序,按ESC退出
		char c = waitKey(50);

		//更新角点坐标和前一帧图像
		std::swap(nextPts, prevPts);
		nextImg.copyTo(preImg);

		//如果角点数目少于30,就重新检测角点
		if (initPoints.size() < 30)
		{
    
    
			goodFeaturesToTrack(preImg, Points, Corners, qualityLevel,
				minDistance, Mat(), blockSize, useHarrisDetector, k);
			initPoints.insert(initPoints.end(), Points.begin(), Points.end());
			prevPts.insert(prevPts.end(), Points.begin(), Points.end());
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_52302919/article/details/130914999