【问题标题】:drawing convex hull of the biggest contour using OpenCV C++使用 OpenCV C++ 绘制最大轮廓的凸包
【发布时间】:2016-04-19 18:43:11
【问题描述】:
int main()
{

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
int largest_area=0;
int largest_contour_index=0;


OriginalImage = imread("C:\\Data Drive\\opencv Projects\\testwithC++\\Plant001-9\\SideView90\\Day_021.png",CV_LOAD_IMAGE_GRAYSCALE);
BackgroundImage = imread("C:\\Data Drive\\opencv Projects\\testwithC++\\Plant001-9\\SideView90\\Day_001.png",CV_LOAD_IMAGE_GRAYSCALE);

absdiff(OriginalImage,BackgroundImage,GrayImage);
threshold(GrayImage,Binary,80,255,CV_THRESH_BINARY);


namedWindow( "OriginalImage", WINDOW_NORMAL);             
imshow("OriginalImage", OriginalImage);                   


namedWindow( "BackgroundImage", WINDOW_NORMAL);             
imshow("BackgroundImage", BackgroundImage);                 

namedWindow( "GrayImage", WINDOW_NORMAL);               
imshow("GrayImage", GrayImage);                         


namedWindow( "Binary", WINDOW_NORMAL);              
imshow("Binary", Binary);                 

ImageROI = Binary(Rect(300,0,Binary.size().width-600,Binary.size().height));

namedWindow( "ImageROI", WINDOW_NORMAL);                
imshow("ImageROI", ImageROI);                           

dilate(ImageROI,BinaryMorph,Mat(),Point(-1,-1),2);

namedWindow( "BinaryMorph", WINDOW_NORMAL);             
imshow("BinaryMorph", BinaryMorph);                 

findContours(BinaryMorph, contours, hierarchy, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

for( int i = 0; i< contours.size(); i++ )
 {
    double a=contourArea(contours[i],false);
    if(a>largest_area)
    {
    largest_area=a;
    largest_contour_index=i; 
    }
 }
Contour = Mat(ImageROI.size().width,ImageROI.size().height,CV_8UC1,Scalar::all(0));


drawContours(Contour, contours,largest_contour_index,Scalar(255),CV_FILLED, 8,hierarchy);

vector<Point>hull;

convexHull(contours[largest_contour_index],hull,CV_CLOCKWISE,true);
drawContours(Contour, Mat(hull),largest_contour_index,Scalar(255),3, 8);

namedWindow( "Contour", WINDOW_NORMAL);             
imshow("Contour", Contour);                       

OriginalImage.release();
BackgroundImage.release();
GrayImage.release();
Binary.release();
BinaryMorph.release();
ImageROI.release();
Contour.release();

waitKey(0);                                              
return 0;}

我已经编写了上面的代码,使用 Microsoft Visual Studio 2010 Express 使用 OpenCV 2.4.9 绘制最大轮廓的凸包。代码符合并执行没有任何错误,成功绘制了最大的轮廓,但不能显示轮廓。

请注意,到目前为止我使用的是 C api,现在正在尝试转换为 C++。所以,我是使用 C++ 的 openCV 的新手。非常感谢您提出让程序工作以绘制凸包的建议。

【问题讨论】:

    标签: c++ opencv


    【解决方案1】:

    这里的主要问题是您调用drawContours 来错误地绘制凸包。

    drawContours 接受InputArrayOfArrays 作为输入点,即二维结构,而hull 只是一维的。

    您可以轻松解决此问题,动态创建一个二维向量,只有一个元素(hull),作为索引0 传递,即您刚刚创建的二维结构的第一个元素:

    vector<Point>hull;
    convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);
    drawContours(Contour, vector<vector<Point>> {hull}, 0, Scalar(128), 3, 8);
    

    或者,如果 C++11 不可用:

    vector<Point>hull;
    convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);
    vector<vector<Point>> tmp;
    tmp.push_back(hull);
    drawContours(Contour, tmp, 0, Scalar(128), 3, 8);
    

    另外,由于您来自 C 背景,因此有一些提示:

    • 在需要变量之前声明变量,而不是在函数的开头。
    • 您无需手动释放Mats,因为当它们超出范围时,它们的析构函数会自动释放它们。

    还有:

    • 如果您不需要调整窗口大小,则无需调用namedWindow,因为imshow 只会为您创建。 (在下面的代码中,我放弃了对 namedWindow 的所有调用,但我无法再调整它们的大小了)
    • 由于您已经知道矩阵的类型 (CV_8UC1),因此您可以使用 Mat_&lt;Tp&gt; 特化,即 Mat1b(又名 Mat_&lt;uchar&gt;)。这将产生更少冗长的代码,并且还允许您访问像 mat(i,j) 这样的元素,而不是 mat.at&lt;uchar&gt;(i,j)(这里不需要,但只是一般建议)。
    • dilate 带有空内核是没用的。请定义一个合适的内核。
    • 这里不需要hierarchy,所以不要使用它。
    • 使用更新的常量名称:IMREAD_GRAYSCALE 代替 CV_LOAD_IMAGE_GRAYSCALECHAIN_APPROX_SIMPLE 代替 CV_CHAIN_APPROX_SIMPLE 等...
    • 最好不要在代码中添加魔法值(例如 ROI 值 300、600)。
    • 如果使用默认值,则无需指定参数。
    • 大写字母的名称通常用于类和结构名称,而不是变量(这有点主观,但我个人认为它使代码更易于阅读)。
    • 如果没有检测到任何轮廓,请跳过计算。
    • 如果您使用的是 VisualStudio,您可以使用 Image Watch,而不是使用所有这些 imshow 进行调试。
    • 评论代码!

    这里是应用了这些提示的工作代码:

    #include <opencv2/opencv.hpp>
    #include <vector>
    #include <string>
    
    using namespace std;
    using namespace cv;
    
    int main()
    {
        // Load images
        Mat1b originalImage = imread("path_to_original_image", IMREAD_GRAYSCALE);
        Mat1b backgroundImage = imread("path_to_bkg_image", IMREAD_GRAYSCALE);
    
        // Create binary mask
        Mat1b grayImage;
        absdiff(originalImage, backgroundImage, grayImage);
        Mat1b binary;
        threshold(grayImage, binary, 80, 255, THRESH_BINARY);
    
        imshow("OriginalImage", originalImage);
        imshow("BackgroundImage", backgroundImage);
        imshow("GrayImage", grayImage);
        imshow("Binary", binary);
    
        // Take a ROI
        Rect roi(binary.cols / 3, 0, (binary.cols * 2) / 3, binary.rows);
        Mat1b imageROI = binary(roi);
    
        imshow("ImageROI", imageROI);
    
        // Apply morphological dilate, 2 times
        Mat1b binaryMorph;
        Mat1b kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
        dilate(imageROI, binaryMorph, kernel, Point(-1, -1), 2);
    
        imshow("BinaryMorph", binaryMorph);
    
        // Find blob contours
        vector<vector<Point>> contours;
        double largest_area = 0.0;
        int largest_contour_index = 0;
        findContours(binaryMorph.clone(), contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, roi.tl());
    
        if (!contours.empty())
        {
            // Find largest contour
            for (size_t i = 0; i < contours.size(); i++)
            {
                double a = contourArea(contours[i], false);
                if (a > largest_area)
                {
                    largest_area = a;
                    largest_contour_index = i;
                }
            }
    
            // Draw largest contors
            Mat3b contour(binary.rows, binary.cols, Vec3b(0, 0, 0));
            drawContours(contour, contours, largest_contour_index, Scalar(255, 255, 255), CV_FILLED);
    
            // Find convex hull of largest contour
            vector<Point>hull;
            convexHull(contours[largest_contour_index], hull, CV_CLOCKWISE, true);
    
            // Draw the convex hull
            vector<vector<Point>> tmp;
            tmp.push_back(hull);
            drawContours(contour, tmp, 0, Scalar(0, 0, 255), 3);
    
            imshow("Contour", contour);
        }
    
        waitKey(0);
        return 0;
    }
    

    【讨论】:

    • 非常感谢您提供的所有有用提示!感谢您花时间修改我的代码。但是,问题没有解决,因为,我在以下行中遇到编译错误: drawContours(contour, vector> {hull} , 0....... 它带有红色下划线并说,此处不允许输入名称。请帮助我。
    • 您可能没有启用 C++11。只需:vector&lt;vector&lt;Point&gt;&gt; tmp; tmp.push_back(hull); drawContours(contour, tmp, 0, Scalar(128), 3);
    • 您好,我进行了建议的更改。编译错误消失了,但是没有像以前那样显示凸包。另外,我想使用白色和红色凸包显示轮廓,而背景是黑色。请帮我。我被困在这里,因为没有显示凸包。
    • 我发现了一个错字,它应该可以正常工作并显示一个红色的外壳。请注意,我还更改了 ROI,因此您可能希望使用预定义的值
    • 非常感谢!!凸包显示完美。现在,我征求你的意见:有什么办法可以在imshow中使用WINDOW_NORMAL的目的?植物(物体)的图像在窗外,所以为了可视化它,我需要使用 namedWindow("contour", WINOW_NORMAL),我想根据你的建议退出。
    猜你喜欢
    • 2015-02-25
    • 2021-05-06
    • 1970-01-01
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 2016-10-21
    • 1970-01-01
    相关资源
    最近更新 更多