关于opencv的contourArea计算方法

这篇具有很好参考价值的文章主要介绍了关于opencv的contourArea计算方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

cv::contourArea计算的轮廓面积并不等于轮廓点计数,原因是cv::contourArea是基于Green公式计算
关于opencv的contourArea计算方法,opencv

老外的讨论 github

举一个直观的例子,图中有7个像素,橙色为轮廓点连线,按照contourArea的定义,轮廓的面积为橙色所包围的区域=3

关于opencv的contourArea计算方法,opencv

以下代码基于opencv4.8.0

cv::contourArea源码位于.\sources\modules\imgproc\src\shapedescr.cpp

// area of a whole sequence
double cv::contourArea( InputArray _contour, bool oriented )
{
    CV_INSTRUMENT_REGION();

    Mat contour = _contour.getMat();
    int npoints = contour.checkVector(2);
    int depth = contour.depth();
    CV_Assert(npoints >= 0 && (depth == CV_32F || depth == CV_32S));

    if( npoints == 0 )
        return 0.;

    double a00 = 0;
    bool is_float = depth == CV_32F;
    const Point* ptsi = contour.ptr<Point>();
    const Point2f* ptsf = contour.ptr<Point2f>();
    Point2f prev = is_float ? ptsf[npoints-1] : Point2f((float)ptsi[npoints-1].x, (float)ptsi[npoints-1].y);

    for( int i = 0; i < npoints; i++ )
    {
        Point2f p = is_float ? ptsf[i] : Point2f((float)ptsi[i].x, (float)ptsi[i].y);
        a00 += (double)prev.x * p.y - (double)prev.y * p.x;
        prev = p;
    }

    a00 *= 0.5;
    if( !oriented )
        a00 = fabs(a00);

    return a00;
}

如果计算面积需要考虑轮廓点本身,可以通过cv::drawContoursI填充轮廓获得mask图像后统计非零点个数

cv::drawContours源码位于.\sources\modules\imgproc\src\drawing.cpp,主要包括两步:收集边缘cv::CollectPolyEdges和填充边缘cv::FillEdgeCollection,How does the drawContours function work in OpenCV when a contour is filled?

struct PolyEdge
{
    PolyEdge() : y0(0), y1(0), x(0), dx(0), next(0) {}
    //PolyEdge(int _y0, int _y1, int _x, int _dx) : y0(_y0), y1(_y1), x(_x), dx(_dx) {}

    int y0, y1;
    int64 x, dx;
    PolyEdge *next;
};

static void
CollectPolyEdges( Mat& img, const Point2l* v, int count, std::vector<PolyEdge>& edges,
                  const void* color, int line_type, int shift, Point offset )
{
    int i, delta = offset.y + ((1 << shift) >> 1);
    Point2l pt0 = v[count-1], pt1;
    pt0.x = (pt0.x + offset.x) << (XY_SHIFT - shift);
    pt0.y = (pt0.y + delta) >> shift;

    edges.reserve( edges.size() + count );

    for( i = 0; i < count; i++, pt0 = pt1 )
    {
        Point2l t0, t1;
        PolyEdge edge;

        pt1 = v[i];
        pt1.x = (pt1.x + offset.x) << (XY_SHIFT - shift);
        pt1.y = (pt1.y + delta) >> shift;

        Point2l pt0c(pt0), pt1c(pt1);

        if (line_type < cv::LINE_AA)
        {
            t0.y = pt0.y; t1.y = pt1.y;
            t0.x = (pt0.x + (XY_ONE >> 1)) >> XY_SHIFT;
            t1.x = (pt1.x + (XY_ONE >> 1)) >> XY_SHIFT;
            Line(img, t0, t1, color, line_type);

            // use clipped endpoints to create a more accurate PolyEdge
            if ((unsigned)t0.x >= (unsigned)(img.cols) ||
                (unsigned)t1.x >= (unsigned)(img.cols) ||
                (unsigned)t0.y >= (unsigned)(img.rows) ||
                (unsigned)t1.y >= (unsigned)(img.rows))
            {
                clipLine(img.size(), t0, t1);

                if (t0.y != t1.y)
                {
                    pt0c.y = t0.y; pt1c.y = t1.y;
                    pt0c.x = (int64)(t0.x) << XY_SHIFT;
                    pt1c.x = (int64)(t1.x) << XY_SHIFT;
                }
            }
            else
            {
                pt0c.x += XY_ONE >> 1;
                pt1c.x += XY_ONE >> 1;
            }
        }
        else
        {
            t0.x = pt0.x; t1.x = pt1.x;
            t0.y = pt0.y << XY_SHIFT;
            t1.y = pt1.y << XY_SHIFT;
            LineAA(img, t0, t1, color);
        }

        if (pt0.y == pt1.y)
            continue;

        edge.dx = (pt1c.x - pt0c.x) / (pt1c.y - pt0c.y);
        if (pt0.y < pt1.y)
        {
            edge.y0 = (int)(pt0.y);
            edge.y1 = (int)(pt1.y);
            edge.x = pt0c.x + (pt0.y - pt0c.y) * edge.dx; // correct starting point for clipped lines
        }
        else
        {
            edge.y0 = (int)(pt1.y);
            edge.y1 = (int)(pt0.y);
            edge.x = pt1c.x + (pt1.y - pt1c.y) * edge.dx; // correct starting point for clipped lines
        }
        edges.push_back(edge);
    }
}

