【opencv】教程代码 —ShapeDescriptors

检测和显示图像的轮廓

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

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

图像轮廓检测和轮廓凸包

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

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

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

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

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

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

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

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

  • 调用thresh_callback函数。

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

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

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

  • 显示窗口:调用imshow函数显示绘制过的轮廓图像。

    最后,代码中还编写了一些错误处理机制,例如,如果找不到图像,则返回-1并退出程序。

cpp 复制代码
/**
 * @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算子的阈值,以达到最佳的轮廓检测效果。

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

这段代码主要用于在图像中寻找轮廓。作者使用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函数在窗口中展示处理结果。

cpp 复制代码
/**
 * @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 获取包含检测到的轮廓的椭圆和旋转的矩形

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

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

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

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

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

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

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

cpp 复制代码
/** 
* @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 图像轮廓检测和轮廓凸包****

cpp 复制代码
/**
 * @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计算图像中的轮廓的矩(包括面积、重心等)并进行显示****

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

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

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

cpp 复制代码
/**
 * @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函数计算的面积。

终端输出:

cpp 复制代码
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 创建和绘制一个多边形图像然后计算并显示图像上每个点到这个多边形的距离

这段代码是一个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,表示窗口将持续显示,直到用户关闭它为止。

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

cpp 复制代码
/**
 * @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库,是一个实际的图像处理过程,具有一定的实用价值,尤其是在图像处理、机器视觉等领域。点到线的距离往往是一个重要的参考因素,可以用于轮廓识别、影像匹配和匹配评分等任务

相关推荐
Redstone Monstrosity2 分钟前
字节二面
前端·面试
东方翱翔10 分钟前
CSS的三种基本选择器
前端·css
FL162386312925 分钟前
AI健身体能测试之基于paddlehub实现引体向上计数个数统计
人工智能
黑客-雨28 分钟前
构建你的AI职业生涯:从基础知识到专业实践的路线图
人工智能·产品经理·ai大模型·ai产品经理·大模型学习·大模型入门·大模型教程
子午30 分钟前
动物识别系统Python+卷积神经网络算法+TensorFlow+人工智能+图像识别+计算机毕业设计项目
人工智能·python·cnn
Fan_web32 分钟前
JavaScript高级——闭包应用-自定义js模块
开发语言·前端·javascript·css·html
yanglamei196240 分钟前
基于GIKT深度知识追踪模型的习题推荐系统源代码+数据库+使用说明,后端采用flask,前端采用vue
前端·数据库·flask
千穹凌帝41 分钟前
SpinalHDL之结构(二)
开发语言·前端·fpga开发
大耳朵爱学习1 小时前
掌握Transformer之注意力为什么有效
人工智能·深度学习·自然语言处理·大模型·llm·transformer·大语言模型
TAICHIFEI1 小时前
目标检测-数据集
人工智能·目标检测·目标跟踪