[双目视差] 立体校正源码分析(opencv)

一、源码解析

立体校正:把实际中非共面行对准的两幅图像,校正成共面行对准
stereoRectify(cameraMatrixL, distCoeffL, cameraMatrixR, distCoeffR, imageSize, R, T, Rl, Rr, Pl, Pr, Q, CALIB_ZERO_DISPARITY,0, imageSize, &validROIL, &validROIR);
为每个摄像头计算立体校正的映射矩阵。所以其运行结果并不是直接将图片进行立体矫正,而是得出进行立体矫正所需要的映射矩阵。
立体矫正前:

立体矫正后:

过程:
(1)共面:先把旋转矩阵变为旋转向量,对旋转向量的模长平分,这样使两个图像平面共面,此时行未对齐。

cvConvert(matR, &om);
cvConvertScale(&om, &om, -0.5);

(2)行对准:建立行对准换行矩阵Rrect使极点转换到无穷远处。

首先创建平移向量T方向的旋转矩阵Rrec=[e1,e2,e3],其中e1为与平移向量T同方向的极点,e2为图像与平移向量同一方向的向量,e3为垂直于e1与e2所在平面的向量,通过叉乘方式获得,RL=Rrect rl ,RRrect rr,最后转为旋转矩阵,在通过转置就得到最终的RL和RR,这里求得的RL和RR是用来校正左右图像到第三平面,进行行对齐。

    cvRodrigues2(&om, &r_r);        // 旋转向量转换为旋转矩阵
cvMatMul(&r_r, matT, &t); //两个数组对应元素的乘法
int idx = fabs(_t[0]) > fabs(_t[1]) ? 0 : 1;
_uu[2] = 1;
cvCrossProduct(&uu, &t, &ww); //对两个三维向量做叉乘
nt = cvNorm(&t, 0, CV_L2); //计算t的绝对范数
CV_Assert(fabs(nt) > 0); //捕获异常而不是程序崩溃
nw = cvNorm(&ww, 0, CV_L2);
CV_Assert(fabs(nw) > 0);
cvConvertScale(&ww, &ww, 1 / nw);
cvCrossProduct(&t, &ww, &w3);
nw = cvNorm(&w3, 0, CV_L2);
CV_Assert(fabs(nw) > 0);
cvConvertScale(&w3, &w3, 1 / nw);
_uu[2] = 0;
for (i = 0; i < 3; ++i)
{
_wr[idx][i] = -_t[i] / nt;
_wr[idx ^ 1][i] = -_ww[i];
_wr[2][i] = _w3[i] * (1 - 2 * idx);
}
cvGEMM(&wR, &r_r, 1, 0, 0, &Ri, CV_GEMM_B_T);
cvConvert( &Ri, _R1 );
cvGEMM(&wR, &r_r, 1, 0, 0, &Ri, 0);
cvConvert( &Ri, _R2 );

左右两个摄像机共面、行对齐后,分别计算两个相机的内参矩阵,即投影矩阵,过程为:
通过原始两相机的内参矩阵,与当前共面对齐后的图像进行比例计算,得到新的内参信息(fx,fy,这里的fx=fy)

newImgSize = newImgSize.width * newImgSize.height != 0 ? newImgSize : imageSize;
const double ratio_x = (double)newImgSize.width / imageSize.width / 2;
const double ratio_y = (double)newImgSize.height / imageSize.height / 2;
const double ratio = idx == 1 ? ratio_x : ratio_y;
fc_new = (cvmGet(_cameraMatrix1, idx ^ 1, idx ^ 1) + cvmGet(_cameraMatrix2, idx ^ 1, idx ^ 1)) * ratio;