struct CmpEdges
{
    bool operator ()(const PolyEdge& e1, const PolyEdge& e2)
    {
        return e1.y0 - e2.y0 ? e1.y0 < e2.y0 :
            e1.x - e2.x ? e1.x < e2.x : e1.dx < e2.dx;
    }
};

static void
FillEdgeCollection( Mat& img, std::vector<PolyEdge>& edges, const void* color, int line_type)
{
    PolyEdge tmp;
    int i, y, total = (int)edges.size();
    Size size = img.size();
    PolyEdge* e;
    int y_max = INT_MIN, y_min = INT_MAX;
    int64 x_max = 0xFFFFFFFFFFFFFFFF, x_min = 0x7FFFFFFFFFFFFFFF;
    int pix_size = (int)img.elemSize();
    int delta;

    if (line_type < CV_AA)
        delta = 0;
    else
        delta = XY_ONE - 1;

    if( total < 2 )
        return;

    for( i = 0; i < total; i++ )
    {
        PolyEdge& e1 = edges[i];
        CV_Assert( e1.y0 < e1.y1 );
        // Determine x-coordinate of the end of the edge.
        // (This is not necessary x-coordinate of any vertex in the array.)
        int64 x1 = e1.x + (e1.y1 - e1.y0) * e1.dx;
        y_min = std::min( y_min, e1.y0 );
        y_max = std::max( y_max, e1.y1 );
        x_min = std::min( x_min, e1.x );
        x_max = std::max( x_max, e1.x );
        x_min = std::min( x_min, x1 );
        x_max = std::max( x_max, x1 );
    }

    if( y_max < 0 || y_min >= size.height || x_max < 0 || x_min >= ((int64)size.width<<XY_SHIFT) )
        return;

    std::sort( edges.begin(), edges.end(), CmpEdges() );

    // start drawing
    tmp.y0 = INT_MAX;
    edges.push_back(tmp); // after this point we do not add
                          // any elements to edges, thus we can use pointers
    i = 0;
    tmp.next = 0;
    e = &edges[i];
    y_max = MIN( y_max, size.height );

    for( y = e->y0; y < y_max; y++ )
    {
        PolyEdge *last, *prelast, *keep_prelast;
        int draw = 0;
        int clipline = y < 0;

        prelast = &tmp;
        last = tmp.next;
        while( last || e->y0 == y )
        {
            if( last && last->y1 == y )
            {
                // exclude edge if y reaches its lower point
                prelast->next = last->next;
                last = last->next;
                continue;
            }
            keep_prelast = prelast;
            if( last && (e->y0 > y || last->x < e->x) )
            {
                // go to the next edge in active list
                prelast = last;
                last = last->next;
            }
            else if( i < total )
            {
                // insert new edge into active list if y reaches its upper point
                prelast->next = e;
                e->next = last;
                prelast = e;
                e = &edges[++i];
            }
            else
                break;

            if( draw )
            {
                if( !clipline )
                {
                    // convert x's from fixed-point to image coordinates
                    uchar *timg = img.ptr(y);
                    int x1, x2;

                    if (keep_prelast->x > prelast->x)
                    {
                        x1 = (int)((prelast->x + delta) >> XY_SHIFT);
                        x2 = (int)(keep_prelast->x >> XY_SHIFT);
                    }
                    else
                    {
                        x1 = (int)((keep_prelast->x + delta) >> XY_SHIFT);
                        x2 = (int)(prelast->x >> XY_SHIFT);
                    }

                    // clip and draw the line
                    if( x1 < size.width && x2 >= 0 )
                    {
                        if( x1 < 0 )
                            x1 = 0;
                        if( x2 >= size.width )
                            x2 = size.width - 1;
                        ICV_HLINE( timg, x1, x2, color, pix_size );
                    }
                }
                keep_prelast->x += keep_prelast->dx;
                prelast->x += prelast->dx;
            }
            draw ^= 1;
        }

        // sort edges (using bubble sort)
        keep_prelast = 0;

        do
        {
            prelast = &tmp;
            last = tmp.next;
            PolyEdge *last_exchange = 0;

            while( last != keep_prelast && last->next != 0 )
            {
                PolyEdge *te = last->next;

                // swap edges
                if( last->x > te->x )
                {
                    prelast->next = te;
                    last->next = te->next;
                    te->next = last;
                    prelast = te;
                    last_exchange = prelast;
                }
                else
                {
                    prelast = last;
                    last = te;
                }
            }
            if (last_exchange == NULL)
                break;
            keep_prelast = last_exchange;
        } while( keep_prelast != tmp.next && keep_prelast != &tmp );
    }
}

