【opencv】教程代码 —ShapeDescriptors

检测和显示图像的轮廓

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

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

图像轮廓检测和轮廓凸包

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

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

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找到的最大值的坐标将赋值给maxDistPtminMaxLoc(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.mzph.cn/news/784937.shtml

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

相关文章

分类预测 | Matlab实现CNN-GRU-Mutilhead-Attention卷积神经网络-门控循环单元融合多头注意力机制多特征分类预测

分类预测 | Matlab实现CNN-GRU-Mutilhead-Attention卷积神经网络-门控循环单元融合多头注意力机制多特征分类预测 目录 分类预测 | Matlab实现CNN-GRU-Mutilhead-Attention卷积神经网络-门控循环单元融合多头注意力机制多特征分类预测分类效果基本介绍模型描述程序设计参考资料…

代码膨胀会破坏开发操作吗,它将如何影响编译时间?

Incredibuild 正潜心研究公司以及客户的未来发展趋势。 过去的一年举步维艰&#xff0c;但聪明的企业总是能够抓住机遇&#xff0c;将大部分业务自动化&#xff0c;保持敏捷度和竞争力&#xff0c;最佳的案例就是游戏工作室。这一年来&#xff0c;用户对新游戏或新版本的期待达…

困难样本挖掘:Hard Sample Mining

Hard Sample Mining Hard Sample Mining&#xff0c;即困难样本挖掘&#xff0c;是目标检测中的一种常用方法。其主要思想是针对训练过程中损失较高的样本&#xff08;即那些难以被正确分类的样本&#xff09;进行挖掘&#xff0c;并将其补充到数据集中重新训练&#xff0c;以…

【React】vite + react 项目,配置项目路径别名 @

vite react 项目&#xff0c;配置项目路径别名 1 安装 types/node2 在 vite.config.ts 中添加配置&#xff1a;3 配置路径别名的提示 使用 vite 开发 react 项目时&#xff0c;可以通过一下步骤配置路径别名&#xff1a; 1 安装 types/node npm i -D types/node2 在 vite.con…

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】

LeetCode-560. 和为 K 的子数组【数组 哈希表 前缀和】 题目描述&#xff1a;解题思路一&#xff1a;一边算前缀和一边统计。这里用哈希表统计前缀和出现的次数&#xff0c;那么和为k的子数组的个数就是当前前缀和-k的个数&#xff0c;即preSums[presum - k]。画个图表述就是&a…

无尘布擦拭过程中的问题及其解决方案

无尘布&#xff0c;作为一种广泛应用于电子、半导体、生物医药等领域的清洁材料&#xff0c;其质量和使用方式直接关系到产品生产的洁净度和质量。然而&#xff0c;在无尘布的擦拭过程中&#xff0c;常常会遇到一些问题&#xff0c;如接触不当的溶剂、胶水选择不当、产品收缩以…

内存管理--柔性数组

本次讲的是&#xff0c;柔性数组&#xff0c;如果哪位小博客想要了解的更多&#xff0c;可以登录下面这个网站&#xff0c;了解详细内容 C语言结构体里的成员数组和指针 | 酷 壳 - CoolShellhttps://coolshell.cn/articles/11377.html 我们就听说过数组&#xff0c;听说过柔性数…

游戏本笔记本更换@添加内存条实操示例@DDR5内存条

文章目录 添加内存条的意义准备工具设备拔出电源适配器并关机&#x1f47a;样机 内存条上的金手指安装过程Notes 安装后开机初次开机速度屏幕显示分辨率和闪烁问题检查安装后的效果 添加内存条的意义 参考双通道内存DDR5多通道内存-CSDN博客 准备工具 准备一个质量差不多的螺…

向量点乘有哪些作用呢

如下&#xff1a; 1.找到两个向量之间的夹角(不用多说) 2.求一个向量投影在另一个向量的投影&#xff1a; 我们把图中b的在a上的投影向量称作b1吧&#xff0c;因为b1就在a上&#xff0c;所以只需要求出b1的大小&#xff0c;然后乘以a的单位向量&#xff0c;我们就得到向量b1了…

Altair® Embed® 嵌入式系统的可视化环境

Altair Embed 嵌入式系统的可视化环境 Altair Embed 是一款成熟的工具&#xff0c;可从框图模型自动生成代码并将其传输到通用控制器硬件来开发嵌入式系统。 基于系统关系图创建工作仿真后&#xff0c;用户即可对硬件控制器自动生成相关代码&#xff0c;并通过硬件在环 (HIL)…

【BlossomConfig】SpringCloud项目是如何对bootstrap配置文件进行加载的?

文章目录 bootstrap配置文件的读取 网关项目源码 RPC项目源码 配置中心项目源码 bootstrap配置文件的读取 我们首先来了解一下springboot是如何做配置管理的。 了解了springboot对配置文件的管理&#xff0c;我们就能知道为什么springcloud类型的项目要使用bootstrap配置文件了…

美国RAKsmart:裸机云站群服务器配置详解

裸机云&#xff0c;也称为物理云&#xff0c;是一种云服务模式&#xff0c;它为用户提供了接近物理机性能的云服务器。而站群服务器&#xff0c;则是为了支持多个网站或应用程序的运行而设计的服务器。美国RAKsmart作为一家知名的云服务提供商&#xff0c;其裸机云站群服务器配…

Proteus 12V to 5V buck电路仿真练习及遇到的一些问题汇总

基础电路仿真实验记录贴&#xff01;&#xff01;&#xff01;如有写的不对的地方欢迎交流指正&#xff01;&#xff01;&#xff01; 平台&#xff1a;PC win10 软件&#xff1a;Proteus8.10 仿真目标&#xff1a;buck降压电路&#xff08;PWM控制输出电压&#xff09; 写在…

八皇后问题解决过程字符串可视化

查找到问题&#xff0c;暂停600毫秒&#xff0c; 穷举本行&#xff0c;200毫秒 返回上一层之前&#xff0c;会弹出回滚上一层&#xff08;4&#xff0c;X&#xff09;&#xff0c;并暂停600毫秒 成功返回时&#xff0c;会弹出上一层具体数据如&#xff08;4&#xff0c;3&a…

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&#…

分治——归并排序算法

例题一 解法&#xff08;归并排序&#xff09;&#xff1a; 算法思路&#xff1a; 归并排序的流程充分的体现了「分⽽治之」的思想&#xff0c;⼤体过程分为两步&#xff1a; ◦ 分&#xff1a;将数组⼀分为⼆为两部分&#xff0c;⼀直分解到数组的⻓度为 1 &#xff0c;使…

【MySQL】DQL-分组查询-语法&where与having的区别&注意事项&可cv例题语句

前言 大家好吖&#xff0c;欢迎来到 YY 滴MySQL系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

【Web应用技术基础】CSS(6)——使用 HTML/CSS 实现 Educoder 顶部导航栏

第一题&#xff1a;使用flex布局实现Educoder顶部导航栏容器布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Educoder</title><script src"https://cdn.staticfile.org/jquery/1.1…

EDM营销文案的撰写技巧?如何提升转化率?

EDM营销文案与SEO策略如何结合&#xff1f;怎么写有效营销邮件&#xff1f; EDM仍然是一种高效且经济的推广手段。撰写优质的EDM营销文案&#xff0c;不仅可以提升品牌形象&#xff0c;还能有效促进销售转化。那么&#xff0c;如何撰写出引人入胜的EDM营销文案呢&#xff1f;A…

KNN算法 | K近邻:KD Tree、球树、KNN数据下采样策略

目录 一. KNN算法实现方式1. 蛮力实现(brute)2. KD树(kd_tree)3. 球树(ball_tree) 二. KD Tree算法1. 构建方式2. KD Tree 查找最近邻 三. 球树(Ball Tree)1. 构建方式 四. KNN评价1. 优点2. 缺点 五. 延申1. KNN数据下采样策略策略1策略2策略3策略4 Condensed Nearest Neighbo…