分别对左右摄像机进行图像矫正为正常的视角,将变化后的点转换为齐次坐标系,同时改变相机内参,计算三维点在平面中的坐标,为简单起见,将两个摄影机的主要点设置为平均值。

    for( k = 0; k < 2; k++ )
{
const CvMat* A = k == 0 ? _cameraMatrix1 : _cameraMatrix2;
const CvMat* Dk = k == 0 ? _distCoeffs1 : _distCoeffs2;
CvPoint2D32f _pts[4] = {};
CvPoint3D32f _pts_3[4] = {};
CvMat pts = cvMat(1, 4, CV_32FC2, _pts);
CvMat pts_3 = cvMat(1, 4, CV_32FC3, _pts_3); for( i = 0; i < 4; i++ )
{
int j = (i<2) ? 0 : 1;
_pts[i].x = (float)((i % 2)*(nx));
_pts[i].y = (float)(j*(ny));
}
//利用undistortPoints()函数将拍摄的图像矫正为正常的视角,便于检测。
cvUndistortPoints( &pts, &pts, A, Dk, 0, 0 );
//将变换后的点先变化为齐次坐标系
cvConvertPointsHomogeneous( &pts, &pts_3 ); //Change camera matrix to have cc=[0,0] and fc = fc_new
double _a_tmp[3][3];
CvMat A_tmp = cvMat(3, 3, CV_64F, _a_tmp);
_a_tmp[0][0]=fc_new;
_a_tmp[1][1]=fc_new;
_a_tmp[0][2]=0.0;
_a_tmp[1][2]=0.0;
//计算三维点在平面中的坐标.
cvProjectPoints2( &pts_3, k == 0 ? _R1 : _R2, &Z, &A_tmp, 0, &pts );
CvScalar avg = cvAvg(&pts);
cc_new[k].x = (nx)/2 - avg.val[0];
cc_new[k].y = (ny)/2 - avg.val[1];
}

设置CALIB_ZERO_DISPARITY,让两幅校正后的图像的主点有相同的像素坐标

 if( flags & CALIB_ZERO_DISPARITY )
{
cc_new[0].x = cc_new[1].x = (cc_new[0].x + cc_new[1].x)*0.5;
cc_new[0].y = cc_new[1].y = (cc_new[0].y + cc_new[1].y)*0.5;
}

获取左右相机的投影P1,P2矩阵

 cvZero( &pp );
_pp[0][0] = _pp[1][1] = fc_new;
_pp[0][2] = cc_new[0].x;
_pp[1][2] = cc_new[0].y;
_pp[2][2] = 1;
cvConvert(&pp, _P1); _pp[0][2] = cc_new[1].x;
_pp[1][2] = cc_new[1].y;
_pp[idx][3] = _t[idx]*fc_new; // baseline * focal length
cvConvert(&pp, _P2);

校正映射:立体校正之后求得左右相机旋转矩阵R、投影矩阵P、重投影矩阵Q后,使用initUndistortRectifyMap()函数,调⽤两次:⼀次为左侧图像,⼀次为右侧图像,求映射变换矩阵
initUndistortRectifyMap(cameraMatrixL, distCoeffL, Rl, Pl, imageSize, CV_32FC1, mapLx, mapLy);
initUndistortRectifyMap(cameraMatrixR, distCoeffR, Rr, Pr, imageSize, CV_32FC1, mapRx, mapRy);

输入单相机内参,畸变参数,旋转矩阵R,投影参数矩阵P(R和P是通过立体矫正stereoRectify()得到),原图像size大小,类型CV_32FC1,输出映射矩阵mapx,mapy

校正映射过程:
获取相机内参cameraMatrix、畸变矩阵distCoeffs、旋转矩阵matR、摄像机投影参数矩阵newCameraMatrix

	//相机内参、畸变矩阵
Mat cameraMatrix = _cameraMatrix.getMat(), distCoeffs = _distCoeffs.getMat();
//旋转矩阵、摄像机参数矩阵
Mat matR = _matR.getMat(), newCameraMatrix = _newCameraMatrix.getMat();
创建相机内参矩阵A、投影参数矩阵Ar,旋转矩阵R、畸变矩阵distCoeffs:
    Mat_<double> R = Mat_<double>::eye(3, 3);
Mat_<double> A = Mat_<double>(cameraMatrix), Ar;
//A为相机内参
//Ar为摄像机坐标参数,
if( !newCameraMatrix.empty() )
Ar = Mat_<double>(newCameraMatrix);
else
Ar = getDefaultNewCameraMatrix( A, size, true ); //R为旋转矩阵
if( !matR.empty() )
R = Mat_<double>(matR); //distCoeffs为畸变矩阵
if( !distCoeffs.empty() )
distCoeffs = Mat_<double>(distCoeffs);
else
{
distCoeffs.create(14, 1, CV_64F);
distCoeffs = 0.;
}

通过LU分解求新的内参矩阵Ar与旋转矩阵R乘积的逆矩阵iR

Mat_<double> iR = (Ar.colRange(0,3)*R).inv(DECOMP_LU);
const double* ir = &iR(0,0);

