目录

基于平行视图的双目立体视觉

图像矫正(如何获得平行视图)

对应点搜索(如何建立点对点的关系)


基于平行视图的双目立体视觉

  • 如下图所示,是一个平行视图:

两个图像平面平行;
基线平行于图像平面,极点e和e'位于无穷远处

  • 基础矩阵F

下面是将左边的像素原点进行相应的变化,到右边的像素原点,进而得到相应的一个变换过程.

  • 在进行相应的变换之前,搞清楚每个变换过程两个相机之间的内在变换关系
  • 从而得到相应的平移视图的基础矩阵就是可以直接得到的.
  • 可以直接得到相应的一个极线

  • 得到平行视图的三角测量图()

上面的这个图是在许多的论文之中提到的,比如说在知网之中的基于双目立体视觉的远距离测量论文之中的图像如下所示:

根据相似三角形关系可以知道,如下所示关系:

视差就是所谓的在两个眼睛之中相应的坐标移动,这里的视差就是和z成反比的关系.

视差原理是当物体越大的时候,视差就是越大,z越小.通过上面可以知道,可以进行实时算出z的大小,不用进行三点化的过程.

  • 视差图

上述的视差图就是根据距离的远近,调节相应的一个图像明暗的过程.

这里给出一个求取视差图的一个参考代码(首先是进行棋盘格标定,后面进行极线矫正,从而求取视差):

