카메라 왜곡보정 - 이론 및 실제

영상처리 2013.02.04 02:49

저번 카메라 캘리브레이션에 대한 포스팅에 이어 오늘은 카메라 렌즈 왜곡 보정에 대해 이론에서 실제까지 전반적인 내용을 포스팅합니다.


카메라 캘리브레이션 및 왜곡 보정은 영상처리 분야에서는 거의 필수적으로 필요한 내용입니다. 하지만 막상 본인 스스로 이런 것들을 하려면 쉽지가 않습니다. 전반적인 카메라 시스템에 대한 이해가 필요하며 이것 저것 알아보는데 많은 시간이 필요합니다. 외국의 경우 (주로 영어권) 이러한 정보공유가 잘 되어 있어서 서로서로 잘 발전해 나가지만 우리에게는 진입장벽이 높은 편입니다. 본 글은 이러한 시간을 단축시키고 많은 분들이 좀더 손쉽게 캘리브레이션 및 영상 왜곡보정에 접근할 수 있도록 관련된 실질적인 이론 및 방법, 절차를 최대한 쉽게 정리하고자 노력하였습니다.


1. 렌즈계 왜곡의 종류

2. 렌즈계 왜곡의 수학적 모델

3. 영상 왜곡 보정

4. 영상좌표 왜곡 보정

5. 샘플 프로그램


카메라에 시야각(FOV)이 넓은 광각렌즈나 초광각 렌즈를 사용하면 넓은 범위를 볼 수 있지만 이로 인해 아래 그림과 같이 상대적으로 영상왜곡이 심해지는 문제가 있습니다.


그림출처: http://www.ivs.auckland.ac.nz/web/calibration.php


이러한 영상 왜곡은 시각적인 문제 외에도 영상분석을 통해 정확한 수치 계산이 필요할 때 특히 문제가 됩니다. 예를 들어서, 영상에서 검출한 물체의 실제 위치를 알기 위해 영상좌표를 물리적인 좌표로 변환한다면 영상왜곡 정도에 따라 심각한 오차가 발생할 것입니다.



1. 렌즈계 왜곡의 종류


렌즈 왜곡에는 크게 방사왜곡(radial distortion)과 접선왜곡(tangential distortion)이 있습니다.


방사왜곡은 볼록렌즈의 굴절률에 의한 것으로서 아래 그림과 같이 영상의 왜곡 정도가 중심에서의 거리에 의해 결정되는 왜곡입니다. 


방사왜곡(radial distortion)


반면, 접선왜곡(tangential distortion)은 카메라 제조(조립) 과정에서 카메라 렌즈와 이미지센서(CCD, CMOS)의 수평이 맞지 않거나 또는 렌즈 자체의 centering이 맞지 않아서 발생하는 왜곡으로서 아래 그림과 같이 타원형 형태로 왜곡 분포가 달라집니다 (접선왜곡은 다른 말로 decentering distortion 이라고도 불립니다). 참고로 http://carstart.tistory.com/181에 보면 방사왜곡과 접선왜곡의 개념이 그림을 통해 직관적으로 잘 설명되어 있습니다. 


접선왜곡(tangential distortion)



2. 렌즈계 왜곡의 수학적 모델


일반적으로 렌즈 왜곡의 수학적 모델은 카메라 내부 파라미터의 영향이 제거된 normalized image plane에서 정의됩니다.


렌즈계의 왜곡이 없다고 할 경우, 3차원 공간상의 한 점 (Xc, Yc, Zc)는 central projection (pinhole projection)에 의해 normalized image plane상의 한 점 (xn_u, yn_u)로 투영됩니다 (첨자 n: normalized, u: undistorted):


(1)


그러나 실제로는 (xn_u, yn_u)는 렌즈계의 비선형성에 의해 왜곡(주로 radial distortion)이 됩니다. (xn_d, yn_d)를 렌즈계의 왜곡이 반영된 normalized 좌표라면, 렌즈계 왜곡 모델은 다음과 같습니다 (첨자 d: distorted):


(2)


단,



위 수식에서, 우변의 첫번째 항은 radial distortion, 두번째 항은 tangential distortion을 나타냅니다 (k1, k2, k3는 radial distortion coefficient, p1, p2는 tangential distortion coefficient). ru는 왜곡이 없을 때의 중심(principal point)까지의 거리(반지름)입니다.


이 때, (xn_d, yn_d)는 normalized image plane에서의 좌표이며 실제 영상 픽셀 좌표 (xp_d, yp_d)는 카메라 내부 파라미터를 반영하여 다음과 같이 구해집니다.


(3)


즉,


(4)


여기서, fx, fy는 초점거리, cx, cy는 렌즈 중심 영상좌표(principal point), skew_c는 비대칭계수를 나타내는 카메라 내부 파라미터들입니다. 카메라 내부 파라미터에 대한 자세한 내용은 [영상처리] - 카메라 캘리브레이션 (Camera Calibration)를 참조하시기 바랍니다.



3. 영상 왜곡 보정


왜곡된 영상으로부터 보정된 영상을 생성하는 것은 비교적 간단하게 구현할 수 있습니다 (참고로, 왜곡된 영상을 보정하기 위해서는 먼저 카메라 캘리브레이션을 통해 카메라 내부 파라미터(intrinsic parameter)를 구해야 합니다. 카메라 캘리브레이션에 대해서는 [영상처리] - 카메라 캘리브레이션 (Camera Calibration)를 참조하시기 바랍니다).


