lsd-slam源码解读第二篇:DataStructures
第一篇: lsd-slam源码解读第一篇:Sophus/sophus
在进入具体算法之前,我觉得有必要先明白内部数据是怎样储存的,所以第一篇之后的内容自然是数据结构,这些这些数据包括图像,深度预测,帧的ID,以及变换矩阵等等,如果你不太清楚变换矩阵(包括平移,旋转等)是什么,请看我写的源码解读第一篇的内容
最科学的欣赏源码方式,必然是先看内存管理,即进入文件夹下的第二个文件FrameMemory.h
这个文件只有一个类FrameMemory,接口也不多,如下:
class FrameMemory
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
static FrameMemory& getInstance();
float* getFloatBuffer(unsigned int size);
void* getBuffer(unsigned int sizeInByte);
void returnBuffer(void* buffer);
boost::shared_lock<boost::shared_mutex> activateFrame(Frame* frame);
void deactivateFrame(Frame* frame);
void pruneActiveFrames();
void releaseBuffes();
private:
FrameMemory();
void* allocateBuffer(unsigned int sizeInByte);
boost::mutex accessMutex;
std::unordered_map< void*, unsigned int > bufferSizes;
std::unordered_map< unsigned int, std::vector< void* > > availableBuffers;
boost::mutex activeFramesMutex;
std::list<Frame*> activeFrames;
};
这个类偷偷地管理了每一帧的所有内存,第一个函数是getINstance();
注意到构造函数被私有化,显然这是初始化对象的函数,转到实现
FrameMemory& FrameMemory::getInstance()
{
static FrameMemory theOneAndOnly;
return theOneAndOnly;
}
这个函数实现就两行,你会发现内部维护的是一个static的对象,然后返回这个对象,也就是说,一个进程里面,有且仅有一个FrameMemory的对象
void* FrameMemory::getBuffer(unsigned int sizeInByte)
{
boost::unique_lock<boost::mutex> lock(accessMutex);
if (availableBuffers.count(sizeInByte) > 0)
{
std::vector< void* >& availableOfSize = availableBuffers.at(sizeInByte);
if (availableOfSize.empty())
{
void* buffer = allocateBuffer(sizeInByte);
// assert(buffer != 0);
return buffer;
}
else
{
void* buffer = availableOfSize.back();
availableOfSize.pop_back();
// assert(buffer != 0);
return buffer;
}
}
else
{
void* buffer = allocateBuffer(sizeInByte);
// assert(buffer != 0);
return buffer;
}
}
float* FrameMemory::getFloatBuffer(unsigned int size)
{
return (float*)getBuffer(sizeof(float) * size);
}
void* FrameMemory::allocateBuffer(unsigned int size)
{
void* buffer = Eigen::internal::aligned_malloc(size);
bufferSizes.insert(std::make_pair(buffer, size));
return buffer;
}
这都是类中的函数,明显的是这三个函数是同一组的,因为getFloatBuffer明显调用了getBuffer这个函数
下面详细介绍一下getBuffer
首先进入这个函数的时候,直接调用boost里面的互斥锁,把这段函数里面的这段内存锁上了
这里会用到两个成员变量:
std::unordered_map< void*, unsigned int > bufferSizes;
std::unordered_map< unsigned int, std::vector< void* > > availableBuffers;
从字面上来说,这是两个无序映射
我上c++官网上查了下,实际上就是hash映射(http://www.cplusplus.com/referece/unordered_map/unordered_map/)
判断可用buffer中是否有sizeInByte这个大小的内存,如果有,那么返回1,没有返回0,所以,搜索到会进入if内部,否则进入else内部
进入if内部:首先是获取sizeInByte所对应的value的引用,也就是需要的内存的首地址,之后会判断
- 如果是空的,那么会调用allocateBUffer申请一段内存,注意在allocateBuffer内部调用了Eigen的内存管理函数(底层实际上还是malloc,如果失败会抛出一个throw_std_bad_alloc),之后做一个映射,把buffer的首地址和尺寸映射起来,之后返回buffer的首地址,这样便可以得到一个buffer
- 如果不是空,那么直接得到一个那个尺寸的内存,然后返回
进入else : 如果没有这个尺寸的内存,就调用allocateBuffer申请一段,之后返回
void FrameMemory::releaseBuffes()
{
boost::unique_lock<boost::mutex> lock(accessMutex);
int total = 0;
for(auto p : availableBuffers)
{
if(printMemoryDebugInfo)
printf("deleting %d buffers of size %d!\n", (int)p.second.size(), (int)p.first);
total += p.second.size() * p.first;
for(unsigned int i=0;i<p.second.size();i++)
{
Eigen::internal::aligned_free(p.second[i]);
bufferSizes.erase(p.second[i]);
}
p.second.clear();
}
availableBuffers.clear();
if(printMemoryDebugInfo)
printf("released %.1f MB!\n", total / (1000000.0f));
}
void FrameMemory::returnBuffer(void* buffer)
{
if(buffer==0) return;
boost::unique_lock<boost::mutex> lock(accessMutex);
unsigned int size = bufferSizes.at(buffer);
//printf("returnFloatBuffer(%d)\n", size);
if (availableBuffers.count(size) > 0)
availableBuffers.at(size).push_back(buffer);
else
{
std::vector< void* > availableOfSize;
availableOfSize.push_back(buffer);
availableBuffers.insert(std::make_pair(size, availableOfSize));
}
}
有申请就有释放,以上两个函数是用来释放内存的,returnBuffer只是把内存还回去(放入map中),而真正释放是releaseBuffer,这里就不细讲了
还有三个函数,分别是
boost::shared_lock<boost::shared_mutex> activateFrame(Frame* frame);
void deactivateFrame(Frame* frame);
void pruneActiveFrames();
他们都是对成员std::list
Frame
帧这玩意儿贯穿始终,是slam中最基本的数据结构,我觉得想要理解这个类,应该从类中的结构体Data开始
struct Data
{
int id;
int width[PYRAMID_LEVELS], height[PYRAMID_LEVELS];
Eigen::Matrix3f K[PYRAMID_LEVELS], KInv[PYRAMID_LEVELS];
float fx[PYRAMID_LEVELS], fy[PYRAMID_LEVELS], cx[PYRAMID_LEVELS], cy[PYRAMID_LEVELS];
float fxInv[PYRAMID_LEVELS], fyInv[PYRAMID_LEVELS],