【opencv】教程代码 —ShapeDescriptors

news2024/11/28 12:44:19

检测和显示图像的轮廓

在图像中搜索并显示轮廓边缘多边形、轮廓矩形和包围圆

获取包含检测到的轮廓的椭圆和旋转的矩形

图像轮廓检测和轮廓凸包

计算图像中的轮廓的矩(包括面积、重心等)并进行显示

创建和绘制一个多边形图像然后计算并显示图像上每个点到这个多边形的距离

1. findContours_demo.cpp 检测和显示图像的轮廓

6efdb6772fa527a33874acbd6e87befa.gif

b1649a014a26d49e2750d16f0fef0e2d.png

这段代码是使用OpenCV库来检测和显示图像的轮廓。这段代码中包含两个主要函数:main函数和thresh_callback函数。

1. main函数用于加载图像,将其转换为灰度图像并对其进行模糊处理,然后在窗口中显示原始图像并创建一个滑块来调整Canny边缘检测的阈值。主要步骤如下:

  • 加载图像:使用OpenCV函数imread加载图像。

  • 转换图像:使用cvtColor函数将图像从BGR色彩空间转换为灰度空间。之后,使用blur函数对图像进行模糊处理以消除噪声。

  • 创建并显示窗口:使用namedWindow和imshow函数创建窗口并显示源图像

  • 创建滑块:使用createTrackbar函数创建一个滑块来调整边缘检测的阈值

  • 调用thresh_callback函数。

2. thresh_callback函数用于边缘检测,找到并绘制轮廓。主要步骤如下:

  • 边缘检测:通过Canny函数检测边缘。

  • 找到轮廓:使用findContours函数找到图像中的轮廓。

  • 绘制轮廓:初始化一个与原图大小、类型相同的全零矩阵。然后在该矩阵上绘制找到的轮廓。每个轮廓有不同的颜色。

  • 显示窗口:调用imshow函数显示绘制过的轮廓图像。
    最后,代码中还编写了一些错误处理机制,例如,如果找不到图像,则返回-1并退出程序。

/**
 * @function findContours_Demo.cpp
 * @brief 这是一个演示如何在图像中找到轮廓的示例代码
 * @author OpenCV团队
 */


#include "opencv2/imgcodecs.hpp" // 引入opencv图像编解码库
#include "opencv2/highgui.hpp" // 引入opencv高级GUI库
#include "opencv2/imgproc.hpp" // 引入opencv图像处理库
#include <iostream> // 引入标准输入输出库


using namespace cv; // 使用cv命名空间,该命名空间包含所有OpenCV函数和类
using namespace std; // 使用std命名空间,该命名空间包含所有标准C++库函数和类


Mat src_gray; // 声明全局变量src_gray,用于存储灰度图像
int thresh = 100; // 声明全局变量thresh,设定轮廓检测的阈值
RNG rng(12345); // 初始化随机数生成器


/// 函数声明
void thresh_callback(int, void* );


/**
 * @function main
 */
int main( int argc, char** argv ) // 主函数
{
    /// 加载源图像
    CommandLineParser parser( argc, argv, "{@input | HappyFish.jpg | input image}" ); // 创建命令行解析器,解析输入的图像文件路径
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) ); // 读取图像文件
    if( src.empty() ) // 如果读取的图像为空
    {
      cout << "Could not open or find the image!\n" << endl; // 输出错误信息
      cout << "Usage: " << argv[0] << " <Input image>" << endl; // 输出正确的使用方法
      return -1;
    }


    /// 将图像转换为灰度图并进行模糊处理
    cvtColor( src, src_gray, COLOR_BGR2GRAY ); // 将图像转换为灰度图
    blur( src_gray, src_gray, Size(3,3) ); // 对图像进行模糊处理,减少噪声


    /// 创建窗口
    const char* source_window = "Source"; // 源图像窗口的名称
    namedWindow( source_window ); // 创建源图像窗口
    imshow( source_window, src ); // 显示源图像


    const int max_thresh = 255; // 设定最大阈值
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback ); // 创建滑动条,调节Canny检测器的阈值
    thresh_callback( 0, 0 ); // 调用thresh_callback函数,进行轮廓检测


    waitKey(); // 等待用户按键,以便可以看到图像
    return 0;
}


/**
 * @function thresh_callback
 * 轮廓检测回调函数
 */
void thresh_callback(int, void* )
{
    /// 使用Canny算子检测边缘
    Mat canny_output; // 声明变量,存储Canny检测结果
    Canny( src_gray, canny_output, thresh, thresh*2 ); // 运用Canny算子进行边缘检测


    /// 查找轮廓
    vector<vector<Point> > contours; // 存储找到的轮廓
    vector<Vec4i> hierarchy; // 存储轮廓的层级信息
    findContours( canny_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE ); // 查找轮廓


    /// 绘制轮廓
    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 ); // 创建空白画布,用来绘制轮廓
    for( size_t i = 0; i< contours.size(); i++ ) // 遍历找到的所有轮廓
    {
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) ); // 生成随机颜色
        drawContours( drawing, contours, (int)i, color, 2, LINE_8, hierarchy, 0 ); // 在drawing上绘制轮廓
    }


    /// 显示在窗口中
    imshow( "Contours", drawing ); // 显示绘制轮廓后的画面
}

这段代码主要功能是载入一个图像进行灰度和模糊处理,然后使用Canny算子进行边缘检测,并找出图像的轮廓,最后将轮廓在窗口中显示出来。图像的路径可以通过命令行参数传入,如果没有参数则默认加载名为"HappyFish.jpg"的图像。在检测轮廓的过程中,可以通过滑动条实时调整Canny算子的阈值,以达到最佳的轮廓检测效果。

83dee4d9b43cfd02735832deac29381a.png

38729636d2592496dbb15969bd6471fb.png

