V-LOAM 源码解析(一)
转载请注明出处:本文转自zhch_pan的博客http://www.cnblogs.com/zhchp-blog/
本博客为本人之前做项目时做的源码阅读工作,po到网上希望帮助其他人更好的理解V-LOAM的工程实现,有些地方代码做了修改,可能和原工程有出入,但对于该工程的整体流程理解没有妨碍。
源码下载链接:https://github.com/Jinqiang/demo_lidar
节点名称:featureTracking
订阅topic:<sensor_msgs::Image>("/camera/image_raw")
发布topic:1、<sensor_msgs::PointCloud2> ("/image_points_last")
2、<sensor_msgs::Image>("/image/show")
1 #include <math.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <ros/ros.h> 5 6 #include "cameraParameters.h" 7 #include "pointDefinition.h" 8 9 using namespace std; 10 using namespace cv; 11 12 bool systemInited = false; 13 double timeCur, timeLast; 14 15 const int imagePixelNum = imageHeight * imageWidth; 16 CvSize imgSize = cvSize(imageWidth, imageHeight); 17 18 IplImage *imageCur = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); 19 IplImage *imageLast = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); 20 21 int showCount = 0; 22 const int showSkipNum = 2; 23 const int showDSRate = 2; 24 CvSize showSize = cvSize(imageWidth / showDSRate, imageHeight / showDSRate); 25 26 IplImage *imageShow = cvCreateImage(showSize, IPL_DEPTH_8U, 1); 27 IplImage *harrisLast = cvCreateImage(showSize, IPL_DEPTH_32F, 1); 28 29 CvMat kMat = cvMat(3, 3, CV_64FC1, kImage); 30 CvMat dMat = cvMat(4, 1, CV_64FC1, dImage); 31 32 IplImage *mapx, *mapy; 33 34 const int maxFeatureNumPerSubregion = 2; 35 const int xSubregionNum = 12; 36 const int ySubregionNum = 8; 37 const int totalSubregionNum = xSubregionNum * ySubregionNum; 38 const int MAXFEATURENUM = maxFeatureNumPerSubregion * totalSubregionNum; 39 40 const int xBoundary = 20; 41 const int yBoundary = 20; 42 const double subregionWidth = (double)(imageWidth - 2 * xBoundary) / (double)xSubregionNum; 43 const double subregionHeight = (double)(imageHeight - 2 * yBoundary) / (double)ySubregionNum; 44 45 const double maxTrackDis = 100; 46 const int winSize = 15; 47 48 IplImage *imageEig, *imageTmp, *pyrCur, *pyrLast; 49 50 CvPoint2D32f *featuresCur = new CvPoint2D32f[2 * MAXFEATURENUM]; 51 CvPoint2D32f *featuresLast = new CvPoint2D32f[2 * MAXFEATURENUM]; 52 char featuresFound[2 * MAXFEATURENUM]; 53 float featuresError[2 * MAXFEATURENUM]; 54 55 int featuresIndFromStart = 0; 56 int featuresInd[2 * MAXFEATURENUM] = {0}; 57 58 int totalFeatureNum = 0; 59 //maxFeatureNumPerSubregion=2 60 int subregionFeatureNum[2 * totalSubregionNum] = {0}; 61 62 pcl::PointCloud<ImagePoint>::Ptr imagePointsCur(new pcl::PointCloud<ImagePoint>()); 63 pcl::PointCloud<ImagePoint>::Ptr imagePointsLast(new pcl::PointCloud<ImagePoint>()); 64 65 ros::Publisher *imagePointsLastPubPointer; 66 ros::Publisher *imageShowPubPointer; 67 cv_bridge::CvImage bridge; 68 69 void imageDataHandler(const sensor_msgs::Image::ConstPtr& imageData) 70 { 71 timeLast = timeCur; 72 timeCur = imageData->header.stamp.toSec() - 0.1163; 73 74 IplImage *imageTemp = imageLast; 75 imageLast = imageCur; 76 imageCur = imageTemp; 77 78 for (int i = 0; i < imagePixelNum; i++) { 79 imageCur->imageData[i] = (char)imageData->data[i]; 80 } 81 82 IplImage *t = cvCloneImage(imageCur); 83 //去除图像畸变 84 cvRemap(t, imageCur, mapx, mapy); 85 //cvEqualizeHist(imageCur, imageCur); 86 cvReleaseImage(&t); 87 88 //缩小一点可能角点检测速度比较快 89 cvResize(imageLast, imageShow); 90 cvCornerHarris(imageShow, harrisLast, 3); 91 92 CvPoint2D32f *featuresTemp = featuresLast; 93 featuresLast = featuresCur; 94 featuresCur = featuresTemp; 95 96 pcl::PointCloud<ImagePoint>::Ptr imagePointsTemp = imagePointsLast; 97 imagePointsLast = imagePointsCur; 98 imagePointsCur = imagePointsTemp; 99 imagePointsCur->clear(); 100 101 if (!systemInited) { 102 systemInited = true; 103 return; 104 } 105 106 int recordFeatureNum = totalFeatureNum; 107 for (int i = 0; i < ySubregionNum; i++) { 108 for (int j = 0; j < xSubregionNum; j++) { 109 //ind指向当前的subregion编号 110 int ind = xSubregionNum * i + j; 111 int numToFind = maxFeatureNumPerSubregion - subregionFeatureNum[ind]; 112 113 if (numToFind > 0) { 114 int subregionLeft = xBoundary + (int)(subregionWidth * j); 115 int subregionTop = yBoundary + (int)(subregionHeight * i); 116 //将当前的subregion框选出来 117 CvRect subregion = cvRect(subregionLeft, subregionTop, (int)subregionWidth, (int)subregionHeight); 118 cvSetImageROI(imageLast, subregion); 119 //在框选出来的subregion中寻找好的特征点 120 cvGoodFeaturesToTrack(imageLast, imageEig, imageTmp, featuresLast + totalFeatureNum, 121 &numToFind, 0.1, 5.0, NULL, 3, 1, 0.04); 122 123 int numFound = 0; 124 for(int k = 0; k < numToFind; k++) { 125 featuresLast[totalFeatureNum + k].x += subregionLeft; 126 featuresLast[totalFeatureNum + k].y += subregionTop; 127 128 int xInd = (featuresLast[totalFeatureNum + k].x + 0.5) / showDSRate; 129 int yInd = (featuresLast[totalFeatureNum + k].y + 0.5) / showDSRate; 130 //查看检测的角点中是否有匹配到的合适的特征点 131 if (((float*)(harrisLast->imageData + harrisLast->widthStep * yInd))[xInd] > 1e-7) { 132 featuresLast[totalFeatureNum + numFound].x = featuresLast[totalFeatureNum + k].x; 133 featuresLast[totalFeatureNum + numFound].y = featuresLast[totalFeatureNum + k].y; 134 featuresInd[totalFeatureNum + numFound] = featuresIndFromStart; 135 136 numFound++; 137 featuresIndFromStart++; 138 } 139 } 140 totalFeatureNum += numFound; 141 subregionFeatureNum[ind] += numFound; 142 143 cvResetImageROI(imageLast); 144 } 145 } 146 } 147 148 cvCalcOpticalFlowPyrLK(imageLast, imageCur, pyrLast, pyrCur, 149 featuresLast, featuresCur, totalFeatureNum, cvSize(winSize, winSize), 150 3, featuresFound, featuresError, 151 cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 30, 0.01), 0); 152 153 for (int i = 0; i < totalSubregionNum; i++) { 154 subregionFeatureNum[i] = 0; 155 } 156 157 ImagePoint point; 158 int featureCount = 0; 159 double meanShiftX = 0, meanShiftY = 0; 160 for (int i = 0; i < totalFeatureNum; i++) { 161 double trackDis = sqrt((featuresLast[i].x - featuresCur[i].x) 162 * (featuresLast[i].x - featuresCur[i].x) 163 + (featuresLast[i].y - featuresCur[i].y) 164 * (featuresLast[i].y - featuresCur[i].y)); 165 166 if (!(trackDis > maxTrackDis || featuresCur[i].x < xBoundary || 167 featuresCur[i].x > imageWidth - xBoundary || featuresCur[i].y < yBoundary || 168 featuresCur[i].y > imageHeight - yBoundary)) { 169 //计算当前特征点是哪个subregion中检测到的,ind是subregion的编号 170 int xInd = (int)((featuresLast[i].x - xBoundary) / subregionWidth); 171 int yInd = (int)((featuresLast[i].y - yBoundary) / subregionHeight); 172 int ind = xSubregionNum * yInd + xInd; 173 174 if (subregionFeatureNum[ind] < maxFeatureNumPerSubregion) { 175 //根据筛选准则将光流法匹配到的特征点进行筛选,这里featureCount是从0开始的, 176 //所以featuresCur[]和featuresLast[]只保存了邻近图像的特征点,很久之前的没有保存 177 featuresCur[featureCount].x = featuresCur[i].x; 178 featuresCur[featureCount].y = featuresCur[i].y; 179 featuresLast[featureCount].x = featuresLast[i].x; 180 featuresLast[featureCount].y = featuresLast[i].y; 181 //有些特征点被筛掉,所以这里featureCount不一定和i相等 182 featuresInd[featureCount] = featuresInd[i]; 183 /* 这一步将图像坐标系下的特征点[u,v],变换到了相机坐标系下,即[u,v]->[X/Z,Y/Z,1],参考《14讲》式5.5 184 * 不过要注意这里加了个负号。相机坐标系默认是z轴向前,x轴向右,y轴向下,图像坐标系默认在图像的左上角, 185 * featuresCur[featureCount].x - kImage[2]先将图像坐标系从左上角还原到图像中心,然后加个负号, 186 * 即将默认相机坐标系的x轴负方向作为正方向,y轴同理。所以此时相机坐标系z轴向前,x轴向左,y轴向上 187 */ 188 point.u = -(featuresCur[featureCount].x - kImage[2]) / kImage[0]; 189 point.v = -(featuresCur[featureCount].y - kImage[5]) / kImage[4]; 190 point.ind = featuresInd[featureCount]; 191 imagePointsCur->push_back(point); 192 193 if (i >= recordFeatureNum) { 194 point.u = -(featuresLast[featureCount].x - kImage[2]) / kImage[0]; 195 point.v = -(featuresLast[featureCount].y - kImage[5]) / kImage[4]; 196 imagePointsLast->push_back(point); 197 } 198 199 meanShiftX += fabs((featuresCur[featureCount].x - featuresLast[featureCount].x) / kImage[0]); 200 meanShiftY += fabs((featuresCur[featureCount].y - featuresLast[featureCount].y) / kImage[4]); 201 202 featureCount++; 203 //subregionFeatureNum是根据当前帧与上一帧的特征点匹配数目来计数的 204 subregionFeatureNum[ind]++; 205 } 206 } 207 } 208 totalFeatureNum = featureCount; 209 meanShiftX /= totalFeatureNum; 210 meanShiftY /= totalFeatureNum; 211 212 sensor_msgs::PointCloud2 imagePointsLast2; 213 pcl::toROSMsg(*imagePointsLast, imagePointsLast2); 214 imagePointsLast2.header.stamp = ros::Time().fromSec(timeLast); 215 imagePointsLastPubPointer->publish(imagePointsLast2); 216 217 //隔两张图像才输出一副图像,如0,1不要,2输出,3,4不要,5输出 218 showCount = (showCount + 1) % (showSkipNum + 1); 219 if (showCount == showSkipNum) { 220 Mat imageShowMat(imageShow); 221 bridge.image = imageShowMat; 222 bridge.encoding = "mono8"; 223 sensor_msgs::Image::Ptr imageShowPointer = bridge.toImageMsg(); 224 imageShowPubPointer->publish(imageShowPointer); 225 } 226 } 227 228 int main(int argc, char** argv) 229 { 230 ros::init(argc, argv, "featureTracking"); 231 ros::NodeHandle nh; 232 233 mapx = cvCreateImage(imgSize, IPL_DEPTH_32F, 1); 234 mapy = cvCreateImage(imgSize, IPL_DEPTH_32F, 1); 235 cvInitUndistortMap(&kMat, &dMat, mapx, mapy); 236 237 CvSize subregionSize = cvSize((int)subregionWidth, (int)subregionHeight); 238 imageEig = cvCreateImage(subregionSize, IPL_DEPTH_32F, 1); 239 imageTmp = cvCreateImage(subregionSize, IPL_DEPTH_32F, 1); 240 241 CvSize pyrSize = cvSize(imageWidth + 8, imageHeight / 3); 242 pyrCur = cvCreateImage(pyrSize, IPL_DEPTH_32F, 1); 243 pyrLast = cvCreateImage(pyrSize, IPL_DEPTH_32F, 1); 244 245 ros::Subscriber imageDataSub = nh.subscribe<sensor_msgs::Image>("/camera/image_raw", 1, imageDataHandler); 246 247 ros::Publisher imagePointsLastPub = nh.advertise<sensor_msgs::PointCloud2> ("/image_points_last", 5); 248 imagePointsLastPubPointer = &imagePointsLastPub; 249 250 ros::Publisher imageShowPub = nh.advertise<sensor_msgs::Image>("/image/show", 1); 251 imageShowPubPointer = &imageShowPub; 252 253 ros::spin(); 254 255 return 0; 256 }