一、单目相机位姿估计
如上图所示,根据图像的情况反推相机的运动情况。
如实现上述功能则需要拍摄当前物体的图像,然后拍摄一段时间之后物体的图像,然后联合两张图像则可以获取两个时刻的相机位姿关系。
位姿估计函数:
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=0realPoint.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-255normalize(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颜色转换为RGBMat 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,30,8.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;
}