levmar 삽질극

학술 2010. 9. 13. 20:12 Posted by 양고
M-estimator 사용을 위해 levmar 콜백함수를 좀 손봤는데, 희한한 증상이 생겨났다.
처음 몇 줄 (Jacobian을 만드는) 은 제대로 돌아가지만, 그 다음부터 최소 gradient 방향으로 진행을 안 하더란 것.


사실 이것만이었더라면 오늘 오후를 몽땅 이걸 디버깅하는데 써버리진 않았겠지만...
저 똑같은 값만 반복하다가 멈추는 것이 불규칙하더라는 거다.
box constraint를 사용할 때와 안 할 때가 또 다르고...
또 central difference를 사용할 경우 heap corruption이 뜨고...!

M-estimator 도입을 위해 변경했던 부분을 모두 원복해가는 과정에서 문제를 발견했다.
levmar 콜백함수에서 levmar parameter p에서 r, t vector로 복사해주는 부분이 있다.
이를 단순 memcpy해서 쓰다가 서브루틴을 따로 만들었는데 이로부터 문제가 생겼던 것이다.

void Eval(float* p_eval, float* hx, int params, int measurements, void* data)
{
    // memcpy(mRVec.data.fl, p_eval, sizeof(float)*3);
    // memcpy(mTVec.data.fl, p_eval + 3, sizeof(float)*3);
    Phi2Vec(p_eval, &mTempRVec, &mTempTVec);
    ...
}

void Phi2Vec(float* param, CvMat* RVec, CvMat* TVec)
{
    for(int i = 0; i < 6; i++)
    {
        if(i < 3)
          RVec->data.fl[i] = phi[i]*scale[i];    // wrong
        else
          TVec->data.fl[i-3] = phi[i]*scale[i];    // wrong
    }
}

지금 보니 금방 눈에 들어온다만... Phi2Vec 함수의 인수는 param인데 함수 안에서 사용한 건 phi이다. param으로 고쳐야 맞다.
Phi2Vec 함수 이름 자체가 잘못 됐네. Param2Vec이 맞겠다.

이로써 알게 된 사실이 있는데, levmar에서는 처음 Jacobian을 만들 때는 levmar 함수에 주어진 파라미터 p를 직접 변경한다. 따라서 위 그림에서도 Jacobian을 구하는 것은 문제가 없었다.
그러나 그 다음부터 Jacobian 방향으로 실제 minimization을 진행하게 되면 내부적으로 임시 변수를 사용하는 것 같다. Jacobian을 만들 때부터 임시변수만 사용했더라면 처음부터 계속 고정값이었을거고 그럼 내가 p_eval이 아닌 다른 걸 쓰고있다는 사실을 쉽게 깨달았을테지...

결론적으로, levmar에서 콜백함수는 그야말로 버그의 온상이니 주의하자!
결론2는, 전역변수 남용을 자제하자!
결론3은, levmar 자체는 (대체로) 버그 프리하다. 신뢰하고 쓰자.

ㅠㅠ