programing

OpenCV – 보정되지 않은 스테레오 시스템의 깊이 맵

batch 2023. 7. 5. 20:34
반응형

OpenCV – 보정되지 않은 스테레오 시스템의 깊이 맵

저는 보정되지 않은 방법으로 깊이 지도를 구하려고 합니다. SIFT를 수 .cv2.findFundamentalMat 사합니다를 합니다.cv2.stereoRectifyUncalibrated각 이미지에 대한 호모그래피 행렬을 가져옵니다.드디어 사용합니다.cv2.warpPerspective차이를 수정하고 계산하지만, 이것은 좋은 깊이 맵을 만들지 않습니다.값이 매우 높아서 사용해야 하는지 궁금합니다.warpPerspective또는 제가 얻은 호모그래피 행렬에서 회전 행렬을 계산해야 하는 경우.stereoRectifyUncalibrated.

저는 다음과 같이 얻은 호모그래피 행렬의 경우를 가진 투영 행렬을 확신할 수 없습니다.stereoRectifyUncalibrated바로잡기 위해

코드의 일부:

#Obtainment of the correspondent point with SIFT
sift = cv2.SIFT()

###find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(dst1,None)
kp2, des2 = sift.detectAndCompute(dst2,None)

###FLANN parameters
FLANN_INDEX_KDTREE = 0
index_params = dict(algorithm = FLANN_INDEX_KDTREE, trees = 5)
search_params = dict(checks=50)

flann = cv2.FlannBasedMatcher(index_params,search_params)
matches = flann.knnMatch(des1,des2,k=2)

good = []
pts1 = []
pts2 = []

###ratio test as per Lowe's paper
for i,(m,n) in enumerate(matches):
    if m.distance < 0.8*n.distance:
        good.append(m)
        pts2.append(kp2[m.trainIdx].pt)
        pts1.append(kp1[m.queryIdx].pt)
    
    
pts1 = np.array(pts1)
pts2 = np.array(pts2)

#Computation of the fundamental matrix
F,mask= cv2.findFundamentalMat(pts1,pts2,cv2.FM_LMEDS)


# Obtainment of the rectification matrix and use of the warpPerspective to transform them...
pts1 = pts1[:,:][mask.ravel()==1]
pts2 = pts2[:,:][mask.ravel()==1]

pts1 = np.int32(pts1)
pts2 = np.int32(pts2)

p1fNew = pts1.reshape((pts1.shape[0] * 2, 1))
p2fNew = pts2.reshape((pts2.shape[0] * 2, 1))
    
retBool ,rectmat1, rectmat2 = cv2.stereoRectifyUncalibrated(p1fNew,p2fNew,F,(2048,2048))

dst11 = cv2.warpPerspective(dst1,rectmat1,(2048,2048))
dst22 = cv2.warpPerspective(dst2,rectmat2,(2048,2048))

#calculation of the disparity
stereo = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET,ndisparities=16*10, SADWindowSize=9)
disp = stereo.compute(dst22.astype(uint8), dst11.astype(uint8)).astype(np.float32)
plt.imshow(disp);plt.colorbar();plt.clim(0,400)#;plt.show()
plt.savefig("0gauche.png")

#plot depth by using disparity focal length `C1[0,0]` from stereo calibration and `T[0]` the distance between cameras

plt.imshow(C1[0,0]*T[0]/(disp),cmap='hot');plt.clim(-0,500);plt.colorbar();plt.show()

되지 않은 방법 다은보을및않방법은지되사(다)여수사한니(하)입진정용정음▁with▁(▁pictures및다▁here니사▁rectified입▁the을)으로 수정한 입니다.warpPerspective):

enter image description here

보정된 방법으로 수정된 사진은 다음과 같습니다.

enter image description here

저는 두 종류의 사진 사이의 차이가 어떻게 그렇게 중요한지 모르겠습니다.그리고 보정된 방법의 경우 정렬되지 않은 것 같습니다.

