此系列博客是在这篇博客( 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.读取图像,进行光度矫正,添加噪声。
在参数设置时可以指定从那张图片开始,运行到那张图片结束。即lstartlend。根据两个参数将会对两个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(一)相关推荐

  1. 从软件工程角度看大前端技术栈

    从软件工程角度看大前端技术栈 优秀人才不缺工作机会,只缺适合自己的好机会.但是他们往往没有精力从海量机会中找到最适合的那个. 100offer 会对平台上的人才和企业进行严格筛选,让「最好的人才」和「 ...

  2. 从全生命周期管理角度看大数据安全技术研究

    从全生命周期管理角度看大数据安全技术研究 李树栋1,2, 贾焰2, 吴晓波3, 李爱平2, 杨小东4, 赵大伟5 1. 广州大学网络空间先进技术研究院,广东 广州 510006 2. 国防科技大学计算 ...

  3. 张力柯:从技术演变的角度看互联网后台架构(附视频回顾)

    5月25日,互联网架构技术沙龙圆满落幕.本期沙龙特邀请腾讯的技术专家分享关于技术架构.落地实践案例.无服务器云函数架构.海量存储系统架构等话题,从技术角度看架构发展,为开发者们带来丰富的实践经验内容, ...

  4. 一个函数的自白:从函数的角度看编程的方式

    以下内容转载自 https://mp.weixin.qq.com/s?__biz=MzA5MzY4NTQwMA==&mid=2651002566&idx=1&sn=76b652 ...

  5. XLNet 发明者杨植麟:从学习的角度看NLP现状与未来(附PPT下载)

    近年来,基于Transformer的一系列大规模预训练模型不断推进NLP领域前沿,也持续冲击着大众和研究者关于NLP任务的认知.GPT-3在多项任务上取得的泛用能力似乎使人们看到了实现通用人工智能的曙 ...

  6. 【报名】杨植麟 :从学习的角度看NLP的现状与未来

    活动议程 日期:11月26日(周四) 时间 主题 19:00-19:05 主持人介绍 张家俊,中国科学院自动化研究所研究员,智源青年科学家 19:05-20:20 <从学习的角度看NLP的现状与 ...

  7. 从主数据的角度看一个零售ERP系统

    What is an ERP Retail System from a_ master Data Perspective 从主数据的角度看一个零售ERP系统 Retailers have lot of ...

  8. 从前端角度看网页渲染慢的原理及解决方案

    从前端角度看网页渲染慢的原理及解决方案 参考文章: (1)从前端角度看网页渲染慢的原理及解决方案 (2)https://www.cnblogs.com/joyho/articles/4455893.h ...

  9. 从JDK源码角度看Long

    概况 Java的Long类主要的作用就是对基本类型long进行封装,提供了一些处理long类型的方法,比如long到String类型的转换方法或String类型到long类型的转换方法,当然也包含与其 ...

最新文章

  1. 好多Javascript日期选择器呀-7
  2. VTK:Texture之TextureCutSphere
  3. PHP的面向对象 — 封装、继承、多态
  4. SIMD via C#
  5. SQLServer判断循环
  6. python专题-读取xml文件
  7. 工业相机选型:相机接口
  8. win11怎样在多屏中设置主显示器 Windows11主显示器的设置方法
  9. day01 --环境搭建 注册 表单校验
  10. java jpa 字段 关联_jpa查询关联表懒加载数据initialize proxy no session
  11. python2.7使用教程_使用模块 - 廖雪峰 Python 2.7 中文教程
  12. jQuery源码解析
  13. Onvif协议学习:5、设备搜索
  14. IC卡卡面卡号如何写入
  15. L13 操作系统的这棵树
  16. 计算机社团感恩节免费维修周策划书,感恩节活动策划以及活动方案!
  17. vb安装过程中 ntvdm.exe[9696]中发生未处理的win32异常
  18. 盒子模型与内外边距设置
  19. JVM,堆,栈,方法区之间的关系
  20. LDK3读书笔记(第一章:LINUX内核简史)

热门文章

  1. 第十一讲 项目3 买五赠一 买二十赠五 优惠计算
  2. Windows Hyper VBS Virtualization-based Security
  3. viewFlipper的基本实现
  4. 什么是维度表和事实表
  5. 拟凸函数和凸函数的区别
  6. 微信小程序上传文件功能实现
  7. 十只老鼠和一千瓶毒药的故事。。。
  8. 番茄编程助手_如何使用番茄法高效的写算法题?
  9. 鱼眼镜头/全景相机原理/全景相机标定
  10. bcm54xx系列phy光口电口的配置方法