package com.yeyupiaoling.teststadiometry; import android.graphics.Bitmap; import org.opencv.calib3d.Calib3d; import org.opencv.calib3d.StereoBM; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.Rect; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.imgproc.Imgproc; import static org.opencv.android.Utils.bitmapToMat; import static org.opencv.android.Utils.matToBitmap; public class StereoBMUtil { private static final String TAG = StereoBMUtil.class.getName(); // 【需要根据摄像头修改参数】 private final int imageWidth = 1280; // 单目图像的宽度 private final int imageHeight = 720; // 单目图像的高度 private Mat Q = new Mat(); //映射表 private Mat mapLx = new Mat(); private Mat mapLy = new Mat(); private Mat mapRx = new Mat(); private Mat mapRy = new Mat(); private StereoBM bm = StereoBM.create(); private Mat xyz; public StereoBMUtil() { Mat cameraMatrixL = new Mat(3, 3, CvType.CV_64F); Mat distCoeffL = new Mat(5, 1, CvType.CV_64F); Mat cameraMatrixR = new Mat(3, 3, CvType.CV_64F); Mat distCoeffR = new Mat(5, 1, CvType.CV_64F); Mat T = new Mat(3, 1, CvType.CV_64F); Mat rec = new Mat(3, 1, CvType.CV_64F); // 【需要根据摄像头修改参数】左目相机标定参数 fc_left_x 0 cc_left_x 0 fc_left_y cc_left_y 0 0 1 cameraMatrixL.put(0, 0, 849.38718, 0, 720.28472, 0, 850.60613, 373.88887, 0, 0, 1); //【需要根据摄像头修改参数】左目相机标定参数 kc_left_01, kc_left_02, kc_left_03, kc_left_04, kc_left_05 distCoeffL.put(0, 0, 0.01053, 0.02881, 0.00144, 0.00192, 0.00000); //【需要根据摄像头修改参数】右目相机标定参数 fc_right_x 0 cc_right_x 0 fc_right_y cc_right_y 0 0 1 cameraMatrixR.put(0, 0, 847.54814, 0, 664.36648, 0, 847.75828, 368.46946, 0, 0, 1); //【需要根据摄像头修改参数】右目相机标定参数 kc_right_01, kc_right_02, kc_right_03, kc_right_04, kc_right_05 distCoeffR.put(0, 0, 0.00905, 0.02094, 0.00082, 0.00183, 0.00000); //【需要根据摄像头修改参数】T平移向量 T.put(0, 0, -59.32102, 0.27563, -0.79807); // 【需要根据摄像头修改参数】rec旋转向量 rec.put(0, 0, -0.00927, -0.00228, -0.00070); Size imageSize = new Size(imageWidth, imageHeight); Mat R = new Mat(); Mat Rl = new Mat(); Mat Rr = new Mat(); Mat Pl = new Mat(); Mat Pr = new Mat(); Rect validROIL = new Rect(); Rect validROIR = new Rect(); Calib3d.Rodrigues(rec, R); //Rodrigues变换 //图像校正之后,会对图像进行裁剪,这里的validROI就是指裁剪之后的区域 Calib3d.stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, Calib3d.CALIB_ZERO_DISPARITY, 0, imageSize, validROIL, validROIR); Imgproc.initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize, CvType.CV_32FC1, mapLx, mapLy); Imgproc.initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CvType.CV_32FC1, mapRx, mapRy); int blockSize = 18; int numDisparities = 11; int uniquenessRatio = 5; bm.setBlockSize(2 * blockSize + 5); //SAD窗口大小 bm.setROI1(validROIL); //左右视图的有效像素区域 bm.setROI2(validROIR); bm.setPreFilterCap(61); //预处理滤波器 bm.setMinDisparity(32); //最小视差,默认值为0, 可以是负值,int型 bm.setNumDisparities(numDisparities * 16); //视差窗口,即最大视差值与最小视差值之差,16的整数倍 bm.setTextureThreshold(10); bm.setUniquenessRatio(uniquenessRatio); //视差唯一性百分比,uniquenessRatio主要可以防止误匹配 bm.setSpeckleWindowSize(100); //检查视差连通区域变化度的窗口大小 bm.setSpeckleRange(32); //32视差变化阈值,当窗口内视差变化大于阈值时,该窗口内的视差清零 bm.setDisp12MaxDiff(-1); } public Bitmap compute(Bitmap left, Bitmap right) { Mat rgbImageL = new Mat(); Mat rgbImageR = new Mat(); Mat grayImageL = new Mat(); Mat rectifyImageL = new Mat(); Mat rectifyImageR = new Mat(); Mat grayImageR = new Mat(); //用于存放每个像素点距离相机镜头的三维坐标 xyz = new Mat(); Mat disp = new Mat(); bitmapToMat(left, rgbImageL); bitmapToMat(right, rgbImageR); Imgproc.cvtColor(rgbImageL, grayImageL, Imgproc.COLOR_BGR2GRAY); Imgproc.cvtColor(rgbImageR, grayImageR, Imgproc.COLOR_BGR2GRAY); Imgproc.remap(grayImageL, rectifyImageL, mapLx, mapLy, Imgproc.INTER_LINEAR); Imgproc.remap(grayImageR, rectifyImageR, mapRx, mapRy, Imgproc.INTER_LINEAR); bm.compute(rectifyImageL, rectifyImageR, disp); //输入图像必须为灰度图 Calib3d.reprojectImageTo3D(disp, xyz, Q, true); //在实际求距离时,ReprojectTo3D出来的X / W, Y / W, Z / W都要乘以16 Core.multiply(xyz, new Mat(xyz.size(), CvType.CV_32FC3, new Scalar(16, 16, 16)), xyz); // 用于显示处理 Mat disp8U = new Mat(disp.rows(), disp.cols(), CvType.CV_8UC1); disp.convertTo(disp, CvType.CV_32F, 1.0 / 16); //除以16得到真实视差值 Core.normalize(disp, disp8U, 0, 255, Core.NORM_MINMAX, CvType.CV_8U); Imgproc.medianBlur(disp8U, disp8U, 9); Bitmap resultBitmap = Bitmap.createBitmap(disp8U.cols(), disp8U.rows(), Bitmap.Config.ARGB_8888); matToBitmap(disp8U, resultBitmap); return resultBitmap; } public double[] getCoordinate(int dstX, int dstY) { double x = xyz.get(dstY, dstX)[0]; double y = xyz.get(dstY, dstX)[1]; double z = xyz.get(dstY, dstX)[2]; return new double[]{x, y, z}; } }