OpenCV实战(20)——图像投影关系

news2024/11/24 4:26:38

OpenCV实战(20)——图像投影关系

    • 0. 前言
    • 1. 相机成像原理
    • 2. 图像对的基本矩阵
    • 3. 完整代码
    • 小结
    • 系列链接

0. 前言

数码相机通过将光线通过镜头投射到图像传感器上来捕捉场景产生图像。由于通过将 3D 场景投影到 2D 平面上形成图像,因此场景与其图像之间以及同一场景的不同图像之间存在重要关系。射影 (Projective geometry) 几何也称投影几何,其使用数学术语描述和表征图像形成过程。在本节中,我们将介绍多视图图像中的一些基本投影关系,并解释如何将它们用于计算机视觉应用程序。我们将学习如何通过使用投影约束使匹配更加准确,以及如何使用两视图关系拼接多个图像。

1. 相机成像原理

我们首先介绍与场景投影和图像形成相关的基本概念。自摄影开始以来,图像的生成过程从本质上来讲并没有改变。来自场景的光线被相机通过正面光圈捕捉到,然后捕获到的光线照射到位于相机背面的图像平面(或图像传感器)。此外,镜头用于汇聚来自不同场景元素的光线,该过程如下图所示:

成像原理
其中, d o d_o do 是镜头到被观察物体的距离, d i d_i di 是镜头到像平面的距离, f f f 是镜头的焦距,凸透镜成像方程 (thin lens equation) 如下:
1 f = 1 d 0 + 1 d i \frac 1f=\frac 1{d_0}+\frac1{d_i} f1=d01+di1
在计算机视觉中,以上相机模型可以通过多种方式进行简化。首先,我们可以忽略镜头的影响,因为当相机具有无穷小光圈时其并不会改变图像外观,我们可以通过创建无限景深 (depth of field) 的图像来忽略焦距效果,在这种情况下,只需要考虑中心光线;其次,由于大部分时间 d o > > d i d_o>>d_i do>>di,我们可以假设图像平面位于焦距处;最后,我们可以从系统的几何结构中注意到平面上的图像是倒置的。我们可以通过将图像平面放置在镜头前面来获得内容相同但与水平垂直的图像,显然这在物理上是不可行的,但从数学的角度来看这两种系统是等价的。这种简化的模型通常被称为针孔相机 (pin-hole camera) 模型,其表示如下:

针孔相机模型
根据以上模型使用相似三角形定律,可以很容易地推导出将图像对象与其图像关联的基本投影方程:
h i = f h o d o h_i=f\frac {h_o}{d_o} hi=fdoho
因此,物体(高度为 h o h_o ho )的图像的大小 ( h i h_i hi) 与其与相机的距离 ( d o d_o do) 成反比。这种关系描述了在给定相机几何形状的情况下,3D 场景点将被投影到图像平面的哪个位置。更具体地说,如果我们假设参考系位于焦点处,那么位于 ( X , Y , Z ) (X, Y, Z) (X,Y,Z) 位置的 3D 场景点将在 ( x , y ) = ( f X Z , f Y Z ) (x,y)=(f\frac XZ,f\frac YZ) (x,y)=(fZX,fZY) 处投影到图像平面上。其中, Z Z Z 坐标对应于点的深度(或点到相机的距离,在以上等式中用 d o d_o do 表示),这种关系可以通过引入齐次坐标将其改写成矩阵形式,其中 2D 点由三个向量表示,3D 点由四个向量表示(额外的坐标是比例因子 S,当需要从三个向量中提取二维坐标时,需要去除该比例因子 S):
S [ x y 1 ] = [ f 0 0 0 0 f 0 0 0 0 1 0 ] [ X Y Z 1 ] S\left[ \begin{array}{ccc} x\\ y\\ 1\\\end{array}\right]=\left[ \begin{array}{ccc} f&0&0&0\\ 0&f&0&0\\ 0&0&1&0\\\end{array}\right]\left[ \begin{array}{ccc} X\\ Y\\ Z\\ 1\\\end{array}\right] S xy1 = f000f0001000 XYZ1
上式中的 3x4 矩阵称为投影矩阵 (projection matrix),如果参考系未与焦点对齐,则必须引入旋转 r r r 和平移 t t t 矩阵,用于将投影的 3D 点表达为以相机为中心的参考系:
S [ x y 1 ] = [ f 0 0 0 f 0 0 0 1 ] [ r 1 r 2 r 3 t 1 r 4 r 5 r 6 t 2 r 7 r 8 r 9 t 3 ] [ X Y Z 1 ] S\left[ \begin{array}{ccc} x\\ y\\ 1\\\end{array}\right]=\left[ \begin{array}{ccc} f&0&0\\ 0&f&0\\ 0&0&1\\\end{array}\right]\left[ \begin{array}{ccc} r_1&r_2&r_3&t_1\\ r_4&r_5&r_6&t_2\\ r_7&r_8&r_9&t_3\\\end{array}\right]\left[ \begin{array}{ccc} X\\ Y\\ Z\\ 1\\\end{array}\right] S xy1 = f000f0001 r1r4r7r2r5r8r3r6r9t1t2t3 XYZ1
以上方程的第一个矩阵包含了相机的内置参数(焦距);第二个矩阵包含外部参数,这些参数是将相机与外部世界相关联的参数。在实际应用中,图像坐标以像素表示,而 3D 坐标以物理长度单位(例如米)表示。

