본 포스트에서는 optical flow와 direct method의 개념을 리뷰한다. direct method 관련 내용은 [4]에서 대부분의 내용을 참고했다.
Optical flow

Optical flow는 시간 경과에 따른 이미지 간 픽셀 이동을 계산하는 알고리즘을 말한다. Opticalflow는 이미지의 밝기 변화를 기반으로 알고리즘을 계산하기 때문에 다음과 같은 key assumption들이 존재한다
1. 밝기 항상성 (brightness consistency) : 어떤 물체의 픽셀은 프레임이 바뀌어도 그 값이 변하지 않아야 한다. gray 영상의 경우 추적하고 있는 물체의 픽셀 밝기는 변하지 않는다고 가정한다.
2. 작은 움직임 (small motion) : 영상 내에서 움직임은 그다지 빠르지 않다고 가정한다. 즉, 영상 내에서 물체의 움직임보다 시간의 변화가 더 빠르게 진행된다.
3. 공간 일관성 (spatial coherence) : 공간적으로 서로 인접하는 점들은 동일한 객체에 속할 가능성이 높고 동일한 움직임을 갖는다고 가정한다.
Formular derivation
위와 같이 영상 내의 한 점 (x,y)가 있을 때 미소시간(dt)동안 이동한 점의 밝기(intensity)는 변하지 않는다.
위 식에 1차 테일러 전개를 적용함으로써 아래와 같은 식을 유도할 수 있다.
위 두번째 공식에서 양변을 dt로 나누고 정리해준다.
위 공식에서 픽셀이 어느방향
-
-
-
-
-
다시 행렬로 표현하면 다음과 같다.
LK optical flow

