Android双目三维重建:Android双目摄像头实现双目测距
Android雙目三維重建:Android雙目攝像頭實現雙目測距
目錄
Android雙目三維重建:Android雙目攝像頭實現雙目測距
1.開發版本
2.Android雙目攝像頭
3.雙目相機標定
?(1)雙目相機標定-Python版
?(2)雙目相機標定-Matlab版
4.相機參數配置
5.Android 雙目測距
(1) 核心算法
(2) JNI C++接口
(3) JNI Java接口
6. Android Demo測試效果
7.雙目三維重建項目代碼(Android版本)下載
8. 雙目三維重建項目代碼(C/C++版本)
9. 雙目三維重建項目代碼(Python版本)
10.參考資料
本篇博文是《雙目三維重建系統(雙目標定+立體校正+雙目測距+點云顯示)Python??????》的續作,我們將使用OpenCV C++實現雙目測距,并將算法移植到Android系統,實現一個Android版本雙目三維重建系統。由于我們只考慮三維重建實現雙目測距效果,因而去除了PCL和Open3d庫三維顯示效果,但依然保留了視差圖,深度圖等可視化效果,用戶可以通過觸摸手機屏幕點擊圖像位置,即可獲得對應的世界坐標以及深度距離信息。
從效果來看,Android版本的雙目測距和Python版本的效果幾乎一致,可以達到準實時的檢測效果,基本可以達到工業級別測距精度,可在Android開發板運行,非常適合應用于無人機,智能小車測距避障等場景。?
?來~先看一下Android版本的Demo效果圖(觸摸手機屏幕點擊圖像位置,會顯示對應距離信息):?
Android版本的Demo體驗:https://download.csdn.net/download/guyuealian/87611878
| 未使用WLS濾波器 | 使用WLS濾波器 |
?誠然,網上有很多C++版本雙測距的代碼,但項目都不是十分完整,而且恢復視差圖效果也一般,難以達到商業實際應用,究其原因,主要有下面幾個:
- 雙目攝像頭質量問題,
- 雙目標定存在問題,導致校準誤差較大
- 沒有使用WLS濾波器對視差圖進行濾波,該方法可以極大提高視差圖的效果
本篇將著重介紹OpenCV C++項目實現雙目測距的過程,關于雙目相機標定+雙目校正+雙目匹配等內容,請查看鄙人另一篇博客?《雙目三維重建系統(雙目標定+立體校正+雙目測距+點云顯示)Python??????》
【尊重原則,轉載請注明出處】https://blog.csdn.net/guyuealian/article/details/129762989
更多項目《OpenCV實現雙目測距》系列文章請參考:
1.開發版本
Android SDK,NDK,Jave等版本信息,請參考:
? 項目開發需要依OpenCV庫,同時也需要用到opencv_contrib庫
- opencv 4.5.3
- opencv_contrib 4.5.3
Android項目源碼,已經配置好了opencv,無需重新下載和配置
2.Android雙目攝像頭
開發前,你需要準備有一臺Android系統的雙目攝像頭,要求如下
- 從雙目三維重建原理中可知,左右攝像頭的成像平面盡可能在一個平面內,成像平面不在同一個平面的,盡管可以立體矯正,其效果也差很多。
- 雙目攝像頭必須是同一個平面,不能一個前置攝像頭,一個后置攝像頭(代碼層面可以分為前置和后置攝像頭,但實物機器必須同時前置或者同時后置)
- 基線不太建議太小,作為測試,一般baseline在3~9cm就可以滿足需求,有些無人車的雙目基線更是恐怖到1~2米長
- 一分錢,一分貨,相機的質量好壞,直接決定了你的成像效果
- 雙目攝像頭必須重新標定獲得雙目相機內外參數信息,然后編輯源碼,修改為自己雙目相機的相機參數
3.雙目相機標定
注意,Android版本的雙目三維重建系統的源碼,不涉及雙目標定的相關內容,如果那你需要適配自己的手機雙目攝像頭,你需要重新標定,詳細步驟參考如下:
?(1)雙目相機標定-Python版
請參考鄙人另一篇博客,無需Matlab,即可進行相機標定:雙目三維重建系統(雙目標定+立體校正+雙目測距+點云顯示)Python
該方法雙目標定完成后,會得到一個雙目相機內外參數信息(stereo_cam.yml)文件:
%YAML:1.0 --- size: !!opencv-matrixrows: 2cols: 1dt: ddata: [ 640., 480. ] K1: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 7.6159209686584518e+02, 0., 3.2031427422505453e+02, 0.,7.6167321445963728e+02, 2.2467546927337131e+02, 0., 0., 1. ] D1: !!opencv-matrixrows: 1cols: 5dt: ddata: [ 3.4834574885170888e-02, -5.5261651661983137e-02,5.7491952731614823e-04, -4.2764224824172658e-05,1.8477350140315381e-02 ] K2: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 7.6327773941976670e+02, 0., 2.8768149948082271e+02, 0.,7.6350419442870850e+02, 2.1897333598636970e+02, 0., 0., 1. ] D2: !!opencv-matrixrows: 1cols: 5dt: ddata: [ 3.5020972475517692e-02, -4.0770660841280497e-02,-4.4231087565750534e-04, -1.0552562170995372e-03,-9.7749906830348537e-02 ] R: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 9.9999370552351063e-01, 7.8563885326366346e-04,3.4600122760633780e-03, -7.9503151737356746e-04,9.9999600079883766e-01, 2.7140949167922721e-03,-3.4578661403601796e-03, -2.7168286517956050e-03,9.9999033095517087e-01 ] T: !!opencv-matrixrows: 3cols: 1dt: ddata: [ -6.0005833133148414e+01, 1.7047017063672587e-01,6.0300223404957642e-01 ] E: !!opencv-matrixrows: 3cols: 3dt: ddata: [ -1.1005724987007073e-04, -6.0346296076620343e-01,1.6883191705475561e-01, 3.9550629985097430e-01,-1.6255182474732952e-01, 6.0007339329190145e+01,-1.2276256904913259e-01, -6.0005727085740176e+01,-1.6345135556766910e-01 ] F: !!opencv-matrixrows: 3cols: 3dt: ddata: [ -6.7250769136371160e-10, -3.6870834234286016e-06,1.6143104894409041e-03, 2.4160347372858321e-06,-9.9287680075344234e-07, 2.7862421257891157e-01,-1.1014218394645766e-03, -2.7856049650040260e-01, 1. ] R1: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 9.9997618806974742e-01, -2.0278309638726887e-03,-6.5963016213173775e-03, 2.0367881225372914e-03,9.9999701250432615e-01, 1.3514719999064883e-03,6.5935413581266105e-03, -1.3648750875444691e-03,9.9997733090723306e-01 ] R2: !!opencv-matrixrows: 3cols: 3dt: ddata: [ 9.9994547731576255e-01, -2.8407384289991728e-03,-1.0048512373976153e-02, 2.8270879178959596e-03,9.9999506202764499e-01, -1.3724045434755307e-03,1.0052361397026631e-02, 1.3439216883706559e-03,9.9994857062992937e-01 ] P1: !!opencv-matrixrows: 3cols: 4dt: ddata: [ 7.3741438842621210e+02, 0., 3.1126281356811523e+02, 0., 0.,7.3741438842621210e+02, 2.2189782714843750e+02, 0., 0., 0., 1.,0. ] P2: !!opencv-matrixrows: 3cols: 4dt: ddata: [ 7.3741438842621210e+02, 0., 3.1126281356811523e+02,-4.4251577456670653e+04, 0., 7.3741438842621210e+02,2.2189782714843750e+02, 0., 0., 0., 1., 0. ] Q: !!opencv-matrixrows: 4cols: 4dt: ddata: [ 1., 0., 0., -3.1126281356811523e+02, 0., 1., 0.,-2.2189782714843750e+02, 0., 0., 0., 7.3741438842621210e+02, 0.,0., 1.6664137886344466e-02, 0. ]參數說明:?
- 參數size,對應圖像寬高(width,height)
- 參數K1,對應左目相機內參矩陣(3×3)
- 參數D1,對應左目相機畸變系數矩陣(5×1)
- 參數K2,對應右目相機內參矩陣(3×3)
- 參數D2,對應右目相機畸變系數矩陣(5×1)
- 參數T,對應雙目相機平移向量T(3×1)
- 參數R,對應雙目相機旋轉矩陣R(3×3)
- 至于配置文件中的參數,如R1, R2, P1, P2, Q這些重投影矩陣,可默寫即可,不用修改,這些在運行時,會重新計算。
?(2)雙目相機標定-Matlab版
網上已經存在很多Matlab雙目相機標定的教程,請自行百度哈 ;使用Matlab工具箱進行雙目相機標定后,請對應參數進行修改
需要注意的是:旋轉矩陣R是(3×3)二維矩陣,而Matlab給出的是旋轉向量om(1×3),請使用cv2.Rodrigues()將旋轉向量轉為旋轉矩陣,參考下面的代碼進行轉換
import cv2import numpy as np# 定義旋轉矩陣R,旋轉向量omR = [[9.9999370551606337e-01, 7.8563882630048958e-04, 3.4600144345510440e-03],[-7.9503149273969136e-04, 9.9999600080163187e-01, 2.7140938945082542e-03],[-3.4578682997252063e-03, -2.7168276311286426e-03, 9.9999033095047696e-01]]R = np.asarray(R)print(f"旋轉矩陣R:\n {R}")# 把旋轉矩陣R轉化為旋轉向量omom, _ = cv2.Rodrigues(R)print(f"旋轉向量om:\n {om}")# 把旋轉向量om轉換為旋轉矩陣RR1, _ = cv2.Rodrigues(om)print(f"旋轉矩陣R1:\n {R1}")4.相機參數配置
- 雙目相機標定完成后,得到了相機內外參數信息
- 根據自己相機參數,修改項目app/src/main/cpp/src/stereo_reconstruct.h文件
- 下面C++代碼中,定義了雙目相機CameraParam變量camera1,用戶需要根據自己的雙目相機,修改對應的相機內外參數。
5.Android 雙目測距
Android OpenCV版本的雙目測距與Python版本雙目測距的效果幾乎一致,基本可以達到工業級別測距精度。由于我們只考慮三維重建實現雙目測距效果,因而去除了PCL和Open3d庫三維顯示效果,但依然保留了視差圖,深度圖等可視化效果,用戶可以通過觸摸手機屏幕點擊圖像位置,即可獲得對應的世界坐標以及深度距離信息。
(1) 核心算法
Android版本的雙目測距算法,核心代碼都使用C++開發,上層應用Java部分通過JNI調用opencv C++算法,函數接口聲明,都已經給出了詳細的參數說明,為了方便大家學習,函數命名和實現邏輯與Python版本的幾乎一致:
| Python版本 | C++版本 |
| ? | ? |
- 這是C++核心算法部分函數定義
(2) JNI C++接口
#include <jni.h> #include <string> #include <fstream> #include "src/stereo_reconstruct.h" #include "android_utils.h" #include "debug.h" #include "opencv2/opencv.hpp"StereoReconstruct *stereo = nullptr;JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {return JNI_VERSION_1_6; }JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved) {}extern "C" JNIEXPORT void JNICALL Java_com_cv_binocular_reconstruct_StereoReconstruct_init(JNIEnv *env, jclass clazz, jboolean use_wls) {CameraParam camera = camera1;//雙目相機參數(請根據自己雙目相機標定結果進行修改)//bool use_wls = true; //是否使用WLS濾波器對視差圖進行濾波stereo = new StereoReconstruct(camera, use_wls, false); }extern "C" JNIEXPORT void JNICALL Java_com_cv_binocular_reconstruct_StereoReconstruct_reBuild(JNIEnv *env, jclass clazz, jobject bitmapL,jobject bitmapR,jobject disp_colormap,jobject depth_colormap) {Mat frameL; // 左視圖Mat frameR; // 右視圖BitmapToMatrix(env, bitmapL, frameL);BitmapToMatrix(env, bitmapR, frameR);stereo->task(frameL, frameR, 0);MatrixToBitmap(env, stereo->disp_colormap, disp_colormap); //視差圖可視化圖(CV_8UC3)MatrixToBitmap(env, stereo->depth_colormap, depth_colormap);//視差圖可視化圖(CV_8UC3)LOGW("frameL : (%d,%d)", frameL.cols, frameL.rows);LOGW("disp_colormap : (%d,%d)", stereo->disp_colormap.cols, stereo->disp_colormap.rows);LOGW("depth_colormap : (%d,%d)", stereo->depth_colormap.cols, stereo->depth_colormap.rows); }extern "C" JNIEXPORT jobject JNICALL Java_com_cv_binocular_reconstruct_StereoReconstruct_getWorld(JNIEnv *env, jclass clazz, jint x,jint y) {cv::Vec3f point = stereo->points_3d.at<Vec3f>(Point(x, y));float cx = point[0];float cy = point[1];float cz = point[2];LOGW("image(x,y)=[%d,%d] world=(x,y,depth)=[%3.2f,%3.2f,%3.2f]mm", x, y, cx, cy, cz);auto cls_point = env->FindClass("com/cv/binocular/reconstruct/Point3d");auto init_id = env->GetMethodID(cls_point, "<init>", "(FFF)V");env->PushLocalFrame(1);jobject obj = env->NewObject(cls_point, init_id, cx, cy, cz);obj = env->PopLocalFrame(obj);return obj; }(3) JNI Java接口
package com.cv.binocular.reconstruct;import android.graphics.Bitmap;public class StereoReconstruct {static {System.loadLibrary("binocular_wrapper");}/**** 初始化* @param use_wls 是否使用WLS濾波器對視差圖進行濾波*/public static native void init(boolean use_wls);/**** 進行雙目三維重建* @param frameL: 輸入左視圖* @param frameR:輸入右視圖* @param disp_colormap:輸出視差圖可視化圖* @param depth_colormap:輸出深度圖可視化圖* @return*/public static native void reBuild(Bitmap frameL, Bitmap frameR, Bitmap disp_colormap, Bitmap depth_colormap);/**** 將圖像坐標映射為世界坐標(world coordinate)* @param x 輸入圖像像素坐標x* @param y 輸入圖像像素坐標y* @return Point3d世界坐標(x, y, z),其中z為深度距離*/public static native Point3d getWorld(int x, int y); }6. Android Demo測試效果
Android版本雙目測距Demo源碼提供圖片,視頻和攝像頭三種方式測試
- Android Demo圖片測試:項目資源(src/main/assets)自帶一對左右視圖的測試圖片,你需要將測試圖片拷貝到你的手機,然后在Demo APP點擊【圖片】打開圖片即可;如果你想測試自己的圖片,請將左視圖文件命名為left***.png,右視圖文件命名為right***.png,否則不能正常加載左右視圖。圖片格式支持jpg,png等多種格式
- Android Demo視頻測試:項目資源(src/main/assets)自帶一對左右視圖的視頻文件,你需要將測試視頻拷貝到你的手機,然后在Demo APP點擊【視頻】打開視頻即可;如果你想測試自己的視頻,請將左視圖視頻文件命名為left***.mp4,右視圖視頻文件命名為right***.mp4,否則不能正常加載左右視圖。視頻格式支持mp4,avi等多種格式
- Android Demo攝像頭測試:需要Android設備支持兩個攝像頭,源碼部分cameraL = 0對應前置攝像頭,cameraR = 1對應后置攝像頭,僅作為Android手機測試使用;真實Android手機,雙目攝像頭必須要同一平面上。
??Android版本的Demo效果圖(觸摸手機屏幕點擊圖像位置,會顯示對應距離信息):??
Android版本的Demo體驗:https://download.csdn.net/download/guyuealian/87611878
?從測試效果可以看到,使用WLS濾波后,視差圖的整體效果都有明顯改善,但速度會變慢哦
| 未使用WLS濾波器 | 使用WLS濾波器 |
7.雙目三維重建項目代碼(Android版本)下載
完整的Android項目代碼請公眾號咨詢聯系(非無償)
整體Android項目源碼包含:
- Demo支持使用WLS濾波器對視差圖進行濾波
- Demo支持雙目測距,誤差在1cm內(觸摸手機屏幕點擊圖像位置,會顯示對應距離信息)
- Demo支持圖片,視頻,攝像頭測試
- 所有依賴庫都已經配置好,可直接build運行
8. 雙目三維重建項目代碼(C/C++版本)
目前已經實現了OpenCV C++版本的雙目測距,與Python版本效果幾乎一致,
詳細請查看鄙人另一篇博客《OpenCV C++雙目攝像頭實現雙目測距》:OpenCV C++雙目三維重建:雙目攝像頭實現雙目測距_opencv雙目三維重建_AI吃大瓜的博客-CSDN博客
?
9. 雙目三維重建項目代碼(Python版本)
如果你需要Python版本的雙目測距,?請查看鄙人另一篇博客《雙目三維重建系統(雙目標定+立體校正+雙目測距+點云顯示)Python》
| 雙目測距Demo視頻 |
10.參考資料
?https://blog.csdn.net/guyuealian/article/details/129762989
總結
以上是生活随笔為你收集整理的Android双目三维重建:Android双目摄像头实现双目测距的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机组成与体系结构(软件设计师考试)
- 下一篇: 上手 WebRTC DTLS 遇到很多