从旧的内参矩阵中取出光心位置u0,v0作为主坐标点,和归一化焦距fx,fy

    double u0 = A(0, 2),  v0 = A(1, 2);
double fx = A(0, 0), fy = A(1, 1);

畸变参数计算,14个畸变系数,不过大多用到的只有(k1,k2,p1,p2),k1,k2为径向畸变系数,p1,p2为切向畸变系数,用不到的置为0,tauX,tauY是梯形畸变

 	const double* const distPtr = distCoeffs.ptr<double>();
double k1 = distPtr[0];
double k2 = distPtr[1];
double p1 = distPtr[2];
double p2 = distPtr[3];
double k3 = distCoeffs.cols + distCoeffs.rows - 1 >= 5 ? distPtr[4] : 0.;
double k4 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[5] : 0.;
double k5 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[6] : 0.;
double k6 = distCoeffs.cols + distCoeffs.rows - 1 >= 8 ? distPtr[7] : 0.;
double s1 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[8] : 0.;
double s2 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[9] : 0.;
double s3 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[10] : 0.;
double s4 = distCoeffs.cols + distCoeffs.rows - 1 >= 12 ? distPtr[11] : 0.;
double tauX = distCoeffs.cols + distCoeffs.rows - 1 >= 14 ? distPtr[12] : 0.;
double tauY = distCoeffs.cols + distCoeffs.rows - 1 >= 14 ? distPtr[13] : 0.;

通过tauX,tauY计算倾斜图像传感器的梯形畸变矩阵matTilt,其中tauX,tauY用不到的话matTilt为单位矩阵

cv::Matx33d matTilt = cv::Matx33d::eye();
cv::detail::computeTiltProjectionMatrix(tauX, tauY, &matTilt);

求得上述提到的逆矩阵ir、梯形矩阵、主坐标点(u0,v0)、焦距(fx,fy)及畸变参数后,反向映射,遍历目标图像所有像素位置,找到畸变图像中对应位置坐标(u,v),并分别保存坐标(u,v)到mapx和mapy中。
过程如下:


parallel_for_(Range(0, size.height),
initUndistortRectifyMapComputer(size, map1, map2, m1type, ir, matTilt, u0, v0,fx, fy, k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4)); const int begin = range.start;
const int end = range.end;
for( int i = begin; i < end; i++ )
{ //定义映射表mapx和mapy行元素指针
float* m1f = map1.ptr<float>(i);//指向第i+1行第一个元素指针
float* m2f = map2.empty() ? 0 : map2.ptr<float>(i);
short* m1 = (short*)m1f;
ushort* m2 = (ushort*)m2f; //利用逆矩阵iR将二维图像坐标(j,i)转换到摄像机坐标系(_x,_y,_w)
double _x = i*ir[1] + ir[2], _y = i*ir[4] + ir[5], _w = i*ir[7] + ir[8]; int j = 0;
//遍历每个像机坐标位置
for( ; j < size.width; j++, _x += ir[0], _y += ir[3], _w += ir[6] )
{
//摄像机坐标系归一化,令Z=1
double w = 1./_w, x = _x*w, y = _y*w;
//根据畸变模型进行变换
double x2 = x*x, y2 = y*y;
double r2 = x2 + y2, _2xy = 2*x*y;
double kr = (1 + ((k3*r2 + k2)*r2 + k1)*r2)/(1 + ((k6*r2 + k5)*r2 + k4)*r2);
double xd = (x*kr + p1*_2xy + p2*(r2 + 2*x2) + s1*r2+s2*r2*r2);
double yd = (y*kr + p1*(r2 + 2*y2) + p2*_2xy + s3*r2+s4*r2*r2);
//根据求取的xd,yd将三维坐标重投影到二维畸变图像坐标(u,v)
cv::Vec3d vecTilt = matTilt*cv::Vec3d(xd, yd, 1);
double invProj = vecTilt(2) ? 1./vecTilt(2) : 1;
double u = fx*invProj*vecTilt(0) + u0;
double v = fy*invProj*vecTilt(1) + v0;
//保存u,v的值到Mapx,Mapy中
if( m1type == CV_16SC2 )
{
int iu = cv::saturate_cast<int>(u*cv::INTER_TAB_SIZE);
int iv = cv::saturate_cast<int>(v*cv::INTER_TAB_SIZE);
m1[j*2] = (short)(iu >> cv::INTER_BITS);
m1[j*2+1] = (short)(iv >> cv::INTER_BITS);
m2[j] = (ushort)((iv & (cv::INTER_TAB_SIZE-1))*cv::INTER_TAB_SIZE + (iu & (cv::INTER_TAB_SIZE-1)));
}
else if( m1type == CV_32FC1 )
{
m1f[j] = (float)u;
m2f[j] = (float)v;
// map_x实际上记录的是对应原图像中(i,j)位置的横坐标,map_y实际上记录的是(i,j)位置的纵坐标
}
else
{
m1f[j*2] = (float)u;
m1f[j*2+1] = (float)v;
}
}
}