2. generalContours_demo1.cpp在图像中搜索并显示轮廓边缘多边形、轮廓矩形和包围圆

ea4c380adf2f8b538ebbf0884d281ca3.gif

c590768d0e6f1ea49562850db828c411.png

这段代码主要用于在图像中寻找轮廓。作者使用OpenCV库编写了代码。下面是我对您提供的代码片段的详细解析和总结:

整个代码中包含两个主要函数:"main" 和 "thresh_callback"。

  1. "main" 函数:

该函数的主要任务是加载图片,将图片转换为灰度图像,模糊处理,并创建窗口显示源图像。接着创建了一个滑条用于调节阈值。完成以上设置后,程序将调用“thresh_callback”回调函数处理边缘检测和轮廓提取。最后,通过waitKey()函数等待用户按键操作。

关键过程如下:
- 加载图像: 使用imread函数读取输入的图像文件。
- 灰度转换: 调用cvtColor函数将RGB图像转化为灰度图像。
- 模糊处理: 使用blur函数对灰度图像进行模糊处理,以去除图像中的噪声。
- 显示窗口: 使用namedWindow创建一个图像窗口,imshow在该窗口中显示源图像。
- 创建滑条: 使用createTrackbar函数创建了滑动条,可以调节Canny边缘检测阈值。

  1. "thresh_callback" 函数:

这个函数执行的主要任务是使用Canny算法进行边缘检测,接着找到图像的轮廓。最后,通过绘制多边形轮廓,包围矩形和圆,将处理后的图像显示在窗口中。

关键过程如下:
- 边缘检测: 调用Canny函数对图像进行边缘检测,生成边缘图像。
- 轮廓提取: 使用findContours函数在二值化图像canny_output中寻找轮廓。
- 对轮廓进行处理: 利用approxPolyDP将轮廓近似为多边形,使用boundingRect获取轮廓的最小外接矩形,使用minEnclosingCircle获取轮廓的最小外接圆。
- 绘制结果:绘制所有的包围矩形,圆和对应的多边形轮廓到图像上。
- 结果展示: 使用imshow函数在窗口中展示处理结果。

/**
 * @function generalContours_demo1.cpp
 * @brief Demo code to find contours in an image(用于在图片中找到轮廓的演示代码)
 * @author OpenCV team (OpenCV团队)
 */


#include "opencv2/imgcodecs.hpp"  // 引入处理图像编码的头文件
#include "opencv2/highgui.hpp"    // 引入GUI的头文件
#include "opencv2/imgproc.hpp"    // 引入处理图像的头文件
#include <iostream>               // 引入基础输入输出流的头文件


using namespace cv;               // 使用cv命名空间
using namespace std;              // 使用std命名空间


Mat src_gray;                     // 声明灰度图像对象
int thresh = 100;                 // 设置阈值
RNG rng(12345);                   // 初始化随机数生成器


/// Function header
void thresh_callback(int, void* );// 声明阈值处理函数


/**
 * @function main
 */
int main( int argc, char** argv ) // 主函数开始
{
    /// Load source image (加载源图片)
    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() ) // 如果无法加载图片,则输出错误信息并返回-1退出程序
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }


    /// Convert image to gray and blur it (将图片转为灰度并进行模糊处理)
    cvtColor( src, src_gray, COLOR_BGR2GRAY ); // BGR转换成灰度
    blur( src_gray, src_gray, Size(3,3) );  // 对灰度图进行模糊处理


    /// Create Window (创建窗口)
    const char* source_window = "Source";
    namedWindow( source_window );
    imshow( source_window, src );  // 展示原图


    /// Create Trackbar (创建滑动条)
    const int max_thresh = 255;
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
    thresh_callback( 0, 0 );  // 调用阈值处理函数


    waitKey();  // 等待用户操作
    return 0;   // 程序正常退出
}


/**
 * @function thresh_callback
 */
void thresh_callback(int, void* ) // 阈值处理函数定义
{
    /// Detect edges using Canny (使用Canny算法进行边缘检测)
    Mat canny_output; 
    Canny( src_gray, canny_output, thresh, thresh*2 ); 


    /// Find contours (查找轮廓)
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE ); 


    /// Approximate contours to polygons + get bounding rects and circles (将轮廓近似为多边形,并找到它们的边界矩形和圆)
    vector<vector<Point> > contours_poly( contours.size() );   // 用于存储多边形轮廓的向量
    vector<Rect> boundRect( contours.size() );                // 用于存储边界矩形的向量
    vector<Point2f>centers( contours.size() );                // 用于存储圆心的向量
    vector<float>radius( contours.size() );                   // 用于存储半径的向量


    for( size_t i = 0; i < contours.size(); i++ )  // 遍历每一个轮廓
    {
        approxPolyDP( contours[i], contours_poly[i], 3, true );  // 近似多边形
        boundRect[i] = boundingRect( contours_poly[i] );         // 计算边界矩形
        minEnclosingCircle( contours_poly[i], centers[i], radius[i] ); // 计算最小闭合圆
    }


    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );   // 创建一个全零矩阵,用于绘制结果图


    /// Draw polygonal contour + bonding rects + circles (绘制多边形轮廓、边界矩形和圆)
    for( size_t i = 0; i< contours.size(); i++ )
    {
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) ); //生成随机颜色
        drawContours( drawing, contours_poly, (int)i, color );  // 绘制轮廓
        rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2 ); // 绘制矩形
        circle( drawing, centers[i], (int)radius[i], color, 2 ); // 绘制圆
    }


    /// Show in a window (显示结果图)
    imshow( "Contours", drawing );  // 显示轮廓图
}

这段代码是一个在图像中搜索并显示轮廓的OpenCV程序,它首先将给定图像转换为灰度图像,然后使用Canny算法检测其边缘,进而使用函数findContours查找这些边缘。找到的边缘以多边形形式近似,并计算其边界矩形和包围圆。最后,所有的轮廓、边框矩形和包围圆都绘制在一张空白图像上并显示出来。

