OpenCv绘制简单的几何图形

前言

导出函数在文件modules\imgproc\include\opencv2\imgproc.hpp中
opencv绘制使用的参数都是基于opencv的类型的
一般第一个参数是cv::Mat 类型的,表示需要处理的图像的像素矩阵
绘制的像素点使用cv::Point
绘制的颜色是cv::Scalar,这个颜色是BGRA的格式
绘制的线宽一般是int的static const int MAX_THICKNESS = 32767;最大线宽是32767,一般用不上
比较常用的参数,线宽,线型,放缩倍率一般都有默认值,线宽默认是1代表单位还需要研究,线型默认是8,放缩倍率默认是0
线型定义如下,其中LINE_AA为边缘像素采用高斯滤波,图像锯齿会稍微好一点

/** types of line
@ingroup imgproc_draw
*/
enum LineTypes {
    FILLED  = -1,
    LINE_4  = 4, //!< 4-connected line
    LINE_8  = 8, //!< 8-connected line
    LINE_AA = 16 //!< antialiased line
};

shift 放缩倍率的值表示缩小到2的几次方,是使用移位操作实现的,所以这个参数实际是一个缩小参数这个缩小的中心点是0,0。因为这个shift是对绘制的点集point直接进行放缩的,所以很不好控制,一般不用这个参数进行放缩。

1.直线line

line的函数原型如下

函数的五个参数分别是图像的mat矩阵,起始点,结束点,颜色,线宽,线型,和放缩倍率(一般不用)

void line( InputOutputArray _img, Point pt1, Point pt2, const Scalar& color,
           int thickness, int line_type, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( 0 < thickness && thickness <= MAX_THICKNESS );
    CV_Assert( 0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData( color, buf, img.type(), 0 );
    ThickLine( img, pt1, pt2, buf, thickness, line_type, 3, shift );
}

使用方法

line(img,(0,0),(100,100),(255,0,0),2)

后面的line_type和shift 一般使用默认值就可以了。

2.箭头arrowedLine

arrowedLine的函数原型如下

相比于划线,只是多了一个参数tipLength,这个参数默认值位0.1代表单位还需要研究,基本这个单位返回绘制的箭头就可见了,具体需求自己可以调节。

void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
           int thickness, int line_type, int shift, double tipLength)
{
    CV_INSTRUMENT_REGION();

    const double tipSize = norm(pt1-pt2)*tipLength; // Factor to normalize the size of the tip depending on the length of the arrow

    line(img, pt1, pt2, color, thickness, line_type, shift);

    const double angle = atan2( (double) pt1.y - pt2.y, (double) pt1.x - pt2.x );

    Point p(cvRound(pt2.x + tipSize * cos(angle + CV_PI / 4)),
        cvRound(pt2.y + tipSize * sin(angle + CV_PI / 4)));
    line(img, p, pt2, color, thickness, line_type, shift);

    p.x = cvRound(pt2.x + tipSize * cos(angle - CV_PI / 4));
    p.y = cvRound(pt2.y + tipSize * sin(angle - CV_PI / 4));
    line(img, p, pt2, color, thickness, line_type, shift);
}

使用方法

arrowedLine(img, Point(0, 0), Point(100, 100), Scalar(255, 0, 0), 1, 8, 0, 0.1);

基本后面的参数都是用的默认值,完全可以这样写

arrowedLine(img, Point(0, 0), Point(100, 100), Scalar(255, 0, 0));

但是一旦需要定制箭头大小,就需要把所有参数都写上

3.矩形rectangle、

rectangle的函数原型如下

可以看到rectangle函数有两个定义,第二个定义只是将两个point用rect代替了,并且最后还是调用第一个函数执行的绘制,所以这里只需要分析第一个函数