#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/calib3d/calib3d.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <fstream>using namespace cv;
using namespace std;const char* imageName_L = "C:\\Users\\td\\Desktop\\项目\\代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/1.jpg"; // 用于检测深度的图像
const char* imageName_R = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/26.jpg";
const char* imageList_L = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/caliberationpics_L.txt"; // 左相机的标定图片名称列表
const char* imageList_R = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/caliberationpics_R.txt"; // 右相机的标定图片名称列表
const char* singleCalibrate_result_L = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/calibrationresults_L.txt"; // 存放左相机的标定结果
const char* singleCalibrate_result_R = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/calibrationresults_R.txt"; // 存放右相机的标定结果
const char* stereoCalibrate_result_L = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/stereocalibrateresult_L.txt"; // 存放立体标定结果
const char* stereoCalibrate_result_R = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/stereocalibrateresult_R.txt";
const char* stereoRectifyParams = "C:/Users/td/Desktop/项目/代码参考/相机标定(单目、双目)/相机标定(单目、双目)/相机标定(单目、双目)/stereoRectifyParams.txt"; // 存放立体校正参数
vector<vector<Point2f>> corners_seq_L; // 所有角点坐标
vector<vector<Point2f>> corners_seq_R;
vector<vector<Point3f>> objectPoints_L; // 三维坐标
vector<vector<Point3f>> objectPoints_R;
Mat cameraMatrix_L = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 相机的内参数
Mat cameraMatrix_R = Mat(3, 3, CV_32FC1, Scalar::all(0)); // 初始化相机的内参数
Mat distCoeffs_L = Mat(1, 5, CV_32FC1, Scalar::all(0)); // 相机的畸变系数
Mat distCoeffs_R = Mat(1, 5, CV_32FC1, Scalar::all(0)); // 初始化相机的畸变系数
Mat R, T, E, F; // 立体标定参数
Mat R1, R2, P1, P2, Q; // 立体校正参数
Mat mapl1, mapl2, mapr1, mapr2; // 图像重投影映射表
Mat img1_rectified, img2_rectified, disparity, result3DImage; // 校正图像 视差图 深度图
Size patternSize = Size(9, 6); // 行列内角点个数
Size chessboardSize = Size(30, 30); // 棋盘上每个棋盘格的大小30mm
Size imageSize; // 图像尺寸
Rect validRoi[2];/*
单目标定
参数:imageList     存放标定图片名称的txtsingleCalibrateResult   存放标定结果的txtobjectPoints  世界坐标系中点的坐标corners_seq       存放图像中的角点,用于立体标定cameraMatrix 相机的内参数矩阵distCoeffs      相机的畸变系数imageSize        输入图像的尺寸(像素)patternSize        标定板每行的角点个数, 标定板每列的角点个数 (9, 6)   chessboardSize  棋盘上每个方格的边长(mm)
注意:亚像素精确化时,允许输入单通道,8位或者浮点型图像。由于输入图像的类型不同,下面用作标定函数参数的内参数矩阵和畸变系数矩阵在初始化时也要数据注意类型。
*/
bool singleCameraCalibrate(const char* imageList, const char* singleCalibrateResult, vector<vector<Point3f>>& objectPoints, vector<vector<Point2f>>& corners_seq, Mat& cameraMatrix, Mat& distCoeffs, Size& imageSize, Size patternSize, Size chessboardSize)
{int n_boards = 0;ifstream imageStore(imageList); // 打开存放标定图片名称的txtofstream resultStore(singleCalibrateResult); // 保存标定结果的txt// 开始提取角点坐标vector<Point2f> corners; // 存放一张图片的角点坐标 string imageName; // 读取的标定图片的名称while (getline(imageStore, imageName)) // 读取txt的每一行(每一行存放了一张标定图片的名称){n_boards++;Mat imageInput = imread(imageName);cvtColor(imageInput, imageInput, CV_RGB2GRAY);//讲颜色空间进行相应的转化:从RBG和BGR颜色空间转换到灰度空间/*void cv::cvtColor(cv::InputArray src, // 输入序列cv::OutputArray dst, // 输出序列int code, // 颜色映射码int dstCn = 0 // 输出的通道数 (0='automatic'));*/imageSize.width = imageInput.cols; // 获取图片的宽度imageSize.height = imageInput.rows; // 获取图片的高度// 查找标定板的角点bool found = findChessboardCorners(imageInput, patternSize, corners); // 最后一个参数int flags的缺省值为:CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE// 亚像素精确化。在findChessboardCorners中自动调用了cornerSubPix,为了更加精细化,我们自己再调用一次。if (found) // 当所有的角点都被找到{TermCriteria criteria = TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 40, 0.001); // 终止标准,迭代40次或者达到0.001的像素精度cornerSubPix(imageInput, corners, Size(11, 11), Size(-1, -1), criteria);// 由于我们的图像只存较大,将搜索窗口调大一些,(11, 11)为真实窗口的一半,真实大小为(11*2+1, 11*2+1)--(23, 23)corners_seq.push_back(corners); // 存入角点序列// 绘制角点drawChessboardCorners(imageInput, patternSize, corners, true);imshow("cornersframe", imageInput);waitKey(500); // 暂停0.5s}}//destroyWindow("cornersframe");// 进行相机标定// 计算角点对应的三维坐标int pic, i, j;for (pic = 0; pic < n_boards; pic++){vector<Point3f> realPointSet;for (i = 0; i < patternSize.height; i++){for (j = 0; j < patternSize.width; j++){Point3f realPoint;// 假设标定板位于世界坐标系Z=0的平面realPoint.x = j * chessboardSize.width;realPoint.y = i * chessboardSize.height;realPoint.z = 0;realPointSet.push_back(realPoint);}}objectPoints.push_back(realPointSet);//存入角点三维坐标}// 执行标定程序vector<Mat> rvec; // 旋转向量vector<Mat> tvec; // 平移向量calibrateCamera(objectPoints, corners_seq, imageSize, cameraMatrix, distCoeffs, rvec, tvec, 0);// 保存标定结果resultStore << "相机内参数矩阵" << endl;resultStore << cameraMatrix << endl << endl;resultStore << "相机畸变系数" << endl;resultStore << distCoeffs << endl << endl;// 计算重投影点,与原图角点比较,得到误差double errPerImage = 0.; // 每张图像的误差double errAverage = 0.; // 所有图像的平均误差double totalErr = 0.; // 误差总和vector<Point2f> projectImagePoints; // 重投影点for (i = 0; i < n_boards; i++){vector<Point3f> tempObjectPoints = objectPoints[i]; // 临时三维点// 计算重投影点projectPoints(tempObjectPoints, rvec[i], tvec[i], cameraMatrix, distCoeffs, projectImagePoints);// 计算新的投影点与旧的投影点之间的误差vector<Point2f> tempCornersPoints = corners_seq[i];// 临时存放旧投影点Mat tempCornersPointsMat = Mat(1, tempCornersPoints.size(), CV_32FC2); // 定义成两个通道的Mat是为了计算误差Mat projectImagePointsMat = Mat(1, projectImagePoints.size(), CV_32FC2);// 赋值for (int j = 0; j < tempCornersPoints.size(); j++){projectImagePointsMat.at<Vec2f>(0, j) = Vec2f(projectImagePoints[j].x, projectImagePoints[j].y);tempCornersPointsMat.at<Vec2f>(0, j) = Vec2f(tempCornersPoints[j].x, tempCornersPoints[j].y);}// opencv里的norm函数其实把这里的两个通道分别分开来计算的(X1-X2)^2的值,然后统一求和,最后进行根号errPerImage = norm(tempCornersPointsMat, projectImagePointsMat, NORM_L2) / (patternSize.width * patternSize.height);totalErr += errPerImage;resultStore << "第" << i + 1 << "张图像的平均误差为:" << errPerImage << endl;}resultStore << "全局平局误差为:" << totalErr / n_boards << endl;imageStore.close();resultStore.close();return true;
}/*
双目标定:计算两摄像机相对旋转矩阵 R,平移向量 T, 本征矩阵E, 基础矩阵F
参数:stereoCalibrateResult 存放立体标定结果的txtobjectPoints            三维点imagePoints              二维图像上的点cameraMatrix         相机内参数distCoeffs             相机畸变系数imageSize             图像尺寸R       左右相机相对的旋转矩阵T        左右相机相对的平移向量E        本征矩阵F       基础矩阵
*/
bool stereoCalibrate(const char* stereoCalibrateResult, vector<vector<Point3f>> objectPoints, vector<vector<Point2f>> imagePoints1, vector<vector<Point2f>> imagePoints2,Mat& cameraMatrix1, Mat& distCoeffs1, Mat& cameraMatrix2, Mat& distCoeffs2, Size& imageSize, Mat& R, Mat& T, Mat& E, Mat& F)
{ofstream stereoStore(stereoCalibrateResult);TermCriteria criteria = TermCriteria(TermCriteria::COUNT | TermCriteria::EPS, 30, 1e-6); // 终止条件stereoCalibrate(objectPoints, imagePoints1, imagePoints2, cameraMatrix1, distCoeffs1,cameraMatrix2, distCoeffs2, imageSize, R, T, E, F, CALIB_FIX_INTRINSIC, criteria); // 注意参数顺序,可以到保存的文件中查看,避免返回时出错stereoStore << "左相机内参数:" << endl;stereoStore << cameraMatrix1 << endl;stereoStore << "右相机内参数:" << endl;stereoStore << cameraMatrix2 << endl;stereoStore << "左相机畸变系数:" << endl;stereoStore << distCoeffs1 << endl;stereoStore << "右相机畸变系数:" << endl;stereoStore << distCoeffs2 << endl;stereoStore << "旋转矩阵:" << endl;stereoStore << R << endl;stereoStore << "平移向量:" << endl;stereoStore << T << endl;stereoStore << "本征矩阵:" << endl;stereoStore << E << endl;stereoStore << "基础矩阵:" << endl;stereoStore << F << endl;stereoStore.close(); return true;
}/*
立体校正
参数:stereoRectifyParams   存放立体校正结果的txtcameraMatrix            相机内参数distCoeffs             相机畸变系数imageSize             图像尺寸R                       左右相机相对的旋转矩阵T                        左右相机相对的平移向量R1, R2                   行对齐旋转校正P1, P2                   左右投影矩阵Q                     重投影矩阵map1, map2             重投影映射表
*/
Rect stereoRectification(const char* stereoRectifyParams, Mat& cameraMatrix1, Mat& distCoeffs1, Mat& cameraMatrix2, Mat& distCoeffs2,Size& imageSize, Mat& R, Mat& T, Mat& R1, Mat& R2, Mat& P1, Mat& P2, Mat& Q, Mat& mapl1, Mat& mapl2, Mat& mapr1, Mat& mapr2)
{Rect validRoi[2];ofstream stereoStore(stereoRectifyParams);stereoRectify(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, imageSize,R, T, R1, R2, P1, P2, Q, 0, -1, imageSize, &validRoi[0], &validRoi[1]);// 计算左右图像的重投影映射表stereoStore << "R1:" << endl;stereoStore << R1 << endl;stereoStore << "R2:" << endl;stereoStore << R2 << endl;stereoStore << "P1:" << endl;stereoStore << P1 << endl;stereoStore << "P2:" << endl;stereoStore << P2 << endl;stereoStore << "Q:" << endl;stereoStore << Q << endl;stereoStore.close();cout << "R1:" << endl;cout << R1 << endl;cout << "R2:" << endl;cout << R2 << endl;cout << "P1:" << endl;cout << P1 << endl;cout << "P2:" << endl;cout << P2 << endl;cout << "Q:" << endl;cout << Q << endl;initUndistortRectifyMap(cameraMatrix1, distCoeffs1, R1, P1, imageSize, CV_32FC1, mapl1, mapl2);initUndistortRectifyMap(cameraMatrix2, distCoeffs2, R2, P2, imageSize, CV_32FC1, mapr1, mapr2);return validRoi[0], validRoi[1];
}/*
计算视差图
参数:imageName1    左相机拍摄的图像imageName2  右相机拍摄的图像img1_rectified  重映射后的左侧相机图像img2_rectified   重映射后的右侧相机图像map  重投影映射表
*/
bool computeDisparityImage(const char* imageName1, const char* imageName2, Mat& img1_rectified,Mat& img2_rectified, Mat& mapl1, Mat& mapl2, Mat& mapr1, Mat& mapr2, Rect validRoi[2], Mat& disparity)
{// 首先,对左右相机的两张图片进行重构Mat img1 = imread(imageName1);Mat img2 = imread(imageName2);if (img1.empty() | img2.empty()){cout << "图像为空" << endl;}Mat gray_img1, gray_img2;cvtColor(img1, gray_img1, COLOR_BGR2GRAY);cvtColor(img2, gray_img2, COLOR_BGR2GRAY);Mat canvas(imageSize.height, imageSize.width * 2, CV_8UC1); // 注意数据类型Mat canLeft = canvas(Rect(0, 0, imageSize.width, imageSize.height));   Mat canRight = canvas(Rect(imageSize.width, 0, imageSize.width, imageSize.height));gray_img1.copyTo(canLeft);gray_img2.copyTo(canRight);imwrite("校正前左右相机图像.jpg", canvas);remap(gray_img1, img1_rectified, mapl1, mapl2, INTER_LINEAR);remap(gray_img2, img2_rectified, mapr1, mapr2, INTER_LINEAR);   imwrite("左相机校正图像.jpg", img1_rectified);imwrite("右相机校正图像.jpg", img2_rectified);img1_rectified.copyTo(canLeft);img2_rectified.copyTo(canRight);rectangle(canLeft, validRoi[0], Scalar(255, 255, 255), 5, 8);rectangle(canRight, validRoi[1], Scalar(255, 255, 255), 5, 8);for (int j = 0; j <= canvas.rows; j += 16)line(canvas, Point(0, j), Point(canvas.cols, j), Scalar(0, 255, 0), 1, 8);imwrite("校正后左右相机图像.jpg", canvas);// 进行立体匹配Ptr<StereoBM> bm = StereoBM::create(16, 9); // Ptr<>是一个智能指针bm->compute(img1_rectified, img2_rectified, disparity); // 计算视差图disparity.convertTo(disparity, CV_32F, 1.0 / 16);// 归一化视差映射normalize(disparity, disparity, 0, 256, NORM_MINMAX, -1);return true;
}// 鼠标回调函数,点击视差图显示深度
void onMouse(int event, int x, int y, int flags, void *param)
{Point point;point.x = x;point.y = y;if(event == EVENT_LBUTTONDOWN){cout << result3DImage.at<Vec3f>(point) << endl;}
}int main()
{singleCameraCalibrate(imageList_L, singleCalibrate_result_L, objectPoints_L, corners_seq_L, cameraMatrix_L,distCoeffs_L, imageSize, patternSize, chessboardSize);cout << "已完成左相机的标定!" << endl;singleCameraCalibrate(imageList_R, singleCalibrate_result_R, objectPoints_R, corners_seq_R, cameraMatrix_R,distCoeffs_R, imageSize, patternSize, chessboardSize);cout << "已完成右相机的标定!" << endl;stereoCalibrate(stereoCalibrate_result_L, objectPoints_L, corners_seq_L, corners_seq_R, cameraMatrix_L, distCoeffs_L,cameraMatrix_R, distCoeffs_R, imageSize, R, T, E, F);cout << "相机立体标定完成!" << endl;//stereoCalibrate(stereoCalibrate_result_R, objectPoints_R, corners_seq_L, corners_seq_R, cameraMatrix_L, distCoeffs_L,//cameraMatrix_R, distCoeffs_R, imageSize, R2, T2, E2, F2);//cout << "右相机立体标定完成!" << endl;validRoi[0], validRoi[1] = stereoRectification(stereoRectifyParams, cameraMatrix_L, distCoeffs_L, cameraMatrix_R, distCoeffs_R,imageSize, R, T, R1, R2, P1, P2, Q, mapl1, mapl2, mapr1, mapr2);cout << "已创建图像重投影映射表!" << endl;computeDisparityImage(imageName_L, imageName_R, img1_rectified, img2_rectified, mapl1, mapl2, mapr1, mapr2, validRoi, disparity);cout << "视差图建立完成!" << endl;// 从三维投影获得深度映射reprojectImageTo3D(disparity, result3DImage, Q);imshow("视差图", disparity);setMouseCallback("视差图", onMouse);waitKey(0);//destroyAllWindows();return 0;
}
  • 视差原理应用

