Нужно ли калибровать камеры, чтобы использовать их с StereoBM?
В моей ситуации данные, возвращаемые OpenCV StereoBM Depth Map, не имеют смысла независимо от настройки параметров.
Я занимаюсь исследованиями для дизайн-проекта, включающего OpenCV и использующего стереозрение для создания карты глубины. В настоящее время я успешно могу загружать обе свои веб-камеры и генерировать карту глубины с помощью StereoBM. Тем не менее, данные результатов в данный момент бесполезны, как показано на скриншоте ниже. Поэтому я создал небольшое приложение на Python, которое помогает мне настроить параметры StereoBM, но это не помогло.
Мой вопрос: нужно ли откалибровать камеры, чтобы использовать их с функцией StereoBM?
Если нет, то какие есть альтернативы, которые помогут мне улучшить мои результаты (например, повысить разрешение, использовать StereoSBGM и т. Д.)
Код
import cv2
import time
import numpy as np
from Tkinter import *
oldVal = 15
def oddVals(n):
global oldVal
n = int(n)
if not n % 2:
window_size.set(n+1 if n > oldVal else n-1)
oldVal = window_size.get()
minDispValues = [16,32,48,64]
def minDispCallback(n):
n = int(n)
newvalue = min(minDispValues, key=lambda x:abs(x-float(n)))
min_disp.set(newvalue)
# Display the sliders to control the stereo vision
master = Tk()
master.title("StereoBM Settings");
min_disp = Scale(master, from_=16, to=64, command=minDispCallback, length=600, orient=HORIZONTAL, label="Minimum Disparities")
min_disp.pack()
min_disp.set(16)
window_size = Scale(master, from_=5, to=255, command=oddVals, length=600, orient=HORIZONTAL, label="Window Size")
window_size.pack()
window_size.set(15)
Disp12MaxDiff = Scale(master, from_=5, to=30, length=600, orient=HORIZONTAL, label="Max Difference")
Disp12MaxDiff.pack()
Disp12MaxDiff.set(0)
UniquenessRatio = Scale(master, from_=0, to=30, length=600, orient=HORIZONTAL, label="Uniqueness Ratio")
UniquenessRatio.pack()
UniquenessRatio.set(15)
SpeckleRange = Scale(master, from_=0, to=60, length=600, orient=HORIZONTAL, label="Speckle Range")
SpeckleRange.pack()
SpeckleRange.set(34)
SpeckleWindowSize = Scale(master, from_=60, to=150, length=600, orient=HORIZONTAL, label="Speckle Window Size")
SpeckleWindowSize.pack()
SpeckleWindowSize.set(100)
master.update()
vcLeft = cv2.VideoCapture(0) # Load video campture for the left camera
#vcLeft.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,420);
#vcLeft.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,340);
vcLeft.set(3,640) # Set camera width
vcLeft.set(4,480) # Set camera height
vcRight = cv2.VideoCapture(1) # Load video capture for the right camera
#vcRight.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH,420);
#vcRight.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT,340);
firstTime = time.time() # First time log
totalFramesPassed = 0 # Number of frames passed
if vcLeft.isOpened() and vcRight.isOpened():
rvalLeft, frameLeft = vcLeft.read()
rvalRight, frameRight = vcRight.read()
else:
rvalLeft = False
rvalRight = False
while rvalLeft and rvalRight: # If the cameras are opened
rvalLeft, frameLeft = vcLeft.read()
rvalRight, frameRight = vcRight.read()
cv2.putText(frameLeft, "FPS : " + str(totalFramesPassed / (time.time() - firstTime)),(40, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, 150, 2, 10)
cv2.imshow("Left Camera", frameLeft)
cv2.putText(frameRight, "FPS : " + str(totalFramesPassed / (time.time() - firstTime)),(40, 40), cv2.FONT_HERSHEY_SIMPLEX, 0.8, 150, 2, 10)
cv2.imshow("Right Camera", frameRight)
frameLeftNew = cv2.cvtColor(frameLeft, cv2.COLOR_BGR2GRAY)
frameRightNew = cv2.cvtColor(frameRight, cv2.COLOR_BGR2GRAY)
num_disp = 112 - min_disp.get()
stereo = cv2.StereoBM_create(numDisparities = num_disp, blockSize = window_size.get())
stereo.setMinDisparity(min_disp.get())
stereo.setNumDisparities(num_disp)
stereo.setBlockSize(window_size.get())
stereo.setDisp12MaxDiff(Disp12MaxDiff.get())
stereo.setUniquenessRatio(UniquenessRatio.get())
stereo.setSpeckleRange(SpeckleRange.get())
stereo.setSpeckleWindowSize(SpeckleWindowSize.get())
disparity = stereo.compute(frameLeftNew, frameRightNew).astype(np.float32) / 16.0
disp_map = (disparity - min_disp.get())/num_disp
cv2.imshow("Disparity", disp_map)
master.update() # Update the slider options
key = cv2.waitKey(20)
totalFramesPassed = totalFramesPassed + 1 # One frame passed, increment
if key == 27:
break
vcLeft.release()
vcRight.release()
2 ответа
Как указано в документации opencv StereoBM, документ opencv stereoBM doc, эти два изображения должны быть "выпрямленной стереопарой".
Это означает, что перед тем, как вычислить несоответствие, вам необходимо исправить две камеры.
Взгляните на stereo_match, где вы можете увидеть, как выпрямить две камеры, прежде чем вычислять разницу.
Когда вы вычисляете несоответствие, используя стереоБМ, вы смотрите на соответствие параллельных эпиполярных линий на обоих изображениях. Это означает, что изображения должны быть выровнены так, чтобы одинаковые строки на обоих изображениях соответствовали одинаковым линиям в пространстве. Процесс исправления заботится об этом.
Для получения дополнительной информации посмотрите на Исправление с opencv
Я узнал, что нам нужно исправить пару, чтобы использовать функцию StereoBM. Кроме того, я обнаружил, что, хотя это более ресурсоемкое, функция StereoSGBM дала мне более оптимальные результаты.
В случае, если кому-то понадобится откалибровать свои камеры в будущем, вы можете использовать этот код, чтобы помочь вам сделать это:
# Imports
import cv2
import numpy as np
# Constants
leftCameraNumber = 2 # Number for left camera
rightCameraNumber = 1 # Number for right camera
numberOfChessRows = 6
numberOfChessColumns = 8
chessSquareSize = 30 # Length of square in millimeters
numberOfChessColumns = numberOfChessColumns - 1 # Update to reflect how many corners are inside the chess board
numberOfChessRows = numberOfChessRows - 1
objp = np.zeros((numberOfChessColumns*numberOfChessRows,3), np.float32)
objp[:,:2] = np.mgrid[0:numberOfChessRows,0:numberOfChessColumns].T.reshape(-1,2)*chessSquareSize
objectPoints = []
leftImagePoints = []
rightImagePoints = []
parameterCriteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# Code
print("Press \"n\" when you're done caputing checkerboards.")
vcLeft = cv2.VideoCapture(leftCameraNumber) # Load video campture for the left camera
vcLeft.set(cv2.CAP_PROP_FRAME_WIDTH,640*3/2);
vcLeft.set(cv2.CAP_PROP_FRAME_HEIGHT,480*3/2);
vcRight = cv2.VideoCapture(rightCameraNumber) # Load video capture for the right camera
vcRight.set(cv2.CAP_PROP_FRAME_WIDTH,640*3/2);
vcRight.set(cv2.CAP_PROP_FRAME_HEIGHT,480*3/2);
if vcLeft.isOpened() and vcRight.isOpened():
rvalLeft, frameLeft = vcLeft.read()
rvalRight, frameRight = vcRight.read()
else:
rvalLeft = False
rvalRight = False
# Number of succesful recognitions
checkerboardRecognitions = 0
while rvalLeft and rvalRight: # If the cameras are opened
vcLeft.grab();
vcRight.grab();
rvalLeft, frameLeft = vcLeft.retrieve()
rvalRight, frameRight = vcRight.retrieve()
frameLeftNew = cv2.cvtColor(frameLeft, cv2.COLOR_BGR2GRAY)
frameRightNew = cv2.cvtColor(frameRight, cv2.COLOR_BGR2GRAY)
foundPatternLeft, cornersLeft = cv2.findChessboardCorners(frameLeftNew, (numberOfChessRows, numberOfChessColumns), None, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE + cv2.CALIB_CB_FAST_CHECK)
foundPatternRight, cornersRight = cv2.findChessboardCorners(frameRightNew, (numberOfChessRows, numberOfChessColumns), None, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_NORMALIZE_IMAGE + cv2.CALIB_CB_FAST_CHECK)
if foundPatternLeft and foundPatternRight: # If found corners in this frame
# Process the images and display the count of checkboards in our array
checkerboardRecognitions = checkerboardRecognitions + 1
print("Checker board recognitions: " + str(checkerboardRecognitions))
objectPoints.append(objp)
exactCornersLeft = cv2.cornerSubPix(frameLeftNew, cornersLeft, (11, 11), (-1, -1), parameterCriteria);
leftImagePoints.append(exactCornersLeft)
exactCornersRight = cv2.cornerSubPix(frameRightNew, cornersRight, (11, 11), (-1, -1), parameterCriteria);
rightImagePoints.append(exactCornersRight)
frameLeft = cv2.drawChessboardCorners(frameLeft, (numberOfChessRows, numberOfChessColumns), (exactCornersLeft), True);
frameRight = cv2.drawChessboardCorners(frameRight, (numberOfChessRows, numberOfChessColumns), (exactCornersRight), True);
# Display current webcams regardless if board was found or not
cv2.imshow("Left Camera", frameLeft)
cv2.imshow("Right Camera", frameRight)
key = cv2.waitKey(250) # Give the frame some time
if key == ord('n'):
break
cameraMatrixLeft = np.zeros( (3,3) )
cameraMatrixRight = np.zeros( (3,3) )
distortionLeft = np.zeros( (8,1) )
distortionRight = np.zeros( (8,1) )
height, width = frameLeft.shape[:2]
rms, leftMatrix, leftDistortion, rightMatrix, rightDistortion, R, T, E, F = cv2.stereoCalibrate(objectPoints, leftImagePoints, rightImagePoints, cameraMatrixLeft, distortionLeft, cameraMatrixRight, distortionRight, (width, height),parameterCriteria, flags=0)
arr1 = np.arange(8).reshape(2, 4)
arr2 = np.arange(10).reshape(2, 5)
np.savez('camera_calibration.npz', leftMatrix=leftMatrix, leftDistortion=leftDistortion, rightMatrix=rightMatrix, rightDistortion=rightDistortion, R=R, T=T, E=E, F=F)
print("Calibration Settings Saved to File!")
print("RMS:")
print(rms)
print("Left Matrix:")
print(leftMatrix)
print("Left Distortion:")
print(leftDistortion)
print("Right Matrix:")
print(rightMatrix)
print("Right Distortion:")
print(rightDistortion)
print("R:")
print(R)
print("T:")
print(T)
print("E:")
print(E)
print("F:")
print(F)
leftRectTransform, rightRectTransform, leftProjMatrix, rightProjMatrix, _, _, _ = cv2.stereoRectify(leftMatrix, leftDistortion, rightMatrix, rightDistortion, (width, height), R, T, alpha=-1);
leftMapX, leftMapY = cv2.initUndistortRectifyMap(leftMatrix, leftDistortion, leftRectTransform, leftProjMatrix, (width, height), cv2.CV_32FC1);
rightMapX, rightMapY = cv2.initUndistortRectifyMap(rightMatrix, rightDistortion, rightRectTransform, rightProjMatrix, (width, height), cv2.CV_32FC1);
minimumDisparities = 0
maximumDisparities = 128
stereo = cv2.StereoSGBM_create(minimumDisparities, maximumDisparities, 18)
while True: # If the cameras are opened
vcLeft.grab();
vcRight.grab();
rvalLeft, frameLeft = vcLeft.retrieve()
rvalRight, frameRight = vcRight.retrieve()
frameLeftNew = cv2.cvtColor(frameLeft, cv2.COLOR_BGR2GRAY)
frameRightNew = cv2.cvtColor(frameRight, cv2.COLOR_BGR2GRAY)
leftRectified = cv2.remap(frameLeftNew, leftMapX, leftMapY, cv2.INTER_LINEAR);
rightRectified = cv2.remap(frameRightNew, rightMapX, rightMapY, cv2.INTER_LINEAR);
disparity = stereo.compute(leftRectified, rightRectified)
cv2.filterSpeckles(disparity, 0, 6000, maximumDisparities);
cv2.imshow("Normalized Disparity", (disparity/16.0 - minimumDisparities)/maximumDisparities);
cv2.imshow("Left Camera", leftRectified)
cv2.imshow("Right Camera", rightRectified)
key = cv2.waitKey(10) # Give the frame some time
if key == 27:
break
print("Finished!")