mono_kitii.cc
1. 이미지 불러오기
2. SLAM system 생성 (MONOCULAR)
- ORB_SLAM2::System SLAM() → <System.cc>
3. main loop (이미지 수만큼 반복)
- imread
- 현재 시간 t1 기록
- SLAM.TrackMonocular() → <System.cc>
- 현재 시간 t2 기록
- 다음 frame 기다림
4. SLAM.Shutdown() → <System.cc>
5. KeyFrameTrajectory.txt 파일 저장
- SLAM.SaveKeyFrameTrajectoryTUM() → <System.cc>
System.cc
System 생성 System::System()
1. 생성시 멤버변수 초기화
- mSensor : MONOCULAR
- mbReset : false
- mbActivateLocalizationMode : false
- mbDeactivateLocalizationMode : false
2. ORB Vocabulary 불러오기
- 기존의
Vocabulary/ORBvoc.txt
파일을 불러온다
- mpVocabulary = new ORBVocabulary();
3. KeyFrame Database 생성
- mpKeyFrameDatabase = new KeyFrameDatabase(*mpVocabulary);
4. Map 생성
5. tracking thread 초기화
- mpTracker = new Tracking() → <Tracking.cc>
6. Local Mapping thread
- mpLocalMapper = new LocalMapping() → <LocalMapping.cc>
7. Loop Closing thread
- mpLoopCloser = new LoopClosing() → <LoopClosing.cc>
8. thread 간의 set pointers
- mpTracker, mpLocalMapper, mpLoopCloser를 서로 연결해준다
TrackMonocular()
unique_lock
unique_lock<mutex> lock(mMutexMode);
- {} 또는 함수가 끝날 때 자동으로 unlock 된다
- 하나의 thread만 접근할 수 있도록 해준다
- mutex : thread들의 동기화를 해줄 수 있게 하는 기능
1. check mode change
- 처음에는 mbActivateLocalizationMode와 mbDeactivateLocalizationMode가 false이기 때문에 진행하지 않는다
- localmapping이 진행중이라면 끝내고 진행
2. check reset
3. tracking 시작
- GrabImageMonocular() → <Tracking.cc>
- mTrackingState : NO_IMAGES_YET, NOT_INITIALIZED 중의 상태
- mTrackedMapPoints : MapPoints
- mTrackedKeyPointsUn : undistorted keypoint 벡터
Tracking.cc
Tracking 생성 Tracking::Tracking()
1. 생성시 멤버변수 초기화
- mState(NO_IMAGES_YET)
- mSensor(sensor)
- mbOnlyTracking(false)
- mbVO(false)
- mpORBVocabulary(pVoc)
- mpKeyFrameDB(pKFDB)
- mpInitializer(static_cast<Initializer*>(NULL))
- mpSystem(pSys)
- mpViewer(NULL)
- mpFrameDrawer(pFrameDrawer)
- mpMapDrawer(pMapDrawer)
- mpMap(pMap)
- mnLastRelocFrameId(0)
2. 카메라 파라미터 설정
- K matix 생성 : intrinsic matrix
- DistCoef 생성 : [0,0,0,0], vector of distortion coefficients
- mbf = 0.0
- fps = 10.0
- mMinFrames = 0, mMaxFrames = fps
3. ORB 파라미터 설정
- nFeatures = 2000 : frame에서 추출하면 feature의 수
- fScaleFactor = 1.2 : scale 피라미드 factor
- nLevels = 8 : 피라미드의 level 수
- fIniThFAST = 20 : grid로 나눈 후 한 cell에서 검출되어야하는 corner의 최소 개수 (초기 임계값)
- fMinThFAST = 7 : corner가 검출되지 않는다면 사용할 더 낮은 thresdhold
FAST 알고리즘 : grid를 나누고 각 grid 안에서 FAST를 사용하여 corner point 검출
- mpORBextractorLeft = new ORBextractor() → <ORBextractor.cc>
- mpIniORBextractor = new ORBextractor() → <ORBextractor.cc>
frame feature 계산 및 tracking 시작 GrabImageMonocular()
1. gray 이미지로 변환
2. mCurrentFrame 지정
- frame 생성 Frame() → <Frame.cc>
- mState가 NOT_INITIALIZED 또는 NO_IMAGES_YET 인 경우
- 그 외의 경우
- Frame이 생성되면 keypoint, descriptor가 계산된 상태
- keypoint는 undistored 상태
- 이미지가 grid로 나뉘고 그 해당되는 grid에 keypoint들이 분류되어 있는 상태
3. tracking 수행
- Track() → <Tracking.cc>의 Tracking::Track()
4. camera pose 반환
tracking 수행 Tracking::Track()
1. mState 확인
- 맨 처음에는 NO_IMAGES_YET 상태
- NOT_INITIALIZED로 변경 후 mLastProcessedState에 저장
2. if) NOT_INITIALIZED 상태이면
- MonocularInitialization() 수행 → <Tracking.cc>의 Tracking::MonocularInitialization()
- frameDrawer update
- mState가 OK가 아니면 return
3. else) tracking 진행
initialization Tracking::MonocularInitialization()
1. if) 맨 처음 mpInitializer가 없을 때
- mCurrentFrame의 keypoint 수가 100개 보다 많을 때
- reference frame 설정
- mInitialFrame, mLastFrame를 mCurrentFrame로 설정
- mvbPrevMatched에 mCurrentFrame의 keypoint 복사
- mpInitializer 생성
- 즉, 현재 이미지인 mCurrentFrame를 reference 이미지로 지정
2. else) mpInitializer가 있을 때
2-1. 초기화
- if) mCurrentFrame의 keypoint가 100개 이하이면
- mpInitializer 삭제 후 return
2-2. correspondences 찾기
- 현재 frame과 reference frame 간의 correspondence 찾기
- orbmatcher 수행
- matcher.SearchForInitialization() → <ORBmatcher.cc>의 ORBmatcher::SearchForInitialization()
1. 생성시 멤버변수 초기화
- nFeatures = 2000
- scaleFactor = 1.2
- nLevels = 8
- fIniThFAST = 20
- fMinThFAST = 7
2. 이미지 pyramid 준비
- mvScaleFactor=[1.0,1.2,1.44,....] : 계속 scaleFactor를 곱하여 원소 nLevels개의 vector
- mvLevelSigma2=[1.0,mvScaleFactor[1]2,mvScaleFactor[2]2,....] : mvScaleFactor의 제곱 vector (원소 nLevels개)
- mvInvScaleFactor : mvScaleFactor의 역수
- mvInvLevelSigma2 : mvLevelSigma2의 역수
- mvImagePyramid
- mnFeaturesPerLevel : 각 레벨에서 원하는 특징점 수를 설정 (레벨이 증가할수록 특징점 수 감소)
- sumFeatures : 모든 level에서의 특징점 수의 합
- nfeatures - sumFeatures : 마지막 레벨에서의 특징점 수 결정
3. descriptor를 위한 pattern 불러오기
- 미리 정해져있는
bit_pattern_31_
에서 point pair 위치를 가져와서 pattern에 복사
- npoints(512개) 즉, 128 * 4(x1,y1,x2,y2) 총 128쌍을 가져온다
BRIEF로 descriptor 계산 방법
bitpattern_31
- 낮은 상관도와 높은 분산을 가진 sampling pair를 원한다
- 낮은 상관도 : 새로운 pair가 새로운 정보를 제공
- 높은 분산 : feature 분별이 뚜렷
- sampling 쌍을 학습하여 좋은 256개의 pair를 남긴다
- 그래서 bitpattern_31는 256개의 pair (256*4)개로 표현된다
4. orient 계산을 위한 준비
- umax : circular patch의 row 끝 지점들을 의미(길이를 의미)
- HALF_PATCH_SIZE는 15인데 그 결과 예를 들면 umax = [15, 15, 15, 15, 14, 14, 14, 13, .... , 1, 0] 이런식으로 된다
기존 FAST는 방향을 계산하지 않는다. rotation invarient를 위해 intensity centroid방법을 사용해서 방향을 계산한다
1. 피라미드 계산
- ComputePyramid(image) → <ORBextractor.cc>의 ORBextractor::ComputePyramid()
2. keypoint 구하기
- ComputeKeyPointsOctTree(allKeypoints); → <ORBextractor.cc>의 ORBextractor::ComputeKeyPointsOctTree()
- 그 결과 allKeypoints : pyramid level별로 keypoint들이 담겨있다
3. descriptors 초기화 및 생성
- nkeypoints : keypoints의 개수
- nkeypoints에 맞게 descriptors, keypoints 초기화
4. descriptors 구하기
- GaussianBlur
- image pyramid에서 resize된 이미지(mvImagePyramid[level])에 블러 처리
- computeDescriptors() → <ORBextractor.cc>의 computeDescriptors()
5. 최종 keypoint 추가
- keypoint를 scale에 맞게 조정
- keypoints vector에 계산한 keypoint 추가
- nlevels(8)만큼 아래의 과정 반복
- 결과 mvImagePyramid에 resize된 이미지가 담긴다
1. 이미지 사이즈 계산
- 원하는 이미지 사이즈에 맞게 col, row 조정
- 반복을 진행할 수록 이미지 사이즈가 줄어든다 (1.2배(mvInvScaleFactor)씩 줄어든다)
- 원하는 이미지 피라미드 크기에 EDGE_THRESHOLD를 더해서 조금 더 크게 이미지 사이즈 만들기
2. 이미지 resize 수행
- 지정한 이미지 사이즈로 이미지 resize() 수행
- level 0일때는 수행하지 않는다
3. 이미지 가장자리 추가
- opencv의 copyMakeBorder 함수 사용
- 위, 아래, 왼쪽, 오른쪽 가장자리를 EDGE_THRESHOLD 정도 비우고 안에 이미지 복사
- borderType은
BORDER_REFLECT_101
으로 가장자리를 반대로 이어붙인다
- 예시 : gfedcb|abcdefg|fedcba
1. cell 설정
- W = 30 : cell의 개수
- nCols : col 개수
- nRows : row 개수
- wCell : cell 하나의 width
- hCell : cell 하나의 height
2. FAST (keypoint 추출)
- 모든 cell을 반복하며 FAST 알고리즘 진행
- vToDistributeKeys : 모든 cell 즉, 전체 이미지에서 추출된 모든 keypoint가 들어있다
- iniThFAST(20)을 threshold로 사용하여 keypoint 추출
- 만약 keypoint가 검출되지 않는다면, minThFAST(7)로 threshold를 낮춰서 FAST 수행
- 추출한 keypoint는 cell 안에 있는 값이므로 이미지에 맞게 (border 제외한 내부의 이미지) x, y값 조정 후 vToDistributeKeys 벡터에 넣는다
- 이때의 x, y값은 border를 제외한 이미지 부분에 위치해있다
- 더 정확히 말하면 border=19, 여기서 사용되는 minBorder=16로 위, 아래, 양옆에 적용된다
3. keypoints 후 작업
- vToDistributeKeys값을 가지고 DistributeOctTree() 수행 → <ORBextractor.cc>의 ORBextractor::DistributeOctTree()
- DistributeOctTree() 수행 결과 keypoint가 추출
- 수행 결과 전체 이미지에 맞게 keypoint의 x, y값 조정
4. orientation 계산
- nlevels(8)만큼 반복
- computeOrientation() → <ORBextractor.cc>의 computeOrientation()
- return vResultKeys
- 각 node마다 가장 좋은 keypoint 선택하여 return
1. 변수 생성
- nIni : 초기 node의 개수 (가로길이 / 세로길이)
- hX : node의 가로 길이 (가로길이 / nIni)
- lNodes : ExtractorNode 리스트
- vpIniNodes : nIni 크기의 초기 ExtractorNode 벡터
ExtractorNode 클래스
- ExtractorNode 클래스는 옥타곤 트리 구조의 노드를 나타내며, 해당 노드에 속하는 키포인트들을 저장하고 분할하는 등의 역할을 수행
- vKeys : 해당 node에 속하는 keypoint 저장 벡터
- UL, UR, BL, BR: 노드의 네 꼭짓점인 상단 왼쪽(UL), 상단 오른쪽(UR), 하단 왼쪽(BL), 하단 오른쪽(BR) 점의 좌표
- lit: 해당 노드가 속한 리스트(ExtractOctTree 클래스에서 사용되는 lNodes 리스트)에서의 반복자(iterator)
- bNoMore: 노드가 더 이상 분할될 수 없는지 여부를 나타내는 플래그, 처음은 false로 지정된다
- DivideNode() : 노드를 분할하여 새로운 4개의 하위 노드들을 생성하는 역할, 이를 통해 keypoint를 분할하여 관리할 수 있다
2. node 설정 (nIni번 반복)
- ExtractorNode ni는 UL(왼쪽 위), UR(오른쪽 위), BL(왼쪽 아래), BR(오른쪽 아래)로 구성되어 있는데 이 범위를 정해준다
- vKeys를 vToDistributeKeys 크기로 지정
- lNodes에 ni 추가
- vpIniNodes[i]를 최근에 추가된 node로 설정
3. node에 keypoint 할당 (vToDistributeKeys.size()번 반복)
- vToDistributeKeys의 keypoint의 x좌표를 통해 어떤 node에 해당하는지 분류
- 해당 node의 vKeys에 keypoint를 추가한다
4. node 정리 (lNodes만큼 반복)
- node의 vKeys의 개수가 1개이면 bNoMore=true
- vKeys가 없다면 node 지우기
5. node를 하위 node로 분할 (bFinish가 true가 될때까지 반복)
- 하위 node들로 분할하기(INodes.end()일때까지 반복)
- if) node가 하나의 keypoint만 가지고 있다면 다음 node로
- else)
- DivideNode() 수행하여 4개의 하위 노드로 분할 → <ORBextractor.cc>의 ExtractorNode::DivideNode()
- 하위 노드들 중에 keypoint가 1개보다 많다면 vSizeAndPointerToNode에 keypoint 수와 해당 node 추가
- 자식 node를 다 추가한 후에는 현재 node 제거
- if) node의 개수가 feature의 수보다 많거나 모든 node가 하나의 point를 가지고 있으면 bFinish=true로 반복문 탈출
- else) node의 개수가 많으면 while(!bFinish) 반복
- vSizeAndPointerToNode에 있는 node들을 분할
- 원소의 개수가 많은 node부터 다시 DivideNode() 수행 (1, 2와 같은 방법으로 수행)
6. 각각의 node에서 best point 선택
- vResultKeys에 각각 node 중에 가장 response가 높은 keypoint 선택
- UL, UR, DL, DR로 분할
- 해당 위치에 해당하는 keypoint도 분할
- 나누어진 node의 keypoint 수가 1개이면 bNoMore = true
keypoint마다의 angle 계산computeOrientation()
- keypoint별로 angle 계산 IC_Angle() → <ORBextractor.cc>의 IC_Angle()
angle 계산 IC_Angle()
- m_01 : y축 방향의 gradient 계산
- m_10 : x축 방향(가로)의 gradient 계산
- gradient를 계산할 때
거리 x 밝기값
으로 계산
- center의 좌표가 (100,100)이라고 할 때, center[-3]은 (100, 97)을 의미
- step은 다음 row로 넘어가는 것 의미
- 결과는 m_01과 m_10의 즉, x와 y 방향의 각도가 나온다
descriptors 구하기 computeDescriptors()
- descriptors 크기 : (keypoints.size(), 32)
- keypoint 마다 computeOrbDescriptor() 수행 → <ORBextractor.cc>의 computeOrbDescriptor()
- 이 결과 keypoint마다의 descriptor가 계산된다
실제 ORB descriptors 계산 computeOrbDescriptor()
- 기존의 정해진 pattern을 활용하여 32bit의 descriptor 계산
- 두 점의 좌표를 비교해가며 계산
|=
는 OR 연산 수행
- 결과로 32크기의 descriptor 계산
Frame.cc
frame 생성 Frame::Frame()
- mpORBvocabulary(voc)
- mpORBextractorLeft(extractor)
- mpORBextractorRight(static_cast<ORBextractor*>(NULL))
- mTimeStamp(timeStamp)
- mK(K.clone())
- mDistCoef(distCoef.clone())
- mbf(bf)
- mThDepth(thDepth)
1. 기본 설정
- mnId : frame ID
- image pyramid 관련 값 가져오기
- ExtractORB() → <Frame.cc>
- ExtractORB(0,imGray);
- 결과 : keypoint, descriptor 계산
3. 왜곡 보정 및 calibration 수행
- mvKeys : Vector of keypoints
- UndistortKeyPoints() → <Frame.cc>의 Frame::UndistortKeyPoints()
4. 이미지 bound 설정(처음 한번만 수행)
- ComputeImageBounds() → <Frame.cc>의 Frame::ComputeImageBounds()
- 그 결과 이미지의 최소, 최대 x,y 좌표가 구해진다
- grid의 width와 height 계산
- mfGridElementWidthInv : grid의 witdh의 inv
- mfGridElementHeightInv : grid의 height의 inv
- mbInitialComputations=false로 변경
5. keypoint를 grid로 분류
- mb : Stereo baseline
- AssignFeaturesToGrid() → <Frame.cc>의 Frame::AssignFeaturesToGrid()
- ORB 추출
- mono이기 때문에 flag가 0이고 mvKeys,mDescriptors를 계산한다
- (*mpORBextractorLeft)(im,cv::Mat(),mvKeys,mDescriptors); → <ORBextractor.cc>의 operator()
calibration & 왜곡 보정 Frame::UndistortKeyPoints()
- if) 왜곡이 없으면 mvKeys 그대로
- keypoints의 x, y 좌표를 mat matrix 변수로 설정
- Undistort 수행
- opencv의 undistortPoints() 이용
- 미리 설정되어있는 K, DistCoef를 활용하여 x, y좌표 왜곡 보정
- 즉, 왜곡과 calibration 수행
- 왜곡 보정된 x,y 좌표를 mvKeysUn에 저장
이미지 테두리 지정 Frame::ComputeImageBounds()
- if) 왜곡이 있으면 이미지의 4 꼭지점의 왜곡을 보정
- opencv의 undistortPoints() 이용
- else) 그냥 이미지의 4 꼭지점 이용
keypoint를 grid로 분류 Frame::AssignFeaturesToGrid()
- grid의 위치에 따라 keypoint를 할당
- PosInGrid()로 keypoint가 해당 grid에 포함되는지 확인 → <Frame.cc>의 Frame::PosInGrid()
grid에 keypoint가 존재하는지 확인 Frame::PosInGrid()
- undistored keypoint가 해당 범위안에 존재하는지 확인
ORBmatcher.cc
nmatches 계산 ORBmatcher::SearchForInitialization()
- F1 : reference frame
- F2 : 현재 frame
1. 두 frame의 descriptor간의 거리 계산
- reference의 keypoint 수 만큼 반복
- F2 특정 영역의 feature의 index 계산
- GetFeaturesInArea()
- 계산 결과 : vIndices2
- d1 : F1의 descriptor
- vIndices2 만큼 반복
- d2 : F2의 descriptor
- dist : 두 descriptor 사이 거리 계산
- DescriptorDistance(d1, d2) → <ORBmatcher.cc>의 ORBmatcher::DescriptorDistance()
- 가장 짧은 2개의 dist를 bestDist, bestDist2에 저장
- 가장 짧은 distance일때의 F1, F2 인덱스를 서로 저장
- vMatchedDistance에 bestDist 저장 후 nmatches 증가
- F1과 F2의 angle 차이를 계산하여 그 구간의 rotHist에 인덱스 저장
- rotHist : 회전 불변성 강화 , 회전 히스토그램은 특징점의 회전에 대한 일치 여부를 판단하고, 일치하는 특징점들을 선택하는 데 활용
2. rotHist에서 가장 큰 3개의 index 구하기
- mbCheckOrientation라면
- ComputeThreeMaxima() → <ORBmatcher.cc>의 ORBmatcher::ComputeThreeMaxima()
3. 매칭 결과 정확도 향상 + 잘못된 매칭 제거
- rotHist 배열에서 큰 3개의 값에 해당하는 경우가 아니면 해당 인덱스의 매칭을 제거하고 nmatches 값 감소
4. 이전 match 업데이트
- F2에서 매칭된 좌표들을 가져와서 mvbPrevMatched 업데이트
- 이후의 특징점 매칭 과정에서 이전에 매칭된 특징점들을 참조할수있다
히스토그램에서 가장 큰 3개의 값 구하기 ORBmatcher::ComputeThreeMaxima()
- 히스토그램에서 가장 큰 세 개의 값과 해당 값들의 인덱스를 계산하는 역할
LocalMapping.cc
LocalMapping 생성LocalMapping::LocalMapping()
생성시 멤버변수 초기화
- mbMonocular(bMonocular)
- mbResetRequested(false)
- mbFinishRequested(false)
- mbFinished(true)
- mpMap(pMap)
- mbAbortBA(false)
- mbStopped(false)
- mbStopRequested(false)
- mbNotStop(false)
- mbAcceptKeyFrames(true)
LoopClosing.cc
LoopClosing 생성 LoopClosing::LoopClosing()
생성시 멤버변수 초기화
- mbResetRequested(false)
- mbFinishRequested(false)
- mbFinished(true)
- mpMap(pMap)
- mpKeyFrameDB(pDB)
- mpORBVocabulary(pVoc)
- mpMatchedKF(NULL)
- mLastLoopKFid(0)
- mbRunningGBA(false)
- mbFinishedGBA(true)
- mbStopGBA(false)
- mpThreadGBA(NULL)
- mbFixScale(bFixScale)
- mnFullBAIdx(0)
- mnCovisibilityConsistencyTh = 3