通过视差感受距离的远近,跟人眼是相同的,诱发大脑的重建机制.

图像矫正(如何获得平行视图)

  • 图像矫正的过程:令两个图像"平行"

  • 步骤(使用五步法)

1.在两个图像之间找到一组匹配点,并且匹配点是不少于8个.

2.计算基础矩阵F,求解两个图像之中的极点.

3.选择透视变换,将原来的右边图像进行映射,映射到无穷远处(f,0,0),具体的步骤是如下所示:

4.寻找对应的透视投影变换矩阵H使得二者之间的变换是最小的.

5.利用上述的矩阵进行重新投影.

对应点搜索(如何建立点对点的关系)

  • 对应点的寻找问题,我们使用相关法,

  • 问题的存在

如果要是在白天和晚上的话,二者之间的数值会变得差异是非常大的,为了规避这个问题的出现,防止出口之中的像素的灰度值发生剧烈的变化,使用归一化相关匹配.

归一化匹配过程

  • 窗口大小对于使用的影响

  • 相关法存在的一些问题

双目立体视觉(平行的视角)相关推荐

  1. 深度相机---(3)双目立体视觉

    导读 为什么非得用双目相机才能得到深度? 双目立体视觉深度相机的工作流程 双目立体视觉深度相机详细工作原理 理想双目相机成像模型 极线约束 图像矫正技术 基于滑动窗口的图像匹配 基于能量优化的图像匹配 ...

  2. 双目立体视觉中的坐标系与转换关系 [留意~摄影测量学与计算机视觉学科中的差异]

    文章目录 前言 影像坐标系 相机坐标系 世界坐标系 影像坐标系与相机坐标系之间的相互转换 相机坐标系与世界坐标系之间的相互转换 前言   通过模拟人眼立体视觉,两个摄像机拍摄同一场景可构成双目成像模型 ...

  3. 双目立体视觉(双摄测距)

    基于双目立体视觉的深度相机类似人类的双眼,和基于TOF.结构光原理的深度相机不同,它不对外主动投射光源,完全依靠拍摄的两张图片(彩色RGB或者灰度图)来计算深度,因此有时候也被称为被动双目深度相机.比 ...

  4. 双目视觉焦距_深度相机原理揭秘--双目立体视觉

    深度相机原理揭秘--双目立体视觉 博文来源:http://www.sohu.com/a/203027140_100007727 导读 为什么非得用双目相机才能得到深度? 双目立体视觉深度相机的工作流程 ...

  5. 【深度相机系列三】深度相机分类之双目立体视觉法

    说明:文中所举例的产品比较早,读者把重点放在学习原理上就好. 1. 双目立体视觉法简介 基于双目立体视觉的深度相机类似人类的双眼,和基于TOF.结构光原理的深度相机不同,它不对外主动投射光源,完全依靠 ...

  6. 双目立体视觉(一) 基本原理和步骤

    目录 一.双目立体视觉系统的四个基本步骤 二.各步骤原理 1.相机标定 2.立体校正 3.立体匹配 一.双目立体视觉系统的四个基本步骤 相机标定主要包含两部分内容: 单相机的内参标定和双目相机的外参标 ...

  7. 【深度相机系列三】深度相机原理揭秘--双目立体视觉

    本文已经首发在个人微信公共号:计算机视觉life(微信号CV_life),欢迎关注! 导读 为什么非得用双目相机才能得到深度? 双目立体视觉深度相机的工作流程 双目立体视觉深度相机详细工作原理 理想双 ...

  8. 基于深度学习算法和传统立体匹配算法的双目立体视觉

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 01 立体视觉是什么? 在开始之前,我相信很多站友都会有这个疑问, ...

  9. 聊聊三维重建-双目立体视觉原理

    原文首发于微信公众号「3D视觉工坊」--聊聊三维重建-双目立体视觉原理 作首:Tengfei Jiang https://zhuanlan.zhihu.com/p/81016834 本文已由原作者授权 ...