3. generalContours_demo2.cpp 获取包含检测到的轮廓的椭圆和旋转的矩形

8bdb95084a0191e99d52d84c8e6d5365.gif

dec9b4028e5b2c7e50596db55d82ecc2.png

这段代码是一个OpenCV的演示程序,用于获取包含检测到的轮廓的椭圆和旋转的矩形。主要包含以下函数:

  1. thresh_callback: 该函数用于检测边缘及寻找轮廓,并通过minAreaRectfitEllipse方法获取每个轮廓的旋转矩阵和椭圆,然后绘制轮廓、椭圆和旋转矩形。

  2. main: 该函数用于加载源图像并转换为灰度图,然后进行模糊处理,最后创建一个窗口并在其中显示源图像。还在窗口中创建了一个跟踪条,用于调整Canny算法的阈值参数。

具体来讲,我来详细解释下这两个函数的工作流程与方法:

  • main: 首先加载输入的图像,并将其转换为灰度图像,然后对该灰度图像进行模糊处理。然后创建一个窗口在其中显示转换后的图像,并在这个窗口中创建一个跟踪条,来调整使用Canny方法进行边缘检测的阈值。在跟踪条的回调函数中,调用了thresh_callback方法进行边缘检测和轮廓寻找。

  • thresh_callback: 在这个函数中,首先使用Canny方法进行边缘检测。然后使用findContours方法寻找图像的轮廓。接着使用minAreaRect方法获取每个轮廓的最小面积旋转矩形,如果轮廓的点数大于5,则使用fitEllipse方法获取该轮廓的拟合椭圆。然后创建一个全零的Mat对象,然后在这个Mat对象上绘制轮廓、椭圆及旋转矩形。最后将这个Mat对象在窗口中显示。

该程序主要用于练习边缘检测、寻找轮廓以及如何在寻找到的轮廓上拟合旋转矩形和椭圆。

/** 
* @function generalContours_demo2.cpp 
* @brief   这是一个演示代码,用于获取包含检测到的轮廓的椭圆和旋转矩形 
* @author  OpenCV 团队 
*/ 


#include "opencv2/imgcodecs.hpp"   // 包括OpenCV的读写头文件
#include "opencv2/highgui.hpp"     // 包括OpenCV的GUI头文件
#include "opencv2/imgproc.hpp"     // 包括OpenCV的图像处理头文件
#include <iostream>                // 包括输入输出流头文件


using namespace cv;               // 声明使用OpenCV的命名空间
using namespace std;              // 声明使用标准模板库命名空间


Mat src_gray;                     // 创建一个空的Mat对象,用于存放灰度图像
int thresh = 100;                 // 设定初始阈值为100
RNG rng(12345);                   // 创建一个随机数生成器对象


/// 函数声明
void thresh_callback(int, void* );


/**
 * @function main
 */
int main( int argc, char** argv )
{
    /// 加载源图像并转化为灰度 
    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() )                  // 检查读取的图像是否为空
    {
        cout << "Could not open or find the image!\n" << endl;         // 输出错误信息
        cout << "Usage: " << argv[0] << " <Input image>" << endl;      // 提示正确的使用方式
        return -1;
    }


    /// 将图像转化为灰度图并模糊处理
    cvtColor( src, src_gray, COLOR_BGR2GRAY );       // 将图像从BGR色彩空间转化为灰度空间
    blur( src_gray, src_gray, Size(3,3) );           // 对图像进行模糊(平滑)处理


    /// 创建窗口 
    const char* source_window = "Source";
    namedWindow( source_window );
    imshow( source_window, src );


    const int max_thresh = 255;                      // 设定阈值调节的最大值
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
    thresh_callback( 0, 0 );


    waitKey();                              // 等待键盘操作
    return 0;
}


/**
 * @function thresh_callback
 */
void thresh_callback(int, void* )
{
    /// 用Canny算子进行边缘检测
    Mat canny_output;
    Canny( src_gray, canny_output, thresh, thresh*2 );


    /// 找到轮廓
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );


    /// 计算每个轮廓对应的旋转矩形和椭圆形状
    vector<RotatedRect> minRect( contours.size() );           // 创建一个旋转矩形的向量,数量等于轮廓数量
    vector<RotatedRect> minEllipse( contours.size() );        // 创建一个轮廓对应椭圆的向量,数量等于轮廓数量
    for( size_t i = 0; i < contours.size(); i++ )             // 遍历每一个轮廓
    {
        minRect[i] = minAreaRect( contours[i] );              // 计算每个轮廓的最小区域旋转矩形
        if( contours[i].size() > 5 )                          // 当轮廓包含的点多于5个时,即可以拟合为椭圆
        {
            minEllipse[i] = fitEllipse( contours[i] );         // 计算每个轮廓的拟合椭圆
        }
    }


    /// 画出轮廓 + 旋转矩形 + 椭圆
    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );     // 创建一个空白画布,尺寸与canny_output相同
    for( size_t i = 0; i< contours.size(); i++ )
    {
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );   // 生成一个随机颜色
        // 画出轮廓
        drawContours( drawing, contours, (int)i, color );
        // 画出椭圆
        ellipse( drawing, minEllipse[i], color, 2 );
        // 画出旋转矩形
        Point2f rect_points[4];
        minRect[i].points( rect_points );
        for ( int j = 0; j < 4; j++ )
        {
            line( drawing, rect_points[j], rect_points[(j+1)%4], color );
        }
    }


    /// 在窗口中展示结果 
    imshow( "Contours", drawing );
}

此代码段是一个 OpenCV 的示例,演示了如何查找图像中的所有轮廓并查找每个轮廓的最小区域旋转矩形和拟合椭圆。在迭代访问每个轮廓的过程中,根据轮廓大小检查是否可以拟合椭圆(轮廓中的点数大于5)。最后,它会在新创建的 Mat 对象上绘制轮廓、旋转的矩形和椭圆,然后在窗口中显示这个 Mat 对象。

