Knowhow/Vision

Disparity generation

침닦는수건 2023. 3. 8. 20:24
반응형

자주 쓰진 않겠지만 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이다.
 
끝!

반응형