特征检测与特征匹配方法笔记+代码分享

        在一幅图像中,总能发现其独特的像素点,这些点可以被视为该图像的特征,我们称之为特征点。在计算机视觉领域中,基于特征点的图像特征匹配是一项至关重要的任务,因此,如何定义并识别一幅图像中的特征点显得尤为重要。本文旨在总结计算机视觉领域中最常用的几种特征点及其特征匹配方法。

        在计算机视觉领域,兴趣点(亦称关键点或特征点)的概念已被广泛采纳,应用于目标识别、图像配准、视觉跟踪、三维重建等多个方面。其原理在于,通过对图像中的某些特征点进行局部分析,而非全面审视整幅图像,只要图像中存在足够多可检测且互不相同的稳定兴趣点,并能被精确定位,上述方法便能发挥显著效果。

        以下用于实验的两幅图像分别是:一幅手机抓拍的风景照,以及一幅遥感图像。

 

 1、SURF

        视觉不变性在特征检测中扮演着举足轻重的角色,其中尺度不变性问题的解决尤为棘手。为了攻克这一难题,计算机视觉领域引入了尺度不变特征的概念。其核心思想在于,无论物体在何种尺度下被拍摄,都能检测到一致的关键点,并且每个检测到的特征点都配备了一个尺度因子。在理想状况下,对于两幅图像中不同尺度的同一物体点,其计算所得的尺度因子之比应等于图像尺度的比率。

        近年来,多种尺度不变特征被相继提出,本节将介绍其中的一种:SURF特征。SURF,全称“加速稳健特征”(Speeded Up Robust Feature),它不仅具备尺度不变性,还拥有较高的计算效率。

        接下来,我们将进行常规的特征提取和特征点匹配,以观察其实际效果。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
    Mat image01 = imread("2.jpg", 1);    //右图
    Mat image02 = imread("1.jpg", 1);    //左图
    namedWindow("p2", 0);
    namedWindow("p1", 0);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    SurfFeatureDetector surfDetector(800);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    vector<KeyPoint> keyPoint1, keyPoint2;
    surfDetector.detect(image1, keyPoint1);
    surfDetector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SurfDescriptorExtractor SurfDescriptor;
    Mat imageDesc1, imageDesc2;
    SurfDescriptor.compute(image1, keyPoint1, imageDesc1);
    SurfDescriptor.compute(image2, keyPoint2, imageDesc2);
 
    //获得匹配特征点,并提取最优配对     
    FlannBasedMatcher matcher;
    vector<DMatch> matchePoints;
 
    matcher.match(imageDesc1, imageDesc2, matchePoints, Mat());
    cout << "total match points: " << matchePoints.size() << endl;
 
 
    Mat img_match;
    drawMatches(image01, keyPoint1, image02, keyPoint2, matchePoints, img_match);
    namedWindow("match", 0);
    imshow("match",img_match);
    imwrite("match.jpg", img_match);
 
    waitKey();
    return 0;
}

        从上述特征点匹配的效果来看,匹配结果并不理想。如果我们基于这样的匹配结果去实现图像拼接或物体追踪,效果肯定会大打折扣。因此,我们需要进一步筛选匹配点,以获取更优质的匹配点,这一过程被称为“去粗取精”。在这里,我们采用了Lowe’s算法来进一步优化匹配点的选择。

        为了剔除因图像遮挡和背景杂乱而产生的无匹配关系的关键点,SIFT算法的提出者Lowe提出了一种基于最近邻距离与次近邻距离比较的SIFT匹配方法。具体而言,对于一幅图像中的一个SIFT关键点,我们在另一幅图像中找出与其欧式距离最近的前两个关键点。如果这两个关键点中,最近距离与次近距离的比率(即ratio)小于某个阈值T,则接受这一对匹配点。由于特征空间的高维性,错误匹配的特征点之间往往存在大量相似的距离,因此其ratio值通常较高。通过降低比例阈值T,虽然SIFT匹配点的数量会减少,但匹配结果会更加稳定;反之,则可能增加错误匹配的数量。

        Lowe推荐的ratio阈值为0.8,但经过对大量存在尺度、旋转和亮度变化的图像进行匹配实验,我们发现ratio的取值在0.4~0.6之间时效果最佳。当ratio小于0.4时,匹配点数量稀少;而当ratio大于0.6时,则可能出现大量错误匹配点。因此,我们建议根据实际需求选择合适的ratio值:

  • 当对匹配准确度要求较高时,ratio可取0.4;
  • 当需要较多的匹配点时,ratio可取0.6;
  • 在一般情况下,ratio可取0.5作为折中选择。