4. hull_demo.cpp 图像轮廓检测和轮廓凸包

7fb85f235b40cf7e93815b6ba8242432.gif

e1015637517e7bfdcf93ec1d9a5c2f0e.png

/**
 * @function hull_demo.cpp   // 声明hull_demo.cpp这个函数
 * @brief Demo code to find contours in an image    // 对图像进行轮廓检测的演示代码
 * @author OpenCV team   // 作者为OpenCV团队
 */


#include "opencv2/imgcodecs.hpp"  // 导入编解码库
#include "opencv2/highgui.hpp"  // 导入高级GUI库
#include "opencv2/imgproc.hpp"  // 导入图像处理库
#include <iostream>   // 导入输入输出库


using namespace cv;
using namespace std;


Mat src_gray;    // 定义源图像的灰度图
int thresh = 100;    // 定义阈值为100
RNG rng(12345);  // 定义一个随机数


/// Function header
void thresh_callback(int, void* );   // 声明一个阈值回调函数


/**
 * @function main   // 主函数
 */
int main( int argc, char** argv )
{
    /// Load source image and convert it to gray  // 加载源图像并转化为灰度图
    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );
    if( src.empty() )
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }


    /// Convert image to gray and blur it // 将图像转为灰度并进行模糊处理
    cvtColor( src, src_gray, COLOR_BGR2GRAY );
    blur( src_gray, src_gray, Size(3,3) );


    /// Create Window // 创建窗口
    const char* source_window = "Source";
    namedWindow( source_window );
    imshow( source_window, src );


    const int max_thresh = 255; // 定义阈值上限
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback ); // 创建滑动条
    thresh_callback( 0, 0 );


    waitKey(); // 等待用户按键退出
    return 0;
}


/**
 * @function thresh_callback // 阈值回调函数
 */
void thresh_callback(int, void* )
{
    /// Detect edges using Canny  // 使用Canny方法进行边缘检测
    Mat canny_output;
    Canny( src_gray, canny_output, thresh, thresh*2 );


    /// Find contours // 寻找轮廓
    vector<vector<Point> > contours;
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );


    /// Find the convex hull object for each contour // 对每个轮廓寻找凸包物体
    vector<vector<Point> >hull( contours.size() );
    for( size_t i = 0; i < contours.size(); i++ )
    {
        convexHull( contours[i], hull[i] );
    }


    /// Draw contours + hull results // 将轮廓和凸包结果绘制出来
    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 );
    for( size_t i = 0; i< contours.size(); i++ )
    {
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
        drawContours( drawing, contours, (int)i, color );
        drawContours( drawing, hull, (int)i, color );
    }


    /// Show in a window // 在窗口中显示结果
    imshow( "Hull demo", drawing );
}

上述代码是一个使用OpenCV进行图像轮廓检测的示例。主函数中先将图像从彩色图转化为灰度图,并进行了模糊处理,然后通过Canny边缘检测器寻找图像的边缘,定义阈值滑动条来动态改变Canny检测的阈值。接着通过findContours函数找到所有的轮廓,convexHull函数找到每个轮廓的凸包物体,最后将所有的轮廓和凸包物体绘制在一个全黑的图像上,颜色随机生成,然后在窗口中展示出来。

5. moments_demo.cpp计算图像中的轮廓的矩(包括面积、重心等)并进行显示

3faad7270ef112785b695643105a7817.gif

3187088d8e0fddb5a5417e3c3ee80711.png

此源文件是用来演示如何使用OpenCV库进行图像轮廓分析的。其中定义了两个函数:mainthresh_callback

  1. main函数是程序的入口函数。尝试从命令行参数中读取输入的图像文件(如果没有给出图像,则默认读取stuff.jpg),然后将图像转化为灰度图,最后对其进行模糊处理。创建一个窗口显示原始图像,并创建一个滑动条来调整Canny算子阈值。然后调用thresh_callback函数对图像进行处理,并等待用户关闭窗口。

  2. thresh_callback函数负责接受Canny算子阈值,并使用该值对灰度图像进行边缘检测。然后,它找到检测到的边缘的轮廓,并计算出这些轮廓的几何中心。最后,它在一个新的图像上绘制出这些轮廓,并在每个轮廓的几何中心处画一个圆点。画出的图像会在一个新窗口中显示出来。然后,此函数将根据计算出来的几何中心,对于每一个轮廓,打印其面积(即轮廓内部的像素点数)以及轮廓的长度(即轮廓上的像素点数)。

/**
 * @function moments_demo.cpp
 * @brief 演示如何计算图像的矩
 * @author OpenCV团队
 */


#include "opencv2/imgcodecs.hpp"       // 引入OpenCV中用于图像编码解码的函数库
#include "opencv2/highgui.hpp"         // 引入OpenCV中用于创建GUI界面的函数库
#include "opencv2/imgproc.hpp"         // 引入OpenCV的图像处理库
#include <iostream>                    // 引入C++标准库中提供的函数对象来进行输入/输出的库
#include <iomanip>                     // 


using namespace cv;                   // 使用OpenCV中的所有函数和类
using namespace std;                  // 使用C++标准库中的所有函数和类


Mat src_gray;                         // 定义存储灰度图的Mat类型变量
int thresh = 100;                     // 定义阈值
RNG rng(12345);                       // 定义随机数生成器


/// 函数声明
void thresh_callback(int, void* );


/**
 * @function main
 * 主函数
 */