보정되지 않은 방법을 사용한 시차 맵:

enter image description here

는 다음과 같이됩니다.C1[0,0]*T[0]/(disp) .stereoCalibrate값이 매우 높습니다.

나중에 편집 ------------

저는 "stereoRectify"로 얻은 호모그래피 매트릭스로 재구성 매트릭스([Devernay97], [Garcia01])를 "탑재"하려고 했습니다.보정되지 않음"이지만 결과가 여전히 좋지 않습니다.제가 제대로 하고 있는 건가요?

Y=np.arange(0,2048)
X=np.arange(0,2048)
(XX_field,YY_field)=np.meshgrid(X,Y)

#I mount the X, Y and disparity in a same 3D array 
stock = np.concatenate((np.expand_dims(XX_field,2),np.expand_dims(YY_field,2)),axis=2)
XY_disp = np.concatenate((stock,np.expand_dims(disp,2)),axis=2)

XY_disp_reshape = XY_disp.reshape(XY_disp.shape[0]*XY_disp.shape[1],3)

Ts = np.hstack((np.zeros((3,3)),T_0)) #i use only the translations obtained with the rectified calibration...Is it correct?


# I establish the projective matrix with the homography matrix
P11 = np.dot(rectmat1,C1)
P1 = np.vstack((np.hstack((P11,np.zeros((3,1)))),np.zeros((1,4))))
P1[3,3] = 1

# P1 = np.dot(C1,np.hstack((np.identity(3),np.zeros((3,1)))))

P22 = np.dot(np.dot(rectmat2,C2),Ts)
P2 = np.vstack((P22,np.zeros((1,4))))
P2[3,3] = 1

lambda_t = cv2.norm(P1[0,:].T)/cv2.norm(P2[0,:].T)


#I define the reconstruction matrix
Q = np.zeros((4,4))

Q[0,:] = P1[0,:].T
Q[1,:] = P1[1,:].T
Q[2,:] = lambda_t*P2[1,:].T - P1[1,:].T
Q[3,:] = P1[2,:].T

#I do the calculation to get my 3D coordinates
test = []
for i in range(0,XY_disp_reshape.shape[0]):
    a = np.dot(inv(Q),np.expand_dims(np.concatenate((XY_disp_reshape[i,:],np.ones((1))),axis=0),axis=1))
    test.append(a)

test = np.asarray(test)

XYZ = test[:,:,0].reshape(XY_disp.shape[0],XY_disp.shape[1],4)

TLDR: 가장자리가 매끄러운 이미지에는 Semi Global Block Matching(Semi Global Block Matching)을 사용하고 더 매끄러운 이미지를 원한다면 일부 포스트 필터링을 사용합니다.

이미지를 OP를 사용하고 Tsukuba미들베리 데이터 세트에서.

일반 스테레오 B를 사용한 결과m

stereobm

스테레오 SGBM을 사용한 결과(튜닝됨)

stereosgbm

문학에서 찾을 수 있는 최고의 결과.

enter image description here

자세한 내용은 여기에서 설명서를 참조하십시오.

사후 필터링의 예(아래 링크 참조)

post filter example

OP 질문에 대한 이론/기타 고려 사항

보정된 수정된 이미지의 크고 검은 영역을 보면 보정이 잘 수행되지 않았다고 생각할 수 있습니다.다양한 이유가 작용할 수 있습니다. 물리적 설정, 보정을 수행할 때 조명 등입니다. 하지만 이를 위한 카메라 보정 튜토리얼이 많이 있습니다. 제가 알기로는 보정되지 않은 설정에서 더 나은 깊이 맵을 얻을 수 있는 방법을 요청하는 것입니다(100% 명확하지는 않습니다.하지만 제목은 이것을 지지하는 것처럼 보이고 저는 그것이 사람들이 찾기 위해 여기에 올 것이라고 생각합니다.)

