opencv0:雙目立體視覺(python 代碼全)

0.雙目立體視覺的基本建立步驟

  • a)雙目標定(samples/cpp/stereo_calib.cpp),由一套操作完成。
  • b)圖像根據標定結果進行極線矯正(stereoRectify 函數)
  • c)在每條極線上尋找對應點(視差)(也有很多種選擇,StereoMatcher)
  • d)根據視差轉換為點云(cv2.reprojectImageTo3D)
  • e)點云存儲(samples/python/stereo_match.py/write_ply)和顯示

1. 雙目棋盤格標定詳解

1.1 c++例子中標定的函數:

    StereoCalib(imagelist, boardSize, squareSize, false, true, showRectified);
  1. 需要一系列的圖像
  2. 標定板格子的個數(比如8*6)
  3. 標定板格子的尺寸(比如20mm)
  4. displayCorners 是否顯示角點
  5. useCalibrated 是否使用標定結果
  6. showRectified 是否展示矯正結果

1.2 標定的流程

  1. 找到亞像素的角點,imagePoints[0]和imagePoints[1],分別對應左右兩圖;
findChessboardCorners 
cornerSubPix
  1. 構建標定板的點坐標,objectPoints
objectPoints[i].push_back(Point3f(k*squareSize, j*squareSize, 0));

3.分別得到兩個相機的初始CameraMatrix

Mat cameraMatrix[2], distCoeffs[2];
cameraMatrix[0] = initCameraMatrix2D(objectPoints,imagePoints[0],imageSize,0);
cameraMatrix[1] = initCameraMatrix2D(objectPoints,imagePoints[1],imageSize,0);

4.雙目視覺進行標定

Mat R, T, E, F;

double rms = stereoCalibrate(objectPoints, imagePoints[0], imagePoints[1],
                    cameraMatrix[0], distCoeffs[0],
                    cameraMatrix[1], distCoeffs[1],
                    imageSize, R, T, E, F,
                    CALIB_FIX_ASPECT_RATIO +
                    CALIB_ZERO_TANGENT_DIST +
                    CALIB_USE_INTRINSIC_GUESS +
                    CALIB_SAME_FOCAL_LENGTH +
                    CALIB_RATIONAL_MODEL +
                    CALIB_FIX_K3 + CALIB_FIX_K4 + CALIB_FIX_K5,
                    TermCriteria(TermCriteria::COUNT+TermCriteria::EPS, 100, 1e-5) );
  1. 標定精度的衡量,這部分注釋就夠了
// CALIBRATION QUALITY CHECK
// because the output fundamental matrix implicitly
// includes all the output information,
// we can check the quality of calibration using the
// epipolar geometry constraint: m2^t*F*m1=0
  1. 保存標定結果
  1. 矯正一張圖像看看,是否可以
Mat R1, R2, P1, P2, Q; // 說明
Rect validRoi[2];
stereoRectify(cameraMatrix[0], distCoeffs[0],
                     cameraMatrix[1], distCoeffs[1],
                     imageSize, R, T, R1, R2, P1, P2, Q,
                     CALIB_ZERO_DISPARITY, 1, imageSize, &validRoi[0], &validRoi[1]);

//Precompute maps for cv::remap(),構建映射圖
initUndistortRectifyMap(cameraMatrix[0], distCoeffs[0], R1, P1, imageSize, CV_16SC2, rmap[0][0], rmap[0][1]);
initUndistortRectifyMap(cameraMatrix[1], distCoeffs[1], R2, P2, imageSize, CV_16SC2, rmap[1][0], rmap[1][1]);

// 讀圖,矯正
Mat img = imread(goodImageList[i*2+k], 0); // 為何要用黑白圖呢?
Mat rimg, cimg;
remap(img, rimg, rmap[k][0], rmap[k][1], INTER_LINEAR);
cvtColor(rimg, cimg, COLOR_GRAY2BGR);

