admin 管理员组文章数量: 1086019
数据流角度看DSO(一)
此系列博客是在这篇博客( DSO代码梳理(一) )的基础继续往下分析的,我觉得从数据流的角度可能能够更加理解算法原理。DSO代码梳理(一) 这篇博客主要介绍了DSO运行时候需要指定的参数。
运行前的准备
1.参数设置
parseArgument(argv[i]);
//等式右边变量是运行软件时指定的参数,等式左边是在程序里面定义的变量
source=files; //图像文件路径
calib=calib; ///相机内参文件路径
vignette=vignette; ///图像渐晕文件路径
gammaCalib=gamma; ///相机响应函数
2.文件读取
ImageFolderReader* reader = new ImageFolderReader(source,calib, gammaCalib, vignette);
this->path = path; ///图像
this->calibfile = calibFile; //相机内参
2.1 图像文件读取: 将图像名称存储到vector<string>files
里面,根据图像是否为压缩文件,采用两种方法读取。
图像为 .zip 文件
ziparchive = zip_open(path.c_str(), ZIP_RDONLY, &ziperror);
const char* name = zip_get_name(ziparchive, k, ZIP_FL_ENC_STRICT);
files.push_back(name); ///存储图像名称的vector
/ 图像未压缩
getdir (path, files);
2.2 矫正文件读取:
undistort = Undistort::getUndistorterForFile(calibFile, gammaFile, vignetteFile);
Undistort* Undistort::getUndistorterForFile(std::string configFilename, std::string gammaFilename, std::string vignetteFilename)
2.2.1 相机内参:支持的相机模型有 RadTan,PINHOLE,ATAN等,然后根据相机模型选择对应的Undistort
函数。
以EuRoc为例简要介绍:
u = new UndistortRadTan(configFilename.c_str(), true); readFromFile(configFileName, 8);
读取第一行的数据到:parsOrg[0]~parsOrg[7]
(相机内参以及畸变矫正变量),读取第二行的数据到:wOrg,hOrg
(图像原始大小),读取第三行数据判断是否对图片进行裁剪操作,读取第四行数据到:w,h
(图像输出大小)。然后根据读取数据计算内参矩阵K,注意如果对图像进行了裁剪,那么内参矩阵K需要重新计算(不能简单利用parsOrg[0]~parsOrg[3]
)。
2.2.2 光度矫正
u->loadPhotometricCalibration(gammaFilename,"",vignetteFilename);
photometricUndist = new PhotometricUndistorter(file, noiseImage, vignetteImage,getOriginalSize()[0], getOriginalSize()[1]);
读取pcalib.txt 文件:std::vector<float> Gvec = std::vector<float>( std::istream_iterator<float>(l1i), std::istream_iterator<float>() );
读取的文件存储到 G[i]
里面,并且数据需要满足严格单调递增。然后对其进行以下转化:使数据在0-255之间。
for(int i=0;i<GDepth;i++) G[i] = 255.0 * (G[i] - min) / (max-min);
读取vignette.png:采用了16位和8位的两种读取方法。
MinimalImage<unsigned short>* vm16 = IOWrap::readImageBW_16U(vignetteImage.c_str());MinimalImageB* vm8 = IOWrap::readImageBW_8U(vignetteImage.c_str());
并对结果进行了归一化处理,并且计算了inv。
///归一化处理
vignetteMap[i] = vm16->at(i) / maxV;
vignetteMap[i] = vm8->at(i) / maxV;
vignetteMapInv[i] = 1.0f / vignetteMap[i]; ///求inv
读取时间戳 times.txt文件:loadTimestamps();
若未给定曝光时间,则默认为0;并且设置exposureGood=false;
timestamps.push_back(stamp); ///时间戳
exposures.push_back(exposure); ///曝光时间
3.setGlobalCalibration(): 建立各层图像金字塔大小,并计算各层图像内参矩阵。
reader->setGlobalCalibration();getCalibMono(K, w_out, h_out); ///获取前面根据相机内参文件建立的内参矩阵setGlobalCalib(w_out, h_out, K); ///建立金字塔,各层金字塔之间的比例为2,并且计算各层金字塔的内参
根据图像输出大小确定金字塔层数,原始图像为第0层,最高层为pyrLevelsUsed-1
层。注意:最高层图像的高和宽要大于100,并且pyrLevelsUsed要大于等于3。
各层金字塔图像之间的内参计算关系为:
for (int level = 1; level < pyrLevelsUsed; ++ level){wG[level] = w >> level;hG[level] = h >> level;///各层内参传递关系fxG[level] = fxG[level-1] * 0.5;fyG[level] = fyG[level-1] * 0.5;cxG[level] = (cxG[0] + 0.5) / ((int)1<<level) - 0.5;cyG[level] = (cyG[0] + 0.5) / ((int)1<<level) - 0.5;KG[level] << fxG[level], 0.0, cxG[level], 0.0, fyG[level], cyG[level], 0.0, 0.0, 1.0; // syntheticKiG[level] = KG[level].inverse();fxiG[level] = KiG[level](0,0);fyiG[level] = KiG[level](1,1);cxiG[level] = KiG[level](0,2);cyiG[level] = KiG[level](1,2);}
4.整个系统初始化:FullSystem* fullSystem = new FullSystem();
比较重要的类的初始化:coarseDistanceMap = new CoarseDistanceMap(wG[0], hG[0]);coarseTracker = new CoarseTracker(wG[0], hG[0]);coarseTracker_forNewKF = new CoarseTracker(wG[0], hG[0]);coarseInitializer = new CoarseInitializer(wG[0], hG[0]);pixelSelector = new PixelSelector(wG[0], hG[0]);ef = new EnergyFunctional();
///以及一些变量的初始值statistics_lastNumOptIts=0;statistics_numDroppedPoints=0;statistics_numActivatedPoints=0;statistics_numCreatedPoints=0;statistics_numForceDroppedResBwd = 0;statistics_numForceDroppedResFwd = 0;statistics_numMargResFwd = 0;statistics_numMargResBwd = 0;isLost=false;initFailed=false;
5.setGammaFunction(): 将经转换后的pcalib.txt 文件的数据G[i]
进行一个运算后赋值给Hcalib.B[i]
。
fullSystem->setGammaFunction(reader->getPhotometricGamma());
/ reader->getPhotometricGamma()获取的是经转换之后的 pcalib.txt 文件的数据,G[i].
获取G[i]
之后的运算为:其中BInv[]
为G[]
。
for(int i=1;i<255;i++){for(int s=1;s<255;s++){if(BInv[s] <= i && BInv[s+1] >= i){Hcalib.B[i] = s+(i - BInv[s]) / (BInv[s+1]-BInv[s]);break;}}}Hcalib.B[0] = 0;Hcalib.B[255] = 255;
6.读取图像,进行光度矫正,添加噪声。
在参数设置时可以指定从那张图片开始,运行到那张图片结束。即lstart
,lend
。根据两个参数将会对两个vector:std::vector<int> idsToPlay; std::vector<double> timesToPlayAt;
进行push_back();根据preload
变量是否预加载图像,预加载的图像存储在std::vector<ImageAndExposure*> preloadedImages;
。
图像读取:通过函数reader->getImage(i);
实现。
///预加载:
preloadedImages.push_back(reader->getImage(i));
img = preloadedImages[ii];
//未预加载:
img = reader->getImage(i);
reader->getImage(i);
函数解析
ImageAndExposure* getImage(int id, bool forceLoadDirectly=false)
{return getImage_internal(id, 0);
}
MinimalImageB* minimg = getImageRaw_internal(id, 0); ///获取原始图像
去畸变
ImageAndExposure* ret2 = undistort->undistort<unsigned char>(minimg,(exposures.size() == 0 ? 1.0f : exposures[id]),(timestamps.size() == 0 ? 0.0 : timestamps[id]));
在undistort函数里面有:photometricUndist->processFrame<T>(image_raw->data, exposure, factor);
执行此函数进行光度校准。执行完之后,photometricUndist->output
存储的是光度校准之后的图像信息,还包括时间戳以及曝光时间。
void PhotometricUndistorter::processFrame(T* image_in, float exposure_time, float factor)data[i] = G[image_in[i]]; ///apply inv.Responsedata[i] *= vignetteMapInv[i]; ///remove V
注意:此处会根据变量:setting_photometricCalibration选择不同的校准形式:
0 = nothing.
1 = apply inv. response.
2 = apply inv. response & remove V.
再进行光度校准之后,会人为的添加噪声,(这里原因是什么我还不太清楚,如果有人知道,麻烦告知我一下,谢谢。)
applyBlurNoise(result->image);
7.读取图像之后,运行前的准备已经完成,开始对每一帧图像进行处理。
图像帧处理入口:
fullSystem->addActiveFrame(img, i);
本文标签: 数据流角度看DSO(一)
版权声明:本文标题:数据流角度看DSO(一) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1686730882a30374.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论