initUndistortRectifyMap

  • 校正映射参数:
    输入单相机内参,畸变参数,旋转矩阵R,投影参数矩阵P(R和P是通过立体矫正stereoRectify()得到),原图像size大小,类型CV_32FC1,输出映射矩阵mapx,mapy

  • 校正映射过程:
    1、获取相机内参cameraMatrix、畸变矩阵distCoeffs、旋转矩阵matR、摄像机投影参数矩阵newCameraMatrix
    2、创建相机内参矩阵A、投影参数矩阵Ar,旋转矩阵R、畸变矩阵distCoeffs
    3、通过LU分解求新的内参矩阵Ar与旋转矩阵R乘积的逆矩阵iR
    4、从旧的内参矩阵中取出光心位置u0,v0作为主坐标点,和归一化焦距fx,fy
    5、畸变参数计算,14个畸变系数,不过大多用到的只有(k1,k2,p1,p2),k1,k2为径向畸变系数,p1,p2为切向畸变系数,用不到的置为0,tauX,tauY是梯形畸变
    6、通过tauX,tauY计算倾斜图像传感器的梯形畸变矩阵matTilt,其中tauX,tauY用不到的话matTilt为单位矩阵
    7、求得上述提到的逆矩阵ir、梯形矩阵、主坐标点(u0,v0)、焦距(fx,fy)及畸变参数后,反向映射,遍历目标图像所有像素位置,找到畸变图像中对应位置坐标(u,v),并分别保存坐标(u,v)到mapx和mapy中。
    ①定义映射表1、2行元素指针
    ②利用逆矩阵iR将二维图像坐标(j,i)转换到摄像机坐标系(_x,_y,_w)
    ③遍历每个相机坐标位置,将相机坐标系归一化,令Z=1平面上。
    ④畸变模型的转换,求得xd,yd
    ⑤根据求取的xd,yd将三维坐标重投影到二维畸变图像坐标(u,v)
    ⑥保存u,v的值到Mapx,Mapy中
    map_x实际上记录的是对应原图像中(i,j)位置的横坐标,map_y实际上记录的是(i,j)位置的纵坐标,而我们在这里把像素操作的i当做了横坐标,j当做了纵坐标

remap

remap( InputArray _src, OutputArray _dst,
InputArray _map1, InputArray _map2,
int interpolation, int borderType, const Scalar& borderValue )
  • 像素重映射:
    重映射,就是把一幅图像中某位置的像素放置到另一个图片指定位置的过程。为了完成映射过程, 我们需要获得一些插值为 非整数像素的坐标,因为源图像与目标图像的像素坐标不是一一对应的
    g(x,y) = f ( h(x,y) )
    g( ) 是目标图像, f() 是源图像, 而h(x,y) 是作用于 (x,y) 的映射方法函数。简单的说就是改变图片的位置(左,右,上,下,颠倒翻转)

  • 像素重映射参数:
    输入源图像src,目标图像dst,输入记录源图像位置的横坐标Mapx,输入记录源图像位置的纵坐标Mapy,使用双线性插值方式INTER_LINEAR,边界模式使用默认值BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改,边界颜色,默认Scalar()黑色

  • 对左右摄像头采集到的数据分别进行remap,使源图像中像素位置通过映射表mapx,mapy位置,映射到新的图像中。最后得到的图像就是共面、行对齐的图像。

二、源码中的方法

