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

영상처리 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 다크 프로그래머


저작자 표시 비영리 변경 금지
신고
  • 이전 댓글 더보기
  • NevFiasco 2015.03.13 10:40 신고 ADDR 수정/삭제 답글

    답변 감사합니다. :)

  • 제네럴백 2015.03.18 14:09 신고 ADDR 수정/삭제 답글

    안녕하세요 다크프로그래머님~ 좋은 글 잘 보고 있습니다.

    calibration parameter가 주어진 stereo video dataset를 최근에 얻을 수 있었는데요. 이러한 경우 왜곡보정과 rectification을 어떻게 수행해야하는지 도저히 감이 안잡히네요. 내부 요인에 의한 왜곡 보정은 다크프로그래머님 따라서 구현할 수 있는데요. 외부 파라미터 (R [3x3], t[1x3]) 을 이용한 retification 과정을 알려주실수 있나요? 최종적인 목표는 retification 후 depth map 계산입니다.!

    항상 감사드립니다

    • BlogIcon 다크pgmr 2015.03.19 06:19 신고 수정/삭제

      스테레오 카메라가 두 카메라 A, B로 구성되어 있을 때, 두 카메라의 원점을 잊는 선이 두 카메라 좌표계의 Y축이 되도록 (그리고 광학축은 서로 평행이 되도록) A 카메라의 이미지 평면과 B 카메라의 이미지 평면을 각각 3차원 회전시키는 변환 과정이 rectification입니다. 구체적인 구현방법은 opencv에서 제공하는 rectification 함수의 구현 소스코드를 살펴보시면 좋을 것 같습니다.

  • pprb 2015.06.08 18:07 신고 ADDR 수정/삭제 답글

    안녕하세요 ! 아침에 답변받고 다시 차근차근 읽어보다 궁금한게 있어 질문드려요
    좌표계 변환을 하기 위해 k1, k2 값을 구하려고 하는데요,
    댓글들까지 읽다보니 어떤 분이 GML 돌리면 distortion에서 k1,k2,p1,p2 값이라고 설명해주셨던데,,
    [ 0.152845 -0.409421 0.015986 0.004005 ] ± [ 0.082452 1.093356 0.003895 0.003730 ]
    값이 이렇게 나왔을 때 순서대로 k1, k2 값인가요??
    저는 테스트할때, 첫번째 세번째 값을 k1, k2로 놓고 했는데,
    첫번째 두번째 값을 넣으니 결과가 제대로 나오지 않아서 혼란에 빠졌어요,,

    • BlogIcon 다크pgmr 2015.06.08 18:11 신고 수정/삭제

      네, 지금 결과에서는 k1=0.152845, k2=-0.409421, p1=0.015986, p2=0.004005 로 해석하시면 됩니다.

    • 달암 2017.08.28 17:32 신고 수정/삭제

      GML 돌리면 된다는 댓글은 어디에 있나요? 검색해도 안나오는데ㅠㅠ

  • 2015.06.20 06:29 신고 ADDR 수정/삭제 답글

    안녕하세요 질문있습니다
    http://carstart.tistory.com/181 여기서 보니 방사왜곡의 식이 x_collected=x(1+k....)으로 되어있고여기서 (x, y)는 왜곡된 점의 원래위치를 나타내고(X_corrected, Y_corrected)는 보정된 새로운 위치를 나타냅니다
    이 포스트에서 식 (2)번의 경우 x_d=x_u(1+k...)으로 되어있는데 x_d는 왜곡된 위치라면 위 두 방정식이 서로 반대가 되는데 어떻게 된걸까요? 어디서부터 이해가 안되는지 잘 모르겟네요

    • BlogIcon 다크pgmr 2015.06.20 14:17 신고 수정/삭제

      네, 말씀하신데로 저도 살펴보니 두 식이 반대네요. 결론부터 말씀드리면 두 식중 어느 것도 틀리다고는 말할 수 없지만 opencv 에서는 사용되는 식은 x_distorted = (1+k1r^2+...)x_undistorted 입니다. 그리고 워낙 opencv가 인지도가 있다가 보니 또한 일반적으로 통용되는 식이기도 합니다. 하지만, 두 식의 차이는 단지 모델링을 어떻게 했으냐의 차이일 뿐 어느 것도 잘못된 것은 아닙니다. 핵심은, "방사왜곡으로 인한 좌표의 스케일 변화를 다항함수로 근사한다"이며 x_d = f(r)*x_u로 할지 x_u = g(r)x_d로 할지는 단순히 선택의 문제입니다. 물론 f(r)과 g(r)은 서로 다른 다항함수가 나올 테지만 어느 것을 사용하든 f(r)과 g(r)이 구해지면 x_d와 x_u 사이의 상호변환이 가능한 점은 변함이 없습니다.

  • 2015.07.21 14:59 신고 ADDR 수정/삭제 답글

    안녕하세요 늘 도움 많이 받고 있습니다
    왜곡 보정을 해야하는데 질문이 있습니다
    혹시 다운 받은 이미지만 가지고도 카메라 내부 파라미터 값을 구하는 방법이 있을까요?
    외부에서 받은 이미지라 카메라 관련 정보를 알 수가 없는데요.. (사진 내부에 체스보드 판도 없습니다)
    어안렌즈로 찍은 사진을 가져와서 왜곡 보정해서 펴는 작업을 하고 싶어서요

    • BlogIcon 다크pgmr 2015.07.21 15:50 신고 수정/삭제

      안녕하세요. 저도 잘은 모릅니다만 혹시 autocalibration 키워드 검색이나 F. Devernay and O. D. Faugeras. Straight lines have to be straight. Machine Vision and Applications, 13(1):14–24, 2001. 논문 내용이 도움이 될지 모르겠습니다.

  • 닉주니어 2015.09.10 20:24 신고 ADDR 수정/삭제 답글

    모델링 식이 픽셀좌표계가 아닌 정규좌표계 기준이었군요!!
    이 블로그를 통해서 알았습니다. ^^
    어쩐지 암만 해도 단위가 안맞아서 완전 헤매고 있었는데, 정말 큰 도움 받았습니다. 꾸벅.
    감사합니다!

  • 울랑꿀떼루 2015.10.21 16:26 신고 ADDR 수정/삭제 답글

    안녕하세요! 이렇게 또 질문을 남기게 되었네요^^; 매번 상세한 답변 감사드리며 항상 열심히 하시는 자세에 많이 배웁니다.
    이번에 여쭤보고자 하는 것은 영상의 중점에 관한 것입니다.
    중점은 principal point라고 하여 cx, cy로 표현되는 것으로 알고 있습니다.
    만약 입력 영상의 크기가 720x480일 경우, 이상적인 cx, cy는 360,240이 됩니다.
    그러나 실제 캘리브레이션을 통해 얻어진 cx, cy가 만약 350,235였다고 할 때, 이 cx, cy의 의미는 카메라 렌즈와 센서의 중점 ( = 카메라를 통해 촬영된 영상의 중점 , 저장된 영상의 중점은 아닐 수 있음)인 것으로 이해하였습니다. 접선왜곡이 있다 할지라도 영상의 중점은 그대로 있고, 상하 좌우로 렌즈와 센서가 뒤틀린 것에 대하여 보정이 이루어 지니까요..
    그렇다면 빈 영상을 하나 생성하고 (350,235)에 점 하나를 찍어 왜곡을 보정하였을 경우, 결과 영상에서도 (350,235)에 점이 그대로 있어야 하는 것이 아닌지요? 실험을 해 보니 400이 넘는 엉뚱한 값이 나와 고민하다 이렇게 질문을 해 봅니다.ㅠㅠ
    최종적으로, 제가 궁금한 것은 보정된 영상의 중점이 어디인가? 하는 것입니다.

    • BlogIcon 다크pgmr 2015.10.21 16:39 신고 수정/삭제

      네 당연히 그대로 있어야 합니다. 어떻게 왜곡을 펴셨는지 궁금하네요.

    • 2015.10.21 18:04 수정/삭제

      비밀댓글입니다

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

      네, 정확합니다. 식에서 in_x = cx, in_y = cy를 대입해 보면 out_x = cx, out_y = cy 가 나옴을 쉽게 확인할 수 있습니다. 타이핑 오류 등 무언가 다른 종류가 오류가 있을 것으로 추정됩니다. step by step으로 디버깅을 해 보시기 바랍니다.

  • 논문작성중인학생 2015.12.19 05:58 신고 ADDR 수정/삭제 답글

    안녕하세요!! 지난번에도 한번 글을 남겼던 적이 있었는데!! 정말 너무너무 위대하시고 대단하십니다 정말!! 너무 감사하단 말씀을 이렇게 글로 밖에 못적어 아쉽습니다! 논문을 이쪽 주제로 현재 진행중인데, 0에서 시작한 지식이 다크님 덕분에 99까지 차올랐다고 할 수 있을정도로 매일 들어와서 공부하고 갑니다! 올려주신 코드와 관련해서 여쭤볼게 있는데요.

    DistortPixel 에서 마지막에 0.5를 더하셔서 정수형으로 바꿔주신 것 같은데(즉 반올림), 혹시 이렇게 픽셀좌표를 정수형으로 썼을 때의 장점을 알 수 있을까요?

    그리고 이건 답변을 부탁드리는 건 아니고요. 그냥 여쭤볼게요~ 혹시 외부 요건에 따라(예를들어 온도의 변화) 카메라의 왜곡계수가 바뀐다고 했을 때(k1',k2'), 이걸 스스로 캘리브레이션을 해서(체커보드 등을 이용한 오프라인 캘리브레이션 과정없이) 값을 맞춰서 보정된 영상이 나오게 하려고 하는데요. 저는 지금 이렇게 진행중입니다.
    1. 일단 가상의 체커보드를 만들어서 3D좌표를 지정해줍니다.
    2. Camera Matrix는 상온에서 한번 캘리브레이션을 했다고 가정하고, 값을 가져옵니다.
    3. [R|t]값은 제가 지정해줍니다.
    4. 3D좌표에 매칭되는 왜곡된 2D좌표값을 구합니다.(k1',k2' 이용)
    5. 구한 3D좌표와 3D좌표를 인풋으로 넣어서 calibrateCamera 함수를 돌려줍니다.

    5번의 결과로 사실 새로운 왜곡계수 값이 나와야 한다고 하는데, 이론상으론 같은 값이 나와야 하는게 맞지 않나요?(즉,k1',k2') 아니면 애초에 왜곡계수가 온도에 따라 변한다고 했을때, 어떤식으로 그 왜곡계수를 맞춰서 왜곡보정을 해야하는지 모르겠습니다.

    결론은,, 다크님은 정말 위대하십니다. 블로그 홍보 주변 지인들에게 많이 하고 있습니다! 언제나 화이팅이십니다!! 감사합니다.

    • BlogIcon 다크pgmr 2016.01.05 17:11 신고 수정/삭제

      온도변화에 대한 내용은 저도 알지 못하는 내용입니다..

  • 회색하늘 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 신고 수정/삭제

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