This post is
continuation of the previous post:
By
detecting a single shape i.e., a triangle, some parameters needed are
merely sum of its corners – without any restriction like object
width. For example a pitfall found on the previous post above when a
square shape – having four corners – is given, the result will
detect two objects while there is just single object given. The
problem is the window where the image attached has four corners too,
and then our program ‘think’ that it is also a square. Therefore
a parameter is needed to give restriction on particular object we
want. Here we can use the width of the object to definitely eliminate
any unwanted shape.
For
the purpose above, an image with four object shapes will be used to
demonstrate role of the parameter. Here is the original image going
to use:
![]() |
Figure 1. Multiple basic shapes |
Image bellow is the result of the code that the parameter has been
set the way we discuss:
![]() |
Figure 2. Objects detected which are intended |
A parameter that has been set in on the line bellow:
if(contourArea(contours[i]) > 100 && contourArea(contours[i]) <= 90000)
{
//detect and label object shape detected
}
a contourArea() function inside if() condition calculates the area of
contour (closed-contour or object detected) in pixel unit. The
parameter passed on the function is just contours that have been
calculated. Then the return value is in the form of double value
which can be compared directly with integer value. The expression:
contourArea(contours[i]) > 100 && contourArea(contours[i]) <= 90000
means that the area of object detected will be in range of 100 to
90000 pixel. Otherwise outside of this range, the object shape will
not be considered as the object intended i.e., we do not give any
label or boundary or any sign to these unwanted shapes. Therefore in
this if() condition is where objects labels and boundaries are drawn.
The line of code for the object shape detected drawing is bellow:
if(contourArea(contours[i]) > 100 && contourArea(contours[i]) <= 90000)
{
unsigned sum_of_pn = pn.size(); //sum of corner
switch(sum_of_pn) //check for number of corner
{
case TRIANGLE: img_label = "triangle"; break;
case SQUARE: img_label = "square"; break;
case PENTA: img_label = "penta"; break; //pentagon
case HEXA: img_label = "hexa"; //hexagon
}
//attach name (mark) to the object shape detected
mark_object_detected(contours[i],pn,im,sum_of_pn,img_label);
}
For the above code, I intentionally attach the if() condition once
more to make a good sense of understanding.
The code line of:
unsigned sum_of_pn = pn.size(); //sum of corner
simply counts contours connected to each other, in this case they are closed-contours, in other words they are corners that we wanted. The siwtch-case conditional is used to selectively choose which label to which shape by passing value of sum_of_pn variable.
switch(sum_of_pn) //check for number of corner
{
case TRIANGLE: img_label = "triangle"; break;
case SQUARE: img_label = "square"; break;
case PENTA: img_label = "penta"; break; //pentagon
case HEXA: img_label = "hexa"; //hexagon
}
The TRIANGLE, SQUARE, and so on are simply constants containing
variable 3, 4 and so on. These constants are declared above of the
code, out of the main() function, or global variables.
constexpr unsigned TRIANGLE = 3; //sum of corner
constexpr unsigned SQUARE = 4;
constexpr unsigned PENTA = 5;
constexpr unsigned HEXA = 6;
note that constexpr keyword is valid for C++11 or higher. For
you using OpenCV 3.x or C++98 or C++03, use const instead.
Back to the switch-case code. Suppose that sum_of_pn had a
value of 3, when passed into switch-case the TRIANGLE (having value
of 3) will be selected and img_label which is a string
instance will be assigned by “triangle” and store that value
(string). Then it ‘break’s or simply the program flow qiuts the
switch-case scope to execute the next next code. For the next code:
mark_object_detected(contours[i],pn,im,sum_of_pn,img_label);
simply gives label, boundary and marks the detected corner on the
shapes that are detected.
The definition of the function is bellow:
void mark_object_detected(vector<Point> cn,const vector<Point>& point,
Mat img,unsigned pn_sz,const string& label_img)
{
Rect r = boundingRect(cn); //points of rectangle
rectangle(img,r,Scalar(255,2,22)); //draw the rectangle
for(unsigned i=0; i < pn_sz; ++i)
circle(img,point[i],8,Scalar(100,100,1),-1);//mark the corner
label(img,label_img,cn); //give label
cout<<label_img<<" detected\n"; //print info message
}
the boundingRect() function just has one parameter passed, i.e.,
input array of point type. This function calculates and returns the
minimal up-right bounding rectangle for the specified point set.
The second funciton, rectangle() simply draws a simple, thick, or
filled up-right rectangle with its parameters passed that can be
specified. The third parameter of this function is for color of the
rectangle that are going to draw.
The third function, circle() simply draws a cricle. The second
argument of this function, point[i] indicates center of the
circle. The third argument is the circle radius or how big the circle
that is going to draw. The next argument is for its color. And for
the last argument is for thickness of the circle, here the negative
value is given meaning that the circle drawn is a filled circle.
for the label() function above has been discussed on the previous article.
for the label() function above has been discussed on the previous article.
Just for to know the deferences to
compare, bellow is the result of the previous article code to use the
same image:
Based
on the figure 3 above you can see in the green circle that I mark, that there are many unwanted objects detected since it lacks of
parameter, i.e., the area of object that are going to be detected.
If there is something you want to ask, please ask in the comment bellow.
0 Comments
Post a Comment