당신의 기본적인 접근 방식은 옳지만, 결과는 확실히 개선될 수 있습니다.이러한 형태의 깊이 매핑은 최고 품질의 지도(특히 보정되지 않음)를 생성하는 것에는 포함되지 않습니다.가장 큰 개선점은 다른 스테레오 매칭 알고리즘을 사용하는 것입니다.조명도 상당한 영향을 미칠 수 있습니다.오른쪽 이미지(적어도 내 육안으로는)는 재구성을 방해할 수 있는 조명이 덜 켜진 것으로 보입니다.먼저 다른 이미지와 동일한 수준으로 밝기를 조정하거나 가능한 경우 새 이미지를 수집할 수 있습니다.이제부터는 원래 카메라에 액세스할 수 없으므로 새 이미지를 수집하거나 설정을 변경하거나 보정을 수행할 수 없습니다.(설정 및 카메라에 액세스할 수 있는 경우 보정을 확인하고 보정된 방법을 사용하는 것이 좋습니다.)

당신은 한용을 했습니다.StereoBM 지도효과가 , 효가있깊시를위차해기계산나, 러그과는맵하.StereoSGBM이 응용 프로그램에 훨씬 적합합니다(더 부드러운 가장자리를 더 잘 처리함).아래의 차이를 보실 수 있습니다.

이 문서에서는 차이점에 대해 자세히 설명합니다.

블록 매칭은 높은 텍스처 이미지(트리 그림처럼 생각)에 초점을 맞추고, 반 전역 블록 매칭은 하위 픽셀 수준 매칭과 더 부드러운 텍스처의 그림(복도 그림처럼 생각)에 초점을 맞춥니다.

명시적인 고유 카메라 매개 변수 없이 카메라 설정에 대한 세부 사항(초점 거리, 카메라 간 거리, 피사체까지의 거리 등), 이미지의 알려진 차원 또는 모션(동작에서 구조를 사용하는 경우),투영 변환까지만 3D 재구성을 얻을 수 있습니다. 스케일 감각이나 회전 감각은 없지만 상대적인 깊이 맵을 생성할 수 있습니다.적절한 카메라 보정을 통해 제거할 수 있는 배럴 및 기타 변형이 발생할 수 있습니다.하지만 카메라가 나쁘지 않고(렌즈 시스템이 너무 왜곡되지 않고) 표준 구성(기본적으로 광학 축이 가능한 한 평행하고 시야가 충분히 겹치도록 방향이 맞춰져 있다는 것을 의미함)만 있으면 카메라 없이도 합리적인 결과를 얻을 수 있습니다.하지만 그가 보정되지 않은 방법으로 정확하게 수정된 이미지를 얻을 수 있었기 때문에 이것은 OPs 문제가 아닌 것으로 보입니다.

기본 절차

  1. 두 이미지 모두에서 기본 매트릭스를 계산하는 데 사용할 수 있는 5개 이상의 잘 일치하는 지점을 찾습니다(Fundamental Matrix(플란)를 유지했지만 SIFT가 4.2.0용 OpenCV의 기본 버전에 없기 때문에 OR을 사용하여 탐지할 수 있습니다).
  2. 기본 행렬 F를 사용하여findFundamentalMat
  3. 이미지를 다음으로 정렬 해제stereoRectifyUncalibrated그리고.warpPerspective
  4. 를 사용하여 시차 계산(깊이 맵)StereoSGBM

결과가 훨씬 더 좋습니다.

ORB 및 FLANN과 일치

Matches

왜곡되지 않은 영상(왼쪽, 오른쪽)

undistorted left
undistorted right

시차

스테레오BM

이 결과는 OPS 문제(스펙팅, 갭, 일부 영역의 잘못된 깊이)와 유사합니다.

stereobm

스테레오 SGBM(튜닝됨)

이 결과는 OP와 거의 동일한 방법을 사용하고 최종 격차 계산을 제외하고 훨씬 더 좋아 보입니다. OP가 제공된다면 OP가 그의 이미지에서 유사한 개선을 볼 수 있을 것이라고 생각하게 합니다.

stereosgbm

