【三维重建】摄像机标定(张正友相机标定法)

news2024/11/9 4:51:23

摄像机标定的目的是为了求解摄像机的内、外参数

 求解投影矩阵M

通过建立特殊的场景,我们能过得到多对世界坐标(P_{1},P_{2} ... P_{n})和对应图像坐标(p_{1},p_{2} ... p _{n})

根据摄像机几何可知 :p = K[R T]P = MP ,M是一个3*4的矩阵,令

M = \begin{bmatrix} m_{1}\\m_{2} \\ m_{3} \end{bmatrix}        p = MP \rightarrow p_{i} =\begin{bmatrix} u_{i}\\v_{i} \end{bmatrix}= \begin{bmatrix} \frac{m_{1}P_{i}}{m_{3}P_{i}} \\ \frac{m_{2}P_{i}}{m_{3}P_{i}} \end{bmatrix}\rightarrow \left\{\begin{matrix} m_{1}P_{i}-u_{i}(m_{3}P_{i} )=0\\ m_{2}P_{i}-v_{i}(m_{3}P_{i} )=0 \end{matrix}\right.

通过一对点可以得到两个方程组,M中一共有11个位置量,因此至少需要6对点,通过最小二乘法求解 Pm=0 可以得到m_{1},m_{2},m_{3}。需要注意的是在求解Ax=0 这个齐次方程组中,x是方程组的解,对于任意 \lambda \neq 0\lambda x也是方程组的解,所以我们加了一个约束,使得||x||=1,因此,我们求解出来的值和实际值的常数倍。

求解内参矩阵K

R是旋转矩阵,因此 \left \| r_{i}\right \| = 1, r_{i}\cdot r_{j}=0(i\neq j),r_{i}\cdot r_{j}=1(i= j)

\left \| \rho a_{3} \right \| = \left \| r_{3} \right \|=1 \rightarrow \rho=\frac{\pm 1}{\left \| a_{3} \right \|}

\left\{\begin{matrix} \rho^{2}(a_3\cdot a_{2}) = v_{0} \\ \rho^{2}(a_3\cdot a_{1}) = u_{0} \end{matrix}\right.

同样因为R旋转矩阵,r_{i}\times r_{j} = r_{k}(i\neq j),r_{i}\times r_{j} = 0(i= j)

\left\{\begin{matrix} \rho^{2}(a_3\times a_{2}) = \alpha r_{2} - \alpha cot \theta r_{1} \\ \rho^{2}(a_3\times a_{1}) = \frac{\beta }{sin \theta} r_{1} \end{matrix}\right.\rightarrow \left\{\begin{matrix} \rho^{2}||a_3\times a_{2}|| =\alpha /sin \theta \\ \rho^{2}||a_3\times a_{1}|| =\beta /sin \theta \end{matrix}\right.

cos \theta = -\frac{(a_{1} \times a_{3}) \cdot(a_{2} \times a_{3})}{||a_{1} \times a_{3}|| \cdot ||a_{2} \times a_{3}||}

\left\{\begin{matrix} \alpha = \rho^2||a_{1} \times a_{3}|| sin \theta\\ \beta = \rho^2||a_{2} \times a_{3}|| sin \theta \end{matrix}\right.

内参矩阵全部求出。

求解外参矩阵R、T

\rho[A \ b] = K[R \ T]

\left\{\begin{matrix} \rho A = KR\rightarrow R=\rho K^{-1}A \\\rho b = KT \rightarrow T=\rho K^{-1}b \end{matrix}\right.

求解畸变参数

p_i =MP_i 所求得的是理想情况下的坐标,下面的建模相当于对理想点进行缩放,d表示坐标点离图像中心的距离,d越大畸变得越厉害。若 k_p > 0  ,\lambda > 1理想点会被往里扯,那对应的就是桶形畸变,若 k_p < 0  ,\lambda > 1理想点会被往外扯,那对应的就是枕形畸变。

\left\{\begin{matrix} m_1P_i-u_i \lambda m_3P_i=0\\ m_2P_i-v_i \lambda m_3P_i=0 \end{matrix}\right.\rightarrow Qm=0

由于Q中包含\lambda,因此这不是一个线性模型,只能用迭代法求取最优解

张正友标定法

接下来我们将介绍张正友标定法,以及相应代码。

GitHub - ldx-star/Zhangzhenyou-Calibration

在张正友标定法中,我们并不需要一个立体的模型,只需要一个平面的标定板。

Step1  获取世界坐标和像素坐标的对应点对

张正友标定法假设Z=0,将世界坐标系建立在靶面上,因此我们在提取世界坐标的时候只需要得到(X,Y)。

我们的棋盘格中每个方格的宽度是15mm,将第一个角点位置定为(0,0),第二个角点就是(0,0.15)... ,而像素坐标就是对应角点在图像上像素的坐标。

bool CamCalibration::getKeyPoints() {
    auto chessBoards = chessBoards_;
    const float square_size = square_size_;
    auto &points_3d_vec = points_3d_vec_;
    auto &points_2d_vec = points_2d_vec_;

    const int row = row_;
    const int col = col_;
    //采集世界坐标
    for (int i = 0; i < chessBoards.size(); i++) {
        std::vector<cv::Point2f> points_3d; // 一张图的世界坐标
        for (int r = 0; r < row; r++) {
            for (int c = 0; c < col; c++) {
                cv::Point2f point;
                point.x = r * square_size;
                point.y = c * square_size;
                points_3d.push_back(point);
            }
        }
        points_3d_vec.push_back(points_3d);
    }
    //采集像素坐标,使用opencv库提取角点
    for (auto img: chessBoards) {
        std::vector<cv::Point2f> points_2d;

        bool found_flag = cv::findChessboardCorners(img, cv::Size(col, row), points_2d, cv::CALIB_CB_ADAPTIVE_THRESH +
                                                                                        cv::CALIB_CB_NORMALIZE_IMAGE); //cv::Size(col,row)
        if (!found_flag) {
            std::cerr << "found chess board corner failed";
            return false;
        }
        //指定亚像素计算迭代标注
        cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 40, 0.001);
        cv::cornerSubPix(img, points_2d, cv::Size(5, 5), cv::Size(-1, -1), criteria);

        //display
//        cv::cvtColor(img,img,cv::COLOR_GRAY2BGR);
//        cv::drawChessboardCorners(img, cv::Size(col, row), points_2d, found_flag);
//        cv::namedWindow("corner img", cv::WINDOW_NORMAL);
//        cv::resizeWindow("corner img", img.cols / 2, img.rows / 2);
//        cv::imshow("corner img", img);
//        cv::waitKey(300);

        points_2d_vec.push_back(points_2d);
    }
    std::cout << "getKeyPoints succeed" << std::endl;
    return true;
}

Step2  计算单应性矩阵H

之前我们讲过世界坐标到像素坐标的映射关系 :

s \begin{bmatrix} u\\v \\ 1 \end{bmatrix}= A[R\ T]\begin{bmatrix} X\\Y \\ Z \\ 1 \end{bmatrix}=A[r_{1},r_{2},r_{3},t]\begin{bmatrix} X\\Y \\ Z \\ 1 \end{bmatrix}

为了和论文对应,我们将字母都换成论文中的字母。A是之前讲的K,表示内参矩阵;s是之间讲的\rho表示系数。

由于Z=0,

s \begin{bmatrix} u\\v \\ 1 \end{bmatrix}=A[r_{1},r_{2},r_{3},t]\begin{bmatrix} X\\Y \\ 0 \\ 1 \end{bmatrix}=A[r_{1},r_{2},t]\begin{bmatrix} X\\Y \\ 1 \end{bmatrix}

单应性矩阵

H=A[r_{1},r_{2},t]

同样的

H = \begin{bmatrix} h_{1}\\h_{2} \\ h_{3} \end{bmatrix}        p = HP \rightarrow p_{i} =\begin{bmatrix} u_{i}\\v_{i} \end{bmatrix}= \begin{bmatrix} \frac{h_{1}P_{i}}{h_{3}P_{i}} \\ \frac{h_{2}P_{i}}{h_{3}P_{i}} \end{bmatrix}\rightarrow \left\{\begin{matrix} h_{1}P_{i}-u_{i}(h_{3}P_{i} )=0\\ h_{2}P_{i}-v_{i}(h_{3}P_{i} )=0 \end{matrix}\right.

通过最小二乘法可以将H求出。

为了使计算稳定,需要先对数据进行Z-Score标准化

x^{'}= (x-mean_x)/variance_x \\ y^{'}= (y-mean_y)/variance_y

转换成矩阵的形式

\begin{bmatrix} x^{'}\\y^{'} \\ 1 \end{bmatrix} = \begin{bmatrix} 1/variance_x&0 &-mean_x/variance_x \\ 0 & 1/variance_y & -mean_y/variance_y \\0&0&1\end{bmatrix} \begin{bmatrix} x\\y \\ 1 \end{bmatrix}

我们需要将归一化矩阵保存下来,在求出H后将数据还原

N_1p = H^{'}N_2P\rightarrow p = (N_1^{-1}H^{'}N_2)P\rightarrow p = HP

/**
 * Z-Score 标准化(均值为0,方差为1)
 * @param points 原始数据点
 * @param normal_points 输出型参数,标准化后的数据点
 * @param normT 输出型参数,归一化矩阵的转置
 */
void CamCalibration::Normalize(const std::vector<cv::Point2f> &points, std::vector<cv::Point2f> &normal_points,
                               cv::Mat &normT) {
    //求均值
    float mean_x = 0;
    float mean_y = 0;
    for (const auto &point: points) {
        mean_x += point.x;
        mean_y += point.y;
    }
    mean_x /= points.size();
    mean_y /= points.size();
    //求方差
    for (const auto &point: points) {
        mean_x += point.x;
        mean_y += point.y;
    }
    float variance_x = 0;
    float variance_y = 0;
    for (const auto &point: points) {
        float tmp_x = pow(point.x - mean_x, 2);
        float tmp_y = pow(point.y - mean_y, 2);
        variance_x += tmp_x;
        variance_y += tmp_y;
    }
    variance_x = sqrt(variance_x);
    variance_y = sqrt(variance_y);

    for (const auto &point: points) {
        cv::Point2f p;
        p.x = (point.x - mean_x) / variance_x;
        p.y = (point.y - mean_y) / variance_y;
        normal_points.push_back(p);
    }
    normT.at<float>(0, 0) = 1 / variance_x;
    normT.at<float>(0, 2) = -mean_x / variance_x;
    normT.at<float>(1, 1) = 1 / variance_y;
    normT.at<float>(1, 2) = -mean_y / variance_y;
}

**
 * 计算单应性矩阵
 */
void CamCalibration::CalcH() {
    const auto &points_3d_vec = points_3d_vec_;
    const auto &points_2d_vec = points_2d_vec_;
    for (int i = 0; i < points_2d_vec.size(); i++) {
        //每一张图的世界坐标和像素坐标
        const auto &points_3d = points_3d_vec[i];
        const auto &points_2d = points_2d_vec[i];
        std::vector<cv::Point2f> normal_points_3d, normal_points_2d;
        cv::Mat normT_3d, normT_2d;
        Normalize(points_3d, normal_points_3d, normT_3d);
        Normalize(points_2d, normal_points_2d, normT_2d);

        cv::Mat H = cv::Mat::eye(3, 3, CV_32F);

        int corner_size = normal_points_2d.size();
        if (corner_size < 4) {
            std::cerr << "corner size < 4";
            exit(-1);
        }

        cv::Mat A(corner_size * 2, 9, CV_32F, cv::Scalar(0));
        for (int i = 0; i < corner_size; i++) {
            cv::Point2f point_3d = points_3d[i];
            cv::Point2f point_2d = points_2d[i];
            A.at<float>(i * 2, 0) = point_3d.x;
            A.at<float>(i * 2, 1) = point_3d.y;
            A.at<float>(i * 2, 2) = 1;
            A.at<float>(i * 2, 3) = 0;
            A.at<float>(i * 2, 4) = 0;
            A.at<float>(i * 2, 5) = 0;
            A.at<float>(i * 2, 6) = -point_2d.x * point_3d.x;
            A.at<float>(i * 2, 7) = -point_2d.x * point_3d.y;
            A.at<float>(i * 2, 8) = -point_2d.x;

            A.at<float>(i * 2 + 1, 0) = 0;
            A.at<float>(i * 2 + 1, 1) = 0;
            A.at<float>(i * 2 + 1, 2) = 0;
            A.at<float>(i * 2 + 1, 3) = point_3d.x;
            A.at<float>(i * 2 + 1, 4) = point_3d.y;
            A.at<float>(i * 2 + 1, 5) = 1;
            A.at<float>(i * 2 + 1, 6) = -point_2d.y * point_3d.x;
            A.at<float>(i * 2 + 1, 7) = -point_2d.y * point_3d.y;
            A.at<float>(i * 2 + 1, 8) = -point_2d.y;
        }
        cv::Mat U, W, VT;                                                    // A =UWV^T
        cv::SVD::compute(A, W, U, VT,
                         cv::SVD::MODIFY_A | cv::SVD::FULL_UV); // Eigen 返回的是V,列向量就是特征向量, opencv 返回的是VT,所以行向量是特征向量
        H = VT.row(8).reshape(0, 3);
        // H = inv_N2 * H * N3
        cv::Mat normT_2d_inv;
        cv::invert(normT_2d, normT_2d_inv);
        H = normT_2d_inv * H * normT_3d;
        H_vec_.push_back(H);
    }
}

Step3  计算内参矩阵A

H=[h_1 \ h_2 \ h_3] = \lambda A[r_1 \ r_2 \ t]\rightarrow [A^{-1}h_1 \ A^{-1}h_2 \ A^{-1}h_3] = \lambda [r_1 \ r_2 \ t]

已知r_1\perp r_2 ,||r_1||=||r_2||=1

(A^{-1}h_1) \cdot(A^{-1}h_2) = (A^{-1}h_1) ^T(A^{-1}h_2)=h_1^TA^{-T}A^{-1}h_2=0

h_1^TA^{-T}A^{-1}h_1 - h_2^TA^{-T}A^{-1}h_2 =0

B是对称矩阵,所以只需存储上半矩阵

b=[B_{11}\ B_{12}\ B_{22}\ B_{13}\ B_{23}\ B_{33}]^T

h_i^TBh_j = \begin{bmatrix} h_{i1} &h_{i2} &h_{i3} \end{bmatrix} \begin{bmatrix} B_{11} &B_{12} &B_{13} \\ B_{21} &B_{22} &B_{23} \\ B_{31}&B_{32} &B{B_{33}} \end{bmatrix} \begin{bmatrix} h_{j1}\\h_{j2} \\ h_{j3} \end{bmatrix} \\\\ =h_{i1}B_{11}h_{j1} + h_{i2}B_{12}h_{j1}+h_{i1}B_{12}h_{j2}+h_{i3}B_{13}h_{j1}+h_{i1}B_{13}h_{j3}+h_{i2}B_{22}h_{j2}+h_{i3}B_{23}h_{j2}+h_{i2}B_{23}h_{j3}+h_{i3}B_{33}h_{j3} \\ \\ =V_{ij}^Tb \\ \\ V_{ij} = \begin{bmatrix} h_{i1}h_{j1} &h_{i1}h_{j2}+h_{i2}h_{j1} & h_{i2}h_{j2}&h_{i1}h_{j3}+h_{i3}h_{j1} &h_{i2}h_{j3}+h_{i3}h_{j2} &h_{i3}h_{j3} \end{bmatrix}^T

\begin{bmatrix} V_{12}^T\\ (V_{11}-V_{12})^T \end{bmatrix}b=0

求出B就可以得到内参矩阵中的参数,需要注意的是,我们之前求的B是不带尺度因子的,真实的B与求出的B差一个系数B=\lambda B^{'}

step4 计算外参矩阵R、T

[A^{-1}h_1 \ A^{-1}h_2 \ A^{-1}h_3] = \lambda [r_1 \ r_2 \ t]\rightarrow \left\{\begin{matrix} r_1 = \frac{1}{\lambda}A^{-1}h_1\\\\r_2 = \frac{1}{\lambda} A^{-1}h_2 \\\\ r_3 = r_1\times r_2 \\\\ t = \frac{1}{\lambda} A^{-1}h_3 \end{matrix}\right.

由于存在噪声,我们求解的R不一定满足选装矩阵的特性R^{T}R=I

因此需要求解最优的旋转矩阵:

\underset{R}{min}\begin{Vmatrix} R-Q \end{Vmatrix}^2_{F} \ ,R^TR=I,Q = \begin{bmatrix} r_1 & r_2 & r_3 \end{bmatrix}

\begin{Vmatrix} R-Q \end{Vmatrix}^{2}_F = trace*(R-Q)^T(R-Q))\\=3+trace(Q^TQ)-2trace(R^TQ)

整个优化就可以看作最小化 trace(R^TQ)

trace(R^TQ) = trace(R^TUSV^T) = trace(V^TR^TUS)

Z = V^TR^TU ,当R = UV^TZ=I

void CamCalibration::CalcRT() {
    const auto &K = K_;
    const auto &H_vec = H_vec_;
    auto &R_vec = R_vec_;
    auto &t_vec = t_vec_;

    cv::Mat K_inverse;
    cv::invert(K, K_inverse);

    for (const auto &H: H_vec) {
        cv::Mat M = K_inverse * H;

        cv::Vec3d r1(M.at<double>(0, 0), M.at<double>(1, 0), M.at<double>(2, 0));
        cv::Vec3d r2(M.at<double>(0, 1), M.at<double>(1, 1), M.at<double>(2, 1));
        cv::Vec3d r3 = r1.cross(r2);
        cv::Mat Q = cv::Mat::eye(3, 3, CV_64F);

        Q.at<double>(0, 0) = r1(0);
        Q.at<double>(1, 0) = r1(1);
        Q.at<double>(2, 0) = r1(2);
        Q.at<double>(0, 1) = r2(0);
        Q.at<double>(1, 1) = r2(1);
        Q.at<double>(2, 1) = r2(2);
        Q.at<double>(0, 2) = r3(0);
        Q.at<double>(1, 2) = r3(1);
        Q.at<double>(2, 2) = r3(2);
        cv::Mat normQ;
        cv::normalize(Q, normQ);

        cv::Mat U, W, VT;
        cv::SVD::compute(normQ, W, U, VT, cv::SVD::MODIFY_A | cv::SVD::FULL_UV);
        cv::Mat R = U * VT;

        R_vec.push_back(R);
        cv::Mat t = cv::Mat::eye(3, 1, CV_64F);
        M.col(2).copyTo(t.col(0));
        t_vec.push_back(t);
    }
}

step4 计算畸变参数

张正友标定法只考虑了切向畸变,畸变公式如下:

\hat{x}=x(1+k_1r^2+k_2r^4) \\ \hat{y}=y(1+k_1r^2+k_2r^4)

(x,y)(\hat{x},\hat{y}) 分别是理想情况的归一化图像坐标和畸变后的归一化图像坐标,r为图像像素点到图像中心点的距离\ (r^2 = x^2 + y^2),距离越大畸变越大。

\begin{bmatrix} \hat{u}\\\hat{v}\\1 \end{bmatrix} = \begin{bmatrix} \alpha & \gamma & u_0\\ 0& \beta & v_0 \\ 0&0 &1 \end{bmatrix}\begin{bmatrix} \hat{x}\\\hat{y}\\1 \end{bmatrix} \rightarrow \left\{\begin{matrix} \hat{u}=\alpha \hat{x} + \gamma \hat{y} + u_0 \\ \\ \hat{v}= \beta \hat{y} + v_0 \end{matrix}\right.

\begin{bmatrix} u\\v\\1 \end{bmatrix} = \begin{bmatrix} \alpha & \gamma & u_0\\ 0& \beta & v_0 \\ 0&0 &1 \end{bmatrix}\begin{bmatrix}x\\y\\1 \end{bmatrix} \rightarrow \left\{\begin{matrix}u=\alpha x + \gamma y + u_0 \\ \\ v= \beta y + v_0 \end{matrix}\right.

像平面一般接近90度,所以\gamma为0,因此

\left\{\begin{matrix} \hat{u}=\alpha \hat{x} + u_0\\ \hat{v}= \beta \hat{y} + v_0\\ u=\alpha x + u_0 \\ v = \beta y +v_0 \end{matrix}\right.\rightarrow \left\{\begin{matrix} \hat{u} = u_0+\frac{u-u_0}{x}\hat{x} \\ \\ \hat{v} = v_0+\frac{v-v_0}{x}\hat{x} \end{matrix}\right.

\left\{\begin{matrix} \hat{u} = u+(u-u_0)(k_1r^2+k_2r^4)\\ \hat{v} = v+(v-v_0)(k_1r^2+k_2r^4) \end{matrix}\right.

\begin{bmatrix} (u-u_0)r^2 &(u-u_0)r^4 \\ (v-v_0)r^2 &(v-v_0)r^4 \end{bmatrix}\begin{bmatrix} k_1\\k_2 \end{bmatrix}=\begin{bmatrix} \hat{u}-u\\ \hat{v}-v \end{bmatrix}

Dk=d

通过最小二乘法可求得k.

k = (D^TD)^{-1}D^Td

void CamCalibration::CalDistCoeff() {
    std::vector<double> r2_vec;
    std::vector<cv::Point2f> ideal_point_vec;

    //用一副图进行计算
    const cv::Mat &K = K_;
    const cv::Mat &R = R_vec_[0];
    const cv::Mat &t = t_vec_[0];
    auto points_3d = points_3d_vec_[0];
    auto &dist_coeff = dist_coeff_;
    for (const auto &point_3d: points_3d) {
        cv::Mat p_3d = (cv::Mat_<double>(3, 1) << point_3d.x, point_3d.y, 0);
        //世界坐标转相机坐标
        cv::Mat p_cam = R * p_3d + t;
        //转换成欧式坐标
        p_cam.at<double>(0, 0) = p_cam.at<double>(0, 0) / p_cam.at<double>(2, 0);
        p_cam.at<double>(1, 0) = p_cam.at<double>(1, 0) / p_cam.at<double>(2, 0);
        p_cam.at<double>(2, 0) = 1;
        double x = p_cam.at<double>(0, 0);
        double y = p_cam.at<double>(1, 0);
        double r2 = x * x + y * y;
        r2_vec.push_back(r2);

        //相机坐标转像素坐标
        cv::Mat p_pix = K * p_cam;
        ideal_point_vec.emplace_back(p_pix.at<double>(0, 0), p_pix.at<double>(1, 0));
    }
    const std::vector<cv::Point2f> &dist_point_vec = points_2d_vec_[0];
    double u0 = K.at<double>(0, 2);
    double v0 = K.at<double>(1, 2);

    cv::Mat D = cv::Mat::eye(ideal_point_vec.size() * 2, 2, CV_64F);
    cv::Mat d = cv::Mat::eye(ideal_point_vec.size() * 2, 1, CV_64F);
    for (int i = 0; i < ideal_point_vec.size(); i++) {
        double r2 = r2_vec[i];
        const auto &ideal_p = ideal_point_vec[i];
        const auto &dist_p = dist_point_vec[i];
        D.at<double>(i * 2, 0) = (ideal_p.x - u0) * r2;
        D.at<double>(i * 2, 1) = (ideal_p.x - u0) * r2 * r2;
        D.at<double>(i * 2 + 1, 0) = (ideal_p.y - v0) * r2;
        D.at<double>(i * 2 + 1, 1) = (ideal_p.y - v0) * r2 * r2;
        d.at<double>(2 * i, 0) = dist_p.x - ideal_p.x;
        d.at<double>(2 * i + 1, 0) = dist_p.y - ideal_p.y;

    }
    cv::Mat DT;
    cv::transpose(D, DT);
//    std::cout << "D:" << D << std::endl;

    cv::Mat DTD_inverse;
    cv::invert(DT * D, DTD_inverse);
//    std::cout << "DTD_inv:" << DTD_inverse << std::endl;

    dist_coeff = DTD_inverse * DT * d;
    std::cout << "distort coeff: " << dist_coeff.at<double>(0, 0) << ", " << dist_coeff.at<double>(1, 0) << std::endl;
}

自此张正友相机标定完成,当然,我们所求出来的参数都是独立的,是理想情况下的值,因此需要使用最大似然估计进行优化,这部分大家自行了解。

参考内容:

计算机视觉之三维重建(深入浅出SfM与SLAM核心算法)——2.摄像机标定_哔哩哔哩_bilibili

张正友标定论文的解读和C++代码编写-CSDN博客

A flexible new technique for camera calibration | IEEE Journals & Magazine | IEEE Xplore

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1261538.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux安全之auditd审计工具使用说明

一、auditd工具简介 audited是Linux审核系统的用户空间组件。它负责将审核记录写入磁盘。查看日志是通过ausearch或aureport实用程序完成的。审核系统或加载规则的配置是使用auditctl实用程序完成的。在启动过程中&#xff0c;/etc/audit/audit.rules中的规则由auditctl读取并加…

全新爱蜗影视优码双端影视源码v9.1/影视视频APP源码+支持代理/在线支付+支持对应苹果CMS

源码简介&#xff1a; 这个是最新爱蜗影视优码双端影视源码&#xff0c;作为实用方便的影视视频APP源码&#xff0c;它不仅支持代理/在线支付&#xff0c;而且也支持对应苹果CMS。 爱蜗影视优码双端影视支持对应苹果CMS支持代理在线支付 带图文教程&#xff0c;全新美化多功能…

深入理解OS--数值编码

信息的表示和处理 寻址和字节顺序 位于0x100处&#xff0c;int类型值0x01234567在大端和小端下的存储。 字符串的存储不受字节序影响。 移位 1.对左移&#xff0c;右边统一补0 2.对右移&#xff0c;分为算术右移&#xff0c;逻辑右移 算术右移下&#xff0c;左边补原最高有效…

jsoup登录日志平台后调企业微信机器人自动发送错误日志告警

一、需求&#xff1a;错误日志Top10告警发送 二、需求分解 jsoup实现登录&#xff0c;获取到cookie和token等用户鉴权信息获取接口相应的key值调用日志平台错误日志Top榜接口&#xff0c;查询到结果集调用企业微信机器人发送消息接口加上定时任务&#xff0c;可以实现定时发送…

【Unity3D】MAX聚合广告SDK——Pangle广告接入失败总结

Pangle, App Monetization Simplified 注册 登录 创建应用 创建广告单元 将其应用ID和广告ID关联到MAX广告。 下载Pangle Unity Plugin包&#xff0c;新建一个空工程&#xff08;很重要&#xff09; Unity版本2019.4.0f1 gradle plugin 4.2.0 gradle版本6.7.1 build_tools 34.…

SpringBoot : ch08 自动配置原理

前言 在现代的Java开发中&#xff0c;Spring Boot已经成为了一个备受欢迎的框架。它以其简化开发流程、提高效率和强大的功能而闻名&#xff0c;使得开发人员能够更加专注于业务逻辑的实现而不必过多地关注配置问题。 然而&#xff0c;你是否曾经好奇过Spring Boot是如何做到…

7000字+24张图带你彻底弄懂线程池

大家好&#xff0c;我是三友。今天跟大家聊一聊无论是在工作中常用还是在面试中常问的线程池&#xff0c;通过画图的方式来彻底弄懂线程池的工作原理&#xff0c;以及在实际项目中该如何自定义适合业务的线程池。 一、什么是线程池 线程池其实是一种池化的技术的实现&#xff0…

控制台gbk乱码

引用IntelliJ IDEA中 统一设置编码为utf-8或GBK-CSDN博客 特别注意file coding 的文件path和java的编码格式 配置

9.增删改操作

目录 一、插入操作 1、为表的所有字段插入数据 2、为表的指定字段插入数据 3、同时插入多条记录 4、将查询结果插入表中&#xff1a; 二、更新操作 三、删除操作 四、练习题 一、插入操作 在使用数据库之前&#xff0c;数据库中必须要有数据&#xff0c;MYSQL中使INSE…

ESP32-Web-Server编程-HTML 基础

ESP32-Web-Server编程-HTML 基础 概述 HTML(HyperText Markup Language) 是用来描述网页的一种语言。其相关内容存储在前端代码的 .html 文件中。 当浏览器向 web 服务器请求网页时&#xff0c;一个 HTML 文件被发送给浏览器&#xff0c;浏览器解释该文件的内容&#xff0c;…

【重磅】:Spring Initializer 已经不支持Java8,也就是SpringBoot2.x项目初始化

Spring Initializer 已经不支持Java8 问题描述解决方案升级java版本更换IDEA内置的Spring Initializer中 Server URL的镜像地址 问题描述 我们可以看到在IDEA内置的Spring Initializer中 Java版本选择模块已经不支持1.8了&#xff0c;同样的&#xff0c;官网也不再支持了 解决…

YOLOv8 onnx 文件推理多线程加速视频流

运行环境&#xff1a; MacOS&#xff1a;14.0Python 3.9Pytorch2.1onnx 运行时 模型文件&#xff1a; https://wwxd.lanzouu.com/iBqiA1g49pbc 密码:f40v 下载 best.apk后将后缀名修改为 onnx 即可模型在英伟达 T4GPU 使用 coco128 训练了 200 轮如遇下载不了可私信获取 代码…

Ubuntu18.04安装Ipopt-3.12.8流程

本文主要介绍在Ubuntu18.04中安装Ipopt库的流程&#xff0c;及过程报错的解决方法&#xff0c;已经有很多关于Ipopt安装的博客&#xff0c;但经过我的测试&#xff0c;很多都失效了&#xff0c;经过探索&#xff0c;找到可流畅的安装Ipopt的方法&#xff0c;总结成本篇博客。 …

《尚品甄选》:后台系统——权限管理之菜单管理,递归实现树形结构查询(debug一遍)

文章目录 一、表结构设计二、菜单管理接口2.1 查询菜单2.2 添加菜单2.3 修改菜单2.4 删除菜单 三、分配菜单3.1 查询菜单3.2 保存菜单(批量插入) 四、动态菜单五、解决bug 一、表结构设计 菜单管理就是对系统的首页中的左侧菜单进行维护。 一个用户可以担任多个角色&#xff…

解密人工智能:线性回归

导言 人工智能&#xff08;AI&#xff09;已经成为当今科技领域的热门话题&#xff0c;其应用领域涵盖了各个行业。线性回归作为人工智能中的一种关键统计学方法&#xff0c;被广泛应用于预测和决策支持系统中。本文将为您详细介绍线性回归在人工智能中的应用原理与方法&#x…

将用户的session改为分布式共享session

将用户的session改为分布式session 分布式session理解 使用分布式session的原因&#xff1a; 后台服务器是分布式的&#xff08;比如要负载均衡&#xff09;&#xff0c;在A服务器请求的的信息&#xff08;如用户登录信息&#xff09;存在A的session中&#xff0c;B服务器并不…

代码随想录算法训练营 ---第四十八天

第一题&#xff1a; 简介&#xff1a; 注&#xff1a;本题简介是我的思路&#xff0c;题解思路看下方。 动态规划五部曲&#xff1a; 1.确定dp数组的含义 //dp[i]表示 偷到第i家能偷到的最大金额 for(int i2;i<nums.size();i){if(i-3>0)dp[i] max(dp[i-2],dp[i-3])nu…

vue中的keep-alive详解与应用场景

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Vue篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来vue篇专栏内容:vue-keep-alive 目录 一、Keep-alive 是什么 二、使用场景 三、原理分析 四、案例实现 activa…

NB-IoT BC260Y Open CPU SDK④开发环境搭建

NB-IoT BC260Y Open CPU SDK④开发环境搭建 1、SDK包的介绍2、编程工具3、程序框架1、SDK包的介绍 (1)、SDK包的下载: 链接: (2)、文件目录介绍 文件名描述device启动文件、底层配置文档等doc存放 QuecOpen 项目相关的说明文档osFreeRTOS 相关代码out输出编译 App 和调…