int main( int argc, char** argv )
{
    /// 加载源图片
    CommandLineParser parser( argc, argv, "{@input | stuff.jpg | input image}" );
    // 读取图片
    Mat src = imread( samples::findFile( parser.get<String>( "@input" ) ) );


    if( src.empty() ) // 如果图片为空
    {
        cout << "Could not open or find the image!\n" << endl; // 打印错误信息
        cout << "usage: " << argv[0] << " <Input image>" << endl; // 打印使用说明
        return -1; // 返回-1退出
    }


    /// 将图片转换为灰度图并进行模糊处理
    cvtColor( src, src_gray, COLOR_BGR2GRAY ); // 转为灰度图
    blur( src_gray, src_gray, Size(3,3) );     // 进行模糊处理


    /// 创建窗口
    const char* source_window = "Source";      // 定义窗口名称
    namedWindow( source_window );              // 创建窗口
    imshow( source_window, src );              // 在窗口中显示图像


    const int max_thresh = 255;                // 定义最大阈值
    // 创建阈值滑动条,并绑定thresh_callback函数
    createTrackbar( "Canny thresh:", source_window, &thresh, max_thresh, thresh_callback );
    // 调用thresh_callback函数
    thresh_callback( 0, 0 );


    // 等待用户动作
    waitKey();
    return 0;
}


/**
 * @function thresh_callback
 * 阈值回调函数
 */
void thresh_callback(int, void* )
{
    /// 使用Canny算子检测边缘
    Mat canny_output;                       // 声明一个Mat对象用来存储Canny检测的结果
    Canny( src_gray, canny_output, thresh, thresh*2, 3 ); // 执行Canny边缘检测


    /// 寻找轮廓
    vector<vector<Point> > contours; // 定义容器存储寻找到的轮廓
    // 使用findContours函数在二值图像中寻找轮廓
    findContours( canny_output, contours, RETR_TREE, CHAIN_APPROX_SIMPLE );


    /// 计算矩
    vector<Moments> mu(contours.size() ); // 定义一个容器存储所有轮廓的矩
    for( size_t i = 0; i < contours.size(); i++ ) // 遍历所有的轮廓
    {
        mu[i] = moments( contours[i] );   // 计算每一个轮廓的矩
    }


    /// 获取质心
    vector<Point2f> mc( contours.size() ); // 申明一个容器存储每一个轮廓的质心
    for( size_t i = 0; i < contours.size(); i++ ) // 遍历所有轮廓
    {
        // 计算每个轮廓的质心,避免零除错误
        mc[i] = Point2f( static_cast<float>(mu[i].m10 / (mu[i].m00 + 1e-5)),
                         static_cast<float>(mu[i].m01 / (mu[i].m00 + 1e-5)) );
        cout << "mc[" << i << "]=" << mc[i] << endl; // 输出计算结果
    }


    /// 画出轮廓
    Mat drawing = Mat::zeros( canny_output.size(), CV_8UC3 ); // 创建一个用于绘制轮廓的黑色画布
    for( size_t i = 0; i< contours.size(); i++ ) // 遍历所有轮廓
    {
        // 随机生成颜色值
        Scalar color = Scalar( rng.uniform(0, 256), rng.uniform(0,256), rng.uniform(0,256) );
        drawContours( drawing, contours, (int)i, color, 2 ); // 将轮廓画到画布上
        circle( drawing, mc[i], 4, color, -1 );  // 将质心画到画布上
    }


    /// 在窗口中显示结果
    imshow( "Contours", drawing );


    /// 使用矩的00计算面积,并与OpenCV函数的结果比较
    cout << "\t Info: Area and Contour Length \n";
    for( size_t i = 0; i < contours.size(); i++ ) // 遍历所有轮廓
    {
        cout << " * Contour[" << i << "] - Area (M_00) = " << std::fixed << std::setprecision(2) << mu[i].m00
             << " - Area OpenCV: " << contourArea(contours[i]) << " - Length: " << arcLength( contours[i], true ) << endl;
    }
}

这段代码是使用OpenCV的C++接口进行图像处理的示例程序,它主要的功能是计算图像中的轮廓的矩(包括面积、重心等)并进行显示。具体步骤包括:读入图像、转成灰度图、进行模糊处理、使用Canny算法进行边缘检测、寻找和绘制轮廓、计算轮廓的矩并获取质心、比较矩计算的面积和OpenCV函数计算的面积。

终端输出:

mc[0]=[328.222, 331.944]
mc[1]=[334.476, 323.523]
mc[2]=[213.035, 312.397]
mc[3]=[194.211, 171.352]
mc[4]=[75.8505, 101.182]
mc[5]=[75.9116, 101.259]
mc[6]=[197.533, 57.6664]
mc[7]=[197.166, 64.3331]
mc[8]=[225.274, 57.9121]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 176.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 233.00 - Area OpenCV: 233.00 - Length: 80.91
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63
mc[0]=[328.22, 331.94]
mc[1]=[334.48, 323.52]
mc[2]=[213.04, 312.40]
mc[3]=[194.21, 171.35]
mc[4]=[75.85, 101.18]
mc[5]=[75.91, 101.26]
mc[6]=[197.53, 57.67]
mc[7]=[197.17, 64.33]
mc[8]=[225.27, 57.91]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 176.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 233.00 - Area OpenCV: 233.00 - Length: 80.91
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63
mc[0]=[328.22, 331.94]
mc[1]=[334.48, 323.52]
mc[2]=[213.04, 312.40]
mc[3]=[194.21, 171.35]
mc[4]=[75.85, 101.18]
mc[5]=[75.91, 101.26]
mc[6]=[197.53, 57.67]
mc[7]=[197.17, 64.33]
mc[8]=[225.27, 57.91]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 176.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 233.00 - Area OpenCV: 233.00 - Length: 80.91
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63
mc[0]=[328.22, 331.94]
mc[1]=[334.48, 323.52]
mc[2]=[213.04, 312.40]
mc[3]=[194.21, 171.35]
mc[4]=[75.85, 101.18]
mc[5]=[75.91, 101.26]
mc[6]=[197.53, 57.67]
mc[7]=[197.17, 64.33]
mc[8]=[225.27, 57.91]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 176.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 233.00 - Area OpenCV: 233.00 - Length: 80.91
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63
mc[0]=[328.22, 331.94]
mc[1]=[334.48, 323.52]
mc[2]=[213.04, 312.40]
mc[3]=[194.21, 171.35]
mc[4]=[75.85, 101.18]
mc[5]=[75.91, 101.26]
mc[6]=[197.53, 57.67]
mc[7]=[197.17, 64.33]
mc[8]=[225.27, 57.91]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 176.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 233.00 - Area OpenCV: 233.00 - Length: 80.91
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63
mc[0]=[328.22, 331.94]
mc[1]=[334.48, 323.52]
mc[2]=[213.04, 312.40]
mc[3]=[194.21, 171.35]
mc[4]=[75.88, 101.20]
mc[5]=[75.91, 101.26]
mc[6]=[197.53, 57.67]
mc[7]=[197.17, 64.33]
mc[8]=[225.27, 57.91]
         Info: Area and Contour Length
 * Contour[0] - Area (M_00) = 9.00 - Area OpenCV: 9.00 - Length: 148.08
 * Contour[1] - Area (M_00) = 7.00 - Area OpenCV: 7.00 - Length: 140.77
 * Contour[2] - Area (M_00) = 23.50 - Area OpenCV: 23.50 - Length: 174.75
 * Contour[3] - Area (M_00) = 269.00 - Area OpenCV: 269.00 - Length: 1217.62
 * Contour[4] - Area (M_00) = 232.50 - Area OpenCV: 232.50 - Length: 79.50
 * Contour[5] - Area (M_00) = 207.50 - Area OpenCV: 207.50 - Length: 72.67
 * Contour[6] - Area (M_00) = 2.50 - Area OpenCV: 2.50 - Length: 35.56
 * Contour[7] - Area (M_00) = 3.00 - Area OpenCV: 3.00 - Length: 38.97
 * Contour[8] - Area (M_00) = 64.50 - Area OpenCV: 64.50 - Length: 511.63

6. pointPolygonTest_demo.cpp 创建和绘制一个多边形图像然后计算并显示图像上每个点到这个多边形的距离

c20a5376d0b2c50103c2a58037a81fc2.png

这段代码是一个C++程序,使用了OpenCV库,主要实现了创建和绘制一个多边形图像然后计算并显示图像上每个点到这个多边形的距离

