Something that frequently occurs is the need to draw some kind of picture or to draw something on top of an image obtained from somewhere else. Toward this end, OpenCV provides a menagerie of functions that will allow us to make lines, squares, circles, and the like.
The simplest of these routines just draws a line by the Bresenham algorithm [Bresenham65]:
void cvLine( CvArr* array, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness = 1, int connectivity = 8 );
The first argument to cvLine()
is the usual
CvArr*
, which in this context typically means an
IplImage*
image pointer. The next two arguments are
CvPoint
s. As a quick reminder, CvPoint
is a simple structure containing only the integer
members x
and y
. We
can create a CvPoint
"on the fly" with the routine
cvPoint(int x, int y)
, which conveniently packs the
two integers into a CvPoint
structure for us.
The next argument, color
, is of type CvScalar
. CvScalar
s are
also structures, which (you may recall) are defined as follows:
typdef struct { double val[4]; } CvScalar;
As you can see, this structure is just a collection of four doubles. In this case, the
first three represent the red, green, and blue channels; the fourth is not used (it can be
used for an alpha channel when appropriate). One typically makes use of the handy macro
CV_RGB(r, g, b)
. This macro takes three numbers and
packs them up into a CvScalar
.
The next two arguments are optional. The thickness
is the thickness of the line (in pixels), and connectivity
sets the anti-aliasing mode. The default is "8 connected", which
will give a nice, smooth, anti-aliased line. You can also set this to a "4 connected"
line; diagonals will be blocky and chunky, but they will be drawn a lot faster.
At least as handy as cvLine()
is cvRectangle()
. It is probably unnecessary to tell you that
cvRectangle()
draws a rectangle. It has the same
arguments as cvLine()
except that there is no connectivity
argument. This is because the resulting
rectangles are always oriented with their sides parallel to the x-
and y-axes. With cvRectangle()
, we
simply give two points for the opposite corners and OpenCV will draw a
rectangle.
void cvRectangle( CvArr* array, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness = 1 );
Similarly straightforward is the method for drawing circles, which pretty much has the same arguments.
void cvCircle ( CvArr* array, CvPoint center, int radius, CvScalar color, int thickness = 1, int connectivity = 8 );
For circles, rectangles, and all of the other closed shapes to come, the thickness
argument can also be set to CV_FILL
, which is just an alias for −1; the result is that the drawn figure
will be filled in the same color as the edges.
Only slightly more complicated than cvCircle()
is
the routine for drawing generalized ellipses:
void cvEllipse( CvArr* img, CvPoint center, CvSize axes, double angle, double start_angle, double end_angle, CvScalar color, int thickness = 1, int line_type = 8 );
In this case, the major new ingredient is the axes
argument, which is of type CvSize
. The structure
CvSize
is very much like CvPoint
and CvScalar
; it is a simple
structure, in this case containing only the members width
and height
. Like CvPoint
and CvScalar
, there
is a convenient helper function cvSize(int height, int
width)
that will return a CvSize
structure
when we need one. In this case, the height
and width
arguments represent the length of the ellipse's major
and minor axes.
The angle
is the angle (in degrees) of the major
axis, which is measured counterclockwise from horizontal (i.e., from the
x-axis). Similarly the start_angle
and end_angle
indicate (also
in degrees) the angle for the arc to start and for it to finish. Thus, for a complete
ellipse you must set these values to 0 and 360, respectively.
An alternate way to specify the drawing of an ellipse is to use a bounding box:
void cvEllipseBox( CvArr* img, CvBox2D box, CvScalar color, int thickness = 1, int line_type = 8, int shift = 0 );
Here again we see another of OpenCV's helper structures, CvBox2D
:
typdef struct { CvPoint2D32f center; CvSize2D32f size; float angle; } CvBox2D;
CvPoint2D32f
is the floating-point analogue of
CvPoint
, and CvSize2D32f
is the floating-point analog of CvSize
. These, along with the tilt angle, effectively specify the bounding
box for the ellipse.
Finally, we have a set of functions for drawing polygons:
void cvFillPoly( CvArr* img, CvPoint** pts, int* npts, int contours, CvScalar color, int line_type = 8 ); void cvFillConvexPoly( CvArr* img, CvPoint* pts, int npts, CvScalar color, int line_type = 8 ); void cvPolyLine( CvArr* img, CvPoint** pts, int* npts, int contours, int is_closed, CvScalar color, int thickness = 1, int line_type = 8 );
All three of these are slight variants on the same idea, with the main difference being how the points are specified.
In cvFillPoly()
, the points are provided as an
array of CvPoint
arrays. This allows cvFillPoly()
to draw many polygons in a single call. Similarly npts
is an array of point counts, one for each polygon to be drawn. If the is_closed
variable is set to true
, then an additional segment will be drawn from the last to the first
point for each polygon. cvFillPoly()
is quite robust
and will handle self-intersecting polygons, polygons with holes, and other such
complexities. Unfortunately, this means the routine is comparatively slow.
cvFillConvexPoly()
works like cvFillPoly()
except that it draws only one polygon at a time
and can draw only convex polygons.[35] The upside is that cvFillConvexPoly()
runs
much faster.
The third function, cvPolyLine()
, takes the same
arguments as cvFillPoly()
; however, since only the
polygon edges are drawn, self-intersection presents no particular complexity. Hence this
function is much faster than cvFillPoly()
.
One last form of drawing that one may well need is to draw text. Of course, text creates its own set of complexities, but—as always with this sort of thing—OpenCV is more concerned with providing a simple "down and dirty" solution that will work for simple cases than a robust, complex solution (which would be redundant anyway given the capabilities of other libraries).
OpenCV has one main routine, called cvPutText()
that just throws some text onto an image. The text indicated by text
is printed with its lower-left corner of the text box at origin
and in the color indicated by color
.
void cvPutText( CvArr* img, const char* text, CvPoint origin, const CvFont* font, CvScalar color );
There is always some little thing that makes our job a bit more complicated than we'd
like, and in this case it's the appearance of the pointer to CvFont
.
In a nutshell, the way to get a valid CvFont*
pointer is to call the function cvInitFont()
. This
function takes a group of arguments that configure some particular font for use on the
screen. Those of you familiar with GUI programming in other environments will find
cvInitFont()
to be reminiscent of similar devices but
with many fewer options.
In order to create a CvFont
that we can pass to
cvPutText()
, we must first declare a CvFont
variable; then we can pass it to cvInitFont()
.
void cvInitFont( CvFont* font, int font_face, double hscale, double vscale, double shear = 0, int thickness = 1, int line_type = 8 );
Observe that this is a little different than how seemingly similar functions, such as
cvCreateImage()
, work in OpenCV. The call to cvInitFont()
initializes an existing CvFont
structure (which means that you create the variable and pass cvInitFont()
a pointer to the variable you created). This is
unlike cvCreateImage()
, which creates the structure for
you and returns a pointer.
The argument font_face
is one of those listed in
Table 3-15 (and pictured in Figure 3-6), and it may optionally be combined
(by Boolean OR) with CV_FONT_ITALIC
.
Table 3-15. Available fonts (all are variations of Hershey)
Identifier |
Description |
---|---|
|
Normal size sanserif |
|
Small size sanserif |
|
Normal size sanserif, more complex than |
|
Normal size serif, more complex than |
|
Normal size serif, more complex than |
|
Smaller version of |
|
Handwriting style |
|
More complex variant of |
Figure 3-6. The eight fonts of Table 3-15 drawn with hscale = vscale = 1.0, with the origin of each line separated from the vertical by 30 pixels
Both hscale
and vscale
can be set to either 1.0
or
0.5
only. This causes the font to be rendered at full
or half height (and width) relative to the basic definition of the particular
font.
The shear
function creates an italicized slant to
the font; if set to 0.0
, the font is not slanted. It
can be set as large as 1.0
, which sets the slope of the
characters to approximately 45 degrees.
Both thickness
and line_type
are the same as defined for all the other drawing functions.
[35] Strictly speaking, this is not quite true; it can actually draw and fill any monotone polygon, which is a slightly larger class of polygons.