자주 쓰진 않겠지만 depth로부터 disparity 데이터를 만들어두는 방법을 정리해두면 좋을 것 같아 이 글을 적게 되었다. 매번 수식 까먹고 검색하는게 귀찮다.
시뮬레이션 데이터든, realsense로 직접 취득한 데이터든 stereo setting에서 이미지와 depth를 얻는 것은 쉽지만 disparity를 얻는 것은 까다롭다. 단순히 focal length와 baseline 길이를 이용해 변환하는 것으로 끝맺을 수 있지만 left disparity와 right disparity를 맞추는 작업을 안하면 left-to-right, right-to-left warping 시 안 맞을 수 있다. 따라서 cross check를 꼭 해줘야 한다.
def convert(depth, f, b):
disp = np.zeros_like(depth)
disp[depth!=0] = b*f / depth[depth!=0]
return disp
일반적으로 위 식을 통해 left depth 와 right depth를 disparity map으로 바꿔주는 것으로 끝나는데 한 단계 더 해줘야 한다. (depth가 비어있는 공간은 disparity 0으로 처리한다. )
def cross_check(ldisp_raw, rdisp_raw, disp_th=8):
ldisp = ldisp_raw.copy()
rdisp = rdisp_raw.copy()
img_h, img_w = np.shape(ldisp)[:2]
### forward-backward
forward = np.argwhere(ldisp != 0) # rc
forward_disp = -1*ldisp[forward[:,0], forward[:,1]].astype(np.int32)
backward = forward.copy()
backward[:, 1] = backward[:, 1] + forward_disp
valid = (backward[:,0] < img_h) * (backward[:,0] > 0) * (backward[:, 1] < img_w) * (backward[:,1] > 0)
backward = backward[valid]
backward_disp = rdisp[backward[:,0], backward[:,1]].astype(np.int32)
forward = forward[valid]
forward_disp = forward_disp[valid]
forward_backward = np.abs(forward_disp + backward_disp)
check = forward_backward < disp_th
left_valid = forward[check]
right_valid = backward[check]
lvalid = ldisp[left_valid[:,0], left_valid[:,1]]
rvalid = rdisp[right_valid[:, 0], right_valid[:, 1]]
ldisp *= 0
rdisp *= 0
ldisp[left_valid[:, 0], left_valid[:, 1]] = lvalid
rdisp[right_valid[:, 0], right_valid[:, 1]] = rvalid
### backward_forward
backward = np.argwhere(rdisp != 0) # rc
backward_disp = rdisp[backward[:, 0], backward[:, 1]].astype(np.int32) # positive
forward = backward.copy()
forward[:, 1] = forward[:, 1] + backward_disp
valid = (forward[:, 0] < img_h) * (forward[:, 0] > 0) * (forward[:, 1] < img_w) * (forward[:, 1] > 0)
forward = forward[valid]
forward_disp = ldisp[forward[:, 0], forward[:, 1]].astype(np.int32)
backward = backward[valid]
backward_disp = backward_disp[valid]
backward_forward = np.abs(backward_disp - forward_disp)
check = backward_forward < disp_th
left_valid = forward[check]
right_valid = backward[check]
lvalid = ldisp[left_valid[:, 0], left_valid[:, 1]]
rvalid = rdisp[right_valid[:, 0], right_valid[:, 1]]
ldisp *= 0
rdisp *= 0
ldisp[left_valid[:, 0], left_valid[:, 1]] = lvalid
rdisp[right_valid[:, 0], right_valid[:, 1]] = rvalid
return ldisp, rdisp
그 다음 left disparity를 이용해 right warped image 생성, right warped image를 다시 right disparity를 이용해 left warped image로 바꾸었을 때 원본 left image랑 같은지 확인하는 식으로 잘못된 disparity를 걸러주어야 깔끔한 disparity를 얻을 수 있다. 약간 더 sparse 해지는 단점이 있긴 하지만 퀄리티를 보장하기 위해선 꼭 해야 한다.
주의
- left disparity를 보통 이미지로 저장하기 때문에 양수값으로 표현되어 있지만 실제로는 음수값이라는 것을 잊지 말자.
- (오른쪽 카메라로는 왼쪽 카메라보다 더 왼쪽을 볼 수 없다.)
- disparity는 integer 값이다. cross check 과정에서 discretization하면서 잃는 정보가 아쉽다면 interpolation을 포함해라.
def vis_disparity(limg, rimg, disp, show=False):
limg = np.asarray(limg)
rimg = np.asarray(rimg)
warpred = np.zeros_like(limg) # [H, W, 3]
img_h, img_w = np.shape(limg)[:2]
u,v = np.meshgrid(np.arange(0, img_w), np.arange(0, img_h))
warp_uv = np.argwhere(disp != 0)
uw = (u-disp)[warp_uv[:,0], warp_uv[:,1]]
vw = v[warp_uv[:,0], warp_uv[:,1]]
uw = uw.astype(np.int32)
vw = vw.astype(np.int32)
warpred[vw, uw] = rimg[vw, uw]
canvas = np.concatenate([limg, warpred, rimg], axis=0)
if show:
cv2.imshow("vis disparity", canvas)
cv2.waitKey(0)
return canvas
다 만든 disparity를 시각적으로 보고 싶으면 위 함수를 이용해 warped 이미지 형태로 볼 수 있다. 입력 disparity는 left disparity이다.
끝!
'Knowhow > Vision' 카테고리의 다른 글
Stereo image rectification (0) | 2023.03.16 |
---|---|
Axis angle(Rodrigues notation) to Rotation matrix (0) | 2023.03.15 |
Tensorboard를 이용한 pb 파일 시각화 (0) | 2023.02.03 |
Basalt 사용, tag 규격 정하기, 이미지로부터 rosbag 파일 만들기 (0) | 2023.01.19 |
Theseus tutorial : simple pose estimation (0) | 2022.11.24 |