왜곡된 영상을 Id, 보정된 Iu라 하겠습니다. 기본 아이디어는 Iu의 각 픽셀값을 해당 픽셀 좌표를 왜곡시켰을 때의 Id의 대응되는 픽셀값으로 채우는 것입니다.


영상 Iu의 한 점을 (xp_u, yp_u)라 하면, 일단 이것을 카메라 파라미터를 역으로 적용하여 normalized 좌표 (xn_u, yn_u)로 변환합니다.


(5)


즉,


(6)


다음으로, 중심까지의 거리 ru (ru2 = xn_u2 + yn_u2)를 구하고 식(2) 왜곡모델을 적용하여 왜곡된 좌표 (xn_d, yn_d)를 구합니다.


(7)


마지막으로 (xn_d, yn_d)를 다시 픽셀 좌표계로 변환하면 (xp_u, yp_u)의 왜곡된 영상에서의 좌표 (xp_d, yp_d)를 구할 수 있습니다.


(8)



영상 왜곡보정 (예제 코드)

function undistort_image(Id, Iu, fx, fy, cx ,cy, skew_c, k1, k2, k3, p1, p2)

    w: image width of Iu

    h: image height of Iu

    for y = 0:h-1,

        for x = 0:w-1,

            y_nu = (y-cy)/fy;

            x_nu = (x-cx)/fx – skew_c*y_nu;


            ru2 = x_nu*x_nu + y_nu*y_nu; // ru2 = ru*ru

            radial_d = 1 + k1*ru2 + k2*ru2*ru2 + k3*ru2*ru2*ru2;


            x_nd = radial_d*x_nu + 2*p1*x_nu*y_nu + p2*(ru2 + 2*x_nu*x_nu);

            y_nd = radial_d*y_nu + p1*(ru2 + 2*y_nu*y_nu) + 2*p2*x_nu*y_nu;


            x_pd = fx*(x_nd + skew_c*y_nd) + cx;

            y_pd = fy*y_nd + cy;


            Iu(x, y) = Id(x_pd, y_pd);

        end;

    end;    

return Iu;