void rectangle( InputOutputArray _img, Point pt1, Point pt2,
                const Scalar& color, int thickness,
                int lineType, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( lineType == CV_AA && img.depth() != CV_8U )
        lineType = 8;

    CV_Assert( thickness <= MAX_THICKNESS );
    CV_Assert( 0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    Point2l pt[4];

    pt[0] = pt1;
    pt[1].x = pt2.x;
    pt[1].y = pt1.y;
    pt[2] = pt2;
    pt[3].x = pt1.x;
    pt[3].y = pt2.y;

    if( thickness >= 0 )
        PolyLine( img, pt, 4, true, buf, thickness, lineType, shift );
    else
        FillConvexPoly( img, pt, 4, buf, lineType, shift );
}


void rectangle( InputOutputArray img, Rect rec,
                const Scalar& color, int thickness,
                int lineType, int shift )
{
    CV_INSTRUMENT_REGION();

    if( !rec.empty() )
        rectangle( img, rec.tl(), rec.br() - Point(1<<shift,1<<shift),
                   color, thickness, lineType, shift );
}

使用方法

rectangle(img, PointLR, PointBR, Scalar(255, 255, 0), 1, 8,0);

参数大部分都是很好理解的,但是有一点是当线宽取值为负的时候,一般可以用CV_FILLED,绘制的矩形会将内部填充

4.圆circle

circle的函数原型如下

可以看到圆形的绘制实际使用了两种方式EllipseEx和Circle,但是这是OpenCV内部的事情,我们可以不关心,我们需要关心的是,当thickness 为负值的时候,圆内部会被填充,其他的参数也是很好理解的。

void circle( InputOutputArray _img, Point center, int radius,
             const Scalar& color, int thickness, int line_type, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( radius >= 0 && thickness <= MAX_THICKNESS &&
        0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    if( thickness > 1 || line_type != LINE_8 || shift > 0 )
    {
        Point2l _center(center);
        int64 _radius(radius);
        _center.x <<= XY_SHIFT - shift;
        _center.y <<= XY_SHIFT - shift;
        _radius <<= XY_SHIFT - shift;
        EllipseEx( img, _center, Size2l(_radius, _radius),
                   0, 0, 360, buf, thickness, line_type );
    }
    else
        Circle( img, center, radius, buf, thickness < 0 );
}

使用方法

circle(img, pointcenter, radius, Scalar(0, 0, 255), 1, 8, 0);

5.椭圆ellipse

ellipse的函数原型如下

函数的第一个定义使用的参数主要是中心点,长轴长和短轴长(通过Size axes传递),然后是angle,可以将椭圆绕中心点旋转指定的度数,这个单位是度(0-360)然后是绘制的椭圆的起始角和结束角,这意味着,绘制椭圆的时候是可以只绘制一段圆弧,这个圆弧是从x轴顺时针旋转的方向,0-180在x轴下方。同样,这里线宽如果为负数的话,图形内部会被填充,如果是弧形,会填充为扇形
函数的第二个定义通过一个RotatedRect包含了长轴短轴和旋转角度,而起始和结束的角没有,这样设置之后实际上最后还是把参数转化为了第一个定义的参数,才进行的绘制,并且由于没有起始角和结束角的信息,这个定义失去了绘制弧线和扇形的功能,不建议使用。

void ellipse( InputOutputArray _img, Point center, Size axes,
              double angle, double start_angle, double end_angle,
              const Scalar& color, int thickness, int line_type, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( axes.width >= 0 && axes.height >= 0 &&
        thickness <= MAX_THICKNESS && 0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    int _angle = cvRound(angle);
    int _start_angle = cvRound(start_angle);
    int _end_angle = cvRound(end_angle);
    Point2l _center(center);
    Size2l _axes(axes);
    _center.x <<= XY_SHIFT - shift;
    _center.y <<= XY_SHIFT - shift;
    _axes.width <<= XY_SHIFT - shift;
    _axes.height <<= XY_SHIFT - shift;

    EllipseEx( img, _center, _axes, _angle, _start_angle,
               _end_angle, buf, thickness, line_type );
}

void ellipse(InputOutputArray _img, const RotatedRect& box, const Scalar& color,
             int thickness, int lineType)
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( lineType == CV_AA && img.depth() != CV_8U )
        lineType = 8;

    CV_Assert( box.size.width >= 0 && box.size.height >= 0 &&
               thickness <= MAX_THICKNESS );

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    int _angle = cvRound(box.angle);
    Point2l center(cvRound(box.center.x),
                 cvRound(box.center.y));
    center.x = (center.x << XY_SHIFT) + cvRound((box.center.x - center.x)*XY_ONE);
    center.y = (center.y << XY_SHIFT) + cvRound((box.center.y - center.y)*XY_ONE);
    Size2l axes(cvRound(box.size.width),
              cvRound(box.size.height));
    axes.width  = (axes.width  << (XY_SHIFT - 1)) + cvRound((box.size.width - axes.width)*(XY_ONE>>1));
    axes.height = (axes.height << (XY_SHIFT - 1)) + cvRound((box.size.height - axes.height)*(XY_ONE>>1));
    EllipseEx( img, center, axes, _angle, 0, 360, buf, thickness, lineType );
}

使用方法

使用方法基本和圆是一样的,只是多几个参数

ellipse(img, pointcenter, Size(radiusx,radiusy),0,0,360 Scalar(0, 0, 255), 1, 8, 0)

6.多边形polylines

polylines的函数原型如下

定义1 第一个参数是mat矩阵,
第二个参数为一个二维的point矩阵,一般一维的point矩阵,每两个点依次相连,可以画出一条折线,而二维的point矩阵就可以绘制多条没有交点的折线,
第三个参数是每条折线的点数,也就是说没条折线的点数可以不同,
第四个参数表示要画的线的条数,
第五个参数表示画的线是不是闭合的,也就是最后一个点和第一个点要不要连接,这里只能统一控制所以的线的状态,不能既有闭合的又有不闭合的。
剩下的参数全部是普通的画线的参数
定义2要比定义1简单很多,用来处理只需要绘制一条折线的情况,而且参数InputArrayOfArrays 是一个可以自己计算自己数组元素的个数的容器,可以是std::vector,然后基于容器的属性,会自动生成一个满足定义1的参数,最后还是使用的定义1绘制的图形,这里如果只需要绘制一条折线的话,推荐使用定义2的方法。

void polylines( InputOutputArray _img, const Point* const* pts, const int* npts, int ncontours, bool isClosed,
                const Scalar& color, int thickness, int line_type, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( pts && npts && ncontours >= 0 &&
               0 <= thickness && thickness <= MAX_THICKNESS &&
               0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData( color, buf, img.type(), 0 );

    for( int i = 0; i < ncontours; i++ )
    {
        std::vector<Point2l> _pts(pts[i], pts[i]+npts[i]);
        PolyLine( img, _pts.data(), npts[i], isClosed, buf, thickness, line_type, shift );
    }
}

void cv::polylines(InputOutputArray img, InputArrayOfArrays pts,
                   bool isClosed, const Scalar& color,
                   int thickness, int lineType, int shift)
{
    CV_INSTRUMENT_REGION();

    bool manyContours = pts.kind() == _InputArray::STD_VECTOR_VECTOR ||
                        pts.kind() == _InputArray::STD_VECTOR_MAT;
    int i, ncontours = manyContours ? (int)pts.total() : 1;
    if( ncontours == 0 )
        return;
    AutoBuffer<Point*> _ptsptr(ncontours);
    AutoBuffer<int> _npts(ncontours);
    Point** ptsptr = _ptsptr.data();
    int* npts = _npts.data();

    for( i = 0; i < ncontours; i++ )
    {
        Mat p = pts.getMat(manyContours ? i : -1);
        if( p.total() == 0 )
        {
            ptsptr[i] = NULL;
            npts[i] = 0;
            continue;
        }
        CV_Assert(p.checkVector(2, CV_32S) >= 0);
        ptsptr[i] = p.ptr<Point>();
        npts[i] = p.rows*p.cols*p.channels()/2;
    }
    polylines(img, (const Point**)ptsptr, npts, (int)ncontours, isClosed, color, thickness, lineType, shift);
}

使用方法

这里是定义二的使用方式

polylines(Frame, LinePoints, false, cv::Scalar(255,0, 0, 255),//颜色
            LineWidth,//线的粗细
            LINE_AA);
        );

7.文字putText

putText的函数原型如下

绘制文字第一个参数是mat
第二个参数是std::string,也就是写的内容
第三个参数是写的位置,是字符串的左上角
第四个参数是写的字体
第五个参数是字体大小
后面的是划线的参数
bottomLeftOrigin 参数控制字体是在左底部还是左顶部,默认为左底部

void putText( InputOutputArray _img, const String& text, Point org,
              int fontFace, double fontScale, Scalar color,
              int thickness, int line_type, bool bottomLeftOrigin )

{
    CV_INSTRUMENT_REGION();

    if ( text.empty() )
    {
        return;
    }
    Mat img = _img.getMat();
    const int* ascii = getFontData(fontFace);

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    int base_line = -(ascii[0] & 15);
    int hscale = cvRound(fontScale*XY_ONE), vscale = hscale;

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    if( bottomLeftOrigin )
        vscale = -vscale;

    int64 view_x = (int64)org.x << XY_SHIFT;
    int64 view_y = ((int64)org.y << XY_SHIFT) + base_line*vscale;
    std::vector<Point2l> pts;
    pts.reserve(1 << 10);
    const char **faces = cv::g_HersheyGlyphs;

    for( int i = 0; i < (int)text.size(); i++ )
    {
        int c = (uchar)text[i];
        Point2l p;

        readCheck(c, i, text, fontFace);

        const char* ptr = faces[ascii[(c-' ')+1]];
        p.x = (uchar)ptr[0] - 'R';
        p.y = (uchar)ptr[1] - 'R';
        int64 dx = p.y*hscale;
        view_x -= p.x*hscale;
        pts.resize(0);

        for( ptr += 2;; )
        {
            if( *ptr == ' ' || !*ptr )
            {
                if( pts.size() > 1 )
                    PolyLine( img, &pts[0], (int)pts.size(), false, buf, thickness, line_type, XY_SHIFT );
                if( !*ptr++ )
                    break;
                pts.resize(0);
            }
            else
            {
                p.x = (uchar)ptr[0] - 'R';
                p.y = (uchar)ptr[1] - 'R';
                ptr += 2;
                pts.push_back(Point2l(p.x*hscale + view_x, p.y*vscale + view_y));
            }
        }
        view_x += dx;
    }
}

opencv支持的字体包括

/** Only a subset of Hershey fonts <https://en.wikipedia.org/wiki/Hershey_fonts> are supported
@ingroup imgproc_draw
*/
enum HersheyFonts {
    FONT_HERSHEY_SIMPLEX        = 0, //!< normal size sans-serif font
    FONT_HERSHEY_PLAIN          = 1, //!< small size sans-serif font
    FONT_HERSHEY_DUPLEX         = 2, //!< normal size sans-serif font (more complex than FONT_HERSHEY_SIMPLEX)
    FONT_HERSHEY_COMPLEX        = 3, //!< normal size serif font
    FONT_HERSHEY_TRIPLEX        = 4, //!< normal size serif font (more complex than FONT_HERSHEY_COMPLEX)
    FONT_HERSHEY_COMPLEX_SMALL  = 5, //!< smaller version of FONT_HERSHEY_COMPLEX
    FONT_HERSHEY_SCRIPT_SIMPLEX = 6, //!< hand-writing style font
    FONT_HERSHEY_SCRIPT_COMPLEX = 7, //!< more complex variant of FONT_HERSHEY_SCRIPT_SIMPLEX
    FONT_ITALIC                 = 16 //!< flag for italic font
};

使用方法

putText(Frame, SAngle, Point, cv::FONT_HERSHEY_SIMPLEX, ScalarTextSize, Scalar(255, 0, 0, 0), ScalarTextThick);

8.不常见功能绘制标记drawMarker

drawMarker的函数原型如下

绘制标记只支持指定的几种类型,从函数定义可以看出来,实际上还是绘制的直线,所以只支持几种简单的图形

/* ----------------------------------------------------------------------------------------- */
/* ADDING A SET OF PREDEFINED MARKERS WHICH COULD BE USED TO HIGHLIGHT POSITIONS IN AN IMAGE */
/* ----------------------------------------------------------------------------------------- */

void drawMarker(InputOutputArray img, Point position, const Scalar& color, int markerType, int markerSize, int thickness, int line_type)
{
    switch(markerType)
    {
    // The cross marker case
    case MARKER_CROSS:
        line(img, Point(position.x-(markerSize/2), position.y), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
        line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
        break;

    // The tilted cross marker case
    case MARKER_TILTED_CROSS:
        line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        break;

    // The star marker case
    case MARKER_STAR:
        line(img, Point(position.x-(markerSize/2), position.y), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
        line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        break;

    // The diamond marker case
    case MARKER_DIAMOND:
        line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x, position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y), color, thickness, line_type);
        line(img, Point(position.x-(markerSize/2), position.y), Point(position.x, position.y-(markerSize/2)), color, thickness, line_type);
        break;

    // The square marker case
    case MARKER_SQUARE:
        line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x-(markerSize/2), position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
        break;

    // The triangle up marker case
    case MARKER_TRIANGLE_UP:
        line(img, Point(position.x-(markerSize/2), position.y+(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y+(markerSize/2)), Point(position.x, position.y-(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
        break;

    // The triangle down marker case
    case MARKER_TRIANGLE_DOWN:
        line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
        line(img, Point(position.x, position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
        break;

    // If any number that doesn't exist is entered as marker type, draw a cross marker, to avoid crashes
    default:
        drawMarker(img, position, color, MARKER_CROSS, markerSize, thickness, line_type);
        break;
    }
}

支持的类型如下

/** Possible set of marker types used for the cv::drawMarker function
@ingroup imgproc_draw
*/
enum MarkerTypes
{
    MARKER_CROSS = 0,           //!< A crosshair marker shape
    MARKER_TILTED_CROSS = 1,    //!< A 45 degree tilted crosshair marker shape
    MARKER_STAR = 2,            //!< A star marker shape, combination of cross and tilted cross
    MARKER_DIAMOND = 3,         //!< A diamond marker shape
    MARKER_SQUARE = 4,          //!< A square marker shape
    MARKER_TRIANGLE_UP = 5,     //!< An upwards pointing triangle marker shape
    MARKER_TRIANGLE_DOWN = 6    //!< A downwards pointing triangle marker shape
};

使用方法

绘制一个星号

drawMarker(Frame,Point,  Scalar(255, 0, 0, 0), cv::MARKER_STAR);

9.不常用方法,填充凸多边形内部fillConvexPoly

fillConvexPoly的函数原型如下

fillConvexPoly有两种定义,第一种定义
从定义不难发现,fillConvexPoly的时候和polylines比较相似,比较像第二种定义,只是参数使用的是数组指针和元素个数,而不是容器。
第二种定义直接使用了容器,这里的InputArray 是可以使用std::vector初始化的,然后也是转化为第一种定义执行,但是第二种明显更符合我们平时使用的习惯

void fillConvexPoly( InputOutputArray _img, const Point* pts, int npts,
                     const Scalar& color, int line_type, int shift )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( !pts || npts <= 0 )
        return;

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    double buf[4];
    CV_Assert( 0 <= shift && shift <=  XY_SHIFT );
    scalarToRawData(color, buf, img.type(), 0);
    std::vector<Point2l> _pts(pts, pts + npts);
    FillConvexPoly( img, _pts.data(), npts, buf, line_type, shift );
}

void cv::fillConvexPoly(InputOutputArray img, InputArray _points,
                        const Scalar& color, int lineType, int shift)
{
    CV_INSTRUMENT_REGION();

    Mat points = _points.getMat();
    CV_Assert(points.checkVector(2, CV_32S) >= 0);
    fillConvexPoly(img, points.ptr<Point>(), points.rows*points.cols*points.channels()/2, color, lineType, shift);
}

使用方法

fillConvexPoly(img,pPoint,npoints,Scalar(255, 0, 0, 0),LINE_AA,0)
fillConvexPoly(img,vectpoint,Scalar(255, 0, 0, 0),LINE_AA,0)

10.不常用方法,填充复杂多边形内部fillPoly

fillPoly的函数原型如下

这个函数的定义更像polylines的第一个定义,想比于fillConvexPoly,他可以绘制非凸多边形,一般fillConvexPoly用来绘制凸多边形,因为fillConvexPoly单独只处理这一种类型,所以会快一点,fillPoly用来绘制非凸多边形,比如五角星等。
参数pts,npts,ncontours的含义和多边形绘制polylines中一样
第二种定义通过使用InputArrayOfArrays ,将pts,npts,ncontours全部包含了,这个类型同样可以使用vector<vector<>> 初始化,只是简化了使用的方法。
函数的最后一个参数Point offset ,是针对所有点的偏移

void fillPoly( InputOutputArray _img, const Point** pts, const int* npts, int ncontours,
               const Scalar& color, int line_type,
               int shift, Point offset )
{
    CV_INSTRUMENT_REGION();

    Mat img = _img.getMat();

    if( line_type == CV_AA && img.depth() != CV_8U )
        line_type = 8;

    CV_Assert( pts && npts && ncontours >= 0 && 0 <= shift && shift <= XY_SHIFT );

    double buf[4];
    scalarToRawData(color, buf, img.type(), 0);

    std::vector<PolyEdge> edges;

    int i, total = 0;
    for( i = 0; i < ncontours; i++ )
        total += npts[i];

    edges.reserve( total + 1 );
    for (i = 0; i < ncontours; i++)
    {
        std::vector<Point2l> _pts(pts[i], pts[i] + npts[i]);
        CollectPolyEdges(img, _pts.data(), npts[i], edges, buf, line_type, shift, offset);
    }

    FillEdgeCollection(img, edges, buf);
}

void cv::fillPoly(InputOutputArray img, InputArrayOfArrays pts,
                  const Scalar& color, int lineType, int shift, Point offset)
{
    CV_INSTRUMENT_REGION();

    bool manyContours = pts.kind() == _InputArray::STD_VECTOR_VECTOR ||
                        pts.kind() == _InputArray::STD_VECTOR_MAT;
    int i, ncontours = manyContours ? (int)pts.total() : 1;
    if( ncontours == 0 )
        return;
    AutoBuffer<Point*> _ptsptr(ncontours);
    AutoBuffer<int> _npts(ncontours);
    Point** ptsptr = _ptsptr.data();
    int* npts = _npts.data();

    for( i = 0; i < ncontours; i++ )
    {
        Mat p = pts.getMat(manyContours ? i : -1);
        CV_Assert(p.checkVector(2, CV_32S) >= 0);
        ptsptr[i] = p.ptr<Point>();
        npts[i] = p.rows*p.cols*p.channels()/2;
    }
    fillPoly(img, (const Point**)ptsptr, npts, (int)ncontours, color, lineType, shift, offset);
}

使用方法

fillPoly(img,pppoint,npts,ncounter,Scalar(255, 0, 0, 0),LINE_AA,0)
fillPoly(img,vectvectpoint,Scalar(255, 0, 0, 0),LINE_AA,0)

11.不常用方法,绘制轮廓drawContours

drawContours的函数原型如下

第一个参数表示mat矩阵
第二个参数为组成轮廓的多边形点的数组,也可以用vector<vector<>> 初始化
第三个参数contourIdx指明画第几个轮廓,如果这个参数为负值,就画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值(CV_FILLED)就填充轮廓内部,
第六个参数lineType为线型,
_hierarchy,maxLevel是一起使用的,只有_hierarchy有数据的时候,maxLevel才有意义。
maxLevel的值控制绘制的轮廓的层数。
@param maxLevel绘制轮廓的最大水平。 如果为0,则仅绘制指定的轮廓。
如果为1,该函数将绘制轮廓和所有嵌套轮廓。 如果为2,则函数
绘制轮廓,所有嵌套轮廓,所有从嵌套到嵌套的轮廓,依此类推。 这
仅当有可用的层次结构时,才考虑该参数

void cv::drawContours( InputOutputArray _image, InputArrayOfArrays _contours,
                   int contourIdx, const Scalar& color, int thickness,
                   int lineType, InputArray _hierarchy,
                   int maxLevel, Point offset )
{
    CV_INSTRUMENT_REGION();

    Mat image = _image.getMat(), hierarchy = _hierarchy.getMat();
    CvMat _cimage = cvMat(image);

    size_t ncontours = _contours.total();
    size_t i = 0, first = 0, last = ncontours;
    std::vector<CvSeq> seq;
    std::vector<CvSeqBlock> block;

    if( !last )
        return;

    seq.resize(last);
    block.resize(last);

    for( i = first; i < last; i++ )
        seq[i].first = 0;

    if( contourIdx >= 0 )
    {
        CV_Assert( 0 <= contourIdx && contourIdx < (int)last );
        first = contourIdx;
        last = contourIdx + 1;
    }

    for( i = first; i < last; i++ )
    {
        Mat ci = _contours.getMat((int)i);
        if( ci.empty() )
            continue;
        int npoints = ci.checkVector(2, CV_32S);
        CV_Assert( npoints > 0 );
        cvMakeSeqHeaderForArray( CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(Point),
                                 ci.ptr(), npoints, &seq[i], &block[i] );
    }

    if( hierarchy.empty() || maxLevel == 0 )
        for( i = first; i < last; i++ )
        {
            seq[i].h_next = i < last-1 ? &seq[i+1] : 0;
            seq[i].h_prev = i > first ? &seq[i-1] : 0;
        }
    else
    {
        size_t count = last - first;
        CV_Assert(hierarchy.total() == ncontours && hierarchy.type() == CV_32SC4 );
        const Vec4i* h = hierarchy.ptr<Vec4i>();

        if( count == ncontours )
        {
            for( i = first; i < last; i++ )
            {
                int h_next = h[i][0], h_prev = h[i][1],
                    v_next = h[i][2], v_prev = h[i][3];
                seq[i].h_next = (size_t)h_next < count ? &seq[h_next] : 0;
                seq[i].h_prev = (size_t)h_prev < count ? &seq[h_prev] : 0;
                seq[i].v_next = (size_t)v_next < count ? &seq[v_next] : 0;
                seq[i].v_prev = (size_t)v_prev < count ? &seq[v_prev] : 0;
            }
        }
        else
        {
            int child = h[first][2];
            if( child >= 0 )
            {
                addChildContour(_contours, ncontours, h, child, seq, block);
                seq[first].v_next = &seq[child];
            }
        }
    }

    cvDrawContours( &_cimage, &seq[first], cvScalar(color), cvScalar(color), contourIdx >= 0 ?
                   -maxLevel : maxLevel, thickness, lineType, cvPoint(offset) );
}

findContours

void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
                   OutputArray _hierarchy, int mode, int method, Point offset )
{
    CV_INSTRUMENT_REGION();

    // Sanity check: output must be of type vector<vector<Point>>
    CV_Assert((_contours.kind() == _InputArray::STD_VECTOR_VECTOR || _contours.kind() == _InputArray::STD_VECTOR_MAT ||
                _contours.kind() == _InputArray::STD_VECTOR_UMAT));

    CV_Assert(_contours.empty() || (_contours.channels() == 2 && _contours.depth() == CV_32S));

    Mat image0 = _image.getMat(), image;
    Point offset0(0, 0);
    if(method != CV_LINK_RUNS)
    {
        offset0 = Point(-1, -1);
        copyMakeBorder(image0, image, 1, 1, 1, 1, BORDER_CONSTANT | BORDER_ISOLATED, Scalar(0));
    }
    else
    {
        image = image0;
    }
    MemStorage storage(cvCreateMemStorage());
    CvMat _cimage = cvMat(image);
    CvSeq* _ccontours = 0;
    if( _hierarchy.needed() )
        _hierarchy.clear();
    cvFindContours_Impl(&_cimage, storage, &_ccontours, sizeof(CvContour), mode, method, cvPoint(offset0 + offset), 0);
    if( !_ccontours )
    {
        _contours.clear();
        return;
    }
    Seq<CvSeq*> all_contours(cvTreeToNodeSeq( _ccontours, sizeof(CvSeq), storage ));
    int i, total = (int)all_contours.size();
    _contours.create(total, 1, 0, -1, true);
    SeqIterator<CvSeq*> it = all_contours.begin();
    for( i = 0; i < total; i++, ++it )
    {
        CvSeq* c = *it;
        ((CvContour*)c)->color = (int)i;
        _contours.create((int)c->total, 1, CV_32SC2, i, true);
        Mat ci = _contours.getMat(i);
        CV_Assert( ci.isContinuous() );
        cvCvtSeqToArray(c, ci.ptr());
    }

    if( _hierarchy.needed() )
    {
        _hierarchy.create(1, total, CV_32SC4, -1, true);
        Vec4i* hierarchy = _hierarchy.getMat().ptr<Vec4i>();

        it = all_contours.begin();
        for( i = 0; i < total; i++, ++it )
        {
            CvSeq* c = *it;
            int h_next = c->h_next ? ((CvContour*)c->h_next)->color : -1;
            int h_prev = c->h_prev ? ((CvContour*)c->h_prev)->color : -1;
            int v_next = c->v_next ? ((CvContour*)c->v_next)->color : -1;
            int v_prev = c->v_prev ? ((CvContour*)c->v_prev)->color : -1;
            hierarchy[i] = Vec4i(h_next, h_prev, v_next, v_prev);
        }
    }
}

void cv::findContours( InputArray _image, OutputArrayOfArrays _contours,
                       int mode, int method, Point offset)
{
    CV_INSTRUMENT_REGION();

    findContours(_image, _contours, noArray(), mode, method, offset);
}

取值一:CV_RETR_EXTERNAL只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
取值二:CV_RETR_LIST   检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
取值三:CV_RETR_CCOMP  检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
取值四:CV_RETR_TREE, 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。

取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

绘制类型mode

绘制的类型定义了几种常见的,但是从drawContours的定义来说,应该有更多种类。

/** Contour retrieval modes */
enum
{
    CV_RETR_EXTERNAL=0,
    CV_RETR_LIST=1,
    CV_RETR_CCOMP=2,
    CV_RETR_TREE=3,
    CV_RETR_FLOODFILL=4
};
/** Contour approximation methods */
enum
{
    CV_CHAIN_CODE=0,
    CV_CHAIN_APPROX_NONE=1,
    CV_CHAIN_APPROX_SIMPLE=2,
    CV_CHAIN_APPROX_TC89_L1=3,
    CV_CHAIN_APPROX_TC89_KCOS=4,
    CV_LINK_RUNS=5
};

使用方法

CV_EXPORTS_W void findContours( InputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());

/** @overload */
CV_EXPORTS void findContours( InputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset = Point());

drawContours一般配合findContours一起使用

 vector<vector<Point>> contours;
 vector<Vec4i> hierarchy;
 findContours(img, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);
 Mat resultImage = Mat ::zeros(img.size(),CV_8U);
 drawContours(resultImage, contours, -1, Scalar(255, 0, 255));

猜你喜欢

转载自blog.csdn.net/u012505629/article/details/116192377