2. 图像对的基本矩阵

在本节中,我们将探索显示相同场景的两个图像之间存在的投影关系。这两个图像可以通过在两个不同位置移动相机从两个视角拍摄,或使用两个相机从不同角度拍摄。从两幅或两幅以上的图像中推断出图像中每个像素点的深度信息,是立体视觉的核心研究内容。考虑使用两个摄像机观察给定的场景点,如下图所示:

请添加图片描述

我们可以通过追踪连接此 3D 点与相机中心的线得到 3DX 的图像 x。而在图像平面的位置 x 处的场景点可以位于 3D 空间中这条连线上的任何位置,这意味着如果我们想在另一幅图像中找到给定图像点的对应场景点,需要沿着中心连线在第二个图像平面上的投影进行搜索。这条假想线称为 X 点的核线 (epipolar line),它定义了对应点必须满足的基本约束;也就是说,给定点的匹配必须在另一个视图中该点的核线上,而该核线的确切方向取决于两个相机各自的位置。事实上,核线的配置表征了双视图系统的几何形状。
可以从这个双视图系统的几何结构中得出另一结论,所有的核线都通过同一点,该点对应于一个摄像机的中心在另一台摄像机上的投影,这个特殊的点称为核 (epipole)。
在数学上,图像点与其对应的核线之间的关系可以使用 3x3 矩阵表示:
[ l 1 ′ l 2 ′ l 3 ′ ] = F [ x y 1 ] \left[ \begin{array}{ccc} l_1'\\ l_2'\\ l_3'\\\end{array}\right]=F\left[ \begin{array}{ccc} x\\ y\\ 1\\\end{array}\right] l1l2l3 =F xy1
在射影几何中,2D 线也由 3 个向量表示,它对应于满足 l 1 ′ x ′ + l 2 ′ y ′ + l 3 ′ = 0 l_1'x'+ l_2'y'+ l_3'=0 l1x+l2y+l3=0 方程的一组 2D ( x ′ , y ′ ) (x',y') (x,y) (质数上标表示这条线属于第二幅图像)。因此,基本矩阵 F F F 可以将一个视图中的 2D 图像点映射到另一个视图中的核线。
图像对的基本矩阵可以通过求解一组方程来估计,这些方程需要两幅图像之间一定数量的已知匹配点,匹配点的最小数量为 7

(1) 为了说明基本的矩阵估计过程,在图像对中,从 SIFT 特征的匹配结果中选择 7 个匹配,用于使用 cv::findFundamentalMat 函数计算基本矩阵。具有选定匹配项的图像对如下图所示:

图像对匹配结果
(2) 如果我们将每张图像中的图像点作为 cv::keypoint 实例,首先需要将它们转换为 cv:: Point2f 以便与 cv::findFundamentalMat 一起使用:

// 关键点和描述符向量
std::vector<cv::KeyPoint> keypoints1;
std::vector<cv::KeyPoint> keypoints2;
cv::Mat descriptors1, descriptors2;

(3) selPoints1selPoints2 这两个向量包含两个图像中的对应点;关键点实例用 keypoints1keypoints2 表示;pointIndexes1pointIndexes2 向量包含要转换的关键点的索引。cv::findFundamentalMat 函数的调用方法如下:

// 根据 7 个匹配计算 F 矩阵
cv::Mat fundamental = cv::findFundamentalMat(
            selPoints1,     // 第一张图像上的点
            selPoints2,     // 第二张图像上的点
            cv::FM_7POINT); // 7 点法

(4) 我们可以通过绘制一些选定点的核线来直观地验证基本矩阵的有效性。通过 OpenCV 函数计算出给定一组点的核线后,就可以使用 cv::line 函数绘制它们,即从左侧图像中的点计算并绘制右侧图像中的核线:

// 在右图像中绘制对应于极线的左侧图像点
std::vector<cv::Vec3f> lines1;
cv::computeCorrespondEpilines(selPoints1, 1, fund, lines1);
std::cout << "size of F matrix:" << fund.rows << "x" << fund.cols << std::endl;
// 循环所有极线
for (std::vector<cv::Vec3f>::const_iterator it=lines1.begin(); it!=lines1.end(); ++it) {
    cv::line(image2,
            cv::Point(0, -(*it)[2]/(*it)[1]),
            cv::Point(image2.cols, -((*it)[2]+(*it)[0]*image2.cols)/(*it)[1]),
            cv::Scalar(255, 255, 255));
}

绘制结果如下图所示:

核线
核点位于核线的交点处,它是另一个相机中心的投影,该核点如上图所示。通常,核线在图像边界外相交,如果两个图像是在同一时刻拍摄的,则交点位于第一个相机可见的位置。需要注意的是,当基本矩阵仅由 7 个匹配项计算时,结果可能并不稳定,使用不同的匹配可能会得到一组差异较大的核线。
我们已经介绍了,对于一个图像中的一个点,基本矩阵给出了在另一视图中找到其对应点的直线方程。如果 p ( x , y ) p(x, y) p(x,y) 点的对应点是 p ′ ( x ′ , y ′ ) p'(x', y') p(x,y) ,且 F F F 是两个视图之间的基本矩阵,那么由于 p ′ ( x ′ , y ′ ) p'(x', y') p(x,y) 位于 F p Fp Fp 极线上,可以得到以下等式:
[ x ′ y ′ 1 ] T F [ x y 1 ] = 0 \left[ \begin{array}{ccc} x'\\ y'\\ 1\\\end{array}\right]^TF\left[ \begin{array}{ccc} x\\ y\\ 1\\\end{array}\right]=0 xy1 TF xy1 =0
该方程表示两个对应点之间的关系,称为对极约束 (epipolar constraint),根据此等式,可以使用已知匹配来估计矩阵项。由于 F F F 矩阵项表示比例因子,因此只有 8 个项需要估计(第 9 个可以设置为 1)。每个匹配都可以得到一个等式,因此,使用 8 个已知匹配项,就可以通过求解所得的线性方程组来完全估计矩阵。可以通过使用 CV_FM_8POINT 标志调用 cv::findFundamentalMat 函数完成以上过程,在这种情况下,可以(并且最好)输入超过 8 个匹配项,然后求解所获得的超定线性方程组。
为了估计基本矩阵,还可以利用附加约束。数学上, F F F 矩阵将 2D 点映射到一维线束(即在公共点相交的线),所有这些核线都通过这个独特的点(即核点)对矩阵施加约束。此约束将估计基本矩阵所需的匹配数减少到 7 个,但在这种情况下,方程组变为非线性方程组,最多有 3 个可能的解,在这种情况下,cv::findFundamentalMat 将返回大小为 9x3 的基本矩阵,即三个 3x3 矩阵叠加。 F F F 矩阵估计的 7 匹配解可以通过使用 CV_FM_7POINT 标志调用 cv::findFundamentalMat 函数。
最后,在图像中选择合适的匹配集对于获得基本矩阵的准确估计十分重要。一般来说,匹配应该均匀的分布在整个图像上,并包括场景中不同深度的点,否则,解将变得不稳定;特别是所选场景点不应共面,因为在这种情况下基本矩阵会退化。

3. 完整代码

完整代码 estimateF.cpp 如下所示:


#include <iostream>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/calib3d/calib3d.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/xfeatures2d.hpp>

int main() {
    // 读取输入图像
    cv::Mat image1= cv::imread("01.png",0);
    cv::Mat image2= cv::imread("02.png",0);
    if (!image1.data || !image2.data)
        return 0;
    cv::namedWindow("Right Image");
    cv::imshow("Right Image",image1);
    cv::namedWindow("Left Image");
    cv::imshow("Left Image",image2);
    // 关键点和描述符向量
    std::vector<cv::KeyPoint> keypoints1;
    std::vector<cv::KeyPoint> keypoints2;
    cv::Mat descriptors1, descriptors2;
    // 构建 SIFT 特征检测器
    cv::Ptr<cv::Feature2D> ptrFeature2D = cv::xfeatures2d::SIFT::create(164);
    // SURF 特征检测
    ptrFeature2D->detectAndCompute(image1, cv::noArray(), keypoints1, descriptors1);
    ptrFeature2D->detectAndCompute(image2, cv::noArray(), keypoints2, descriptors2);
    std::cout << "Number of SIFT points (1): " << keypoints1.size() << std::endl;
    std::cout << "Number of SIFT points (2): " << keypoints2.size() << std::endl;
    // 绘制关键点
    cv::Mat imageKP;
    cv::drawKeypoints(image1,keypoints1,imageKP,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::namedWindow("Right SIFT Features");
    cv::imshow("Right SIFT Features",imageKP);
    cv::drawKeypoints(image2,keypoints2,imageKP,cv::Scalar(255,255,255),cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
    cv::namedWindow("Left SIFT Features");
    cv::imshow("Left SIFT Features",imageKP);
    // 构建匹配器
    cv::BFMatcher matcher(cv::NORM_L2, true);
    // 匹配两个图像的描述符
    std::vector<cv::DMatch> matches;
    matcher.match(descriptors1, descriptors2, matches);
    std::cout << "Number of matched points: " << matches.size() << std::endl;
    // 手动选择几个匹配
    std::vector<cv::DMatch> selMatches;
    // 需要确保匹配的有效性
    selMatches.push_back(matches[2]);  
    selMatches.push_back(matches[5]);
    selMatches.push_back(matches[16]);
    selMatches.push_back(matches[29]);
    selMatches.push_back(matches[14]);
    selMatches.push_back(matches[34]);
    selMatches.push_back(matches[20]);
    // 绘制所选择的匹配
    cv::Mat imageMatches;
    cv::drawMatches(image1, keypoints1,     // 第一张图像及其关键点
                image2, keypoints2,         // 第二张图像及其关键点
                selMatches,                 // 所选择的匹配
                imageMatches,               // 生成结果
                cv::Scalar(255, 255, 255),
                cv::Scalar(255, 255, 255)   /*,
                std::vector<char>(),
                2*/);
    cv::namedWindow("Matches");
    cv::imshow("Matches",imageMatches);
    // 将关键点矢量转换为 Point2f 矢量
    std::vector<int> pointIndexes1;
    std::vector<int> pointIndexes2;
    for (std::vector<cv::DMatch>::const_iterator it=selMatches.begin(); it!=selMatches.end(); ++it) {
        // 获取所选择的匹配关键点的索引
        pointIndexes1.push_back(it->queryIdx);
        pointIndexes2.push_back(it->trainIdx);
    }
    std::vector<cv::Point2f> selPoints1, selPoints2;
    cv::KeyPoint::convert(keypoints1, selPoints1, pointIndexes1);
    cv::KeyPoint::convert(keypoints2, selPoints2, pointIndexes2);
    // 绘制点
    std::vector<cv::Point2f>::const_iterator it = selPoints1.begin();
    while (it!=selPoints1.end()) {
        cv::circle(image1, *it, 3, cv::Scalar(255, 255, 255), 2);
        ++it;
    }
    it = selPoints2.begin();
    while (it!=selPoints2.end()) {
        cv::circle(image2, *it, 3, cv::Scalar(255, 255, 255), 2);
        ++it;
    }
    // 根据 7 个匹配计算 F 矩阵
    cv::Mat fundamental = cv::findFundamentalMat(
                selPoints1,     // 第一张图像上的点
                selPoints2,     // 第二张图像上的点
                cv::FM_7POINT); // 7 点法
    std::cout << "F-Matrix size= " << fundamental.rows << "," << fundamental.cols << std::endl;
    cv::Mat fund(fundamental, cv::Rect(0, 0, 3, 3));
    // 在右图像中绘制对应于极线的左侧图像点
    std::vector<cv::Vec3f> lines1;
    cv::computeCorrespondEpilines(selPoints1, 1, fund, lines1);
    std::cout << "size of F matrix:" << fund.rows << "x" << fund.cols << std::endl;
    // 循环所有极线
    for (std::vector<cv::Vec3f>::const_iterator it=lines1.begin(); it!=lines1.end(); ++it) {
        cv::line(image2,
                cv::Point(0, -(*it)[2]/(*it)[1]),
                cv::Point(image2.cols, -((*it)[2]+(*it)[0]*image2.cols)/(*it)[1]),
                cv::Scalar(255, 255, 255));
    }
    // 在左图像中绘制对应于极线的左侧图像点 
    std::vector<cv::Vec3f> lines2;
    cv::computeCorrespondEpilines(cv::Mat(selPoints2), 2, fund, lines2);
    for (std::vector<cv::Vec3f>::const_iterator it=lines2.begin(); it!=lines2.end(); ++it) {
        cv::line(image1,
                cv::Point(0, -(*it)[2]/(*it)[1]),
                cv::Point(image1.cols, -((*it)[2]+(*it)[0]*image1.cols)/(*it)[1]),
                cv::Scalar(255, 255, 255));
    }
    // 合并两张图像
    cv::Mat both(image1.rows, image1.cols+image2.cols, CV_8U);
    image1.copyTo(both.colRange(0, image1.cols));
    image2.copyTo(both.colRange(image1.cols, image1.cols+image2.cols));
    cv::namedWindow("Epilines");
    cv::imshow("Epilines", both);
    /*
    // 将关键点转换为 Point2f
    std::vector<cv::Point2f> points1, points2, newPoints1, newPoints2;
    cv::KeyPoint::convert(keypoints1, points1);
    cv::KeyPoint::convert(keypoints2, points2);
    cv::correctMatches(fund, points1, points2, newPoints1, newPoints2);
    cv::KeyPoint::convert(newPoints1, keypoints1);
    cv::KeyPoint::convert(newPoints2, keypoints2);

    cv::drawMatches(image1, keypoints1, // 第一张图像及其关键点
                image2, keypoints2,     // 第二张图像及其关键点
                matches,                // 匹配
                imageMatches,           // 结果图像
                cv::Scalar(255, 255, 255),
                cv::Scalar(255, 255, 255),
                std::vector<char>(),
                2
                ); // color of the lines
    cv::namedWindow("Corrected matches");
    cv::imshow("Corrected matches", imageMatches);
    */
   cv::waitKey();
   return 0;
}

小结

在本节中,我们介绍了多视图图像中的一些基本投影关系,并解释如何将它们用于计算机视觉应用程序,重点介绍了如何使用 cv::findFundamentalMat 函数计算基本矩阵,同时通过使用投影约束使匹配更加准确。

系列链接

OpenCV实战(1)——OpenCV与图像处理基础
OpenCV实战(2)——OpenCV核心数据结构
OpenCV实战(3)——图像感兴趣区域
OpenCV实战(4)——像素操作
OpenCV实战(5)——图像运算详解
OpenCV实战(6)——OpenCV策略设计模式
OpenCV实战(7)——OpenCV色彩空间转换
OpenCV实战(8)——直方图详解
OpenCV实战(9)——基于反向投影直方图检测图像内容
OpenCV实战(10)——积分图像详解
OpenCV实战(11)——形态学变换详解
OpenCV实战(12)——图像滤波详解
OpenCV实战(13)——高通滤波器及其应用
OpenCV实战(14)——图像线条提取
OpenCV实战(15)——轮廓检测详解
OpenCV实战(16)——角点检测详解
OpenCV实战(17)——FAST特征点检测
OpenCV实战(18)——特征匹配
OpenCV实战(19)——特征描述符

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

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

相关文章

时序预测 | MATLAB实现BO-CNN-GRU贝叶斯优化卷积门控循环单元时间序列预测

时序预测 | MATLAB实现BO-CNN-GRU贝叶斯优化卷积门控循环单元时间序列预测 目录 时序预测 | MATLAB实现BO-CNN-GRU贝叶斯优化卷积门控循环单元时间序列预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 基于贝叶斯(bayes)优化卷积神经网络-门控循环单元(CNN-GR…

2023年人工智能GPT-4时代,最新13个ChatGPT商业市场AIGC应用正在掀起革命性变革!

目录 前言ChatGPT商业应用——LLM是星辰大海1. 研究背景1.1 研究背景1.2 研究方法 2. 商业应用和案例分析2.1 工具层ChatGPT搜索ChatGPT办公ChatGPT教育 2.2 行业层ChatGPT游戏ChatGPT音乐ChatGPT零售电商ChatGPT广告营销ChatGPT媒体新闻ChatGPT金融ChatGPT医疗ChatGPT设计Chat…

AI绘画能力的起源:通俗理解VAE、扩散模型DDPM、ViT/Swin transformer

前言 2018年我写过一篇博客&#xff0c;叫&#xff1a;《一文读懂目标检测&#xff1a;R-CNN、Fast R-CNN、Faster R-CNN、YOLO、SSD》&#xff0c;该文相当于梳理了2019年之前CV领域的典型视觉模型&#xff0c;比如 2014 R-CNN2015 Fast R-CNN、Faster R-CNN2016 YOLO、SSD2…

Linux命令集(Linux网络连接管理命令--ifconfig指令篇)

Linux命令集&#xff08;Linux网络连接管理命令--ifconfig指令篇&#xff09; Linux网络连接管理命令集&#xff08;ifconfig指令篇&#xff09;1. ifconfig(interface configuration)1. 信息显示2. 接口配置 Linux网络连接管理命令集&#xff08;ifconfig指令篇&#xff09; 如…

C++Primer第五版【阅读笔记】

CPrimer第五版 阅读笔记 第1章开始1.1 编写一个简单的C程序1.1.1 编译、运行程序 1.2 初识输入输出1.3 注释简介1.4 控制流1.4.1 while语句1.4.2 for语句1.4.3 读取数量不定的输入数据1.4.4 if语句 1.5 类简介1.5.1 Sales_item 类1.5.2 初识成员函数 1.6 书店程序第一章小结 第…

【Linux入门】linux指令(1)

【Linux入门】linux指令&#xff08;1&#xff09; 目录 【Linux入门】linux指令&#xff08;1&#xff09;操作系统登录服务器Linux下的基本指令ls指令pwd指令Linux路径分割符 /cd指令touch指令mkdir指令&#xff08;重要&#xff09;rmdir指令&&rm指令&#xff08;重…

4.30下周美联储携非农来袭黄金多空该如何布局?

近期有哪些消息面影响黄金走势&#xff1f;下周黄金多空该如何研判&#xff1f; ​黄金消息面解析&#xff1a;周五(4月28日)当周金价维持震荡交投&#xff0c;金价基本持稳于2000美元下方。支撑和打压金价的因素参半。经济衰退的担忧&#xff0c;以及避险情绪支持金价&#x…

Fabric.js 讲解官方demo:Stickman

theme: smartblue 本文简介 戴尬猴&#xff0c;我是德育处主任 Fabric.js 官网有很多有趣的Demo&#xff0c;不仅可以帮助我们了解其功能&#xff0c;还可以为我们提供创意灵感。其中&#xff0c;Stickman是一个非常有趣的例子。 先看看效果图 从上图可以看出&#xff0c;在拖拽…

【SpringBoot2】二:基础入门---自动配置原理(SpringBoot特点+容器功能)

文章目录 1.SpringBoot特点1.1 依赖管理1.2 自动配置 2.容器功能2.1 组件添加2.1.1Configuration2.1.2 Bean、Component、Controller、Service、Repository2.1.3 ComponentScan、Import2.1.4 Conditional 2.2 原生配置引入ImportResource2.3 配置绑定2.3.1 Component Configur…

如何保障网络安全

网络安全是一个涵盖范围广、深入浅出的话题。随着互联网在现代社会中扮演的重要角色日益突出&#xff0c;网络安全问题成为各个领域所关注的焦点。在此&#xff0c;我们将从以下几个方面来阐述网络安全的重要性&#xff0c;并讨论几种保障网络安全的方式。 一、网络安全的重要性…

K8s(1.20.15版本)部署(3master+2node)

1.准备工作 准备五台centos 7的虚拟机&#xff08;每台虚拟机分配2核2G&#xff0c;存储使用20G硬盘&#xff0c;必须2核不然报错&#xff09;&#xff1a;如下图机器的分配情况&#xff1a; IP节点名称节点类型10.10.10.11k8s-master11master10.10.10.12k8s-master12master1…

解决centos8下域名raw.githubusercontent.com解析错误

在win10环境下执行命令 D:\test>ping raw.githubusercontent.com Ping 请求找不到主机 raw.githubusercontent.com。请检查该名称&#xff0c;然后重试。 解决很简单&#xff0c;把ipv6的DNS服务器设为240c::6666就行了&#xff0c;改完后执行命令 D:\test>ping raw.g…

C语言函数大全-- s 开头的函数(2)

C语言函数大全 本篇介绍C语言函数大全-- s 开头的函数&#xff08;2&#xff09; 1. setlinestyle 1.1 函数说明 函数声明函数功能void setlinestyle( int linestyle, unsigned upattern, int thickness );设置当前绘图窗口的线条样式、线型模式和线条宽度 参数&#xff1a…

SQL中去除重复数据的几种方法,我一次性都告你​

使用SQL对数据进行提取和分析时&#xff0c;我们经常会遇到数据重复的场景&#xff0c;需要我们对数据进行去重后分析。 以某电商公司的销售报表为例&#xff0c;常见的去重方法我们用到distinct 或者group by 语句&#xff0c; 今天介绍一种新的方法&#xff0c;利用窗口函数…

不得不说的行为型模式-命令模式

目录 命令模式&#xff1a; 代码实例&#xff1a; 下面是面试中可能遇到的问题&#xff1a; 命令模式&#xff1a; 命令模式(Command Pattern)是一种行为型设计模式&#xff0c;它允许将请求封装成对象&#xff0c;从而让你能够用不同的请求对客户端进行参数化&#xff0c;对…

Guitar Pro8苹果mac最新版本下载安装教程

Guitar Pro是一款专业的吉他制谱软件&#xff0c;现在已更新至Guitar Pro8&#xff0c;新增了支持添加音频轨道、支持嵌套连音符、直观的效果器视图、让指法一目了然的音阶示意图等实用新功能。下面我们来看Guitar Pro8 Mac如何安装。 guitar pro是一款专业的吉他学习软件&…

Linux内核(十四)Input 子系统详解 I —— 子系统介绍以及相关结构体解析

文章目录 概述input 子系统框架input 子系统相关结构体介绍input_dev结构体input_handler结构体input_handle结构体Evdev事件相关结构体input_event结构体&#xff08;标准按键编码信息&#xff09;设备相关信息结构体 概述 input子系统就是管理输入的子系统&#xff0c;和Lin…

HTML中的常用标签

HTML中的常用标签 &#x1f50e;注释标签&#x1f50e;标题标签&#x1f50e;段落标签&#x1f50e;换行标签&#x1f50e;格式化标签&#x1f50e;图片标签&#x1f50e;超链接标签&#x1f50e;表格标签合并单元格 &#x1f50e;列表标签无序列表有序列表自定义列表 &#x1…

新手如何快速学会Python?

在本文中&#xff0c;我们将介绍如何有效地学习 Python 。你应该知道「数据科学」是用于解决、探究问题并从数据中提取有价值信息的科学。 为了有效地做到这一点&#xff0c;你需要整理数据集、训练机器学习模型、可视化结果等等。 这是学习 Python 的最佳时机。 事实上&#x…

【五一创作】数据可视化之美 ( 三 ) - 动图展示 ( Python Matlab )

1 Introduction 在我们科研学习、工作生产中&#xff0c;将数据完美展现出来尤为重要。 数据可视化是以数据为视角&#xff0c;探索世界。我们真正想要的是 — 数据视觉&#xff0c;以数据为工具&#xff0c;以可视化为手段&#xff0c;目的是描述真实&#xff0c;探索世界。 …