2014-04-12 19 views
11

Ho messo insieme un impianto di riproduzione stereo e sto avendo problemi ad usarlo per produrre una buona mappa di disparità. Ecco un esempio di due immagini raddrizzate e la mappa disparità ho prodotto con loro:Mappa di disparità errata utilizzando StereoBM in OpenCV

Rectified images with disparity map

Come si può vedere, i risultati sono piuttosto male. Cambiare le impostazioni di StereoBM non cambia molto.

Il setup

  • Entrambe le fotocamere sono dello stesso modello e connettono al computer con USB.
  • Sono fissati a una tavola di legno rigida in modo che non si muovano. Li ho allineati al meglio, ma ovviamente non è perfetto. Non sono in grado di muoversi, quindi le loro posizioni durante e dopo la calibrazione sono le stesse.
  • Ho calibrato la coppia stereo utilizzando OpenCV e sto usando la classe StereoBM di OpenCV per produrre la mappa di disparità.
  • Probabilmente non è rilevante, ma sto codificando in Python.

Problemi potevo immaginare

sto facendo questo per la prima volta, quindi sono ben lungi dall'essere un esperto, ma sto cercando di indovinare il problema è nella calibrazione o nel rettifica stereo, piuttosto che il calcolo della mappa di disparità. Ho provato tutte le permutazioni delle impostazioni per lo StereoBM e, anche se ottengo risultati diversi, sono tutte come la mappa delle disparità mostrata sopra: Patch in bianco e nero.

Questa idea è ulteriormente supportata dal fatto che, a quanto ho capito, la rettifica stereo dovrebbe allineare tutti i punti su ciascuna immagine in modo che siano collegati da una linea diritta (nel mio caso orizzontale). Se esamino entrambe le immagini rettificate l'una accanto all'altra, è ovvio che non è così. I punti corrispondenti sono molto più alti nell'immagine a destra che a sinistra. Non sono sicuro se la calibrazione o la rettifica sia il problema, però.

Il codice

Il codice attuale è avvolto in oggetti - nel caso in cui siete interessati a vedere nella sua interezza, è disponibile on GitHub. Ecco un esempio semplificato di ciò che è effettivamente eseguito (ovviamente nel codice vero e proprio ho calibrazione utilizzando più di appena 2 foto):

import cv2 
import numpy as np 

## Load test images 
# TEST_IMAGES is a list of paths to test images 
input_l, input_r = [cv2.imread(image, cv2.CV_LOAD_IMAGE_GRAYSCALE) 
        for image in TEST_IMAGES] 
image_size = input_l.shape[:2] 

## Retrieve chessboard corners 
# CHESSBOARD_ROWS and CHESSBOARD_COLUMNS are the number of inside rows and 
# columns in the chessboard used for calibration 
pattern_size = CHESSBOARD_ROWS, CHESSBOARD_COLUMNS 
object_points = np.zeros((np.prod(pattern_size), 3), np.float32) 
object_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2) 
# SQUARE_SIZE is the size of the chessboard squares in cm 
object_points *= SQUARE_SIZE 
image_points = {} 
ret, corners_l = cv2.findChessboardCorners(input_l, pattern_size, True) 
cv2.cornerSubPix(input_l, corners_l, 
       (11, 11), (-1, -1), 
       (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 
        30, 0.01)) 
image_points["left"] = corners_l.reshape(-1, 2) 
ret, corners_r = cv2.findChessboardCorners(input_r, pattern_size, True) 
cv2.cornerSubPix(input_r, corners_r, 
       (11, 11), (-1, -1), 
       (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 
        30, 0.01)) 
image_points["right"] = corners_r.reshape(-1, 2) 

## Calibrate cameras 
(cam_mats, dist_coefs, rect_trans, proj_mats, valid_boxes, 
undistortion_maps, rectification_maps) = {}, {}, {}, {}, {}, {}, {} 
criteria = (cv2.TERM_CRITERIA_MAX_ITER + cv2.TERM_CRITERIA_EPS, 
      100, 1e-5) 
flags = (cv2.CALIB_FIX_ASPECT_RATIO + cv2.CALIB_ZERO_TANGENT_DIST + 
     cv2.CALIB_SAME_FOCAL_LENGTH) 
(ret, cam_mats["left"], dist_coefs["left"], cam_mats["right"], 
dist_coefs["right"], rot_mat, trans_vec, e_mat, 
f_mat) = cv2.stereoCalibrate(object_points, 
           image_points["left"], image_points["right"], 
           image_size, criteria=criteria, flags=flags) 
(rect_trans["left"], rect_trans["right"], 
proj_mats["left"], proj_mats["right"], 
disp_to_depth_mat, valid_boxes["left"], 
valid_boxes["right"]) = cv2.stereoRectify(cam_mats["left"], 
              dist_coefs["left"], 
              cam_mats["right"], 
              dist_coefs["right"], 
              image_size, 
              rot_mat, trans_vec, flags=0) 
for side in ("left", "right"): 
    (undistortion_maps[side], 
    rectification_maps[side]) = cv2.initUndistortRectifyMap(cam_mats[side], 
                  dist_coefs[side], 
                  rect_trans[side], 
                  proj_mats[side], 
                  image_size, 
                  cv2.CV_32FC1) 

## Produce disparity map 
rectified_l = cv2.remap(input_l, undistortion_maps["left"], 
         rectification_maps["left"], 
         cv2.INTER_NEAREST) 
rectified_r = cv2.remap(input_r, undistortion_maps["right"], 
         rectification_maps["right"], 
         cv2.INTER_NEAREST) 
cv2.imshow("left", rectified_l) 
cv2.imshow("right", rectified_r) 
block_matcher = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET, 0, 5) 
disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F) 
cv2.imshow("disparity", disp) 

cosa sta andando male qui?

risposta

13

Si è scoperto che il problema era la visualizzazione e non i dati stessi. Da qualche parte ho letto che cv2.reprojectImageTo3D richiedeva una mappa di disparità come valori in virgola mobile, motivo per cui stavo richiedendo cv2.CV_32F da block_matcher.compute.

lettura della documentazione OpenCV con più attenzione ha mi ha portato a pensare che stavo pensando questo errore, e mi piacerebbe davvero piacerebbe lavorare con numeri interi di galleggianti per il bene della velocità, ma la documentazione per cv2.imshow non era chiaro su cosa fa con interi a 16 bit con segno (rispetto a 16 bit senza segno), quindi per la visualizzazione sto lasciando i valori come float.

Il documentation of cv2.imshow rivela che i valori in virgola mobile a 32 bit sono assunti essere tra 0 e 1, quindi sono moltiplicati per 255. 255 è il punto di saturazione in cui un pixel viene visualizzato come bianco. Nel mio caso, questa ipotesi ha prodotto una mappa binaria. L'ho ridimensionato manualmente nell'intervallo 0-255 e poi diviso per 255 per annullare il fatto che OpenCV fa lo stesso. Lo so, è un'operazione orribile, ma lo sto facendo solo per accordare il mio StereoBM offline, quindi le prestazioni non sono critiche. La soluzione si presenta così:

# Other code as above 
disp = block_matcher.compute(rectified_l, rectified_r, disptype=cv2.CV_32F) 
norm_coeff = 255/disp.max() 
cv2.imshow("disparity", disp * norm_coeff/255) 

Poi la mappa disparità sembra a posto.

Problemi correlati