(
위 식은
최종적으로 least square의 정규방정식을 통하여 최적의 근사해
-
-
위 식을 통해 근사해
-
-
Direct method
Direct method는 밝기 변화를 통해 photometric error를 최소화하는 카메라의 포즈를 계산한다. Direct method도 opticalflow와 동일하게 이미지의 밝기 변화를 기반으로 알고리즘을 계산하기 때문에 다음과 같은 key assumption들이 존재한다
1. 밝기 항상성 (brightness consistency) : 어떤 물체의 픽셀은 프레임이 바뀌어도 그 값이 변하지 않아야 한다. gray 영상의 경우 추적하고 있는 물체의 픽셀 밝기는 변하지 않는다고 가정한다.
2. 작은 움직임 (small motion) : 영상 내에서 움직임은 그다지 빠르지 않다고 가정한다. 즉, 영상 내에서 물체의 움직임보다 시간의 변화가 더 빠르게 진행된다.
3. 공간 일관성 (spatial coherence) : 공간적으로 서로 인접하는 점들은 동일한 객체에 속할 가능성이 높고 동일한 움직임을 갖는다고 가정한다.
Visual odmetry (VO)의 종류 중 하나인 feature-based와 비교했을 때 direct method의 장단점은 다음과 같다.
Pros
1. 이미지의 모든 정보를 사용 가능
2. keypoint, descriptor 계산 시간 생략
3. feature matching이 힘든 경우에도 사용 가능 (low texture 환경)
4. semi-dense 또는 dense 매핑 가능
Cons
1. gradient에 전적으로 의존하는 목적 함수
2. gray scale 값이 불변한다는 강력한 가정
Photometric error
NOMENCLATURE of photometric error
- 이미지 평면에 프로젝션하기 위해 3차원 공간 상의 점
를 non-homogeneous 변환한 점
- 이미지 평면에 프로젝션하기 위해 3차원 공간 상의 점
- 렌즈 왜곡을 보정한 후 이미지 평면에 프로젝션한 점. 만약 왜곡 보정이 입력 단계에서 이미 수행된 경우
이 된다.
- 렌즈 왜곡을 보정한 후 이미지 평면에 프로젝션한 점. 만약 왜곡 보정이 입력 단계에서 이미 수행된 경우
: 카메라 내부(intrinsic) 파라미터 : 로 프로젝션하기 위해 내부 파라미터의 마지막 행을 생략했다. : 이미지 내의 모든 특징점들의 집합 : 일반적으로 표기를 생략하여 간단하게 나타내기도 한다. : 첫번째 이미지와 두번째 이미지에서 i번째 특징점의 픽셀 좌표 : 두 SE(3) 군을 결합(composition)하는 연산자 : Transformation, 3차원 점 를 카메라 좌표계로 변환, : 3차원 각속도와 속도로 구성된 벡터. twist라고 불린다. : hat 연산자가 적용된 twist의 lie algebra (4x4 행렬)

위 그림에서 3차원 점

direct method의 특징 중 하나는 feature-based와 달리 어떤
photometric 에러는 grayscale 불변성 가정에 기반하며 스칼라 값을 가진다. photometric 에러를 통해 non-linear least squares를 풀기 위해 다음과 같은 cost function
엄밀하게 말하면 상태 증분량
이는 1차 테일러 근사를 통해 다음과 같이 표현이 가능하다.
이를 미분하여 최적의 증분량
Jacobian of the photometric error
(
이를 자세히 풀어서 보면 다음과 같다.
자코비안
자세한 자코비안의 유도 과정은 [SLAM] Errors and Jacobian Derivations for SLAM 정리 포스트를 참조하면 된다.
Coarse-to-fine scheme
본 섹션에서는 direct method에서 일반적으로 성능 개선을 위해 사용하는 coarse-to-fine 방법에 대해 설명한다. Coarse-to-fine 방법이란 이미지 피라미드를 사용하여 높은 피라미드에서 대략(coarse)적인 카메라 움직임 계산한 후 점차적으로 낮은 피라미드로 낮춰가면서 정밀(fine)한 카메라 움직임 계산까지 진행하는 알고리즘을 의미한다.
이전 섹션에서 설명한 photometric error 모델의 경우 단일 픽셀

위 그림은 기존의 단일 픽셀 기반 photometric error 계산 모델과 coarse-to-fine 모델의 차이를 나타낸 그림이다. 위 그림 왼쪽과 같이 단일 픽셀 방법의 경우 키포인트의 이동 범위가 커지면, 즉 image velocity가 커지면 매우 높은 확률로 추적에 실패하여 전체적인 pose tracking 성능이 낮아진다. 반면, 위 그림 오른쪽과 같이 coarse-to-fine 방법의 경우 각 레벨 별 이미지 패치를 통해 높은 레벨부터 대략적으로(coarse) 픽셀의 이동을 추적하여 작은 레벨로 갈수록 점차적으로 정밀(fine)하게 픽셀의 이동을 추적할 수 있다.

이와 같은 multi-level pyramid 방법의 경우 이미지 내에서 픽셀의 위치 변화에 대한 가속도(image velocity)를 처리할 수 있는 범위를 계산할 수 있다. 현재 적용된 가장 높은 단계인 level 5에서 처리할 수 있는 image velocity를 생각해보자. 우선 level 5의 image patch 크기를 KITTI 원본 이미지 1241 x 376 [pixel]에 복원시킬 경우 위 그림과 같이 8 x 8 [pixel]
하지만 이는 이론적인 값으로써 실제 픽셀이 위와 같이 (
Outlier rejection
본 섹션에서는 Non-linear Least Square Optimization에서 일반적으로 성능 개선을 위해 사용하는 threshold-based outlier rejection 방법과 huber loss function에 대해 설명한다. Coarse-to-fine 방법이 적용된 direct method 방법의 경우에도 종종 이미지 패치 추적에 실패하는 경우가 발생한다. 추적에 실패한 이미지 패치의 경우 photometric eror가 급격하게 증가하면서 전체적인 pose tracking 성능을 저하시킨다. 이렇게 추적에 실패하여 큰 에러를 가지는 이미지 패치를 outlier로 처리하여 성능을 보존하는 방법을 outlier rejection 방법이라고 부르며 크게 다음과 같은 두 가지 방법으로 분류할 수 있다.
Threshold-based
residual의 값이 특정 threshold 이상으로 커지는 경우 optimization에서 제외하는 방법을 말한다.
Parameterized robust kernels
Robust estimator를 사용하여 큰 에러로 인한 시스템 성능의 영향을 감소시키는 방법을 말한다. DSO는 기본적으로 huber robust estimator를 사용한다. Huber robust estimator(or loss/kernel function)은 매우 큰 에러를 갖는 outlier의 loss를 down-weighting 시킴으로써 loss의 영향력을 줄여주는 robust loss function 중 하나이다.

위 그림은 huber loss가 적용된 에러 함수와 원래 에러 함수를 비교한 그림이다. 그림과 같이 huber loss는 에러가 일정
Code review
본 섹션에서는 [4] 책의 Chapter 8에서 소개한 direct method 코드를 리뷰한다. 코드는 링크를 통해 확인할 수 있다.
project3Dto2D
inline Eigen::Vector2d project3Dto2D(float x, float y, float z, float fx, float fy, float cx, float cy)
{
float u = fx*x/z+cx;
float v = fy*y/z+cy;
return Eigen::Vector2d ( u,v );
}
위 함수는 3차원 점을 2차원 픽셀 좌표로 projection하는 함수이다.
project2Dto3D
inline Eigen::Vector3d project2Dto3D(int x, int y, int d, float fx, float fy, float cx, float cy, float scale)
{
float zz = float ( d ) /scale;
float xx = zz* ( x-cx ) /fx;
float yy = zz* ( y-cy ) /fy;
return Eigen::Vector3d(xx, yy, zz);
}
위 함수는 2D 픽셀 좌표를 3차원 점으로 projection하는 함수이다.
computeError
virtual void computeError() {
const VertexSE3Expmap* v = static_cast<const VertexSE3Expmap*>(_vertices[0]);
Eigen::Vector3d x_local = v->estimate().map(x_world_);
float x = x_local[0] * fx_/x_local[2] + cx_;
float y = x_local[1] * fy_/x_local[2] + cy_;
// check x,y is in the border of the image
if(x-4 < 0 || (x+4) >image_->cols || (y-4) <0 || (y+4) >image_->rows) {
_error(0,0) = 0.0;
this->setLevel(1);
}
else {
_error(0,0) = getPixelValue(x,y) - _measurement;
}
}
photometric error는 아래와 같이 정의한다.
이 때 _measurement =
linearizeOplus
// plus in manifold
virtual void linearizeOplus()
{
if(level() == 1) {
_jacobianOplusXi = Eigen::Matrix<double, 1, 6>::Zero();
return;
}
VertexSE3Expmap* vtx = static_cast<VertexSE3Expmap*>(_vertices[0]);
Eigen::Vector3d xyz_trans = vtx->estimate().map(x_world_); // q in book
double x = xyz_trans[0];
double y = xyz_trans[1];
double invz = 1.0/xyz_trans[2];
double invz_2 = invz*invz;
float u = x*fx_*invz + cx_;
float v = y*fy_*invz + cy_;
// jacobian from se3 to u,v
// NOTE that in g2o the Lie algebra is (\omega, \epsilon), where \omega is so(3) and \epsilon the translation
Eigen::Matrix<double, 2, 6> jacobian_uv_ksai;
jacobian_uv_ksai(0,0) = -x*y*invz_2 *fx_;
jacobian_uv_ksai(0,1) = (1+(x*x*invz_2) ) *fx_;
jacobian_uv_ksai(0,2) = -y*invz *fx_;
jacobian_uv_ksai(0,3) = invz *fx_;
jacobian_uv_ksai(0,4) = 0;
jacobian_uv_ksai(0,5) = -x*invz_2 *fx_;
jacobian_uv_ksai(1,0) = -(1+y*y*invz_2) *fy_;
jacobian_uv_ksai(1,1) = x*y*invz_2 *fy_;
jacobian_uv_ksai(1,2) = x*invz *fy_;
jacobian_uv_ksai(1,3) = 0;
jacobian_uv_ksai(1,4) = invz *fy_;
jacobian_uv_ksai(1,5) = -y*invz_2 *fy_;
Eigen::Matrix<double, 1, 2> jacobian_pixel_uv;
jacobian_pixel_uv(0,0) = (getPixelValue(u+1,v) - getPixelValue(u-1,v)) / 2;
jacobian_pixel_uv(0,1) = (getPixelValue(u,v+1) - getPixelValue(u,v-1)) / 2;
_jacobianOplusXi = jacobian_pixel_uv * jacobian_uv_ksai;
}
위 코드는 Lie Theory를 사용하여 manifold 상에서 최적화를 수행할 때 사용하는 Jacobian을 정의한 코드이다. 이 때 이전에 설명한 Jacobian
References
1. [blog] Introduction to Motion Estimation with Optical Flow
2. [blog] Optical flow 루카스-카나데 방법 - 임이지님 블로그
'Fundamental' 카테고리의 다른 글
칼만 필터(Kalman Filter) 개념 정리 (+ KF, EKF, ESKF, IEKF, IESKF) Part 1 (15) | 2022.06.18 |
---|---|
선형대수학 (Linear Algebra) 개념 정리 Part 2 (1) | 2022.06.18 |
[SLAM] Bundle Adjustment 개념 리뷰 (3) | 2022.06.10 |
[SLAM] Feature-based와 Direct method VO 개념 비교 (0) | 2022.06.02 |
[SLAM] Pose Graph Optimization 개념 설명 및 예제 코드 분석 (20) | 2022.01.05 |