#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
 
    Mat image01 = imread("g2.jpg", 1);    
    Mat image02 = imread("g4.jpg", 1);    
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    SurfFeatureDetector surfDetector(2000);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    vector<KeyPoint> keyPoint1, keyPoint2;
    surfDetector.detect(image1, keyPoint1);
    surfDetector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SurfDescriptorExtractor SurfDescriptor;
    Mat imageDesc1, imageDesc2;
    SurfDescriptor.compute(image1, keyPoint1, imageDesc1);
    SurfDescriptor.compute(image2, keyPoint2, imageDesc2);
 
    FlannBasedMatcher matcher;
    vector<vector<DMatch> > matchePoints;
    vector<DMatch> GoodMatchePoints;
 
    vector<Mat> train_desc(1, imageDesc1);
    matcher.add(train_desc);
    matcher.train();
 
    matcher.knnMatch(imageDesc2, matchePoints, 2);
    cout << "total match points: " << matchePoints.size() << endl;
 
    // Lowe's algorithm,获取优秀匹配点
    for (int i = 0; i < matchePoints.size(); i++)
    {
        if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)
        {
            GoodMatchePoints.push_back(matchePoints[i][0]);
        }
    }
    Mat first_match;
    drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
    imshow("first_match ", first_match);
    waitKey();
    return 0;
}

2、SIFT算法

        SURF算法作为SIFT算法的加速版本,在特征点检测速度上有了显著提升,因此在实时视频流物体匹配等应用中具有显著优势。相比之下,SIFT(尺度不变特征转换,Scale-Invariant Feature Transform)算法虽然特征计算量大,导致特征点提取过程耗时较长,在一些追求速度的场合难以应用,但其基于浮点内核计算特征点的特性,使得SIFT算法在空间和尺度上的定位通常更为精确。因此,在匹配精度要求极高且对匹配速度无严格要求的场合,SIFT算法仍是一个值得考虑的选择。

        值得一提的是,SIFT特征检测的代码实现与SURF算法的代码实现非常相似,仅需要对SURF代码进行微小的修改即可适用于SIFT特征检测。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
    Mat image01 = imread("1.jpg", 1);    //右图
    Mat image02 = imread("2.jpg", 1);    //左图
    namedWindow("p2", 0);
    namedWindow("p1", 0);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    SiftFeatureDetector siftDetector(2000);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    vector<KeyPoint> keyPoint1, keyPoint2;
    siftDetector.detect(image1, keyPoint1);
    siftDetector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SiftDescriptorExtractor SiftDescriptor;
    Mat imageDesc1, imageDesc2;
    SiftDescriptor.compute(image1, keyPoint1, imageDesc1);
    SiftDescriptor.compute(image2, keyPoint2, imageDesc2);
 
    //获得匹配特征点,并提取最优配对     
    FlannBasedMatcher matcher;
    vector<DMatch> matchePoints;
 
    matcher.match(imageDesc1, imageDesc2, matchePoints, Mat());
    cout << "total match points: " << matchePoints.size() << endl;
 
 
    Mat img_match;
    drawMatches(image01, keyPoint1, image02, keyPoint2, matchePoints, img_match);
 
    imshow("match",img_match);
    imwrite("match.jpg", img_match);
 
    waitKey();
    return 0;
}