OpenCV双目视觉:Bouguet立体校正https://jingyan.baidu.com/article/a681b0de74312a3b1843460d.html

  • 将旋转矩阵转换为旋转向量:
    cvConvert函数用于图像和矩阵之间的相互转换 为什么要用cvConvert 把IplImage转为矩阵? 因为IplImage里的数据,你只能用uchar的形式存放,当你需要这些图像数据看作数据矩阵来运算时,0~255的精度显然满足不了要求; 然而CvMat里却可以存放任意通道数、任意格式的数据,这个机制方便了研究中的这种需求,转化为矩阵就可以进行更自由的计算。

  • 获得平均旋转向量,等比缩放一半:
    函数 cvConvertScale 有多个不同的目的因此就有多个同义函数(如上面的#define所示)。该函数首先对输入数组的元素进行比例缩放,然后将shift加到比例缩放后得到的各元素上,即: dst(I)=src(I)*scale + (shift,shift,…)

  • 旋转向量转换为旋转矩阵:
    cvRodrigues2()

  • 旋转矩阵r_r 与平移矩阵相乘得到t(3,1)
    cvMatMul()

  • cvCrossProduct(&uu, &t, &ww); //对两个三维向量做叉乘

  • cvNorm(&t, 0, CV_L2); //计算t的绝对范数

  • CV_Assert(fabs(nt) > 0); //捕获异常而不是程序崩溃

  • 对WR矩阵值做变换,与旋转矩阵进行广义矩阵乘法分别得到左旋转矩阵和右旋转矩阵

  • void cvGEMM( const CvArr* src1, const CvArr* src2, double alpha,const CvArr* src3, double beta, CvArr* dst, int tABC=0 ); 广义矩阵的乘法
    src1:第一输入数组
    src2:第二输入数组
    alpha:系数
    src3“第三输入数组(偏移量),如果没有偏移量,可以为空(NULL)
    beta:表示偏移量的系数
    dst:输出数组
    tABC:
    转置操作标志,可以是0。当为0时,没有转置。或者还有下面的值的组合:
    CV_GEMM_A_T:表示src1转置
    CV_GEMM_B_T:表示src2转置
    CV_GEMM_C_T:表示src3转置
    例如,CV_GEMM_A_T+CV_GEMM_C_T对应
    alpha*(src1转置)src2+beta(src3转置)

  • opencv实现中要先把旋转矩阵变为旋转向量,对旋转向量的模长平分,就得到可以把光轴 摆平的左右矩阵,然后用这个矩阵乘以T,归一化得到e1,然后根据上面的公式构建e2,e3就可以通过叉乘获得,最后转为旋转矩阵,在通过转置就可以得到最终的RL和RR,RL和RR是用来校正左右图像到第三平面,行对齐

  • 获取左右摄像头的内参及畸变系数利用undistortPoints()函数将拍摄的图像矫正为正常的视角,便于检测
    cvUndistortPoints:https://yongqi.blog.csdn.net/article/details/52946821

  • cvConvertPointsHomogeneous//将变换后的点先变化为齐次坐标系

cvmGet直接存取矩阵元素

  • Opencv,计算三维点在平面中的坐标.
    void cvProjectPoints2
    (
    const CvMat* objectPoints, //是需要投影的点的序列,是一个点位置的N3的矩阵。
    const CvMat
    rvec,
    const CvMat* tvec, //建立两个坐标系的联系
    const CvMat* cameraMatrix,
    const CvMat* distCoeffs,//内参数矩阵和形变系数
    CvMat* imagePoints,//N2的矩阵将被写入计算结果
    CvMat
    dpdrot=NULL,
    CvMat* dpdt=NULL,
    CvMat* dpdf=NULL,
    CvMatdpdc=NULL,
    CvMat
    dpddist=NULL //偏导数的雅克比矩阵
    )

  • 如果设置为CALIB_ZERO_DISPARITY的话,该函数会让两幅校正后的图像的主点有相同的像素坐标。否则该函数会水平或垂直的移动图像,以使得其有用的范围最大。

  • // 获取左右相机的投影P1,P2矩阵

  • icvGetRectangles功能是获取标准图像的有效像素(有效像素指在畸变图像中有对应像素的像素)所构成的区域的最大内接矩阵和最小外接矩阵,其实现方式是:取畸变图像中的一些特殊点(四条边上的点和中间区域的一些点)

  • 当alpha为0时,取inner即内矩阵,用内矩阵大小作为新的图像大小,重新得到fx,fy,cx,cy,因此新的内参矩阵诞生了. 当alpha为1时,取outer即外矩阵。当alpha介于0~1时,则按照比例重新计算fx,fy,cx,cy。
    //事实上,内矩阵等同于不含任何黑色边框的图幅大小,而外矩阵等同于原图大小。

[双目视差] 立体校正源码分析(opencv)的更多相关文章

  1. OpenCV SIFT原理与源码分析

    http://blog.csdn.net/xiaowei_cqu/article/details/8069548 SIFT简介 Scale Invariant Feature Transform,尺度 ...

  2. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  3. 【OpenCV】SIFT原理与源码分析:DoG尺度空间构造

    原文地址:http://blog.csdn.net/xiaowei_cqu/article/details/8067881 尺度空间理论   自然界中的物体随着观测尺度不同有不同的表现形态.例如我们形 ...

  4. 【OpenCV】SIFT原理与源码分析

    SIFT简介 Scale Invariant Feature Transform,尺度不变特征变换匹配算法,是由David G. Lowe在1999年(<Object Recognition f ...

  5. 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊的好同志 | 百篇博客分析OpenHarmony源码 | v26.02

    百篇博客系列篇.本篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊的好同志 | 51.c.h .o 进程通讯相关篇为: v26.xx 鸿蒙内核源码分析(自旋锁篇) | 当立贞节牌坊 ...

  6. 【OpenCV】SIFT原理与源码分析:关键点描述

    <SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<方向赋值>,为找到的关键点即SI ...

  7. 【OpenCV】SIFT原理与源码分析:方向赋值

    <SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一篇<关键点搜索与定位>,我们已经找到 ...

  8. 【OpenCV】SIFT原理与源码分析:关键点搜索与定位

    <SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了 ...

  9. SURF算法与源码分析、上

    如果说SIFT算法中使用DOG对LOG进行了简化,提高了搜索特征点的速度,那么SURF算法则是对DoH的简化与近似.虽然SIFT算法已经被认为是最有效的,也是最常用的特征点提取的算法,但如果不借助于硬 ...

  10. SURF算法与源码分析、下

    上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SU ...

随机推荐

  1. SpringCloud之旅

    现在大部分公司的项目架构都选择了微服务,我们公司也不例外,那么什么是微服务呢?今天就来开启SpringCloud之旅! SpringCloud是基于SpringBoot的一整套的微服务架构.他提供了微 ...

  2. h5移动端开发经验积累篇

    h5键盘控制 const el = document.documentElement || document.body const originHeight = el.clientHeight win ...

  3. 前端复习之css

    1.css概述 1 1.CSS3概述 2 1.问题 3 1.设置页面中所有的文本颜色为红色 4 2.设置页面中所有div的文本的颜色为蓝色 5 3.将所有的div的文本的颜色改为黄色 6 7 HTML ...

  4. 记录linux上无法和本地传输文件

    在学习docker搭建nacos的过程中,涉及到上传本地文件,但是包括从xshell直接拖拽还是xftp上传,都是失败, 最后百度查找多种不同的结果,最后实验下来是文件夹没有权限的问题. 解决步骤如下 ...

  5. Python 跳动的小球

    一.实验内容:跳动的小球游戏介绍 二.实验对象:<零基础学Python>第13章 Pygame游戏编程 实例01 用以下代码创建一个游戏弹窗: 导入pygame模块并且用init()方法初 ...

  6. vue下拉选择select option el-cascader删除重选值的问题

    select当下拉值多的时候 以及input cascader级联选择一个值后  后面我不想要了 vue  提供了一个关键字  可以帮你全部清空 这个关键字就是:clearable

  7. windows代码获取系统硬件信息的两种方式

    欢迎访问我的个人博客:xie-kang.com 原文地址 目前windows有两种方式获取系统硬件信息: 1)通过GetSystemFirmwareTable API获取SMBIOS信息,一段含丰富信 ...

  8. (Winform程序带源码) 弹出输入框和获取输入框的值

    弹出输入框和获取输入框的值: private void button1_Click(object sender, EventArgs e) { string returnValue = Microso ...

  9. flask - fastapi (python 异步API 框架 可以自动生成swagger 文档) 常用示例 以及整合euraka nacos

    flask - fastapi    (python 异步API 框架  可以自动生成swagger 文档)  常用示例: 之前使用 flask 需要手动写文档, 这个可以自动生成, fastapi ...

  10. 一站式微服务治理中台,Water v2.10.2 发布

    Water(水孕育万物...) Water 为项目开发.服务治理,提供一站式解决方案(可以理解为微服务架构支持套件).基于 Solon 框架开发,并支持完整的 Solon Cloud 规范:已在生产环 ...