主要步骤如下:

  1. 创建一个4r x 4r的全零矩阵作为图像(r=100,因此图像的大小为400x400;

  2. 定义一个六边形的顶点,并将几何图形画在图像上;

  3. 使用 findContours 方法从图像中提取轮廓;

  4. 计算图像上每个点到轮廓的距离,并存储在一个单独的矩阵中;

  5. 查找离多边形最远和最近的点,获取最大和最小的距离值;

  6. 为距离图像上色,表示点到多边形的距离,其中蓝色表示该点在多边形内部,红色表示在外部,白色表示在边界上;

  7. 在距离最远的点处画一个圆,圆心为此点,半径为最大距离;

  8. 最后展示原始图像和距离图像。

典型的方法和数据结构包括:

  1. Mat:是OpenCV库中的一个主要数据结构,用于存储图像和矩阵数据;

  2. Point:OpenCV中的一个数据结构,储存2D点的坐标(x,y);

  3. findContours:OpenCV的一个函数,用于在二值图像中查找轮廓;

  4. pointPolygonTest:OpenCV中的一个函数,用于计算2D点到一个多边形的最短距离;

  5. imshow:OpenCV的一个函数,用于显示图像;

  6. waitKey:OpenCV的一个函数,用于等待用户按键,如果没有这行代码,imshow显示的图像窗口会立即消失;如果参数为0,表示窗口将持续显示,直到用户关闭它为止。

同时需要注意的是,这段代码只针对单通道黑白图像进行处理。如果要处理多通道图像,还需要进行相应的修改。

/**
 * @function pointPolygonTest_demo.cpp     //定义了程序的入口函数main函数
 * @brief Demo用来距离检测函数示例——相当简单         //描述了这段代码的主要功能
 * @author OpenCV team    //说明这段代码的作者
 */


// 导入opencv的用户选择界面(UI)库和图像处理库
#include "opencv2/highgui.hpp"        
#include "opencv2/imgproc.hpp"


// 导入C++标准输入输出库
#include <iostream>                     


// 名字空间的定义,方便在代码中使用opencv和std库的函数和对象
using namespace cv;                   
using namespace std;                  


/**
 * @function main   //定义了程序的入口函数main函数
 */
int main( void )                     //定义了程序的入口函数main函数
{
    /// 创建一个图像
    // 初始化图像src的大小与类型 
    const int r = 100;                                       //定义了一个常量r,用于确定绘制图形边长的依赖参数
    Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8U );         //使用opencv的Mat类创建一个4r*4r大小的8位无符号整型全黑色(所有像素值都为0)图像,作为绘制六边形的画布


    /// 创建一系列的坐标点来生成一个轮廓
    // 建立一个包含6个Point2f对象的vert数组,用于存储六边形的六个顶点坐标
    vector<Point2f> vert(6);


    // 为数组的每个元素赋值,确定各顶点的坐标
    vert[0] = Point( 3*r/2, static_cast<int>(1.34*r) ); 
    vert[1] = Point( 1*r, 2*r );
    vert[2] = Point( 3*r/2, static_cast<int>(2.866*r) );
    vert[3] = Point( 5*r/2, static_cast<int>(2.866*r) );
    vert[4] = Point( 3*r, 2*r );
    vert[5] = Point( 5*r/2, static_cast<int>(1.34*r) );


    /// 在src上绘制图形
    // 对vert数组中的每个元素都执行循环操作,用line函数在src图像上绘制从第i个顶点到下一个顶点的线段,颜色为白色,线条粗细为3。总的效果就是在src上绘制了一个白色边框的六边形
    for( int i = 0; i < 6; i++ )
    {
        line( src, vert[i],  vert[(i+1)%6], Scalar( 255 ), 3 );
    }


    /// 获取轮廓
    // 建立一个contours二维数组,作为存储src中所有轮廓的容器
    vector<vector<Point> > contours;


    // 使用findContours方法在src图像中寻找轮廓,findContours会先使用阈值将图像二值化,然后寻找二值化图像中的轮廓。经过该操作后,contours中将包含src图像中所有轮廓的顶点坐标的列表
    findContours( src, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);


    /// 计算到轮廓的最短距离
    // 创建raw_dist图像,用于存储src中每个点到最近轮廓的最短距离
    Mat raw_dist( src.size(), CV_32F );


    // 对src中的每个像素都执行循环操作:
    for( int i = 0; i < src.rows; i++ )
    {
        for( int j = 0; j < src.cols; j++ )
        {
            // 使用pointPolygonTest方法计算src中点(j,i)到最近轮廓的最短距离,然后将计算结果存入raw_dist相应位置
            raw_dist.at<float>(i,j) = (float)pointPolygonTest( contours[0], Point2f((float)j, (float)i), true );
        }
    }


    // 创建minVal和maxVal两个变量,作为存储raw_dist中的最小值和最大值的容器
    double minVal, maxVal;


    // 创建maxDistPt对象,作为存储最大距离对应坐标的容器
    Point maxDistPt;


    // minMaxLoc方法会在raw_dist矩阵中寻找最小和最大值,然后分别赋值给minVal和maxVal。同时,minMaxLoc找到的最大值的坐标将赋值给maxDistPt
    minMaxLoc(raw_dist, &minVal, &maxVal, NULL, &maxDistPt);


    // minVal和maxVal都取绝对值
    minVal = abs(minVal);
    maxVal = abs(maxVal);
    
    // 创建drawing空白图像,用于后续的绘制操作
    Mat drawing = Mat::zeros( src.size(), CV_8UC3 );


    // 对于src图像中的每个像素,依次执行以下操作
    for( int i = 0; i < src.rows; i++ )
    {
        for( int j = 0; j < src.cols; j++ )
        {
            // 如果raw_dist中存储的距离值小于零,则在drawing中的对应位置绘制蓝色,蓝色程度按照距离值的绝对值的大小确定
            if( raw_dist.at<float>(i,j) < 0 )
            {
                drawing.at<Vec3b>(i,j)[0] = (uchar)(255 - abs(raw_dist.at<float>(i,j)) * 255 / minVal);
            }
            // 如果raw_dist中存储的距离值大于零,则在drawing中的对应位置绘制红色,红色程度按照距离值的大小确定
            else if( raw_dist.at<float>(i,j) > 0 )
            {
                drawing.at<Vec3b>(i,j)[2] = (uchar)(255 - raw_dist.at<float>(i,j) * 255 / maxVal);
            }
            // 如果raw_dist中存储的距离值等于零,则在drawing中的对应位置绘制白色
            else
            {
                drawing.at<Vec3b>(i,j)[0] = 255;
                drawing.at<Vec3b>(i,j)[1] = 255;
                drawing.at<Vec3b>(i,j)[2] = 255;
            }
        }
    }


    // 绘制一个以最大距离所在的点为圆心,最大距离为半径的白色圆形
    circle(drawing, maxDistPt, (int)maxVal, Scalar(255,255,255));


    /// 旋轉圖像
    imshow( "Source", src );                   //显示原始图像
    imshow( "Distance and inscribed circle", drawing ); //显示带距离颜色及内切圆或者外接圆的图像


    // 一直等待,直到用户按下任意键
    waitKey();
    // 返回程序退出状态值
    return 0;
}

以上提供了代码,主要是在OpenCV中使用pointPolygonTest函数来计算点和多边形之间的最短距离,并且在图中绘制了点和多边形的关系。该代码中使用了C++和OpenCV库,是一个实际的图像处理过程,具有一定的实用价值,尤其是在图像处理、机器视觉等领域。点到线的距离往往是一个重要的参考因素,可以用于轮廓识别、影像匹配和匹配评分等任务

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

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

相关文章

使用Vue3组件的计算属性

计算属性在Vue.js的computed选项中定义&#xff0c;它可以在模板上进行双向数据绑定以展示出结果或者进行其他处理。 通常用户会在模板中定义表达式&#xff0c;非常便利&#xff0c;Vue.js的设计初衷也是用于简单运算。但是在模板中放入太多的逻辑&#xff0c;会让模板变得臃…

项目管理中,项目延期的这些原因,你经历过几个

在项目管理过程中&#xff0c;项目延期是一种常见的现象&#xff0c;深入了解项目延期的原因&#xff0c;并制定相应的应对策略&#xff0c;对于提高项目管理的效率和成功率至关重要。 项目延期的主要原因&#xff1a; 1、估计不足&#xff1a;在项目初期&#xff0c;对项目…

iOS网络抓包工具大曝光:深入了解常用工具的工作原理与应用场景

摘要 本文将深入探讨iOS平台上常用的网络抓包工具&#xff0c;包括Charles、克魔助手、Thor和Http Catcher&#xff0c;以及通过SSH连接进行抓包的方法。此外&#xff0c;还介绍了克魔开发助手作为iOS应用开发的辅助工具&#xff0c;提供的全方面性能监控和调试功能。 在iOS应…

DC-DC芯片D1509, 适用于工控主板、TV板卡、安卓主板、车载功放电源等产品方案应用。

一、应用领域 适用于工控主板、TV板卡、安卓主板、车载功放电源等产品方案应用。 二、功能介绍 D1509是芯谷科技推出的一款输入耐压40V、输出电压1.23-37V可调、输出电流最大2.0A的高效率、高精度DC-DC芯片&#xff0c;其输出电压有固定3.3V、5.0V和12.0V的版本&#xff0c;…