没有经过点筛选的匹配效果同样糟糕。下面继续采用Lowe‘s的算法选出优秀匹配点。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
 
    Mat image01 = imread("1.jpg", 1);
    Mat image02 = imread("2.jpg", 1);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    SiftFeatureDetector siftDetector(800);  // 海塞矩阵阈值,在这里调整精度,值越大点越少,越精准 
    vector<KeyPoint> keyPoint1, keyPoint2;
    siftDetector.detect(image1, keyPoint1);
    siftDetector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SiftDescriptorExtractor SiftDescriptor;
    Mat imageDesc1, imageDesc2;
    SiftDescriptor.compute(image1, keyPoint1, imageDesc1);
    SiftDescriptor.compute(image2, keyPoint2, imageDesc2);
 
    FlannBasedMatcher matcher;
    vector<vector<DMatch> > matchePoints;
    vector<DMatch> GoodMatchePoints;
 
    vector<Mat> train_desc(1, imageDesc1);
    matcher.add(train_desc);
    matcher.train();
 
    matcher.knnMatch(imageDesc2, matchePoints, 2);
    cout << "total match points: " << matchePoints.size() << endl;
 
    // Lowe's algorithm,获取优秀匹配点
    for (int i = 0; i < matchePoints.size(); i++)
    {
        if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)
        {
            GoodMatchePoints.push_back(matchePoints[i][0]);
        }
    }
    Mat first_match;
    drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
    imshow("first_match ", first_match);
    imwrite("first_match.jpg", first_match);
    waitKey();
    return 0;
}

3、ORB算法

        ORB,全称Oriented BRIEF,是BRIEF算法的改进版本。与SIFT算法相比,ORB算法的运行速度快了100倍;与SURF算法相比,则快了10倍。在计算机视觉领域,ORB算法因其在各种测评中展现出的卓越综合性能,而被广泛认为是一种顶尖的特征提取算法。

        既然ORB算法是对BRIEF算法的改进,我们首先要了解BRIEF算法的不足之处。BRIEF算法的优势在于其极高的运行速度,但其缺点也较为明显:不具备旋转不变性,对噪声敏感,以及缺乏尺度不变性。针对其中的前两个缺点,ORB算法提出了一种全新的解决方案。然而,值得注意的是,ORB算法并未解决尺度不变性的问题。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
 
    Mat image01 = imread("g2.jpg", 1);
    Mat image02 = imread("g4.jpg", 1);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    OrbFeatureDetector OrbDetector(1000);  // 在这里调整精度,值越小点越少,越精准 
    vector<KeyPoint> keyPoint1, keyPoint2;
    OrbDetector.detect(image1, keyPoint1);
    OrbDetector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    OrbDescriptorExtractor OrbDescriptor;
    Mat imageDesc1, imageDesc2;
    OrbDescriptor.compute(image1, keyPoint1, imageDesc1);
    OrbDescriptor.compute(image2, keyPoint2, imageDesc2);
 
    flann::Index flannIndex(imageDesc1, flann::LshIndexParams(12, 20, 2), cvflann::FLANN_DIST_HAMMING);
 
    vector<DMatch> GoodMatchePoints;
 
    Mat macthIndex(imageDesc2.rows, 2, CV_32SC1), matchDistance(imageDesc2.rows, 2, CV_32FC1);
    flannIndex.knnSearch(imageDesc2, macthIndex, matchDistance, 2, flann::SearchParams());
 
    // Lowe's algorithm,获取优秀匹配点
    for (int i = 0; i < matchDistance.rows; i++)
    {
        if (matchDistance.at<float>(i,0) < 0.6 * matchDistance.at<float>(i, 1))
        {
            DMatch dmatches(i, macthIndex.at<int>(i, 0), matchDistance.at<float>(i, 0));
            GoodMatchePoints.push_back(dmatches);
        }
    }
    Mat first_match;
    drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
    imshow("first_match ", first_match);
    imwrite("first_match.jpg", first_match);
    waitKey();
    return 0;
}