其中填充算法为scan-line polygon filling algorithm,下面转载该文

Polygon Filling? How do you do that?

In order to fill a polygon, we do not want to have to determine the type of polygon that we are filling. The easiest way to avoid this situation is to use an algorithm that works for all three types of polygons. Since both convex and concave polygons are subsets of the complex type, using an algorithm that will work for complex polygon filling should be sufficient for all three types. The scan-line polygon fill algorithm, which employs the odd/even parity concept previously discussed, works for complex polygon filling.

Reminder: The basic concept of the scan-line algorithm is to draw points from edges of odd parity to even parity on each scan-line.
What is a scan-line? A scan-line is a line of constant y value, i.e., y=c, where c lies within our drawing region, e.g., the window on our computer screen.

The scan-line algorithm is outlined next.

When filling a polygon, you will most likely just have a set of vertices, indicating the x and y Cartesian coordinates of each vertex of the polygon. The following steps should be taken to turn your set of vertices into a filled polygon.

1.Initializing All of the Edges:
The first thing that needs to be done is determine how the polygon’s vertices are related. The all_edges table will hold this information.
Each adjacent set of vertices (the first and second, second and third, …, last and first) defines an edge. For each edge, the following information needs to be kept in a table:
缩进 1.The minimum y value of the two vertices.
缩进 2.The maximum y value of the two vertices.
缩进 3.The x value associated with the minimum y value.
缩进 4.The slope of the edge.
The slope of the edge can be calculated from the formula for a line:
y = mx + b;
where m = slope, b = y-intercept,
y0 = maximum y value,
y1 = minimum y value,
x0 = maximum x value,
x1 = minimum x value

The formula for the slope is as follows:
m = (y0 - y1) / (x0 - x1).