☞ (2017.05.16) 정말 많은 분들이 왜 이렇게 왜곡보정을 해야 하는지 이해하기 힘들다고 합니다. 그래서 부연설명을 추가합니다. I가 입력영상(왜곡된 영상), I'을 왜곡을 편 영상(보정된 영상)이라 하겠습니다. 직관적으로 생각할 수 있는 방법은 원본영상의 각 픽셀 (x, y)에 대응되는 왜곡 보정된 좌표 (x', y')을 구한 후, 해당 위치의 보정영상에 대입하는 것입니다 (즉, ∀x,y, I'(x',y') = I(x,y)). 그런데, 이렇게 왜곡을 펴면 보정 영상(I')에 구멍이 뚫려서 보기 흉하게 되는 문제가 발생합니다. 이미지의 왜곡을 펴는 것은 손으로 잡고 쭉 늘이는 것과 같기 때문에 원본 픽셀을 보정영상에 대입하면 보정된 영상에서는 중간 중간 이빨이 빠진 것처럼 빈 픽셀(pixel)들이 발생하게 됩니다. 따라서 이러한 문제를 해결하기 위해서는 위의 예제 코드와 같이 발상의 전환이 필요합니다. 그건 입력영상의 왜곡을 펴서 보정영상을 만드는 것이 아니라 보정영상을 먼저 만들어 놓고 보정영상의 각 픽셀에 대응되는 원본영상의 픽셀을 가져오는 것입니다. 즉, 보정영상의 각 픽셀좌표 (x', y')에 대해 왜곡된(왜곡을 일부러 가한) 좌표를 구하고 이렇게 구한 좌표에 대응하는 원본영상의 픽셀을 가져와 보정영상에 대입합니다. 이렇게 하면 이빨빠짐 현상이 없는 깨끗한 영상이 얻어집니다.


만일, 단순한 모델을 사용하고 싶거나 카메라 파라미터가 충분치 않으면 위 수식들을 적절히 수정하면 됩니다. 예를 들어, 대부분의 경우 radial distortion이 주이고 tangential distortion은 미약하기 때문에 식 (2)에서 tangential term을 생략하기도 합니다 (p1=p2=0). 또한 radial term도 k1, k2까지만 사용하는 것이 일반적입니다 (즉, k3 = 0). 비대칭 계수인 skew_c도 대부분 생략됩니다 (skew_c = 0). (cx, cy)도 구하기 힘든 경우에는 영상 중심(image_width/2, image_height/2)으로 놓기도 합니다 (하지만 cx, cy는 중요하기 때문에 가급적 정확하게 측정하는게 좋습니다).



4. 영상좌표 왜곡 보정


실제 영상처리에서는 종종 보정된 영상을 구하는 것보다는 보정된 영상좌표를 구하는 것이 훨씬 중요합니다. 왜냐하면 때로는 영상전체를 보정한 후에 영상처리 알고리즘을 적용하는 것보다는 원래 왜곡된 영상에서 처리를 한 후에 그 결과만 보정(undistort)을 하는 것이 효과적일 수 있기 때문입니다.


그런데, 왜곡된 영상좌표로부터 왜곡 보정된 영상좌표를 구하는 것은 closed-form solution이 없는 매우 어려운 문제로서 보통 근사적으로 해를 구합니다 (식 7을 잘 보면 n_u로부터 n_d를 구하는 것은 쉬우나 반대로 n_d에서 n_u를 구하는 것은 7차 방정식을 풀어야 하는 매우 어려운 문제임을 알 수 있습니다).


왜곡된 영상좌표 p_d로부터 왜곡 보정된 좌표 p_u를 구하는 한 방법은 p_u = p_d로 가정하고 식 (2)를 이용해서 p_u를 왜곡시켜 보는 것입니다. 이렇게 구한 점이 실제 p_d와 얼마나 오차가 나는지를 계산해서 그 오차를 p_u에 역으로 반영시킵니다. 예를 들어, p_u를 왜곡시킨 점이 p_d보다 δ만큼 크다면 p_u를 δ만큼 감소시킵니다. 이러한 과정을 오차가 임계값 이하가 될 때까지 반복합니다 (보통 3~4번만 반복해도 거의 정확한 p_u값을 얻을 수 있습니다).


영상좌표 왜곡보정 (예제 코드)

function undistort(point p_d)

      p_d’ = normalize(p_d);

      p_u’ = p_d’;

      while (1)

            err = distort_normal(p_u’) – p_d’;

            p_u’ = p_u’ – err;

            if(err<err_threshold) break;

      end;

      p_u = denormalize(p_u’);

return p_u;


function normalize(x, y)

      y_n = (y-cy)/fy;

      x_n = (x-cx)/fx – skew_c*y_n;

return (x_n, y_n);


function denormalize(x, y)

      x_p = fx*(x + skew_c*y) + cx;

      y_p = fy*y + cy;

return (x_p, y_p);


function distort_normal(x, y)

      r2 = x*x + y*y;

      radial_d = 1 + k1*r2 + k2*r2*r2 + k3*r2*r2*r2;

      x_d = radial_d*x + 2*p1*x*y + p2*(r2 + 2*x*x);

      y_d = radial_d*y + p1*(r2 + 2*y*y) + 2*p2*x*y;

return (x_d, y_d);



5. 샘플 프로그램


라이브로 카메라 영상 또는 비디오 파일을 왜곡 보정하여 보여주는 예제 프로그램(소스 포함)은 [개발한 것들] - 라이브 왜곡 보정 프로그램을 참조해 주세요.



관련 포스팅


by 다크 프로그래머


  • 이전 댓글 더보기
  • 회색하늘 2016.09.19 14:47 신고 ADDR 수정/삭제 답글

    왜곡 관련해서 이런저런 검색을 하다고 굉장히 좋은 글을 발견한 기분이지만,
    제 지식이 부족해서 이해하기가 쉽지 않네요.

    항상 동일하게 왜곡되는 환경 - 즉 왜곡된 형상의 렌즈를 통한 결과물 (사진 or 영상)의 보정의 경우
    좌우 또는 상하 대칭이 되지 않는 것이라도 왜곡 보정이 가능할까요?
    (물론 그로 인한 해상력 저하 등은 감수한다고 할 때)

    • BlogIcon 다크pgmr 2016.09.19 17:26 신고 수정/삭제

      사실 질문하신 내용이 잘 파악은 안되는데요.. 우리가 왜곡을 펴는 것은 렌즈에서 일어나는 왜곡은 항상 어떤 특정한 모델을 따른다라고 가정하고 이 모델에 맞춰서 뫠곡을 펴는 것입니다. 따라서 기존의 렌즈 왜곡 모덜에서 벗어나는 예외적인 형태의 왜곡이라면 당연히 기존의 보정 방법으로는 보정이 안됩니다. 그런데, 질문하신 상하좌우 대칭에서 대칭의 기준이 중요한데요 일반적으로 왜곡모델에서 대칭의 기준은 이미지 센터가 아니라 이미지 주점(principal point)입니다. 만일 주점에 대해서 대칭이라면 기존 왜곡보정 방법을 쓰면 되구요, 만일 정말로 기존 모델에서 벗어나서 왜곡이 이루어진다면 일일히 매핑 테이블(mapping table)을 만들어서 펴는 수밖에는 없겠습니다.

  • DoubleMan 2016.09.25 15:51 신고 ADDR 수정/삭제 답글

    안녕하세요. 영상처리에 관련된 글을 보며 도움을 많이 받고 있습니다. 특히 calibration과 영상왜곡보정에 대해 관심을 갖고 있는데요.. Windshield(차량 앞유리)에도 왜곡이 있는데 차량내부에서 촬영한 이미지를 가지고 어떻게 보정을 해야할까요? No windshield일때 와 windshield 일때 각각 체스보드를 이용해 calibration을 했을때 카메라내부파라미터를 비교를 하면 좋을까요? 조언 부탁드립니다.

    • BlogIcon 다크pgmr 2016.09.26 09:29 신고 수정/삭제

      안녕하세요. 차량 앞유리의 왜곡은 생각해 본 적이 없는데요, 차량 앞유리에도 왜곡이 존재하나요? 만일 왜곡이 있다면 차량 내부에서 촬영한 이미지에 이미 왜곡이 반영되어 있을 터이니 그 왜곡을 반영한 캘리브레이션 파라미터를 그대로 사용하면 되지 않을까 싶습니다.

  • 진짜대단하네요.. 2016.10.07 18:59 신고 ADDR 수정/삭제 답글

    안녕하십니까 항상 수고가 많으십니다. 한가지 읽다가 궁금점이 생겨서 질문합니다!
    ru는 왜곡이 없을 때의 중심(principal point)까지의 거리(반지름)거리랬는데 어디서부터 중심까지의 거리인가요?

    • BlogIcon 다크pgmr 2016.10.08 05:36 신고 수정/삭제

      안녕하세요. 왜곡보정된 정규좌표를 (uu, vu)라 하면 ru = sqrt(uu*uu + vu*vu)입니다. 즉, 왜곡을 편 좌표에서 중심까지의 거리입니다. 왜곡 보정은 정규좌표계에서 이루어집니다. 좌표계에 대해서는 http://darkpgmr.tistory.com/77 글을 참조하시기 바랍니다.

  • doolin 2016.11.19 22:39 신고 ADDR 수정/삭제 답글

    안녕하세요 감사하게 글 잘 봤습니다.
    그런데 궁금한점이 있습니다. 위 글에서 왜곡을 감안한 좌표 구하는 방법을 파노라마 영상에도
    적용할 수 있을까요??
    그렇지 않다면.. 구체적으로 파노라마 영상의 좌표 (x, y)가 있다면 왜곡을 감안했을 때의 원래 좌표 (x_t, y_t)는 어떻게 구할 수 있을지 궁금합니다..

    • BlogIcon 다크pgmr 2016.11.20 11:33 신고 수정/삭제

      파노라마 영상은 카메라 여러 대의 이미지를 붙이거나 한 대의 카메라를 돌려가면서 찍은 여러 장의 이미지를 붙여서 생성한 것이기 때문에 왜곡은 원래의 이미지 또는 원래의 카메라에서 보정하면 될 것입니다.

    • doolin 2016.11.20 17:37 신고 수정/삭제

      저같은 경우는 360도 카메라로 촬영된 파노라마 영상에서 특징 검출을 목표로하고 있거든요.. 하지만 왜곡때문에 특징이 제대로 검출될 수가 없는데 파노라마 영상에서 왜곡 감안했을 시 좌표를 구하는 방법은 어떤식으로 할 수 있을까요..

    • BlogIcon 다크pgmr 2016.11.20 17:55 신고 수정/삭제

      360도 카메라도 결국 여러 개의 카메라 모듈을 구형으로 배치해서 영상을 획득하지 않나요? 아니면 가지고 계신 카메라가 거울을 이용해서 영상을 찍는 Catadioptric 카메라인가요?

    • doolin 2016.11.21 13:30 신고 수정/삭제

      영상을 구형으로 배치하는 카메라가 맞습니다..

  • supermario 2017.02.21 23:34 신고 ADDR 수정/삭제 답글

    안녕하세요?
    영상 공부하는데 따라하기 정말 좋게 해주셔서 많은 도움이 되고 있습니다.
    계산을 쭈욱~ 따라하다보니 (6)번식이 나오는 역행렬을 잘못쓴게 아닌가 싶네요
    x_nu = (x-cx)/fx – (y-cy)*skew_c/fy
    제가 계산해보니 이렇게 나오는데 한번 검토 부탁드립니다.

    • BlogIcon 다크pgmr 2017.02.22 13:49 신고 수정/삭제

      저도 다시 계산해 봤는데, 틀린 부분은 없는 것 같습니다. xn = (xp-cx)/fx-skew_c yn의 yn에 yn = (yp-cy)/fy를 대입하면 말씀하신 식이 나옵니다.

    • supermario 2017.02.22 15:09 신고 수정/삭제

      아 그렇네요..yn 인거를 간과했네요..밤에 공부하다 머리가 잠깐 어떻게 됐나봐요. 불편하게 해드려 죄송합니다.

    • BlogIcon 다크pgmr 2017.02.23 10:10 신고 수정/삭제

      너무 몰두하다 보면 그럴 수 있습니다. 파이팅입니다.

  • 한수좀요 2017.04.11 16:06 신고 ADDR 수정/삭제 답글

    마지막 왜곡된 영상좌표로부터 보정된 영상을 얻는것 관련해서 두가지 질문 드립니다. 'p_u를 구하는 한 방법은 p_u = p_d로 가정하고 식 (2)를 이용해서 p_u를 왜곡시켜 보는 것입니다. 이렇게 구한 점이 실제 p_d와 얼마나 오차가 나는지를 계산해서 그 오차를 p_u에 역으로 반영시킵니다.'라고 적혀있는데 아래 코드의 식을 참고하면 p_u'=p_d'라고 가정하면 p_u'=p_d'-(distort_normal(p_d’) – p_d’;)=2p_d'-distort_normal(p_d)의 식이 되고 에러가 distort_normal(p_d’) – p_d’가 되는데 제가 실제로 해본 결과 에러인 distort_normal(p_d’) – p_d’ 값이 발산하여 쓰레쉬홀드 내 값으로 들어오지 않고 발산하는 상태에서 그냥 for 루프를 두번정도 돌리면 보정된 좌표가 나오던데 제가 식을 잘못썼는지 단조증가 함수가 아닌건지 궁금합니다. ! 또 보정 평면에서 데이터가 없는 부분에 대해서는 어떻게 interpolate같은 방식으로 채워넣는지 궁금합니다. 도움 많이 되고있습니다. 감사합니다.

    • BlogIcon 다크pgmr 2017.04.12 19:22 신고 수정/삭제

      일반적인 영상에서는 발산하지 않는데.. 발산하는 이유는 제가 확인하기(알기) 어렵습니다. 그리고 보정평면에 데이터가 없는 부분은 제가 적어놓은 왜곡보정 예제코드를 사용하면 발생하지 않습니다. 실제 위 코드를 적용했을 때 빈 곳이 발생한 것인지 궁금합니다.

    • vision은처음 2017.09.07 18:45 신고 수정/삭제

      저도 발산/수렴에 대해 수식 보고 바로 의문이 들었는데요.

      distort_normal에 의해 확장(radial distortion을 통해 1보다 큰 값을 곱하게 되면 크기가 커짐, Pincushion distortion)되는 정도가 오차를 빼면서 줄어드는(비례해서 shrink하는건 아니지만 오차가 참값보다 크기가 작다면 뺄셈에 의해 일정 부분 감소할 여지가 있죠.) 정도보다 더 크다면, 발산할 수도 있을 것 같네요. 보완하려면 오차에 1보다 작은 상수를 곱하는 것도 좋을 것 같습니다.

      참고: https://en.wikipedia.org/wiki/Distortion_(optics)

    • BlogIcon 다크pgmr 2017.09.08 08:59 신고 수정/삭제

      네, pincushion 왜곡이라면 첫 번째 iteration에서 원점을 넘어서 반대편으로 넘어가 버릴 수도 있겠습니다. 그런 경우 말씀하신 것처럼 작은 상수를 곱하면 좋을 것 같습니다. 감사합니다.

  • 초보중의초보 2017.05.02 15:18 신고 ADDR 수정/삭제 답글

    안녕하세요 다크님. 엄청난 블로그를 발견한것 같습니다. ㅎㅎ
    올려주신 자료를 바탕으로 150도 짜리 광각 렌즈를 탑재한 카메라 모듈의 영상을 보정하는 작업을 진행하고 있습니다.

    첫번째 방법은 영상왜곡보정방법을썼을때는 보정은 잘 되지만(데이터손실없이), 화각의 손실이 발생하는 현상이 생기더군요.

    그래서 두번째 영상좌표 왜곡을 보정하는 방법을 사용했더니 화각의 손실없이 보정이 잘 되는데 위의 "한수좀요" 님의 말씀대로 데이터 손실이 되는 부분이 발생했습니다. 적용한 순서는 다음과 같습니다.

    1. 기존의 영상좌표 왜곡보정식에서 k3=p1=p2=skew_c=0, cx = Image W/2, cy = Image H/2 로 단순화 하고 왜곡된 좌표에 대응하는 보정된 좌표집합을 구한다.[구조체의 형태로 저장합니다.]
    x_pu = (x_pd - cx)/(1 + k1*ru^2 + k2*ru^4) + cx
    y_pu = (y_pd - cy)/(1 + k1*ru^2 + k2*ru^4) + cy

    2. 보정된 좌표의 각 x,y 최대 최소값을 찾아서 원래 이미지 크기로 스캐일링을 한 구조체를 출력
    (원래 이미지 크기로 스케일링한 구조체를 출력할때 double 자료형의 x,y 좌표를 int 형으로 바꿉니다.)

    3. 출력된 구조체를 센서보드의 펌웨어에 올려서 원본 이미지의 픽셀크기만큼 for 문을 돌려서 왜곡된 좌표의 픽셀값을 보정된 좌표의 픽셀값에 넣어준다.

    결과는 아래의 링크와 같이 나왔습니다. 조언 부탁드리겠습니다.

    Original Image : https://drive.google.com/file/d/0B2O-5uYaOjV5YUhYVUhvX1lHWlU/view?usp=sharing
    영상왜곡보정 : https://drive.google.com/file/d/0B2O-5uYaOjV5bjlyc1AyUzczNDQ/view?usp=sharing
    영상좌표왜곡보정 : https://drive.google.com/a/atticfab.com/file/d/0B2O-5uYaOjV5VmlQME1oQVpEMmc/view?usp=sharing

    • BlogIcon 다크pgmr 2017.05.02 15:51 신고 수정/삭제

      안녕하세요. 올려주신 결과는 잘 보았습니다. 영상의 왜곡을 펴면 이미지가 불가사리처럼 쭈욱 늘어납니다. 따라서 원본 이미지의 크기와 같은 크기의 창(틀)을 사용한다면 늘어난 이미지를 모두 담을수 없기 때문에 경험하신 것처럼 결과적으로 화각 손실이 일어난 것처럼 보이게 됩니다. 따라서 해결책은 결과이미지에 원본보다 더 큰 크기의 창(or 틀)을 사용하면 됩니다.

  • 피야프 2017.05.12 23:07 신고 ADDR 수정/삭제 답글

    안녕하세요 다크님. 항상 많은 도움을 받고 있어 우선 감사드립니다.

    opencv의 fisheye model 설명(http://docs.opencv.org/trunk/db/d58/group__calib3d__fisheye.html#gab738cdf90ceee97b2b52b0d0e7511541)을 보니, fisheye distortion을 아래 식으로 설명하고 있는데요.
    θd = θ(1 + k1*θ^2 + k2 *θ^4 + k3*θ^6 + k4*θ^8)
    opencv의 code중 fisheye.cpp 에 구현된 것도 정확하게 이 식 그대로이구요.

    즉, 다항식의 input과 output이 다크님 글이나 Zhang의 논문에서 처럼 r(거리)가 아니라, θ(각도)로 되어 있습니다. 이걸 서로 어떻게 연관지어 이해할 수 있을까요?

    그리고, 올려주신 DarkCamCalibrator에는 Zhang 방법에 해당하는 opencv 코드를 그대로 쓰셨다고 하셨는데요. 위에 제가 말씀드린 opencv 의 fisheye model의 경우 2014년에 opencv 에 포함되었던데, 혹시 calibrator 구현시 참조하신 opencv 소스는 그 이전 버전인가요?

    • BlogIcon 다크pgmr 2017.05.13 00:04 신고 수정/삭제

      안녕하세요. 부산에서 서울가는 길이 한가지가 아니듯이 카메라의 왜곡을 모델링하는 방법도 여러 가지가 있다고 생각하시면 될 것 같습니다. opencv에 새로 추가된 fisheye 모델은 http://docs.opencv.org/3.1.0/db/d58/group__calib3d__fisheye.html 를 보시면 간략한 설명을 볼 수 있습니다. 광학축과의 거리 r 대신에 광학축(optical axis)과 이루는 각 θ를 이용해서 왜곡을 모델링 한 것으로 보시면 됩니다. calibrator 구현시 사용한 opencv 버전은 opencv 3.1로서 fisheye 모델이 추가된 이후 버전입니다.

  • 영상처리초보 2017.05.16 14:49 신고 ADDR 수정/삭제 답글

    안녕하세요. 여러가지 정보들 계속 정독하며 공부하는 영상처리 초보 학생입니다. 캘리브레이션과, 이 알고리즘을 몇번씩 읽고 나름대로 코드화 해 보아도 상황에 진전이 없어 댓글을 작성하게 되었습니다 ㅠ.ㅠ 1) 캘리브레이션을 통해, cx,cy,k1,k2,fx,fy 등 파라미터들은 미리 구해놓았습니다. 소수점 몇째짜리까지 사용을 해야할까요? 2) 매트랩이나, OpenCV환경에서 영상처리를 하고자 하는데, 영상 왜곡보정 알고리즘이 눈으로는 이해가 가나, lu(x,y)와 ld(x_pd,y_pd)부분을 실제로 구현하기가 너무 어렵습니다. 제가 이해한 바로는 lu는 보정된 좌표(왜곡보정이 완료된)이고, ld는 왜곡이 된 좌표인 것 같은데 input이미지와 output이미지를 저 수식에 대입하는것이 당최 이해가 안됩니다. 요 부분 질문드려도 괜찮을까요?

    • 영상처리초보 2017.05.16 15:16 신고 수정/삭제

      설명이 좀 애매한것 같아
      코드 첨부하여 재답글 드립니다.
      OpenCV (C++) 환경입니다.
      위쪽에 알고리즘 작성 후
      마지막줄에서,

      output.at<uchar>(y, x) = input.at<uchar>(y_pd, x_pd);

      이런식으로 좌표를 대입해주는데,
      원하는 결과가 나오질 않습니다.
      제가 생각하는 바로는
      output(x,y) 라고하면은 그 좌표의 픽셀값(밝기)를 input(x_pd,y_pd)의 픽셀값으로 대치되기 때문인것 같은데 이게 맞는 생각인가요?

    • BlogIcon 다크pgmr 2017.05.16 16:44 신고 수정/삭제

      안녕하세요. 원하는 결과가 어떤 식으로 나오지 않는 건가요? 구현은 맞게 하신 것 같은데요..
      그리고 질문하신 원리에 대한 내용은 많은 분들이 어려워하는 부분입니다. 글 본문의 영상 왜곡보정 예제코드 밑에 관련 설명을 추가하였습니다. 한번 읽어보시고 또 의문이 생기면 다시 질문해 주시기 바랍니다.

  • 영상처리 2017.06.19 16:03 신고 ADDR 수정/삭제 답글

    안녕하세요 한가지 여쭤보고싶습니다.
    2번식에서 n_d와 n_u의 위치가 바뀐건 아닌가 하는 의문이 들어서 댓글남겨봅니다.
    n_d값을 radial distortion과 tangential distortion을 나타내는 값들로 보정했을때 나오는것이 n_u가 아닌가요?

    • BlogIcon 다크pgmr 2017.06.20 15:49 신고 수정/삭제

      바뀐 건 아니구요 원래부터 왜곡모델이 2번식으로 정의되어서 그렇습니다. opencv나 기존 캘리브레이션 툴에서 반환하는 왜곡계수(k, p) 값들은 모두 2번 식에 따른 값입니다. 사람에 따라서는 n_d로부터 n_u가 얻어지도록 모델을 잡을수도 있겠지만 그러면 다른 형태의 식이 나왔겠지요.. 어차리 모델이라는 것은 사람이 정의하기 나름입니다.

  • 달암 2017.08.28 17:13 신고 ADDR 수정/삭제 답글

    매우 유용한 포스팅 감사드립니다.

    http://enow.co.kr/220955821839
    위 링크에서 보니 촬영 거리에 따라 왜곡의 형태(볼록/오목)가 달라지는 군요.
    그런데 본 포스팅의 2번식 우변의 첫째항을 보면 그러한 왜곡의 형태가 오로지 내부파라미터 k1, k2, k3에 의해서만 결정되네요. 만약 k1, k2, k3이 모두 양수라면 왜곡의 형태는 촬영 거리랑 상관없이 무조건 볼록(barrel)형이 된다는 얘기이므로 참조드린 링크의 내용과 상충하는 것으로 해석이 되는데.. 제가 잘못이해하고 있는 부분이 무엇일까요?

    + 추가로, k1, k2, k3를 얻는 방법은 어디에 나와있나요?ㅠ
    + Cx, Cy는 보정하려는 직사각형의 격자 좌표계를 기준으로 카메라의 중심점이 어디있는지를 말하는 건가요?

    • BlogIcon 다크pgmr 2017.08.30 11:50 신고 수정/삭제

      안녕하세요. 거리에 따라서 왜곡의 형태가 달라진다는 것은 저는 처음 들어봤는데요, 제가 광학전문가가 아니니 단정하긴 힘듭니다. 하지만, 말씀하신 링크의 두번째 사진은 pincushion 왜곡의 예로 보기는 힘듭니다. 사진에는 근거리 사물과 원거리 사물이 섞여있기 때문에 원거리 부분에 대해서는 어떤 왜곡모델이 적용되었는지 확인이 힘듭니다. 실제로 이론을 증명하기 위해서는 초대형 체스판을 설치하고 원거리(수십 km)에서 체스판을 봤을 때 그 형태가 어떻게 되는지를 확인해야 할 것입니다. 하지만 제가 아는 상식으로는 동일하게 barrel 왜곡이 발생할 것으로 생각됩니다. cx, cy, k1, k2 등의 카메라 파라미터를 얻기 위해서는 공개된 캘리브레이션 툴들을 이용하시면 됩니다 (http://darkpgmr.tistory.com/32 글 참조).

  • :) 2017.09.18 13:29 신고 ADDR 수정/삭제 답글

    안녕하세요, 언제나 분에 넘치는 글을 읽을 수 있어 감사합니다!

    본문 내용 중 영상 왜곡보정(예제코드) 아래 문장에
    " I가 입력영상(왜곡된 영상), [* I'을 왜곡을 편 영상(보정된 영ㅅ아)이라 *] ..." 부근에 영ㅅ
    아 라고 되있네요. 너무 좋은 글이라 오타가 있는 점이 아쉬워서 남깁니다... :)

    항상 값비싼 정보를 공개해주셔서 감사합니다!

    • BlogIcon 다크pgmr 2017.09.18 13:48 신고 수정/삭제

      설마 그런 오타가... 했는데 정말 있네요. 감사합니다. 덕분에 잘 고쳤습니다 ^^

  • 안녕하세요 2017.11.06 10:55 신고 ADDR 수정/삭제 답글

    안녕하세요. 항상 자료 잘 보고있습니다~
    영상처리 초보자라 질문이 있어 댓글을 답니다.
    4. 영상좌표 왜곡 보정에서 "영상전체를 보정한 후에 영상처리 알고리즘을 적용하는 것보다는 원래 왜곡된 영상에서 처리를 한 후에 그 결과만 보정(undistort)을 하는 것이 효과적일 수 있기 때문입니다."라고 말씀하셨는데 이유를 알 수 있을까요~?
    제 짧은 추측으로 아래와 같이 생각해봤는데
    보정된 영상(보정 과정에서 오차 존재) -> 영상처리 알고리즘 -> 정확도 낮은편
    오리지널 영상(오차 없음) -> 영상처리 알고리즘 -> 결과 보정 -> 정확도 높은편
    이게 맞는건가요?
    바쁘신 와중에 긴 댓글 읽어주셔서 감사합니다~

    • BlogIcon 다크pgmr 2017.11.06 11:59 신고 수정/삭제

      제 의도는 시간적인 측면이었는데요, 영상 전체를 펴지 않고 처리할 수 있다면 시간이 절약될 테니까요.. 정확도 측면에서는 큰 차이가 없거나 왜곡을 펴서 작업하는 것이 좀더 정확할 것 같습니다. 왜곡이 있는 상태에서 추출한 좌표에는 아무래도 오차가 반영되어 있을 테니까요..

  • 질문드립니다 2017.11.17 16:51 신고 ADDR 수정/삭제 답글

    안녕하세요. 이곳에 질문을 해도 될런지는 모르겠지만 카메라 calibration의 전문가이신것 같아 여쭤보겠습니다. 만약 한 카메라로 1번위치에서 calibration을 했고 좌표값에도 문제가 없는데, 같은 카메라로 2번위치에서 실제 (10,10) 인 위치에 존재하는 점의 좌표값이 (10,20)으로 Y값만 두배가 되어 측정되는 이유가 무엇일까요?ㅠㅠ 아신다면 부탁드리겠습니다.

    • BlogIcon 다크pgmr 2017.11.17 17:06 신고 수정/삭제

      질문하신 내용만으로는 어떤 내용인지 파악이 힘드네요. 좀더 구체적인 정보가 필요할 것 같습니다. 굳이 비유하자면 1번 위치에서 공을 찼을 때는 골대에 잘 들어갔는데 2번 위치에서 공을 차니 안들어간다.. 그런데 이유를 모르겠다라고 물어보는 것과 비슷합니다 ^^ (그냥 웃자고 적은 내용입니다). 원인은 여러 가지가 있을 수 있으니, 구체적으로 어떤 방법으로 캘리브레이션을 했고 또 어떤 방법으로 좌표값을 추정(측정?)했는지 정보가 있어야 문제를 생각해 볼 수 있을 것 같습니다.

  • 안녕하세요 2018.01.11 17:58 신고 ADDR 수정/삭제 답글

    올려주신 프로그램코드를 확인해봤는데요.
    제가 배움이 부족해서 그런지
    아무리 찾아봐도 구해진 캘리브레이션값을 어디에 적용시키는지를 못찾겟어요..

    • BlogIcon 다크pgmr 2018.01.11 20:33 신고 수정/삭제

      CameraDistortion::SetParams() 함수를 호출해서 적용시킵니다.

  • 토이 2018.06.15 13:21 신고 ADDR 수정/삭제 답글

    다크님 질문이 있습니다.
    err = distort_normal(p_u’) – p_d’;
    p_u’ = p_u’ – err;
    if(err<err_threshold) break;
    위에 코드에서 좌표값 err 포인트는 x, y 좌표로 표현 될텐데요
    if(err<err_threshold) break; threshold는 상수 값으로 생각됩니다.
    그러면 err는 distort_normal(p_u’) – p_d’; 사이의 거리를 계산해서 넣어주면 되나요?

  • 홍장어 2018.08.14 17:08 신고 ADDR 수정/삭제 답글

    안녕하세요. 다크님! 비전을 공부하고 있는 학생입니다.
    영상 왜곡은 시각적인 문제 외에도 영상분석을 통해 정확한 수치 계산이 필요할때 특히 문제가 된다고 하셨는데요 이말은 영상 왜곡을 제거하면 정확한 수치 계산을 할수 있다는거죠~
    그런데 제가 스트레오 카메라를 이용하여 체스보드로 확인을 해봤는데 결과값이 이상하게 나오네요...
    체스보드를 이용해 캘리브레이션을 한 후 양쪽 체스보드의 교차점 이미지포인트(픽셀좌표)의 3차원 좌표를 계산했는데요. 교차점의 간격은 5mm입니다.
    왜곡보정을 하지 않은 원본 이미지의 체스보드 교차점 3차원좌표의 간격은 5mm정도로 나오는데
    왜곡보정을 한 이미지의 체스보드 교차점 3차원좌표의 간격은 4.8mm정도로 나오네요....
    보정을 하지 않은 이미지가 더 정확한 값을 출력하는데 어떻게 된걸까요???
    opencv에서 제공하는 함수를 이용하여 3차원 좌표를 구했습니다.
    다크님의 생각은 어떠하십니까?

    • BlogIcon 다크pgmr 2018.08.16 16:55 신고 수정/삭제

      상식적으로 왜곡보정을 했을 때 결과가 더 정확하게 나와야 하구요, 만일 그렇지 않았다면 어디선가 문제가 있었다는 것이니 그 원인을 찾는 것이 중요하겠습니다.

  • 검색 2018.09.08 19:23 신고 ADDR 수정/삭제 답글

    글은 잘 보았습니다. 이 블로그에 몇번이나 방문하여 참고를 했는지 모르겠습니다. 감사합니다.

    100*100 의 픽셀을 갖는 ccd 를 통해 보정한 결과 cx 혹은 cy 가 100 이상의 값을 갖는다면, 보정이 잘못된 것으로 생각할 수 있는데, 이미지를 잘 획득하고 코너점들을 적절히 찾았다고 가정할 때, 이런 문제가 생기는 원인이 또 뭐가 있을지 혹시 짐작이 가시는지요?

    • BlogIcon 다크pgmr 2018.09.10 22:59 신고 수정/삭제

      'ccd를 통해 보정한 결과'에서 보정은 왜곡보정을 말하는 것이지요?(캘리브레이션을 보정이라고 말한 것인지 혼동스럽긴 합니다). 왜곡보정이라면, 보정은 항상 cx, cy를 중심(center)으로 이루어지기 때문에 보정을 한다고 해서 cx, cy의 값은 변하지 않습니다만.. 왜곡보정 과정의 수식 어딘가에서 문제가 있었다고 생각됩니다만 구체적으로 어떤 문제일지는 저로서는 알기 힘듭니다.

  • 피야드 2018.11.15 11:12 신고 ADDR 수정/삭제 답글

    안녕하세요. 최근에 radial distortion에 대해 찾아보다가 inverse radial distortion에 관한 2016년 논문을 보게 되었습니다. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4934233/
    tangential은 빼고 radial distortion만 따지는 경우에는 이 논문에 나오는 polynomial 공식을 써도 될 듯 합니다.
    유도 과정까지 자세히 보지는 않았으나, 이게 아주 오래된 이슈일텐데 왜 이런 논문이 최근에야 나오는지 궁금하네요. 감사합니다.

    • BlogIcon 다크pgmr 2018.11.15 18:32 신고 수정/삭제

      좋은 정보 감사합니다. 지금은 못보더라도 여유가 되는대로 잘 참고토록 하겠습니다.
      오래된 이슈가 논문으로 나오는 건 현재의 모델이 불편하거나 완벽하지 않기 때문일 텐데요. 저도 일반적으로 쓰는 기본모델은 불편해서 잘 안쓰고 straigt line have to be straight라는 논문의 방법을 주로 사용합니다.

  • Peregrino 2018.12.09 15:51 신고 ADDR 수정/삭제 답글

    안녕하세요 다크님. 항상 블로그 잘 보다가 질문이생겨 남깁니다. 해당글에서 올려주신 다크님의 영상 왜곡보정 예제코드를 사용해보려고 하고 있는데요 처음에 사용하시는 x,y좌표의 경우는 그냥 이미지상의 픽셀좌표를 사용하면 되는건가요? 아니면 픽셀좌표계를 다른 (예를들면 normalized) 좌표계로 옮긴 후에 사용하는건가요? 기초지식이 부족해 기초적인 질문을 하게되네요 ^^; 항상 블로그 잘 보고있습니다