4、FAST特征检测算子

       FAST,全称Features from Accelerated Segment Test,是一种专门用于快速检测兴趣点的算子。其工作原理极为简洁,仅通过对比少数几个像素的强度值,即可迅速判断一个点是否为关键点。

        与Harris检测器类似,FAST算法同样基于对角点的定义进行工作。它通过对候选特征点周围的图像强度值进行分析,来判断该点是否为关键点。具体而言,以某个点为中心作一个圆,然后检查圆上像素的强度值。如果存在一段连续长度超过圆周长3/4的圆弧,且该圆弧上所有像素的强度值都与圆心的强度值存在显著差异(要么全部更暗,要么全部更亮),则认定该点为关键点。

        由于FAST算法检测兴趣点的速度极快,因此非常适合那些对速度有严格要求的应用场景,如实时视觉跟踪和目标识别等。在这些应用中,需要在实时视频流中快速跟踪或匹配多个点,FAST算法的高效性显得尤为重要。

        在OpenCV中,我们可以使用FastFeatureDetector进行特征点提取。然而,由于OpenCV并未提供针对FAST的专用描述子提取器,因此我们可以借助SiftDescriptorExtractor来实现描述子的提取。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
 
    Mat image01 = imread("1.jpg", 1);
    Mat image02 = imread("2.jpg", 1);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    FastFeatureDetector Detector(50);  //阈值 
    vector<KeyPoint> keyPoint1, keyPoint2;
    Detector.detect(image1, keyPoint1);
    Detector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SiftDescriptorExtractor   Descriptor;
    Mat imageDesc1, imageDesc2;
    Descriptor.compute(image1, keyPoint1, imageDesc1);
    Descriptor.compute(image2, keyPoint2, imageDesc2);
 
    BruteForceMatcher< L2<float> > matcher;   
    vector<vector<DMatch> > matchePoints;
    vector<DMatch> GoodMatchePoints;
 
    vector<Mat> train_desc(1, imageDesc1);
    matcher.add(train_desc);
    matcher.train();
 
    matcher.knnMatch(imageDesc2, matchePoints, 2);
    cout << "total match points: " << matchePoints.size() << endl;
 
    // Lowe's algorithm,获取优秀匹配点
    for (int i = 0; i < matchePoints.size(); i++)
    {
        if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)
        {
            GoodMatchePoints.push_back(matchePoints[i][0]);
        }
    }
    Mat first_match;
    drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
    imshow("first_match ", first_match);
    imwrite("first_match.jpg", first_match);
    waitKey();
    return 0;
}

如果我们把描述子换成SurfDescriptorExtractor,即FastFeatureDetector + SurfDescriptorExtractor的组合,看看效果

可以看出,这种组合下的特征点匹配并不精确。

如果我们把描述子换成SurfDescriptorExtractor,即FastFeatureDetector + BriefDescriptorExtractor 的组合,看看效果

        尽管FAST特征点检测速度飞快,但其精度却略显不足。这可能会引发一些疑问:既然FAST特征点检测速度这么快,为什么我们还需要借助SiftDescriptorExtractor或其他描述子提取器来提取特征呢?

        对此,我有以下理解:特征点匹配的首要步骤是识别并提取每幅图像的特征点,这被称为特征检测。例如,我们使用FastFeatureDetector或SiftFeatureDetector等工具来完成这一任务。然而,仅仅识别特征点并不足以实现精确匹配,我们还需要对这些特征点进行更深入的分析,通过数学特征(如梯度直方图、局部随机二值特征等)进行描述。在这一步骤中,我们可以选择使用不同的描述子提取器来对特征点进行描述,从而更精确地实现特征点的匹配。

        在OpenCV库中,SURF、ORB和SIFT算法不仅包含特征检测器(FeatureDetector),还配备了描述子提取器(DescriptorExtractor)。因此,在使用这些算法进行特征匹配时,我们通常会直接使用它们自带的配套方法。

        那么,如果我们希望使用FAST角点检测来进行特征点匹配,应该怎么办呢?此时,我们可以采用FastFeatureDetector与BriefDescriptorExtractor的组合方式,这种组合实际上就是著名的ORB算法。因此,特征点检测和特征点匹配是两个独立的步骤,我们可以根据项目需求自由组合这两个步骤的方法。

