use*_*754 41 python opencv disparity-mapping stereo-3d
我试图从未校准的方法获得深度图.我可以通过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()
Run Code Online (Sandbox Code Playgroud)
这里用未经校准的方法(和warpPerspective)修正了图片:
这里使用校准方法校正图片:
我不知道两种图片之间的差异是如此重要......对于校准方法,它似乎没有对齐...奇怪未校准方法的视差图:
并且深度图用以下公式计算:C1 [0,0]*T [0] /(disp),其中T来自"stereocalibrate",但值非常高......
-------- EDIT LATER ------------
我试图用"stereoRectifyUncalibrated"获得的单应矩阵"装载"重建矩阵([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)
Run Code Online (Sandbox Code Playgroud)
Mat*_*one 15
OP 没有提供原始图像,所以我使用Tsukuba
的是Middlebury 数据集。
有关详细信息,请参阅此处的出版物。
校准后的校正图像的大黑色区域会让我相信,对于那些,校准做得不是很好。可能有多种原因在起作用,可能是物理设置,可能是校准时的照明等,但是有很多相机校准教程可以解决这个问题,我的理解是您正在寻求一种方法从未校准的设置中获得更好的深度图(这不是 100% 清楚,但标题似乎支持这一点,我认为这就是人们会来这里试图找到的)。
您的基本方法是正确的,但结果肯定可以改进。这种形式的深度映射不是产生最高质量地图(尤其是未校准的)的那些。最大的改进可能来自使用不同的立体匹配算法。照明也可能具有显着影响。正确的图像(至少在我的肉眼看来)似乎光线不足,这可能会干扰重建。您可以先尝试将其调亮到与另一个相同的级别,或者如果可能的话收集新的图像。从这里开始,我假设您无法访问原始相机,因此我将考虑收集新图像、更改设置或执行超出范围的校准。(如果您确实有权访问设置和相机,
您用于StereoBM
计算确实有效的视差(深度图),但StereoSGBM
更适合此应用程序(它可以更好地处理更平滑的边缘)。您可以在下面看到差异。
这篇文章更深入地解释了差异:
块匹配侧重于高纹理图像(想想一棵树的图片),半全局块匹配将侧重于亚像素级匹配和纹理更平滑的图片(想想走廊的图片)。
没有任何明确的内在相机参数、关于相机设置的细节(如焦距、相机之间的距离、到主体的距离等)、图像中的已知维度或运动(使用来自运动的结构),您可以仅获得投影变换的 3D 重建;您也不会产生比例感或必要的旋转感,但您仍然可以生成相对深度图。您可能会遭受一些桶形和其他扭曲,这些扭曲可以通过适当的相机校准来消除,但是只要相机不糟糕(镜头系统不是太扭曲)并且设置得很好,没有它您也可以获得合理的结果接近规范配置(这基本上意味着它们的定向使得它们的光轴尽可能接近平行,并且它们的视野充分重叠)。然而,这似乎不是 OP 的问题,因为他确实使用未校准的方法设法获得了正确的校正图像。
findFundamentalMat
stereoRectifyUncalibrated
和使您的图像不失真warpPerspective
StereoSGBM
结果要好得多:
这个结果看起来类似于 OPs 问题(斑点、间隙、某些区域的错误深度)。
这个结果看起来好多了,并且使用与 OP 大致相同的方法,减去最终的视差计算,让我认为 OP 会在他的图像上看到类似的改进,如果提供的话。
OpenCV 文档中有一篇关于此的好文章。如果您需要非常平滑的地图,我建议您查看它。
上面的示例照片是MPI Sintel 数据ambush_2
集中场景中的第1 帧。
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.
/sf/ask/3583796401/
"""
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()
Run Code Online (Sandbox Code Playgroud)
可能有几个可能的问题导致低质量Depth Channel
以及Disparity Channel
导致我们产生低质量立体声序列的原因。以下是其中 6 个问题:
作为一个词uncalibrated
意味着,stereoRectifyUncalibrated
实例方法计算整改转化为你,如果你不知道或者不知道您的立体声对,并在环境中的相对位置的内部参数。
cv.StereoRectifyUncalibrated(pts1, pts2, fm, imgSize, rhm1, rhm2, thres)
Run Code Online (Sandbox Code Playgroud)
在哪里:
# 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
Run Code Online (Sandbox Code Playgroud)
你的方法看起来像这样:
cv2.StereoRectifyUncalibrated(p1fNew, p2fNew, F, (2048, 2048))
Run Code Online (Sandbox Code Playgroud)
因此,您不考虑三个参数:rhm1
,rhm2
和thres
。如果 a threshold > 0
,则在计算单应性之前拒绝所有不符合对极几何的点对。否则,所有点都被视为内点。这个公式看起来像这样:
(pts2[i]^t * fm * pts1[i]) > thres
# t –> translation vector between coordinate systems of cameras
Run Code Online (Sandbox Code Playgroud)
因此,我认为由于公式的计算不完整,可能会出现视觉上的不准确。
您可以在官方资源上阅读相机校准和 3D 重建。
interaxial distance
左右相机镜头之间必须坚固not greater than 200 mm
。当interaxial distance
大于interocular
距离时,该效果称为hyperstereoscopy
或hyperdivergence
,不仅会导致场景中的深度夸大,还会导致观看者的身体不便。阅读 Autodesk 的立体电影制作白皮书以了解有关此主题的更多信息。
Disparity Map
由于相机模式计算不正确,可能会导致视觉不准确。许多立体摄影师更喜欢,Toe-In camera mode
但例如皮克斯更喜欢Parallel camera mode
.
在立体视觉中,如果发生垂直偏移(即使其中一个视图向上偏移 1 毫米),它也会破坏强大的立体体验。因此,在生成之前,Disparity Map
您必须确保立体对的左右视图相应地对齐。查看Technicolor Stereoscopic Whitepaper,了解立体中的 15 个常见问题。
立体声整流矩阵:
? ?
| f 0 cx tx |
| 0 f cy ty | # use "ty" value to fix vertical shift in one image
| 0 0 1 0 |
? ?
Run Code Online (Sandbox Code Playgroud)
这是一个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)
Run Code Online (Sandbox Code Playgroud)
镜头失真是立体合成中非常重要的话题。在生成 a 之前,Disparity Map
您需要对左右视图进行失真处理,在此之后生成视差通道,然后再次对两个视图进行重新失真处理。
为了创造一个高质量的Disparity Map
你需要左右Depth Channels
,必须预先生成。当您在 3D 包中工作时,只需单击一下即可渲染高质量的深度通道(边缘清晰)。但是从视频序列生成高质量的深度通道并不容易,因为立体对必须在您的环境中移动,以便为未来的深度运动算法生成初始数据。如果帧中没有运动,则深度通道将非常糟糕。
此外,
Depth
通道本身还有一个缺点——它的边缘与 RGB 的边缘不匹配,因为它没有抗锯齿。
在这里,我想代表一种生成 的快速方法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()
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2333 次 |
最近记录: |