1.3 python的實現代碼

    # 0.基本配置
    show_corners = False

    image_number = 13
    board_size = (9, 6)  # 也就是boardSize
    square_Size = 20

    image_lists = []  # 存儲獲取到的圖像
    image_points = []  # 存儲圖像的點

    # 1.讀圖,找角點
    image_dir = "/home/wukong/opencv-4.1.0/samples/data"
    image_names = []

    [image_names.append(image_dir + "/left%02d.jpg" % i) for i in
     [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]]  # 沒有10,坑爹
    [image_names.append(image_dir + "/right%02d.jpg" % i) for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14]]
    print(len(image_names))

    for image_name in image_names:
        print(image_name)
        image = cv2.imread(image_name, 0)
        found, corners = cv2.findChessboardCorners(image, board_size)  # 粗查找角點
        if not found:
            print("ERROR(no corners):" + image_name)
            return None
        # 展示結果

        if show_corners:
            vis = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
            cv2.drawChessboardCorners(vis, board_size, corners, found)
            cv2.imwrite(image_name.split(os.sep)[-1], vis)
            cv2.namedWindow("xxx", cv2.WINDOW_NORMAL)
            cv2.imshow("xxx", vis)
            cv2.waitKey()
        term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.01)
        cv2.cornerSubPix(image, corners, (11, 11), (-1, -1), term)  # 精定位角點
        image_points.append(corners.reshape(-1, 2))
        image_lists.append(image)

    # 2. 構建標定板的點坐標,objectPoints
    object_points = np.zeros((np.prod(board_size), 3), np.float32)
    object_points[:, :2] = np.indices(board_size).T.reshape(-1, 2)
    object_points *= square_Size
    object_points = [object_points] * image_number

    # object_points = np.repeat(object_points[np.newaxis, :], 13, axis=0)
    # print(object_points.shape)

    # 3. 分別得到兩個相機的初始CameraMatrix
    h, w = image_lists[0].shape
    camera_matrix = list()

    camera_matrix.append(cv2.initCameraMatrix2D(object_points, image_points[:image_number], (w, h), 0))
    camera_matrix.append(cv2.initCameraMatrix2D(object_points, image_points[image_number:], (w, h), 0))

    # 4. 雙目視覺進行標定
    term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 100, 1e-5)
    retval, cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, R, T, E, F = \
        cv2.stereoCalibrate(object_points, image_points[:image_number], image_points[image_number:], camera_matrix[0],
                            None, camera_matrix[1], None, (w, h),
                            flags=cv2.CALIB_FIX_ASPECT_RATIO | cv2.CALIB_ZERO_TANGENT_DIST | cv2.CALIB_USE_INTRINSIC_GUESS |
                                  cv2.CALIB_SAME_FOCAL_LENGTH | cv2.CALIB_RATIONAL_MODEL | cv2.CALIB_FIX_K3 | cv2.CALIB_FIX_K4 | cv2.CALIB_FIX_K5,
                            criteria=term)

    # 5. 標定精度的衡量, TODO

    # 6. 保存標定結果 TODO

    # 7. 矯正一張圖像看看,是否完成了極線矯正
    R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = \
        cv2.stereoRectify(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, (w, h), R, T)

    map1_1, map1_2 = cv2.initUndistortRectifyMap(cameraMatrix1, distCoeffs1, R1, P1, (w, h), cv2.CV_16SC2)
    map2_1, map2_2 = cv2.initUndistortRectifyMap(cameraMatrix2, distCoeffs2, R2, P2, (w, h), cv2.CV_16SC2)

    start_time = time.time()
    result1 = cv2.remap(image_lists[0], map1_1, map1_2, cv2.INTER_LINEAR)
    result2 = cv2.remap(image_lists[image_number], map2_1, map2_2, cv2.INTER_LINEAR)
    print("變形處理時間%f(s)" % (time.time() - start_time))

    result = np.concatenate((result1, result2), axis=1)
    result[::20, :] = 0
    cv2.imwrite("rec.png", result)
極線矯正結果

整個結果看著還行哈。

2.圖像根據標定結果進行極線矯正(stereoRectify 函數)

根據標定結果,放置新的相機

  • 確認新的虛擬相機位置,滿足極線平行關系
  • 構造映射map
  • 執行map 的變換 remap
    R1, R2, P1, P2, Q, validPixROI1, validPixROI2 = \
        cv2.stereoRectify(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, (w, h), R, T)

    map1_1, map1_2 = cv2.initUndistortRectifyMap(cameraMatrix1, distCoeffs1, R1, P1, (w, h), cv2.CV_16SC2)
    map2_1, map2_2 = cv2.initUndistortRectifyMap(cameraMatrix2, distCoeffs2, R2, P2, (w, h), cv2.CV_16SC2)

    result1 = cv2.remap(image_lists[0], map1_1, map1_2, cv2.INTER_LINEAR)
    result2 = cv2.remap(image_lists[image_number], map2_1, map2_2, cv2.INTER_LINEAR)

3.在每條極線上尋找對應點(視差)

方法有很多


立體匹配的方法
  • StereoBM, block matching 算法,像素級別的位移,速度快

  • StereoSGBM,semi-global block matching算法,亞像素的精度,速度慢很多了,實時應用是不考慮的

  • StereoBeliefPropagation,據說是把這個問題當做了Markov隨機場處理的,所以可以用信念傳播的機制求解,這個目前尚未精通。也是要處理和學習的點。#TODO

4.根據視差轉換為點云(cv2.reprojectImageTo3D)

只需要一步操作就完成了,很簡單

points = cv2.reprojectImageTo3D(disparity, Q)

5. 點云存儲和顯示

略,這些個在opencv/example/python中,應該都可以查看到。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,401評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,011評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,263評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,543評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,323評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,874評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,968評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,095評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,605評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,551評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,720評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,242評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,961評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,358評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,612評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,330評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,690評論 2 370

推薦閱讀更多精彩內容