目录
2.3.4 要分清构造图像的大小Sz、构造空图像Mat、填补图像resize、扩充图像MakeBorder的区别!!!
1.ComputePyramid函数作用
根据输入的图片CV::Mat构建图像金字塔图像。
2. 代码解释
2.1 整体代码
void ORBextractor::ComputePyramid(cv::Mat image) { //开始遍历所有的图层,levels是yaml文件里面的 for (int level = 0; level < nlevels; ++level) { //获取本层图像的缩放系数,mvInvScaleFactor[level]是从orbextrator得到的 float scale = mvInvScaleFactor[level]; //计算本层图像的像素尺寸大小 Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale)); //全尺寸图像。包括无效图像区域的大小。将图像进行“补边”,EDGE_THRESHOLD区域外的图像不进行FAST角点检测 Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2); // temp是扩展了边界的图像,是一个构造函数,拷贝了wholeSize的图像 Mat temp(wholeSize, image.type()), masktemp; // mvImagePyramid 刚开始时是个...空的vector<Mat> // 将扩充后的图像拷贝给mvImagePyramid容器 mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height)); // Compute the resized image //计算第0层以上resize后的图像 if( level != 0 ) { //将上一层金字塔图像根据前文设定sz缩放到当前层级 resize(mvImagePyramid[level-1], //输入图像 mvImagePyramid[level], //输出图像 sz, //输出图像的尺寸 0, //水平方向上的缩放系数,留0表示自动计算 0, //垂直方向上的缩放系数,留0表示自动计算 cv::INTER_LINEAR); //图像缩放的差值算法类型,这里的是线性插值算法 //把源图像拷贝到目的图像的中央,四面填充指定的像素。图片如果已经拷贝到中间,只填充边界 //这样做是为了能够正确提取边界的FAST角点 //EDGE_THRESHOLD指的这个边界的宽度,由于这个边界之外的像素不是原图像素而是算法生成出来的,所以不能够在EDGE_THRESHOLD之外提取特征点 copyMakeBorder(mvImagePyramid[level], //源图像 temp, //目标图像(此时其实就已经有大了一圈的尺寸了) EDGE_THRESHOLD, EDGE_THRESHOLD, //top & bottom 需要扩展的border大小 EDGE_THRESHOLD, EDGE_THRESHOLD, //left & right 需要扩展的border大小 BORDER_REFLECT_101+BORDER_ISOLATED); //扩充方式,opencv给出的解释: } else { //对于第0层未缩放图像,直接将图像深拷贝到temp的中间,并且对其周围进行边界扩展。此时temp就是对原图扩展后的图像 copyMakeBorder(image, //这里是原图像 temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, BORDER_REFLECT_101); } } }
2.2 几个图像的区别
for (int level = 0; level < nlevels; ++level) { //获取本层图像的缩放系数,mvInvScaleFactor[level]是从orbextrator得到的 float scale = mvInvScaleFactor[level]; //计算本层图像的像素尺寸大小 Size sz(cvRound((float)image.cols*scale), cvRound((float)image.rows*scale)); //全尺寸图像。包括无效图像区域的大小。将图像进行“补边”,EDGE_THRESHOLD区域外的图像不进行FAST角点检测 Size wholeSize(sz.width + EDGE_THRESHOLD*2, sz.height + EDGE_THRESHOLD*2); // temp是扩展了边界的图像,是一个构造函数,拷贝了wholeSize的图像 Mat temp(wholeSize, image.type()), masktemp; // mvImagePyramid 刚开始时是个...空的vector<Mat> // 把中间区域拷贝给mvImagePyramid容器 mvImagePyramid[level] = temp(Rect(EDGE_THRESHOLD, EDGE_THRESHOLD, sz.width, sz.height));
我们从第0层到第八层确定金字塔的尺寸及图像。
std::vector<float> mvScaleFactor; ///<每层图像的缩放因子 std::vector<float> mvInvScaleFactor; ///<以及每层缩放因子的倒数 std::vector<float> mvLevelSigma2; ///<存储每层的sigma^2,即上面每层图像相对于底层图像缩放倍数的平方 std::vector<float> mvInvLevelSigma2; ///<sigma平方的倒数 ///这个是用来存储图像金字塔的变量,一个元素存储一层图像 std::vector<cv::Mat> mvImagePyramid;
先获取本层的缩放系数,然后构造本层图像像素的尺寸大小sz,这个大小就是传进来的参数图像的大小乘以缩放因子。
然后构造全尺寸图像尺寸大小,全尺寸图像是将上下左右都镶上边框了,对图像进行补边,如下图所示。
再构造空图片temp,用全尺寸大小进行初始化,并将格式设置与输入图片一致,最后一行代码的意思是将temp图片扩充边界大小保存到mvImagePyramid容器中,mvImagePyramid容器保存金字塔各层图像。
2.3 拷贝图像的方式
2.3.1 图像扩充边界的方式
在opencv里面提供。
2.3.2 为什么要扩充图像边界
利用FAST算法在提取特征点时,图像边缘的特征点半径为3的圆无法取到(边界外无像素点),为了解决此问题,我们对图像边界进行填充。
2.3.3 拷贝图像代码解释
// Compute the resized image //计算第0层以上resize后的图像 if( level != 0 ) { //将上一层金字塔图像根据前文设定sz缩放到当前层级 resize(mvImagePyramid[level-1], //输入图像 mvImagePyramid[level], //输出图像 sz, //输出图像的尺寸 0, //水平方向上的缩放系数,留0表示自动计算 0, //垂直方向上的缩放系数,留0表示自动计算 cv::INTER_LINEAR); //图像缩放的差值算法类型,这里的是线性插值算法 //把源图像拷贝到目的图像的中央,四面填充指定的像素。图片如果已经拷贝到中间,只填充边界 //这样做是为了能够正确提取边界的FAST角点 //EDGE_THRESHOLD指的这个边界的宽度,由于这个边界之外的像素不是原图像素而是算法生成出来的,所以不能够在EDGE_THRESHOLD之外提取特征点 copyMakeBorder(mvImagePyramid[level], //源图像 temp, //目标图像(此时其实就已经有大了一圈的尺寸了) EDGE_THRESHOLD, EDGE_THRESHOLD, //top & bottom 需要扩展的border大小 EDGE_THRESHOLD, EDGE_THRESHOLD, //left & right 需要扩展的border大小 BORDER_REFLECT_101+BORDER_ISOLATED); //扩充方式,opencv给出的解释: } else { //对于第0层未缩放图像,直接将图像深拷贝到temp的中间,并且对其周围进行边界扩展。此时temp就是对原图扩展后的图像 copyMakeBorder(image, //这里是原图像 temp, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, EDGE_THRESHOLD, BORDER_REFLECT_101); } }
如果图像是非0层的,将上一层的图像作为输入缩放到本层并存储到mvImagePyramid容器作为本函数的输出,图像大小为本层计算出来的sz(不包含边界的),copyMakeBorder函数把源图像拷贝到目的图像的中央,四面填充指定的像素。图片如果已经拷贝到中间,只填充边界。
如果图像是0层的,只填充边界。