오늘은 blender에서 어떻게 camera parameter(intrinsic & extrinsic)를 추출할 수 있는지 소개하고자 한다.
아래 코드는 글쓴이가 코딩한 코드는 아니고, 분석만 해봤다.
Intrinsic parameter
3x4 camera matrix from blender camera
In computer vision, the transformation from 3D world coordinates to pixel coordinates is often represented by a 3x4 (3 rows by 4 cols) matrix P as detailed below. Given a camera in Blender, I need ...
blender.stackexchange.com
def get_sensor_size(sensor_fit, sensor_x, sensor_y): # 센서의 가장 긴 부분 활용
if sensor_fit == 'VERTICAL':
return sensor_y
return sensor_x
def get_sensor_fit(sensor_fit, size_x, size_y):
if sensor_fit == 'AUTO': # 자동으로 이미지의 가로, 세로 크기를 비교하며 sensor의 적합성 결정
if size_x >= size_y: return 'HORIZONTAL' # 이미지의 가로가 더 길면 horizontal로 설정,
else: return 'VERTICAL' # 이미지의 세로가 더 길면 vertical로 설정
return sensor_fit
'''
sensor_fit == vertical
센서의 세로 길이를 최대한 활용해 이미지의 세로 방향을 강조할 수 있음
이미지의 세로 방향의 왜곡을 감소시키고, 시각적인 디테일 강조 가능
'''
def get_camera_parameters_intrinsic(camera_obj, scene):
""" Get intrinsic camera parameters: focal length and principal point. """
# ref: https://blender.stackexchange.com/questions/38009/3x4-camera-matrix-from-blender-camera/120063#120063
focal_length = camera_obj.data.lens # [mm], focal length
res_x, res_y = bpy.context.scene.render.resolution_x, bpy.context.scene.render.resolution_y # image resolution (가로, 세로)
# res_x: 1920, res_y: 1080
cam_data = camera_obj.data # 활성화된 camera
sensor_size_in_mm = get_sensor_size(cam_data.sensor_fit, cam_data.sensor_width, cam_data.sensor_height)
# sensor_width: 1920, sensor_height: 1080 --> sensor_size_in_mm: 36
sensor_fit = get_sensor_fit(cam_data.sensor_fit,scene.render.pixel_aspect_x * res_x,scene.render.pixel_aspect_y * res_y)
# sensor_fit: Horizontal - 넓은 시야각과 가로로 긴 이미지 영역 제공
# sensor_fit: Vertical - 좁은 시야각과 세로로 긴 이미지 영역 제공
pixel_aspect_ratio = scene.render.pixel_aspect_y / scene.render.pixel_aspect_x
# 2D 이미지 픽셀의 비율: 일반적으로 pixel은 정사각형이라 pixel_aspect_y, pixel_aspect_x는 모두 1이기에 가로 세로 픽셀의 비율은 1
if sensor_fit == 'HORIZONTAL': view_fac_in_px = res_x
else: view_fac_in_px = pixel_aspect_ratio * res_y
# view_fac_in_px: 센서를 통해 투영된 이미지가 몇 개의 픽셀로 구성되는지를 나타냄
pixel_size_mm_per_px = (sensor_size_in_mm / focal_length) / view_fac_in_px
f_x = 1.0 / pixel_size_mm_per_px # mm --> pixel
# f_x = focal length * (pixel / mm)
f_y = (1.0 / pixel_size_mm_per_px) / pixel_aspect_ratio # mm --> pixel
# f_y = focal length * (pixel / mm) / pixel_aspect_ratio
# ex. pixel의 가로, 세로 비율이 2:1인 경우 pixel_aspect_ratio는 1/2 이 되고,
# --> f_y에 pixel_aspect_ratio를 나눠줘서 세로 방향의 픽셀 크기를 적절히 늘려서, pixel의 가로 비율 때문에 이미지가 가로로 늘어난 왜곡을 보정
c_x = (res_x - 1) / 2.0 - cam_data.shift_x * view_fac_in_px
# cam_data.shift_x * view_fac_in_px: 센서가 x축 방향으로 이동한 양을 픽셀 단위로 변환해 보정
c_y = (res_y - 1) / 2.0 + (cam_data.shift_y * view_fac_in_px) / pixel_aspect_ratio
# (cam_data.shift_y * view_fac_in_px) / pixel_aspect_ratio: 센서가 y축 방향으로 이동한 양을 픽셀 단위로 변환해 보정
# (res_x, res_y - 1): 픽셀 인덱스가 0부터 시작하기 때문에 1 빼줌 - 이미지의 정확한 중심 찾기
# 카메라 센서의 주점이 왜곡된 경우, 이미지의 중심에서 가로, 세로 방향으로 얼마나 이동해야 하는지를 픽셀 단위로 계산
intrinsic_matrix = np.array([[f_x, 0, c_x], [0, f_y, c_y], [0, 0, 1]])
# intrinsic_matrix:
#[[2.66666667e+03 0.00000000e+00 9.59500000e+02]
# [0.00000000e+00 2.66666667e+03 5.39500000e+02]
# [0.00000000e+00 0.00000000e+00 1.00000000e+00]]
return intrinsic_matrix
Extrinsic parameter
Blender는 Opengl 좌표계를 이용하기 때문에 extrinsic matrix를 구하는 코드를 보기 전, Opengl 좌표계에서 Opencv 좌표계로 어떻게 변환할 수 있는지 확인해보자.
아래 이미지는 Opencv와 Opengl 좌표계간의 차이다.
Opencv: x(+), y(+), z(+)
Opengl: x(+), y(-), z(-)