For example, the edge values may be kept as follows, where N is equal to the total number of edges - 1 and each index into the all_edges array contains a pointer to the array of edge values.
关于opencv的contourArea计算方法,opencv
2.Initializing the Global Edge Table:
The global edge table will be used to keep track of the edges that are still needed to complete the polygon. Since we will fill the edges from bottom to top and left to right. To do this, the global edge table table should be inserted with edges grouped by increasing minimum y values. Edges with the same minimum y values are sorted on minimum x values as follows:
缩进 1.Place the first edge with a slope that is not equal to zero in the global edge table.
缩进 2.If the slope of the edge is zero, do not add that edge to the global edge table.
缩进 3.For every other edge, start at index 0 and increase the index to the global edge table once each time the current edge’s y value is greater than that of the edge at the current index in the global edge table.

Next, Increase the index to the global edge table once each time the current edge’s x value is greater than and the y value is less than or equal to that of the edge at the current index in the global edge table.

If the index, at any time, is equal to the number of edges currently in the global edge table, do not increase the index.

Place the edge information for minimum y value, maximum y value, x value, and 1/m in the global edge table at the index.

The global edge table should now contain all of the edge information necessary to fill the polygon in order of increasing minimum y and x values.

3.Initializing Parity
The initial parity is even since no edges have been crossed yet.

4.Initializing the Scan-Line
The initial scan-line is equal to the lowest y value for all of the global edges. Since the global edge table is sorted, the scan-line is the minimum y value of the first entry in this table.

5.Initializing the Active Edge Table
The active edge table will be used to keep track of the edges that are intersected by the current scan-line. This should also contain ordered edges. This is initially set up as follows:

Since the global edge table is ordered on minimum y and x values, search, in order, through the global edge table and, for each edge found having a minimum y value equal to the current scan-line, append the edge information for the maximum y value, x value, and 1/m to the active edge table. Do this until an edge is found with a minimum y value greater than the scan line value. The active edge table will now contain ordered edges of those edges that are being filled as such:
关于opencv的contourArea计算方法,opencv
6.Filling the Polygon
Filling the polygon involves deciding whether or not to draw pixels, adding to and removing edges from the active edge table, and updating x values for the next scan-line.

Starting with the initial scan-line, until the active edge table is empty, do the following:
缩进 1.Draw all pixels from the x value of odd to the x value of even parity edge pairs.
缩进 2.Increase the scan-line by 1.
缩进 3.Remove any edges from the active edge table for which the maximum y value is equal to the scan_line.
缩进 4.Update the x value for for each edge in the active edge table using the formula x1 = x0 + 1/m. (This is based on the line formula and the fact that the next scan-line equals the old scan-line plus one.)
缩进 5.Remove any edges from the global edge table for which the minimum y value is equal to the scan-line and place them in the active edge table.
缩进 6.Reorder the edges in the active edge table according to increasing x value. This is done in case edges have crossed.文章来源地址https://www.toymoban.com/news/detail-721715.html