사후 필터링

OpenCV 문서에 이에 대한 좋은 기사가 있습니다.만약 당신이 정말 부드러운 지도가 필요하다면 그것을 보는 것을 추천합니다.

위의 예시 사진들은 현장의 프레임 1입니다.ambush_2MPI Sintel 데이터 집합에 있습니다.

post filter example

전체 코드(OpenCV 4.2.0에서 테스트됨):

import cv2
import numpy as np
import matplotlib.pyplot as plt

imgL = cv2.imread("tsukuba_l.png", cv2.IMREAD_GRAYSCALE)  # left image
imgR = cv2.imread("tsukuba_r.png", cv2.IMREAD_GRAYSCALE)  # right image


def get_keypoints_and_descriptors(imgL, imgR):
    """Use ORB detector and FLANN matcher to get keypoints, descritpors,
    and corresponding matches that will be good for computing
    homography.
    """
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(imgL, None)
    kp2, des2 = orb.detectAndCompute(imgR, None)

    ############## Using FLANN matcher ##############
    # Each keypoint of the first image is matched with a number of
    # keypoints from the second image. k=2 means keep the 2 best matches
    # for each keypoint (best matches = the ones with the smallest
    # distance measurement).
    FLANN_INDEX_LSH = 6
    index_params = dict(
        algorithm=FLANN_INDEX_LSH,
        table_number=6,  # 12
        key_size=12,  # 20
        multi_probe_level=1,
    )  # 2
    search_params = dict(checks=50)  # or pass empty dictionary
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    flann_match_pairs = flann.knnMatch(des1, des2, k=2)
    return kp1, des1, kp2, des2, flann_match_pairs


def lowes_ratio_test(matches, ratio_threshold=0.6):
    """Filter matches using the Lowe's ratio test.

    The ratio test checks if matches are ambiguous and should be
    removed by checking that the two distances are sufficiently
    different. If they are not, then the match at that keypoint is
    ignored.

    https://stackoverflow.com/questions/51197091/how-does-the-lowes-ratio-test-work
    """
    filtered_matches = []
    for m, n in matches:
        if m.distance < ratio_threshold * n.distance:
            filtered_matches.append(m)
    return filtered_matches


def draw_matches(imgL, imgR, kp1, des1, kp2, des2, flann_match_pairs):
    """Draw the first 8 mathces between the left and right images."""
    # https://docs.opencv.org/4.2.0/d4/d5d/group__features2d__draw.html
    # https://docs.opencv.org/2.4/modules/features2d/doc/common_interfaces_of_descriptor_matchers.html
    img = cv2.drawMatches(
        imgL,
        kp1,
        imgR,
        kp2,
        flann_match_pairs[:8],
        None,
        flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS,
    )
    cv2.imshow("Matches", img)
    cv2.imwrite("ORB_FLANN_Matches.png", img)
    cv2.waitKey(0)


def compute_fundamental_matrix(matches, kp1, kp2, method=cv2.FM_RANSAC):
    """Use the set of good mathces to estimate the Fundamental Matrix.

    See  https://en.wikipedia.org/wiki/Eight-point_algorithm#The_normalized_eight-point_algorithm
    for more info.
    """
    pts1, pts2 = [], []
    fundamental_matrix, inliers = None, None
    for m in matches[:8]:
        pts1.append(kp1[m.queryIdx].pt)
        pts2.append(kp2[m.trainIdx].pt)
    if pts1 and pts2:
        # You can play with the Threshold and confidence values here
        # until you get something that gives you reasonable results. I
        # used the defaults
        fundamental_matrix, inliers = cv2.findFundamentalMat(
            np.float32(pts1),
            np.float32(pts2),
            method=method,
            # ransacReprojThreshold=3,
            # confidence=0.99,
        )
    return fundamental_matrix, inliers, pts1, pts2


