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));