5、Harris角点检测

        在图像中搜寻有价值的特征点时,角点无疑是一种高效且实用的方法。角点作为图像中易于定位的局部特征,广泛存在于人造物体中,如墙壁、门、窗户、桌子等形成的角落。这些角点之所以具有价值,是因为它们作为两条边缘线的交汇点,不仅是一种显著的二维特征,而且能够被精确地定位,甚至达到子像素级别的精度。

        相比之下,位于图像均匀区域或物体轮廓上的点,以及在同一物体的不同图像上难以重复精确定位的点,其应用价值就相对较低。为了有效地检测这些角点,Harris特征检测算法应运而生,它成为了检测角点的经典方法之一。

        在这里,我们将展示如何利用GoodFeaturesToTrackDetector(一个用于检测图像中可跟踪特征点的OpenCV工具)与SiftDescriptorExtractor(一个用于提取SIFT描述子的OpenCV工具)的组合方式来进行角点检测和描述子提取。当然,除了这种组合方式,还存在其他多种可能的组合,但在此我们不再一一展示。

#include "highgui/highgui.hpp"    
#include "opencv2/nonfree/nonfree.hpp"    
#include "opencv2/legacy/legacy.hpp"   
#include <iostream>  
 
using namespace cv;
using namespace std;
 
 
int main()
{
 
    Mat image01 = imread("1.jpg", 1);
    Mat image02 = imread("2.jpg", 1);
    imshow("p2", image01);
    imshow("p1", image02);
 
    //灰度图转换  
    Mat image1, image2;
    cvtColor(image01, image1, CV_RGB2GRAY);
    cvtColor(image02, image2, CV_RGB2GRAY);
 
 
    //提取特征点    
    GoodFeaturesToTrackDetector Detector(500);  //最大点数,值越大,点越多
    vector<KeyPoint> keyPoint1, keyPoint2;
    Detector.detect(image1, keyPoint1);
    Detector.detect(image2, keyPoint2);
 
    //特征点描述,为下边的特征点匹配做准备    
    SiftDescriptorExtractor  Descriptor;
    Mat imageDesc1, imageDesc2;
    Descriptor.compute(image1, keyPoint1, imageDesc1);
    Descriptor.compute(image2, keyPoint2, imageDesc2);
 
    BruteForceMatcher< L2<float> > matcher;   
    vector<vector<DMatch> > matchePoints;
    vector<DMatch> GoodMatchePoints;
 
    vector<Mat> train_desc(1, imageDesc1);
    matcher.add(train_desc);
    matcher.train();
 
    matcher.knnMatch(imageDesc2, matchePoints, 2);
    cout << "total match points: " << matchePoints.size() << endl;
 
    // Lowe's algorithm,获取优秀匹配点
    for (int i = 0; i < matchePoints.size(); i++)
    {
        if (matchePoints[i][0].distance < 0.6 * matchePoints[i][1].distance)
        {
            GoodMatchePoints.push_back(matchePoints[i][0]);
        }
    }
    Mat first_match;
    drawMatches(image02, keyPoint2, image01, keyPoint1, GoodMatchePoints, first_match);
    imshow("first_match ", first_match);
    imwrite("first_match.jpg", first_match);
    waitKey();
    return 0;
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【陕西】《陕西省省级政务信息化项目投资编制指南(建设类)(试行)》-省市费用标准解读系列07

《陕西省省级政务信息化项目投资编制指南&#xff08;建设类&#xff09;&#xff08;试行&#xff09;》规定了建设类项目的费用投资测算方法与计价标准&#xff0c;明确指出建设类项目费用包括项目建设费和项目建设其他费&#xff08;了解更多可直接关注咨询我们&#xff09;…

无人机干扰与抗干扰,无人机与反制设备的矛与盾

无人机干扰与抗干扰&#xff0c;以及无人机与反制设备之间的关系&#xff0c;可以形象地比喻为矛与盾的较量。以下是对这两方面的详细探讨&#xff1a; 一、无人机干扰与抗干扰 1. 无人机干扰技术 无人机干扰技术是指通过各种手段对无人机系统进行干扰&#xff0c;使其失去正…

Github配置ssh key原理及操作步骤

文章目录 配置SSH第一步&#xff1a;检查本地主机是否已经存在ssh key第二步&#xff1a;生成ssh key第三步&#xff1a;获取ssh key公钥内容第四步&#xff1a;Github账号上添加公钥第五步&#xff1a;验证是否设置成功验证原理 往github上push项目的时候&#xff0c;如果走ht…

MySQL日期类型选择建议

我们平时开发中不可避免的就是要存储时间&#xff0c;比如我们要记录操作表中这条记录的时间、记录转账的交易时间、记录出发时间、用户下单时间等等。你会发现时间这个东西与我们开发的联系还是非常紧密的&#xff0c;用的好与不好会给我们的业务甚至功能带来很大的影响。所以…

【Ant.designpro】上传图片

文章目录 一、前端二、后端 一、前端 fieldProps:可以监听并且获取到组件输入的内容 action{“/api/upload_image”} 直接调用后端接口 <ProFormUploadButtonlabel{"上传手续图片"}name{"imgs"}action{"/api/upload_image"}max{5} fieldPro…

vue3 基于element-plus进行的一个可拖动改变导航与内容区域大小的简单方法

1、先上个截图&#xff1a; 说明&#xff1a;拖动上面的分隔栏就可以实现&#xff0c;改变左右区域的大小。 2、上面的例子来自官网的&#xff1a; Container 布局容器 | Element Plus 3、拖动的效果来自&#xff1a; https://juejin.cn/post/7029640316999172104#heading-1…

Android Studio加载旧的安卓工程项目报错处理

文章目录 Invalid Gradle JDK configuration foundNDK not configuredCMake 3.10.2 was not found安装cmake适配cmake版本号 com.intellij.openapi.externalSystem.model.ExternalSystemExceptiongradle版本过低或下载不了下载gradle与依赖库超时替换gradle国内源替换Maven 仓库…

Qt中的Model与View 4:QStandardItemModel与QTableView

目录 QStandardItemModel API QTableView 导航 视觉外观 坐标系统 API 样例&#xff1a;解析一个表格txt文件 QStandardItemModel QStandardItemModel 可用作标准 Qt 数据类型的存储库。它是模型/视图类之一&#xff0c;是 Qt 模型/视图框架的一部分。它提供了一种基于…

web——sqliabs靶场——第一关

今天开始搞这个靶场&#xff0c;从小白开始一点点学习,加油&#xff01;&#xff01;&#xff01;&#xff01; 1.搭建靶场 注意点&#xff1a;1.php的版本问题&#xff0c;要用老版本 2.小p要先改数据库的密码&#xff0c;否则一直显示链接不上数据库 2.第一道题&#xff0…

Markdown快速上手(typora)

一级标题~六级标题 可以选中文本在这里直接设置&#xff0c;后面也有快捷键&#xff0c;也可以使用其语法&#xff0c;一个#&#xff0c;对应一级标题&#xff0c;两个#&#xff0c;对应二级标题&#xff0c;等。 我这里使用Ctrl1没生效是因为快捷键冲突&#xff0c;也需要注意…

一招解决Mac没有剪切板历史记录的问题

使用Mac的朋友肯定都为Mac的剪切功能苦恼过&#xff0c;旧内容覆盖新内容&#xff0c;导致如果有内容需要重复输入的话&#xff0c;就需要一次一次的重复复制粘贴&#xff0c;非常麻烦 但其实Mac也能够有剪切板历史记录功能&#xff0c;iCopy&#xff0c;让你的Mac也能拥有剪切…

在鱼皮的模拟面试里面学习有感

文章目录 1.上半场1.1.引言1.2.鱼皮的建议 2.下半场2.1中间问题 3.我的总结3.1我的体会3.2我的计划 1.上半场 今天的直播&#xff0c;第一次全程的跟下来&#xff1a;也算是放松一下~~ 1.1.引言 上半场是后来总结的&#xff0c;听的时候没有随手记录&#xff1a; 1&#xf…

windows在两台机器上测试 MySQL 集群实现实时备份

在两台机器上测试 MySQL 集群实现实时备份的基本步骤&#xff1a; 一、环境准备 机器配置 确保两台机器&#xff08;假设为服务器 A 和服务器 B&#xff09;能够互相通信&#xff0c;例如它们在同一个局域网内&#xff0c;并且开放了 MySQL 通信所需的端口&#xff08;默认是 …

MQTT从入门到精通之MQTT入门

MQTT入门 1 MQTT概述 1.1 MQTT简介 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;由IBM于1999年开发的一种基于**"发布订阅模式"的轻量级的消息传输协议**&#xff01; 发布订阅模式是一种传统的客户端-服务器架构的替代方案&#xff0c;因为…

python在word中插入图片

本文讲解python如何在word文档中插入图片&#xff0c;以及指定插入图片的段落。 1、在新建的word文档中插入图片 import win32com.client as win32 from win32com.client import constants # 1&#xff09;打开word应用程序 doc_app win32.gencache.EnsureDispatch(Word.App…

Linux(CentOS)安装 Nginx

CentOS版本&#xff1a;CentOS 7 Nginx版本&#xff1a;1.24.0 有两种安装方式 一、通过编译源码包安装 编译源码包&#xff1a;.tar.gz或.tar包文件 1、下载 Nginx 打开Nginx官网&#xff1a;https://nginx.org/ 2、上传 Nginx 文件到 CentOS 使用FinalShell远程登录工…

TESSY学习笔记—project view界面的架构

1&#xff1a;project view界面能添加的元素 project view界面能添加的元素&#xff08;暂且称为元素&#xff09;&#xff0c;打开project view界面&#xff0c;下图中红框勾选出来的就是 2&#xff1a;一共存在5种可添加元素 **1&#xff09;Test collection 测试集合&…

vue中如何关闭eslint检测?

ESLint作为一个用于JavaScript代码的验证工具&#xff0c;主要用于检查代码语法和编码规范。本文旨在指导那些希望在Vue.js项目中禁用ESLint验证功能的用户。对于需要这一操作的朋友&#xff0c;以下内容将提供参考。 vue中如何关闭eslint检测&#xff1f; 有了eslint的校验&…

uniApp之uni-file-picker使用踩坑

标题党~也不算坑吧 就是初体验 上传是需要存储一下子的&#xff0c;我以为uniApp是自己免费开的服务给大家中转使用&#xff0c;就没管这个事&#xff0c;但是官网是这么说的&#xff1a; 就我是怎么发现的&#xff0c;使用了一段时间后&#xff0c;上传的图片都裂了&#xff…

K8s小白入门

文章目录 前言一、特性二、集群的结构控制平面内部组件Node内部组件 三、Docker与K8s的关系总结 前言 Kubernetes&#xff08;K8s&#xff09;是一个开源的容器编排平台&#xff0c;用于自动化容器化应用的部署、扩展和管理。是支持云原生部署的一个平台&#xff0c;它前生是谷…