############## Find good keypoints to use ##############
kp1, des1, kp2, des2, flann_match_pairs = get_keypoints_and_descriptors(imgL, imgR)
good_matches = lowes_ratio_test(flann_match_pairs, 0.2)
draw_matches(imgL, imgR, kp1, des1, kp2, des2, good_matches)


############## Compute Fundamental Matrix ##############
F, I, points1, points2 = compute_fundamental_matrix(good_matches, kp1, kp2)


############## Stereo rectify uncalibrated ##############
h1, w1 = imgL.shape
h2, w2 = imgR.shape
thresh = 0
_, H1, H2 = cv2.stereoRectifyUncalibrated(
    np.float32(points1), np.float32(points2), F, imgSize=(w1, h1), threshold=thresh,
)

############## Undistort (Rectify) ##############
imgL_undistorted = cv2.warpPerspective(imgL, H1, (w1, h1))
imgR_undistorted = cv2.warpPerspective(imgR, H2, (w2, h2))
cv2.imwrite("undistorted_L.png", imgL_undistorted)
cv2.imwrite("undistorted_R.png", imgR_undistorted)

############## Calculate Disparity (Depth Map) ##############

# Using StereoBM
stereo = cv2.StereoBM_create(numDisparities=16, blockSize=15)
disparity_BM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_BM, "gray")
plt.colorbar()
plt.show()

# Using StereoSGBM
# Set disparity parameters. Note: disparity range is tuned according to
#  specific parameters obtained through trial and error.
win_size = 2
min_disp = -4
max_disp = 9
num_disp = max_disp - min_disp  # Needs to be divisible by 16
stereo = cv2.StereoSGBM_create(
    minDisparity=min_disp,
    numDisparities=num_disp,
    blockSize=5,
    uniquenessRatio=5,
    speckleWindowSize=5,
    speckleRange=5,
    disp12MaxDiff=2,
    P1=8 * 3 * win_size ** 2,
    P2=32 * 3 * win_size ** 2,
)
disparity_SGBM = stereo.compute(imgL_undistorted, imgR_undistorted)
plt.imshow(disparity_SGBM, "gray")
plt.colorbar()
plt.show()

품질 저하를 초래할 수 있는 몇 가지 문제가 있을 수 있습니다.Depth Channel그리고.Disparity Channel우리를 저품질 스테레오 시퀀스로 이끄는 것.다음은 6가지 문제입니다.

발생 가능한 문제 I

  • 불완전 수식

말 그대로uncalibrated암시하는 바는,stereoRectifyUncalibrated인스턴스 메소드는 사용자가 스테레오 쌍의 본질적인 매개 변수와 환경에서의 상대적인 위치를 모르거나 알 수 없는 경우를 대비하여 수정 변환을 계산합니다.

cv.StereoRectifyUncalibrated(pts1, pts2, fm, imgSize, rhm1, rhm2, thres)

위치:

# pts1    –> an array of feature points in a first camera
# pts2    –> an array of feature points in a first camera
# fm      –> input fundamental matrix
# imgSize -> size of an image
# rhm1    -> output rectification homography matrix for a first image
# rhm2    -> output rectification homography matrix for a second image
# thres   –> optional threshold used to filter out outliers

방법은 다음과 같습니다.

cv2.StereoRectifyUncalibrated(p1fNew, p2fNew, F, (2048, 2048))

따라서 다음 세 가지 매개 변수는 고려하지 않습니다.rhm1,rhm2그리고.thres만약에threshold > 0에피폴라 지오메트리를 준수하지 않는 모든 점 쌍은 호모그래피를 계산하기 전에 거부됩니다.그렇지 않으면 모든 점이 기울임꼴로 간주됩니다.이 공식은 다음과 같습니다.

(pts2[i]^t * fm * pts1[i]) > thres

# t   –> translation vector between coordinate systems of cameras

따라서, 저는 불완전한 공식의 계산으로 인해 시각적인 부정확성이 나타날 수 있다고 생각합니다.

공식 자료에서 카메라 보정3D 재구성을 읽을 수 있습니다.