到了这里,关于关于opencv的contourArea计算方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • openCV实战-系列教程4:图像梯度计算(Sobel算子/开运算/梯度计算方法/scharr算子/lapkacian算子)、源码解读 ?????OpenCV实战系列总目录

    打印一个图片单独做出一个函数: 先读进来一个原型白色图 打印结果: 如图有两个3*3的卷积核,其中A是原始图片中的一个3*3的区域,这个A和3*3的卷积核所谓对应位置相乘的结果就分别是左右梯度和上下梯度 假如A是这个矩阵:  那么Gx的计算结果就为:-x1+x3-2x4+2x6-x7+x9 代码实

    2024年02月10日
    浏览(32)
  • C++计算机视觉库OpenCV在Visual Studio 2022的配置方法

      本文介绍在 Visual Studio 2022 中配置、编译 C++ 计算机视觉库 OpenCV 的方法。   首先,我们进行 OpenCV 库的下载与安装。作为一个开源的库,我们直接在其官方下载网站(https://opencv.org/releases/)中进行下载即可;如下图所示,我们首先选择需要下载的操作系统。   随后

    2024年02月16日
    浏览(46)
  • 关于opencv无损保存图片的说明

    结论: 选择无损压缩格式(如.png)就能无损保存图片,可选压缩程度 选择有损压缩格式(如.jpg)只能有损保存图片,可选损失程度 opencv使用函数 cv2.imwrite() 用于将图像保存到指定的文件 函数说明: cv2.imwrite() 将 OpenCV 图像保存到指定的文件。 cv2.imwrite() 基于保存文件的扩

    2024年02月06日
    浏览(34)
  • 关于OpenCV中minAreaRect角度记录

    最近看到stackflow关于minAreaRect的讨论: MinAreaRect angles - Unsure about the angle returned OpenCV’s RotatedRect angle does not provide enough information 大概问题是minAreaRect这个接口返回的角度信息不足以反映返回的旋转矩形的旋转信息,例如返回角度为30度,那它到底是下面图中的哪一个呢? 那有

    2024年02月14日
    浏览(26)
  • 关于如何用Cmake配置opencv C++环境

    基于 opencv3.4.6 在vs 2017 中的实现。 首先百度搜搜并安装opencv3.4.6 下载链接: https://nchc.dl.sourceforge.net/project/opencvlibrary/3.4.6/opencv-3.4.6-vc14_vc15.exe https://nchc.dl.sourceforge.net/project/opencvlibrary/3.4.6/opencv-3.4.6-vc14_vc15.exe 在安装Cmake 官网连接: CMake https://cmake.org/ 接下来我们就可以开始配

    2024年02月09日
    浏览(32)
  • 关于uniapp中使用opencv.js拍照提取纸张轮廓

    1.效果图片 2.下载opencv.js   比如下载 4.5.0 版本的 opencv.js 文件 https://docs.opencv.org/4.5.0/opencv.js 3.引入 opencv.js放在static文件夹下 页面中引入 let cv = require(\\\'../../static/opencv/opencv.js\\\'); 4.进入正题    //页面先放一个隐藏图片     img id=\\\"imageUrl\\\" alt=\\\"No Image\\\" style=\\\"display: none;\\\" /    //获取

    2024年02月04日
    浏览(26)
  • 【OpenCV】关于OpenCV中imread/imwrite/imshow/cvtColor等 Api的简单描述和使用

    01、imread 官方定义 imread函数是OpenCV中的一个函数,用于从文件中读取图像。该函数支持读取多种图像格式,包括BMP、JPEG、PNG、TIF等常见格式。 tips: 注意这里imread函数加载的是8bit 图像 示例 02、imwrite 官方定义 imwrite函数是OpenCV中的一个函数,用于将图像写入文件中。该函数支

    2024年02月11日
    浏览(26)
  • 关于pycharm中OpenCV没有代码提示解决(一次就解决)

    hello,小伙伴们,大家好。最近我也是在学习python的OpenCV库,最主要呢是完成学校布置的创新创业项目。我就最近根据我安装的OpenCV库和OpenCV没有代码提示,遇到的问题,将它们在这里分享给大家,希望对大家有帮助! 首先,我是用的pycharm,我们打开pycharm终端下载OpenCV库,

    2024年02月04日
    浏览(34)
  • OpenCV 视频处理(关于摄像头和视频文件的读取、显示、保存等等)

    OpenCV不仅能够处理图像,还能够处理视频 视频是由大量的图像构成的 ,这些图像是以固定的时间间隔从视频中获取的。这样,就能够使用图像处理的方法对这些图像进行处理,进而达到处理视频的目的。要想处理视频,需要先对视频进行读取、显示、保存等相关操作。为此

    2024年03月14日
    浏览(34)
  • ubuntu20.04下安装,运行关于opencv的Python程序相关库

    最初对ubuntu20.04以及opencv和python运行环境一窍不通,折磨了好几天,虚拟机重建了好几次,各个帖子的方法都跑遍了。最后终于装上,记录一下,免得搞忘。 先总结下之前的问题:推测之前的问题都在于我先入为主安装了vscode上的Python,但其实ubuntu20.04他是自带了Python3.8.10的

    2024年02月13日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包