만약 opengl → opencv 또는 opencv → opengl로 좌표계를 변환하고 싶다면 extrinsic matrix에 해당하는 y, z axis(3x4 행렬에서 2, 3번째 행)에 -를 곱해주면 된다.

아래 코드에서 blender가 opengl를 따르기 때문에 opengl에서 opencv로 coordinate을 변환하기 위해서 extrinsic matrix를 구할 때 np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]])에 해당하는 matrix를 곱해줬다.
def get_camera_parameters_extrinsic(camera_obj):
# blender는 opengl을 기본으로 하고, 아래는 extrinsic matrix를 opencv의 좌표계로 변환하기 위해서 extrinsic matrix의 2, 3번째 행(y, z axis)에 -를 곱한다.
# ref: https://blender.stackexchange.com/questions/38009/3x4-camera-matrix-from-blender-camera
bcam = camera_obj # 활성화된 camera
R_bcam2cv = np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]]) # opengl --> opencv로 coordinate을 바꾸기 위해 coordinate의
# blender coord = opengl: np.array([[1, 0, 0], [0, -1, 0], [0, 0, -1]]) x축: 양옆, y축: 앞/뒤, z축: 위/아래
# 일반적인 coord = opencv: np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) x축: 양옆, y축: 위/아래, z축: 앞/뒤
location = np.array([bcam.matrix_world.decompose()[0]]).T
# camera location == translation
# bcam.matrix_world.decompose(): (Vector((2.819077968597412, 0.0, -1.0260604619979858)), Quaternion((0.4055797755718231, 0.5792279839515686, 0.5792280435562134, 0.40557974576950073)), Vector((1.0, 1.0, 1.0)))
# bcam.matrix_world.decompose(): position, rotation(Quaternion), scale
R_world2bcam = np.array(bcam.matrix_world.decompose()[1].to_matrix().transposed())
# Quanternion == bcam.matrix_world.decompose()[1]
# Quanternion으로 3x3 rotation matrix 만들기: bcam.matrix_world.decompose()[1].to_matrix()
# C: world coordinate 기준 카메라의 위치, Rc: world coordinate 기준 카메라의 방향
# R_world2bcam == (Rc)T <-- (Rc)T == R, camera coordinate 기준 카메라의 위치
T_world2bcam = np.matmul(R_world2bcam.dot(-1), location)
# T_world2bcam == -(Rc)T C <-- -(Rc)T C == t, camera coordinate 기준 카메라의 방향
R_world2cv = np.matmul(R_bcam2cv, R_world2bcam)
T_world2cv = np.matmul(R_bcam2cv, T_world2bcam)
# rotation, translation matrix(camera coord 기준) 모두 opengl에서 opencv 좌표계 표현으로 변환
extr = np.concatenate((R_world2cv, T_world2cv), axis=1)
extr = np.concatenate((extr, np.array([[0,0,0,1]])), axis=0)
return extr
'실습 & 활동 > Computer vision' 카테고리의 다른 글
[Omniverse] Camera parameter 추출 및 시각화 (0) | 2024.05.24 |
---|---|
[Blender] CC4 coordinate to Blender coordinate (0) | 2024.05.24 |
[Omniverse] Synthetic Data 만들기 (0) | 2024.05.23 |
[SMPL] 실습 코드 분석 (0) | 2024.05.08 |
[SMPL] single human synthetic data SMPL 실습 결과 (0) | 2024.05.07 |