발생 가능한 문제 II

  • 축간 거리

건장한interaxial distance왼쪽과 오른쪽 카메라 렌즈 사이에 있어야 합니다.그 때.interaxial distance 큽니다.interocular를 리거, 효고합니다라고 .hyperstereoscopy또는hyperdivergence그리고 그 결과는 장면의 깊이 있는 과장뿐만 아니라 시청자의 신체적 불편함을 초래합니다.이 주제에 대한 자세한 내용은 Autodesk의 Stereoscopic Filmaking 백서를 참조하십시오.

enter image description here


발생 가능한 문제 III

  • 병렬 카메라 모드와 토우 인 카메라 모드

으로 부정확한 했습니다.Disparity Map카메라 모드 계산이 잘못되어 발생할 수 있습니다.많은 입체파 작가들이 선호합니다.Toe-In camera mode하지만 픽사는, 예를 들어,Parallel camera mode.

enter image description here

enter image description here


발생 가능한 문제 IV

  • 수직 정렬

스테레오스코픽에서 수직 이동이 발생하면(보기 중 하나를 1mm 위로 이동하더라도) 강력한 스테레오 경험이 손상됩니다.생하기전을 생성하기 에.Disparity Map스테레오 쌍의 왼쪽 및 오른쪽 보기가 그에 따라 정렬되었는지 확인해야 합니다.스테레오의 일반적인 15가지 문제에 대한 테크니컬러 입체 백서를 보세요.

스테레오 정류 매트릭스:

   ┌                  ┐
   |  f   0   cx  tx  |
   |  0   f   cy  ty  |   # use "ty" value to fix vertical shift in one image
   |  0   0   1   0   |
   └                  ┘

여기에 여에가 StereoRectify방법:

cv.StereoRectify(cameraMatrix1, cameraMatrix2, distCoeffs1, distCoeffs2, imageSize, R, T, R1, R2, P1, P2, Q=None, flags=CV_CALIB_ZERO_DISPARITY, alpha=-1, newImageSize=(0, 0)) -> (roi1, roi2)


발생 가능한 문제 V

  • 렌즈 변형

렌즈 왜곡은 스테레오 작곡에서 매우 중요한 주제입니다.Disparity Map왼쪽 및 오른쪽 보기의 왜곡을 해제한 후 시차 채널을 생성한 다음 두 보기를 다시 정렬해야 합니다.

enter image description here

enter image description here


발생 가능한 문제 VI

  • 앤티앨리어싱이 없는 저품질 깊이 채널

고품질 생성을 위한Disparity Map당신은 왼쪽과 오른쪽이 필요합니다.Depth Channels미리 생성해야 합니다.3D 패키지에서 작업할 때 클릭 한 번으로 고품질의 깊이 채널(가장자리가 선명함)을 렌더링할 수 있습니다.그러나 비디오 시퀀스에서 고품질의 깊이 채널을 생성하는 것은 쉽지 않습니다. 스테레오 쌍이 사용자 환경에서 이동해야 향후 깊이-이동 알고리즘을 위한 초기 데이터를 생성할 수 있기 때문입니다.프레임에 모션이 없으면 깊이 채널이 매우 불량해집니다.

enter image description here

또한.Depth채널 자체에는 안티에일리어싱이 없기 때문에 RGB의 에지와 일치하지 않는다는 단점이 하나 더 있습니다.

enter image description here


차이 채널 코드 스니펫:

여기서 저는 다음을 생성하기 위한 빠른 접근 방식을 제시하고자 합니다.Disparity Map:

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt

imageLeft = cv.imread('paris_left.png', 0)
imageRight = cv.imread('paris_right.png', 0)
stereo = cv.StereoBM_create(numDisparities=16, blockSize=15)
disparity = stereo.compute(imageLeft, imageRight)
plt.imshow(disparity, 'gray')
plt.show()

enter image description here

언급URL : https://stackoverflow.com/questions/36172913/opencv-depth-map-from-uncalibrated-stereo-system

반응형