最新文章

  1. 如何让带有批注等修改痕迹的word文档编程“正规”文档?
  2. vue2.0小示例一个~~新鲜出炉哦。欢迎大家留言
  3. 09 ORA系列:ORA-06553 PLS-306
  4. 搭建Python+Eclipse开发环境
  5. OC Foundation框架—字符串
  6. JAVA 求数组中的最大值
  7. JDBC教程– ULTIMATE指南(PDF下载)
  8. iOS 使点击事件穿透透明的UIView
  9. 3.15 送货通知单
  10. 共享资源为目标的计算机网络的特点有,注册电气工程师考试基础试题:计算机基础知识试题答案(4)...
  11. Gifxing在线图片无损压缩,gif压缩
  12. iscsi服务器搭建
  13. php微信摇一摇,怎样使用JS+H5实现微信摇一摇
  14. 【ML特征工程】第 5 章 :分类变量:机器鸡时代的鸡蛋计数
  15. matlab棋盘格标定角点,相机标定(Camera calibration)Matlab——棋盘格标定原理,流程...
  16. 【每日一练】day(7)
  17. 卡特兰数 二叉树相关公式 二叉树ADT操作
  18. 2021年江西省安全员C证考试题及江西省安全员C证免费试题
  19. 使用opencv实现美颜(磨皮、祛痘)
  20. python将字符串变成复数_Python list和str互转的实现示例

热门文章

  1. 腾讯企业邮箱发送邮件异常(501):ÇëµÇ¼exmail.qq.comÐÞ¸ÄÃÜÂë
  2. Redis 面霸绝杀:连环五十二问!三万字 + 八十图详解!
  3. 华天动力OA系统全国渠道布局 20个城市分公司初露端倪
  4. 3an推客推广有效果吗?
  5. 软件架构设计的核心:抽象与模型、“战略编程”
  6. 百度点石大数据开放平台,赋能营销场景,打造行业生态
  7. sql 某字段关联今年1-12月月份值查询
  8. Jdb命令(The Java Debugger)
  9. 小程序体验版本接口是http协议,打开不调用接口
  10. 如何在Thymeleaf 模板中使用片段Fragments