WPF OpenCV 프로젝트 #28: Camera Calibration (ChessBoard)

Camera Calibration (카메라 캘리브레이션)과 관련하여 Barrel distortion (방사 왜곡) 이 Camera 에서 발생하였때 이것을 처리하는 방법에 대해 WPF OpenCV 프로젝트에 추가 하도록 하겠습니다.

지난 포스팅(#26, #27)에서는 우리가 Lens Distortion Simulation을 통해 일부러 멀쩡한 이미지를 볼록하거나 오목하게 찌그러뜨려 봤습니다. 그것도 두 번에 걸쳐서 진행을 했었죠. 하나는 OpenCV의 Polar 함수를 이용한 방법이었고, 다른 하나는 수학 연산을 이용한 Remap() 을 통해 왜곡 효과를 진행했었습니다. 둘의 차이점도 정리를 했었으니까 잘 생각이 안 난다면, 꼭 다시 한번 확인 해 보세요.

이번에 다룰 내용은 현장의 실제 영상 처리 검사 장비에서 Camera 를 포함한 광학계를 설치 하고, 이미지를 획득 하였는데, Barrel distortion ( 방사 왜곡)이 발생하는 경우 입니다. 무슨 말이냐 하면 설치된 Camera를 이용하여 검사 대상 이미지를 가져왔는데, 볼록 하게 이미지가 보이는 경우 입니다. 이 상태의 이미지를 가지고 영상 처리 검사를 진행 할 수는 없습니다. 그래서 Camera Lens 에서 발생하는 왜곡을 없애는 작업이 필요합니다. 이것을 Calibration 한다 라고 합니다. 발생하는 왜곡을 없애서 반듯하게 만드는 작업이죠.
대부분의 카메라(CCTV, 웹캠, 블랙박스 등)는 더 넓은 시야를 담기 위해 볼록 렌즈를 사용합니다. 이 때문에 직선인 벽면이나 도로가 둥글게 휘어져 보이는 방사 왜곡(Radial Distortion/Barrel Distortion)이 필연적으로 발생합니다. Calibration 되지 않은 상태에서 이미지를 획득하여 영상 처리를 진행하면, 정밀 계측, 검사 장비에서 치명적인 오차를 유발 하게 되는 것이죠. 그러면 Barrel Distion에 대해 수학적으로 가지는 의미와 Camera Calibration 에 대해 간략히 정리하도록 하겠습니다.

.

Barrel Distortion

Barrel Distortion(방사 왜곡)은 이미지의 중심에서 멀어질수록(반지름 rr이 커질수록) 굴절률이 달라져 발생합니다. 이를 수학적으로 모델링할 때는 다항식(Polynomial)을 사용하여 근사화 해야 합니다. 렌즈 중심에서 멀어질수록 빛이 굴절되는 정도가 심해집니다. 이를 수학적으로는 rr(중심에서의 거리)에 대한 다항식으로 표현합니다.

xdistorted=x(1+k1r2+k2r4+k3r6)x_{distorted} = x(1 + k_1 r^2 + k_2 r^4 + k_3 r^6)

ydistorted=y(1+k1r2+k2r4+k3r6)y_{distorted} = y(1 + k_1 r^2 + k_2 r^4 + k_3 r^6)

  • x,yx, y : 왜곡되지 않은 이상적인 좌표
  • k1,k2,k3k_1, k_2, k_3 : 우리가 찾아야 할 왜곡 계수(Distortion Coefficients)
  • (xdistorted, ydistorted): 실제 카메라 센서에 맺힌 왜곡된 좌표
  • r: 이미지 중심(Principal Point)으로부터의 거리 (r2 = x2 + y2)
  • k 값이 양수(+)이면: 배럴 왜곡 (Barrel, 술통형) – 이미지가 밖으로 팽창하는 느낌.
  • k 값이 음수(-)이면: 핀쿠션 왜곡 (Pincushion, 오목형) – 이미지가 안으로 빨려 들어가는 느낌.
  • 일반적인 이미지 검사 장비에서는 보통 k1, k2까지만 사용해도 충분하지만, 어안 렌즈(Fish-eye)급의 광각 렌즈를 쓴다면 k3까지 사용하여 고차항 보정을 수행합니다.

결국 Camera Calibration 이란, 설치된 Camera와 광학계를 통해 획득한 여러 장의 사진을 분석해서 저 수식 속의 미지수(kk 값들과 카메라 초점 거리 등)를 역으로 찾아내는 과정입니다.

ChessBoard

Camera Calibration (카메라 캘리브래이션)을 하려면 “원래는 직선이어야 하는 패턴”이 필요합니다. 체스보드는 흑백이 교차하는 격자 무늬가 명확해서, 영상 처리 시스템이 코너(Corner) 포인트를 찾기에 가장 완벽한 패턴입니다.

Camera Calibration Sequence

실제 영상 처리 검사 장비에서는 언급한 왜곡을 없애고, 이미지 상의 픽셀(Pixel) 좌표를 실제 물리적 거리(mm)로 변환하기 위해 캘리브레이션을 수행합니다. 이 과정은 카메라의 내부 파라미터(Intrinsic Parameters)를 구하는 방법인데, 가장 보편적으로 사용하는 장(Zhang)이라는 방법으로 정리 해볼게요.
실제 검사 장비에서는 다음 두 가지를 미리 준비해야 합니다.

첫 번째Calibration Pattern (캘리브레이션 패턴) 으로 일반적으로 ChessBoard (체스 보드) 또는 Circle Gird(원형 그리드: 비대칭 원형 그리드)가 그려진 대상체(Glass, 출력물) 입니다.
두 번째는 반사가 일어 나지 않고, 균일하게 비추는 조명이 필요합니다.
둘다 현장에서 설치된 장비에서 진행할 때 필요한 것인데, 우리의 프로젝트에서는 첫 번째 준비물만 있으면 됩니다. 즉 두 번째의 조명이 설치된 장비 에서 획득한 Calibration Pattern의 이미지가 있다는 조건으로 진행하는 거죠.

Seq 1: ChessBoard 또는Circle Grid 가 그려진 대상체의 패턴을 다양한 각도, 거리, 위치에서 촬영해서 최대 20장 까지 획득합니다. 이렇게 획득한 이미지의 패턴이 중심 뿐만 아니라, 구석 (Corner) 부분에도 위치 한것도 필요합니다. 왜곡은 중심 보다 구석 즉 외곽에서 심하니까요.

Seq 2: 아직 관련 알고리즘에 대해 포스팅하지 않았지만, 특징점 추출(Feature Extraction) 알고리즘을 이용해서 패턴의 코너나, 원의 중심을 찾아야 합니다. Sub-Pixel (서브 픽셀) 알고리즘을 적용해서 픽셀 단위보다 더 정밀하게 좌표를 찾아내야 합니다. 영상 처리 검사 장비에서는 거의 필수적인 과정입니다.

Seq 3: 카메라 행렬 계산 (Optimization) 획득한 2D 이미지 좌표와 실제 3D 월드 좌표(패턴의 격자 크기, 예: 20mm) 간의 관계를 이용하여 방정식을 풉니다. 이를 통해 다음 두 가지 핵심 정보를 얻습니다.

첫 번째는 내부 파라미터(Intrinsic Matrix) 인데, 초점 거리 (fx, fy)이며, 렌즈와 센서 사이의 거리, 그리고 렌즈의 광학적 중심점인 주점(Cx, Cy)의 정보를 찾습니다. (이미지의 정중앙이 반드시 광학적 중심이 아닐 수 있습니다.)
두 번째왜곡 계수(Distortion Coefficients)로 앞서 수학적 의미에서 표현했던, k1, k2, k3, k4(방사 왜곡)p1, p2(접선 왜곡: 렌즈와 센서가 수평이 아닌 경우 발생) 입니다.

Seq 4: Seq3에서 획득한 파라미터왜곡 계수를 역으로 이용해서 찌그러진 이미지를 펴주는 작업 즉, 수학적으로 왜곡된 픽셀 위치를 계산하고, 새로운 이미지의 픽셀 값을 채워 넣는 Remapping 과정을 진행합니다. 이제 WPF OpenCV 프로젝트에 연결된 Camera는 없지만, 이미지가 준비되었다는 전제로 Calibration 기능을 구현해 보도록 하겠습니다. 늘 그래왔던 것 처럼 구현 내용을 요약을 하고 시작하겠습니다.

구현 요약

Calibration 데이터 모델을 저장하고, 불러오기 위한 CalibrationData.cs 클래스를 생성합니다.
AlgorithmParameters.cs 파일에 CameraCalibrationParams 클래스를 생성합니다.
UI(View)에서 CameraCalibrationParamsProperty 속성을 UI에 연결합니다.
MainViewModelCalibration 명령과 체크 박스 속성을 추가합니다.
OpenCVServiceChessboard 를 인식하고, Calibration을 실행 및 왜곡 보정 함수를 추가합니다.

Step 1: CalibrationData 클래스 생성 (Model 생성)

CalibrationData 클래스는 Camera의 속성을 나타내는 클래스 입니다. 이 클래스를 통해 Camera 정보 (행렬, 왜곡계수, 해상도)를 가져와 저장하는 역활을 하는 것이죠. 그리고 그 정보는 반드시 기억하고 있어야, 이 카메라를 이용해서 얻은 다음 이미지에도 해당 정보를 입혀서 이미지 보정을 할 수 있는 거겠죠. 다만, 우리는 연결된 카메라가 없고 동일한 카메라를 이용해서 획득했다는 이미지만 있는 상태인것입니다. 정리하면 두가지 역활을 합니다. 계산된 카메라 정보를 calibration.json 이라는 파일로 예쁘게 포장해서 저장하구요. 프로그램이 실행될 때, 저정해 둔 정보 파일을 읽어 와서 해당 특성으로 복구 시키는 것입니다.

클래스의 주요 변수와 함수는 다음과 같습니다.
CameraMatrix : 카메라 메트릭스인 3×3 행렬 데이터
DistCoeffs : 왜곡 계수
ImageWidth : 이미지 가로 크기
ImageHeight :이미지 세로 크기
Save : 속성 데이터 저장.
Load : 속성 값 불러오기.

Step 2: CameraCalibrationParams 클래스 생성 (Model 생성)

AlgrorithmParameters.cs 파일에 CameraCalibrationParams 클래스를 생성합니다. 이제 까지 그래 왔던것 처럼 사용자가 조절할 수 있는 부분에 대한 모델 속성 클래스를 생성하여 추가합니다. Camera Calibration에 사용할 이미지는 Chessboard 이며, PatternWidthPatternHeight 를 조절 할 수 있도록 Property 변수를 정의합니다. 아래 코드를 참고해 주세요.

Step 3: UI(View)

MainWindow.xaml 파일 에서 CameraCalibrationParams 클래스 객체를 연결 하도록 합니다. CameraCalibration 알고리즘을 선택하였을 때, CameraCalibrationParams 클래스의 Property 변수를 접근할 수 있도록하고, Calibration 이미지를 선택할 수 있도록 Button 을 생성하고, 이미지가 선택이되면 카메라 메트릭스 행렬과, 왜곡 계수를 즉시 연산할 수 있도록 했습니다. 아래의 코드가 약간 헤깔릴 수 있는데, 기존 ICommad 를 사용한 방식을 그대로 적용하려고, Button 에 대한 CommandDataContext로 연결하였으니 이해하는데 아래의 DataTemplate 코드를 참고해 주세요.

Step 4: ViewModel

MainViewModel.cs 파일에는 기존 방식과 동일하게 진행 하도록 하죠. 제일 먼저 MainViewModel() 생성자 함수에 Camera Calibration 항목을 추가합니다. 사용자가 Camera Calibration 항목을 선택하면, CameraCalibrationParams 클래스 객체를 생성하도록 CreateParametersForAlgorithm() 함수에 아래와 같이 코드를 추가하여 업데이트 합니다. 추가할 함수가 하나 더 있는데요. UI(View)에서 카메라 캘리브레이션을 할때, Chessboard 이미지 여러 장을 가져와 카메라 매트릭스 데이터와 왜곡 계수 연산을 완료하는 버튼을 하나 만들었던 것 기억하나요? 바로 위에서 말이죠. 이미지를 불러오고, Step 5에서 수행할 영상 처리 함수를 호출 하는 함수가 하나 필요합니다. 엄연히 이미지를 불러오는 부분은 ViewModel 로 분리되어 처리되어야 하기에 여기에 함수를 추가하였습니다

Step 5 : OpenCVService (Model)

OpenCVService.cs 파일에서 이제 Camera Calibration 을 위한 영상 처리를 진행하도록 해야 겠죠. Calibration 변수들을 선언합니다. 선언된 변수들은 Calibration 데이터의 존재 유/무에 대한 변수와 Calibration 파일 명, 왜곡계수, 카메라 메트릭스 변수, CalibrationData 클래스 변수 입니다. OpenCVService 생성자 함수에는 그동안 아무런 코드가 없었는데, Camera 설정 값과 왜곡 변수를 그대로 적용한다는 전제하에서 생성자함수에서 Calibration 데이터를 가져오도록 LoadCalibrationData() 함수를 추가하였으며, RunCalibration() 함수를 통해 Camera Calibration이 수행되도록 함수를 추가했고, Calibration 데이터가 적용된 이미지로 업데이트 하기 위한 UpdateCalibrationMatrices () 함수도 추가했으니, 아래코드를 참고해 주세요. (생각 보다 추가한 부분이 많네요. 사실 ProcessImageAsync() 함수에서 길게 나열하듯이 코드를 작성해도 되긴 하는데 가독성이 떨어져서 분리 한것 밖에 없으니 어려워 하지 마세요. ^^)


실행 및 확인

추가할 내용이 좀 있긴 했지만, 그리 길진 않았습니다. 최대한 기존 프로젝트에서 진행하던 방식을 따라 가느라, UI(View) 부분이 조금 어렵게 느껴지지 않았을 까 생각하는데, 어려운 내용 아니니까 찬찬히 읽어 보면 충분히 이해할 수 있을거라 생각합니다. 잘 따라 왔다면, 빌드에 별다른 문제는 없을 겁니다. 프로그램 빌드 후 실행을 위해서는 Chessboard 이미지가 필요합니다. 인터넷으로 체스트 보드를 검색하시면 무료 이미지들이 있을 거니까 그것을 사용해도 됩니다. 다만 chessboard를 그대로 사용할 수는 없구요. 왜곡된 이미지를 만들어야 합니다. 약 10 장에서 20장 정도요. 방법은 쉬워요. 이전 포스트에서 Lens distortion 효과를 입힐 수 있었던것 기억하죠? 그 기능으로 만들면 됩니다. 임의의 4 점을 지정해서 볼록한 왜곡 효과를 입힌 이미지를 아래와 같이 여러 장 만들어서 준비해 주세요.

이제 WPF OpenCV 프로그램을 실행 합니다. 알고리즘 선택 부분에서 Camera Calibration 을 선택하면 아래와 같이 알고리즘 속성이 보일 겁니다. Select Image & Run 버튼을 클릭하면 이미지를 선택할 수 있는 윈도우가 실행 됩니다.

Calibration에 사용할 볼록 왜곡된 이미지를 선택합니다. (그림에서는 11.png ~ a.png)

이렇게 하면 카메라 메트릭스 행렬과 왜곡 계수를 진행하게 되고, 완료가 되면 아래와 같이 메시지 박스가 보일거에요. (확인을 눌러 메시지 박스를 종료 해주세요.)

Camera Calibration을 위해 정확히는 이미지 10장에 대한 왜곡계수를 구해서 이제부터 불러 들인 이미지를 왜곡계수카메라 메트릭스 행렬에 맞쳐 변환할 수 있게 됩니다. 정리하면, Camera Calibration 알고리즘이 선택된 상태에서 왜곡 계수 값이 이제부터 불러 들이는 이미지에 적용할 수 있게 되는 것이죠. 아래와 같이 이미지 파일 열기 버튼을 눌러 Calibration에 사용된 왜곡된 이미지 한 장을 불러 들입니다.

이 상태에서 적용 버튼을 누르면, 아래의 이미지와 같이 위의 왜곡된 이미지가 어느 정도 보정되는 것을 확인 할 수 있을거에요. ^^

혹시 예상했던 보정 이미지와 많이 다른가요? 사실 사용된 왜곡 이미지는 이미지 중심에서 너무 볼록하게 보였는데요. 실제 Camera에서 왜곡된 이미지를 획득해보면, 가장 자리(edge) 로 갈수록 휘어 보입니다. 중심은 반듯하게 보이구요. Calibration(보정)된 이미지의 중앙이 휘어져 있는 것처럼 보이는 건 여기서 별로 의미는 없습니다. 가장자리로 갈수록 ChessBoard 의 선이 반듯하게 되는것이 중요합니다. 그 관점에서 보정 전과 보정 후의 이미지를 살펴봐 주세요. 그리고, Calibration 수행에 필요한 왜곡 이미지가 조금 더 많아야야 하구요. Calibration 진행 후 RMS 값이 사실 조금 크긴 합니다. 이것이 줄어야 더 향상된 보정이 이뤄지죠. 아무튼 이렇게 구해진 _cameraMatrix_distCoeffs 값만 저장해 두면, 앞으로 해당 카메라로 찍는 모든 영상은 실시간으로 펴줄 수 있습니다. 이미지를 10장 밖에 만들지 않았는데, 시간이 될 때 더 많이 만들면 다시 이번 포스팅을 다시 한번 업데이트 하도록 하겠습니다. 이것으로 Geometric Transform은 마무리하도록 하겠습니다.
다음 포스팅에서는 영상 필터 (Filter) 관련 내용으로 이어 가도록 하겠습니다.

참고 자료

[Post #26] 렌즈 왜곡 시뮬레이션: [WPF OpenCV Project #26] – 일부러 왜곡 만들기
[Post #27] 왜곡 최적화: [WPF OpenCV Project #27] – Remap과 성능 최적화
CalibrateCamera: OpenCV Docs – 카메라 캘리브레이션 함수 상세
Undistort: OpenCV Docs – 왜곡 보정 함수
Camera Calibration: OpenCV Tutorial – 캘리브레이션의 수학적 원리와 예제

댓글 남기기