opencv_tutorials_t7
Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
opencv_tutorials_t7 [2016/06/06 18:19] – created joaomatos | opencv_tutorials_t7 [2016/06/07 15:57] (current) – joaomatos | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | Tutorial 7. | + | ===== Tutorial 7 ===== |
+ | On this tutorial you will learn how to apply threshold to track an object by its center of mass using image moments. | ||
+ | |||
+ | |||
+ | I recommend you to type the code on your own to get familiarized with the program language. If you have trouble , the original code is attached bellow ( Running on Visual Studio 2015 + OpenCV 3.1 ) * Check the installation guide to make sure that you linked all the OpenCV modules to your Visual Studio. | ||
+ | |||
+ | {{:: | ||
+ | |||
+ | ---- | ||
+ | ===== Tracking an object using image moments ===== | ||
+ | |||
+ | {{ :: | ||
+ | |||
+ | <Code C++ linenums: | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | |||
+ | #define _CRT_SECURE_NO_WARNINGS | ||
+ | |||
+ | using namespace cv; | ||
+ | using namespace std; | ||
+ | |||
+ | //initial min and max HSV filter values. | ||
+ | //these will be changed using trackbars | ||
+ | int iLowH = 0; | ||
+ | int iHighH = 179; | ||
+ | |||
+ | int iLowS = 0; | ||
+ | int iHighS = 255; | ||
+ | |||
+ | int iLowV = 0; | ||
+ | int iHighV = 255; | ||
+ | |||
+ | //default capture width and height | ||
+ | const int FRAME_WIDTH = 640; | ||
+ | const int FRAME_HEIGHT = 480; | ||
+ | //max number of objects to be detected in frame | ||
+ | const int MAX_NUM_OBJECTS = 50; | ||
+ | //minimum and maximum object area | ||
+ | const int min_area = 20 * 20; | ||
+ | const int max_area = FRAME_HEIGHT*FRAME_WIDTH /2; | ||
+ | |||
+ | |||
+ | String intToString(int number) { | ||
+ | |||
+ | |||
+ | stringstream ss; | ||
+ | ss << number; | ||
+ | return ss.str(); | ||
+ | } | ||
+ | |||
+ | //Function to create a window with the Trackbars to apply the Threshold. | ||
+ | void createTrackbars() { | ||
+ | |||
+ | //Open the window to display the Trackbars | ||
+ | namedWindow(" | ||
+ | |||
+ | //Hue values (0 - 179) | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | // | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | //Value (0-255) | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | //Function to apply the erode and dilate features. | ||
+ | void DilateAndErode(Mat &image) { | ||
+ | |||
+ | //Defining the erode and dilate properties | ||
+ | //the erode element chosen here is a 3x3 piexels rectangle. | ||
+ | //Change the Size argument to optimize your threshold. | ||
+ | //dilate with 8x8 size element to make the threshold object more visible | ||
+ | |||
+ | Mat erodeElement = getStructuringElement(MORPH_RECT, | ||
+ | Mat dilateElement = getStructuringElement(MORPH_RECT, | ||
+ | |||
+ | //Apply erode and dilate | ||
+ | erode(image, | ||
+ | dilate(image, | ||
+ | |||
+ | } | ||
+ | |||
+ | //Function to track an object using image moments. | ||
+ | void TrackObject(Mat threshold, Mat &image) { | ||
+ | |||
+ | //We will not use the threshold image directly we will use a copy | ||
+ | //if you use the original threshold , when you turn the tracking ON , the window | ||
+ | // | ||
+ | Mat temp; | ||
+ | threshold.copyTo(temp); | ||
+ | |||
+ | //x and y values for the location of the object | ||
+ | int x = 0, y = 0; | ||
+ | |||
+ | // | ||
+ | vector< | ||
+ | vector< | ||
+ | |||
+ | // | ||
+ | findContours(temp, | ||
+ | |||
+ | // | ||
+ | int largest_area = 0; | ||
+ | |||
+ | //Tracking success. | ||
+ | bool objectFound = false; | ||
+ | |||
+ | //use moments method to find our filtered object | ||
+ | if (contours.size() > 0) | ||
+ | { | ||
+ | int numObjects = contours.size(); | ||
+ | //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter | ||
+ | if (numObjects< | ||
+ | { | ||
+ | for (int i = 0; i < contours.size(); | ||
+ | { | ||
+ | |||
+ | //Image moments: m00 is the area , m10/m00 is the column of the center of mass | ||
+ | // | ||
+ | Moments moment; | ||
+ | |||
+ | moment = moments((Mat)contours[i]); | ||
+ | double area = moment.m00; | ||
+ | |||
+ | //Find the largest contour to be draw on the image | ||
+ | //Also filter using a minimum and maximum area value to avoid false positives. | ||
+ | //When all the conditions are true we probably have a track able object. | ||
+ | |||
+ | if (area> | ||
+ | { | ||
+ | x = moment.m10 / area; //center of mass column | ||
+ | y = moment.m01 / area; //center of mass row | ||
+ | objectFound = true; | ||
+ | largest_area = area; | ||
+ | } | ||
+ | |||
+ | else objectFound = false; | ||
+ | |||
+ | |||
+ | } | ||
+ | //if we found an object lets track it | ||
+ | if (objectFound == true) | ||
+ | { | ||
+ | putText(image, | ||
+ | circle(image, | ||
+ | putText(image, | ||
+ | } | ||
+ | else putText(image, | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | //some boolean variables for different functionality within this | ||
+ | // | ||
+ | bool useTrack = false; | ||
+ | bool useFeatures = false; | ||
+ | char key = 0; | ||
+ | |||
+ | //Matrix to store each frame of the webcam feed | ||
+ | Mat coloredimage; | ||
+ | //matrix storage for HSV image | ||
+ | Mat HSV; | ||
+ | //matrix storage for binary threshold image | ||
+ | Mat threshold; | ||
+ | |||
+ | //create slider bars for HSV filtering | ||
+ | createTrackbars(); | ||
+ | //video capture object to acquire webcam feed | ||
+ | VideoCapture capture; | ||
+ | //open capture object at location zero (default location for webcam) | ||
+ | capture.open(0); | ||
+ | //set height and width of capture frame | ||
+ | capture.set(CV_CAP_PROP_FRAME_WIDTH, | ||
+ | capture.set(CV_CAP_PROP_FRAME_HEIGHT, | ||
+ | |||
+ | |||
+ | //Until the User press q the loop will run | ||
+ | //Get the image from the webcam -> convert to HSV -> Threshold the image | ||
+ | //using the HSV max and min set on the Trackbar window. | ||
+ | while (key != ' | ||
+ | { | ||
+ | //Get the image from the webcam | ||
+ | capture >> coloredimage; | ||
+ | //Convert the frame from BGR (RGB) to HSV | ||
+ | cvtColor(coloredimage, | ||
+ | |||
+ | //filter the HSV image using the minimum and maximum values set on the | ||
+ | // | ||
+ | inRange(HSV, | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | //If ' | ||
+ | //dilate and erode | ||
+ | if (key == ' | ||
+ | { | ||
+ | useFeatures = !useFeatures; | ||
+ | } | ||
+ | |||
+ | if (useFeatures) | ||
+ | { | ||
+ | DilateAndErode(threshold); | ||
+ | } | ||
+ | |||
+ | |||
+ | //If ' | ||
+ | //First we need to treshold the image by hand , so it will start as false | ||
+ | //After you get a good threshold image you can turn the tracking ON by pressing ' | ||
+ | if (key == ' | ||
+ | { | ||
+ | useTrack = !useTrack; | ||
+ | } | ||
+ | |||
+ | if (useTrack) | ||
+ | { | ||
+ | TrackObject(threshold, | ||
+ | } | ||
+ | |||
+ | //show frames | ||
+ | imshow(" | ||
+ | imshow(" | ||
+ | imshow(" | ||
+ | |||
+ | key = waitKey(25); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | ---- | ||
+ | |||
+ | ===== Understanding the Code ===== | ||
+ | |||
+ | <Code C++ linenums: | ||
+ | //initial min and max HSV filter values. | ||
+ | //these will be changed using trackbars | ||
+ | int iLowH = 0; | ||
+ | int iHighH = 179; | ||
+ | |||
+ | int iLowS = 0; | ||
+ | int iHighS = 255; | ||
+ | |||
+ | int iLowV = 0; | ||
+ | int iHighV = 255; | ||
+ | |||
+ | //default capture width and height | ||
+ | const int FRAME_WIDTH = 640; | ||
+ | const int FRAME_HEIGHT = 480; | ||
+ | //max number of objects to be detected in frame | ||
+ | const int MAX_NUM_OBJECTS = 50; | ||
+ | //minimum and maximum object area | ||
+ | const int min_area = 20 * 20; | ||
+ | const int max_area = FRAME_HEIGHT*FRAME_WIDTH /2; | ||
+ | </ | ||
+ | |||
+ | The code start with the same idea of the tracking using the object' | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | String intToString(int number) { | ||
+ | |||
+ | |||
+ | stringstream ss; | ||
+ | ss << number; | ||
+ | return ss.str(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Defining a simple function that will convert an integer number to a string. It will be useful when printing the center value on the screen (we want to track the object by showing its center and center position live on the image). It will take the integer from the center mass column and row positions and will return a stream with these values. | ||
+ | |||
+ | |||
+ | ---- | ||
+ | |||
+ | <Code C++ linenums: | ||
+ | //Function to create a window with the Trackbars to apply the Threshold. | ||
+ | void createTrackbars() { | ||
+ | |||
+ | //Open the window to display the Trackbars | ||
+ | namedWindow(" | ||
+ | |||
+ | //Hue values (0 - 179) | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | // | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | //Value (0-255) | ||
+ | cvCreateTrackbar(" | ||
+ | cvCreateTrackbar(" | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //Function to apply the erode and dilate features. | ||
+ | void DilateAndErode(Mat &image) { | ||
+ | |||
+ | //Defining the erode and dilate properties | ||
+ | //the erode element chosen here is a 3x3 piexels rectangle. | ||
+ | //Change the Size argument to optimize your threshold. | ||
+ | //dilate with 8x8 size element to make the threshold object more visible | ||
+ | |||
+ | Mat erodeElement = getStructuringElement(MORPH_RECT, | ||
+ | Mat dilateElement = getStructuringElement(MORPH_RECT, | ||
+ | |||
+ | //Apply erode and dilate | ||
+ | erode(image, | ||
+ | dilate(image, | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | Again Exactly the same function used on Tutorials 5 and 6. You can read the step by step [[http:// | ||
+ | |||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //Function to track an object using image moments. | ||
+ | void TrackObject(Mat threshold, Mat &image) { | ||
+ | |||
+ | //We will not use the threshold image directly we will use a copy | ||
+ | //if you use the original threshold , when you turn the tracking ON , the window | ||
+ | // | ||
+ | Mat temp; | ||
+ | threshold.copyTo(temp); | ||
+ | |||
+ | //x and y values for the location of the object | ||
+ | int x = 0, y = 0; | ||
+ | |||
+ | //Tracking success. | ||
+ | bool objectFound = false; | ||
+ | |||
+ | //Variables to store the largest area on the threshold image | ||
+ | int largest_area = 0; | ||
+ | |||
+ | // | ||
+ | vector< | ||
+ | vector< | ||
+ | |||
+ | // | ||
+ | findContours(temp, | ||
+ | </ | ||
+ | |||
+ | To make the program more readable lets separate the algorithm that will track the object from the main program - making it as a function and only be used when it's called on the main program ( we will create a on/off toggle similar to the morphological transformations use from the previous tutorials ). The arguments will be the column position of the center of mass (x) , the row position of the center o mass (y) , the image after the threshold made by the **" | ||
+ | |||
+ | To avoid errors lets use a copy of the threshold image on the tracking algorithm , this way you will not get any interference on the window that is showing the threshold image when you toggle on/off the tracking algorithm. We can easily copy the threshold image into other variable using **" | ||
+ | |||
+ | Also lets define the vectors that will be used by the **" | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //use moments method to find our filtered object | ||
+ | if (contours.size() > 0) | ||
+ | { | ||
+ | int numObjects = contours.size(); | ||
+ | //if number of objects greater than MAX_NUM_OBJECTS we have a noisy filter | ||
+ | if (numObjects< | ||
+ | { | ||
+ | </ | ||
+ | |||
+ | Now we start the loop that will search for the largest area , and will calculate the center of mass of this area using the image moments. Image moments is analogy to the moments in physics. The ' | ||
+ | |||
+ | The loop will run every time that we find a contour , i.e size >0 (the contours vector is outputted by the **" | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | for (int i = 0; i < contours.size(); | ||
+ | { | ||
+ | |||
+ | //Image moments: m00 is the area , m10/m00 is the column of the center of mass | ||
+ | // | ||
+ | Moments moment; | ||
+ | |||
+ | moment = moments((Mat)contours[i]); | ||
+ | double area = moment.m00; | ||
+ | |||
+ | //Find the largest contour to be draw on the image | ||
+ | //Also filter using a minimum and maximum area value to avoid false positives. | ||
+ | //When all the conditions are true we probably have a track able object. | ||
+ | |||
+ | if (area> | ||
+ | { | ||
+ | x = moment.m10 / area; //center of mass column | ||
+ | y = moment.m01 / area; //center of mass row | ||
+ | objectFound = true; | ||
+ | largest_area = area; | ||
+ | } | ||
+ | |||
+ | else objectFound = false; | ||
+ | </ | ||
+ | |||
+ | To find the image moments we can use a OpenCV function for it , but first we need to set a variable that will store those moments. The variable type is " | ||
+ | |||
+ | You can check [[http:// | ||
+ | |||
+ | We will enter in the conditional statement that will define if we found an object or no based on the area value. If the area value is greater than a minimum and less than a maximum that we set on the beginning of the program and if the area is greater than the previous largest area. If all is true then we still have track of the object and the new column and row position of the center of mass is calculated, also the Boolean that flag if the object is found will be TRUE. If the conditional statement is not satisfied the Boolean will be FALSE . | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //if we found an object lets track it | ||
+ | if (objectFound == true) | ||
+ | { | ||
+ | putText(image, | ||
+ | circle(image, | ||
+ | putText(image, | ||
+ | } | ||
+ | else putText(image, | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Now if we found an object we want to track its position. So the conditional statement if the objectFound variable is true will be used to print on the screen some useful informations. Lets draw a small circle on the center of mass of the contour and print the value of the center of mass column and row. | ||
+ | |||
+ | The first **" | ||
+ | |||
+ | If the object found flag is not true , it will display on the top of the screen that the searching is on progress. And this is the end of our tracking algorithm. | ||
+ | |||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | int main(int argc, char* argv[]) | ||
+ | { | ||
+ | //some boolean variables for different functionality within this | ||
+ | // | ||
+ | bool useTrack = false; | ||
+ | bool useFeatures = false; | ||
+ | char key = 0; | ||
+ | |||
+ | //Matrix to store each frame of the webcam feed | ||
+ | Mat coloredimage; | ||
+ | //matrix storage for HSV image | ||
+ | Mat HSV; | ||
+ | //matrix storage for binary threshold image | ||
+ | Mat threshold; | ||
+ | |||
+ | //create slider bars for HSV filtering | ||
+ | createTrackbars(); | ||
+ | //video capture object to acquire webcam feed | ||
+ | VideoCapture capture; | ||
+ | //open capture object at location zero (default location for webcam) | ||
+ | capture.open(0); | ||
+ | //set height and width of capture frame | ||
+ | capture.set(CV_CAP_PROP_FRAME_WIDTH, | ||
+ | capture.set(CV_CAP_PROP_FRAME_HEIGHT, | ||
+ | |||
+ | </ | ||
+ | |||
+ | We already have all the algorithm separated into functions , our main program will only be to make sure everything is called in the right time and to create the on/off toggles to show the morphological transformations and the tracking algorithm. | ||
+ | |||
+ | We start defining a set of variables that we will use. Mat variables to store the web camera video , the HSV version of this video and a threshold version after the " | ||
+ | |||
+ | Also lets initialize the video capture from the web camera video , and set the video properties using **" | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //Until the User press q the loop will run | ||
+ | //Get the image from the webcam -> convert to HSV -> Threshold the image | ||
+ | //using the HSV max and min set on the Trackbar window. | ||
+ | while (key != ' | ||
+ | { | ||
+ | //Get the image from the webcam | ||
+ | capture >> coloredimage; | ||
+ | //Convert the frame from BGR (RGB) to HSV | ||
+ | cvtColor(coloredimage, | ||
+ | |||
+ | //filter the HSV image using the minimum and maximum values set on the | ||
+ | // | ||
+ | inRange(HSV, | ||
+ | |||
+ | </ | ||
+ | |||
+ | The loop will run until the ' | ||
+ | The threshold process is done by hand , slinding the bars in the Trackbars window . The maximum and minimum values will be used by the **" | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //If ' | ||
+ | //dilate and erode | ||
+ | if (key == ' | ||
+ | { | ||
+ | useFeatures = !useFeatures; | ||
+ | } | ||
+ | |||
+ | if (useFeatures) | ||
+ | { | ||
+ | DilateAndErode(threshold); | ||
+ | } | ||
+ | |||
+ | |||
+ | //If ' | ||
+ | //First we need to treshold the image by hand , so it will start as false | ||
+ | //After you get a good threshold image you can turn the tracking ON by pressing ' | ||
+ | if (key == ' | ||
+ | { | ||
+ | useTrack = !useTrack; | ||
+ | } | ||
+ | |||
+ | if (useTrack) | ||
+ | { | ||
+ | TrackObject(threshold, | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | Here we create the on/off toggles to use the morphological transformations (by pressing ' | ||
+ | |||
+ | |||
+ | ---- | ||
+ | <Code C++ linenums: | ||
+ | //show frames | ||
+ | imshow(" | ||
+ | imshow(" | ||
+ | imshow(" | ||
+ | |||
+ | key = waitKey(25); | ||
+ | |||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | return 0; | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | |
opencv_tutorials_t7.1465262371.txt.gz · Last modified: 2016/06/06 18:19 by joaomatos