Pygame基础8-碰撞

Collisions 在Pygame中&#xff0c;我们使用矩形来移动物体&#xff0c;并且用矩形检测碰撞。 colliderect检测两个矩形是否碰撞&#xff0c;但是没法确定碰撞的方向。 Rect1.colliderect(Rect2) # collision -> return Ture # else -> return Falsecollidepoint可以…

中视频双去重,一键多平台,可达日入2000+

【核心】将不可复制的飞书文档&#xff0c;用大模型二创&#xff0c;进而生成思维导图&#xff08;视频&#xff09;&#xff0c;以下全过程使用工具半自动完成。 【飞书】https://puojikpj98.feishu.cn/docx/KUNad8Y0UoFHEexMyAfc7sA1nQf 【文案】 中视频双去重与多平台发布项…

“315晚会”中的“网络水军”是什么?

水军一词&#xff0c;源自网络用语&#xff0c;通常指的是一群在网络上被雇佣来进行特定活动的人群。他们的主要任务通常是在各种社交媒体平台、论坛或者评论区发表大量的帖子、评论或者回复&#xff0c;以此来达到某种特定的目的。这些目的可能包括提升某个产品、服务或者个人…

Python(django)之单一接口展示功能前端开发

1、代码 建立apis_manage.html 代码如下&#xff1a; <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>测试平台</title> </head> <body role"document"> <nav c…

OpenTofu路在何方:定量分析Terraform issue数据,洞察用户需求|OpenTofu Day 闪电演讲

数澈软件 Seal 首席架构师李平辉提交的演讲议题“Alias TerraformTofu. Job’s Done, Now What?”入选 KubeCon EU 同场活动 OpenTofu Day&#xff0c;本文为演讲实录。 大家好&#xff0c;我是 Lawrence&#xff0c;是 Seal 的首席架构师。今天将由我为大家带来 Lightening T…

基于springboot实现房产销售系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现房产销售系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于房产销售系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了房产销售系统…

B树、B+树、哈夫曼树

目录 1. B树2. B树3. 哈夫曼树 1. B树 特点&#xff1a;一个节点当中可以有多个值&#xff0c;节点内部key 值是有序的&#xff0c;节点内部存储的是key-value类型的数据 磁盘中文件存储用B树。 4阶B树一个节点最多三个key值 5阶B树一个节点最多四个key值 B树有很多的分支&…

数字化转型如何帮助企业降低经营成本?

数字化转型通过多种方式帮助企业降低经营成本&#xff0c;提高效率和竞争力。以下是一些关键的转型策略和实践&#xff1a; 1.自动化流程&#xff1a;利用自动化工具和软件&#xff0c;如机器人流程自动化&#xff08;RPA&#xff09;和人工智能&#xff08;AI&#xff09;&am…

优化页面加载时间:改善用户体验的关键

✨✨ 祝屏幕前的您天天开心&#xff0c;每天都有好运相伴。我们一起加油&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、为什么页面加载时间重要&#xff1f; 二、如何减少页面加载时间&#xff1f; …

MySQL count函数的使用

count&#xff08;&#xff09;函数在使用时参数好像不能设置为表达式&#xff0c;只能设置成指定字段或* 比如在查询性别为男的成员数目时不能写&#xff1a; select count(gendermale) from user_profile ; 否则直接得到6&#xff0c;也就是等价于select count(gender) fro…

SpringBoot常见注解有哪些

Spring Boot的核心注解是SpringBootApplication , 他由几个注解组成 : ● SpringBootConfiguration&#xff1a; 组合了- Configuration注解&#xff0c;实现配置文件的功能&#xff1b; ● EnableAutoConfiguration&#xff1a;打开自动配置的功能&#xff0c;也可以关闭某个自…

鸿蒙OS开发实例:【Web网页】

背景 HarmonyOS平台通过Web控件可支持网页加载展示&#xff0c;Web在中是作为专项参考的。 本篇文章将从Android和iOS平台研发角度出发来实践学习API功能 说明 整个示例是以HarmonyOS开发文档网址作为加载目标页面布局增加了三个按钮“后退”&#xff0c;“前进”&#xff…

StableDiffusion Web UI开启FP8,极大节约显存

升级了Pytorch后&#xff0c;StableDiffusion最新版本就可以有使用FP8的基础了&#xff0c;因此把秋叶的LINUX包也升级到了最新的版本。 升级Pytorch参考我的升级记录&#xff1a; ComfyUI SDWebUI升级pytorch随记-CSDN博客 然后下一步就是如何开启FP8了。与ComfyUI不同&…

内容营销新手指南:Kompas.ai的智能起步策略

内容营销是当今企业获取客户、建立品牌认知和忠诚度的关键手段。然而&#xff0c;对于刚刚踏入这一领域的新手来说&#xff0c;内容营销的世界可能会显得既广阔又复杂。从内容创作到发布&#xff0c;再到分析和优化&#xff0c;每一步都充满了挑战。本文旨在为内容营销新手提供…

PMP怎么免试申请CSPM啊?

CSPM证书现在还在红利期&#xff0c;有 PMP 证书的不用参加考试就能申请增持 CSPM-2 证书&#xff0c;有 PgMP 证书可以免考增持 CSPM-3 证书&#xff0c;有 PMP 跟 PgMP 证书的不要错过哦~ 如果没有PMP证书跟 PgMP 证书的话&#xff0c;就只能参加考试了。 一、PMP证书免考增持…

Java | Leetcode Java题解之第3题无重复字符的最长子串

题目&#xff1a; 题解&#xff1a; class Solution {public int lengthOfLongestSubstring(String s) {// 哈希集合&#xff0c;记录每个字符是否出现过Set<Character> occ new HashSet<Character>();int n s.length();// 右指针&#xff0c;初始值为 -1&#…