diff --git a/doc/conf.py b/doc/conf.py index a195d4727c..28100b3d1b 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -355,10 +355,32 @@ extlinks = {'cvt_color': ('http://opencv.willowgarage.com/documentation/cpp/imgp 'utilitysystemfunctions':('http://opencv.itseez.com/modules/core/doc/utility_and_system_functions_and_macros.html#%s', None), 'imgprocfilter':('http://opencv.itseez.com/modules/imgproc/doc/filtering.html#%s', None), 'svms':('http://opencv.itseez.com/modules/ml/doc/support_vector_machines.html#%s', None), + 'drawingfunc':('http://opencv.itseez.com/modules/core/doc/drawing_functions.html#%s', None), 'xmlymlpers':('http://opencv.itseez.com/modules/core/doc/xml_yaml_persistence.html#%s', None), 'huivideo' : ('http://opencv.itseez.com/modules/highgui/doc/reading_and_writing_images_and_video.html#%s', None), - 'filtering':('http://opencv.itseez.com/modules/imgproc/doc/filtering.html#%s', None), - 'point_polygon_test' : ('http://opencv.willowgarage.com/documentation/cpp/imgproc_structural_analysis_and_shape_descriptors.html#cv-pointpolygontest%s', None) + 'gpuinit' : ('http://opencv.itseez.com/modules/gpu/doc/initalization_and_information.html#%s', None), + 'gpudatastructure' : ('http://opencv.itseez.com/modules/gpu/doc/data_structures.html#%s', None), + 'gpuopmatrices' : ('http://opencv.itseez.com/modules/gpu/doc/operations_on_matrices.html#%s', None), + 'gpuperelement' : ('http://opencv.itseez.com/modules/gpu/doc/per_element_operations.html#%s', None), + 'gpuimgproc' : ('http://opencv.itseez.com/modules/gpu/doc/image_processing.html#%s', None), + 'gpumatrixreduct' : ('http://opencv.itseez.com/modules/gpu/doc/matrix_reductions.html#%s', None),'filtering':('http://opencv.itseez.com/modules/imgproc/doc/filtering.html#%s', None), + 'point_polygon_test' : ('http://opencv.willowgarage.com/documentation/cpp/imgproc_structural_analysis_and_shape_descriptors.html#cv-pointpolygontest%s', None), + 'feature_detector' : ( 'http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_feature_detectors.html#featuredetector%s', None), + 'feature_detector_detect' : ('http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_feature_detectors.html#cv-featuredetector-detect%s', None ), + 'surf_feature_detector' : ('http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_feature_detectors.html#surffeaturedetector%s', None ), + 'draw_keypoints' : ('http://opencv.willowgarage.com/documentation/cpp/features2d_drawing_function_of_keypoints_and_matches.html#cv-drawkeypoints%s', None ), + 'descriptor_extractor': ( 'http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_descriptor_extractors.html#descriptorextractor%s', None ), + 'descriptor_extractor_compute' : ( 'http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_descriptor_extractors.html#cv-descriptorextractor-compute%s', None ), + 'surf_descriptor_extractor' : ( 'http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_descriptor_extractors.html#surfdescriptorextractor%s', None ), + 'draw_matches' : ( 'http://opencv.willowgarage.com/documentation/cpp/features2d_drawing_function_of_keypoints_and_matches.html#cv-drawmatches%s', None ), + 'find_homography' : ('http://opencv.willowgarage.com/documentation/cpp/calib3d_camera_calibration_and_3d_reconstruction.html?#findHomography%s', None), + 'perspective_transform' : ('http://opencv.willowgarage.com/documentation/cpp/core_operations_on_arrays.html?#perspectiveTransform%s', None ), + 'flann_based_matcher' : ('http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_descriptor_matchers.html?#FlannBasedMatcher%s', None), + 'brute_force_matcher' : ('http://opencv.willowgarage.com/documentation/cpp/features2d_common_interfaces_of_descriptor_matchers.html?#BruteForceMatcher%s', None ), + 'flann' : ('http://opencv.willowgarage.com/documentation/cpp/flann_fast_approximate_nearest_neighbor_search.html?%s', None ), + 'cascade_classifier' : ('http://opencv.willowgarage.com/documentation/cpp/objdetect_cascade_classification.html#cascadeclassifier%s', None ), + 'cascade_classifier_load' : ('http://opencv.willowgarage.com/documentation/cpp/objdetect_cascade_classification.html#cv-cascadeclassifier-load%s', None ), + 'cascade_classifier_detect_multiscale' : ('http://opencv.willowgarage.com/documentation/cpp/objdetect_cascade_classification.html#cv-cascadeclassifier-detectmultiscale%s', None ) } diff --git a/doc/tutorials/core/adding_images/adding_images.rst b/doc/tutorials/core/adding_images/adding_images.rst index e37f4f7064..2df40c2701 100644 --- a/doc/tutorials/core/adding_images/adding_images.rst +++ b/doc/tutorials/core/adding_images/adding_images.rst @@ -8,11 +8,13 @@ Goal In this tutorial you will learn how to: -* What is *linear blending* and why it is useful. -* Add two images using :add_weighted:`addWeighted <>` +.. container:: enumeratevisibleitemswithsquare -Cool Theory -================= + * What is *linear blending* and why it is useful. + * Add two images using :add_weighted:`addWeighted <>` + +Theory +======= .. note:: @@ -24,12 +26,12 @@ From our previous tutorial, we know already a bit of *Pixel operators*. An inter g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x) -By varying :math:`\alpha` from :math:`0 \rightarrow 1` this operator can be used to perform a temporal *cross-disolve* between two images or videos, as seen in slide shows and film production (cool, eh?) +By varying :math:`\alpha` from :math:`0 \rightarrow 1` this operator can be used to perform a temporal *cross-disolve* between two images or videos, as seen in slide shows and film productions (cool, eh?) Code ===== -As usual, after the not-so-lengthy explanation, let's go to the code. Here it is: +As usual, after the not-so-lengthy explanation, let's go to the code: .. code-block:: cpp @@ -116,4 +118,4 @@ Result .. image:: images/Adding_Images_Tutorial_Result_0.jpg :alt: Blending Images Tutorial - Final Result - :align: center \ No newline at end of file + :align: center diff --git a/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.rst b/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.rst index 068ebc10d3..752527202f 100644 --- a/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.rst +++ b/doc/tutorials/core/basic_geometric_drawing/basic_geometric_drawing.rst @@ -7,13 +7,15 @@ Goals ====== In this tutorial you will learn how to: -* Use :point:`Point <>` to define 2D points in an image. -* Use :scalar:`Scalar <>` and why it is useful -* Draw a **line** by using the OpenCV function :line:`line <>` -* Draw an **ellipse** by using the OpenCV function :ellipse:`ellipse <>` -* Draw a **rectangle** by using the OpenCV function :rectangle:`rectangle <>` -* Draw a **circle** by using the OpenCV function :circle:`circle <>` -* Draw a **filled polygon** by using the OpenCV function :fill_poly:`fillPoly <>` +.. container:: enumeratevisibleitemswithsquare + + * Use :point:`Point <>` to define 2D points in an image. + * Use :scalar:`Scalar <>` and why it is useful + * Draw a **line** by using the OpenCV function :line:`line <>` + * Draw an **ellipse** by using the OpenCV function :ellipse:`ellipse <>` + * Draw a **rectangle** by using the OpenCV function :rectangle:`rectangle <>` + * Draw a **circle** by using the OpenCV function :circle:`circle <>` + * Draw a **filled polygon** by using the OpenCV function :fill_poly:`fillPoly <>` OpenCV Theory =============== @@ -22,7 +24,10 @@ For this tutorial, we will heavily use two structures: :point:`Point <>` and :sc Point ------- -It represents a 2D point, specified by its image coordinates :math:`x` and :math:`y`. We can define it as: + +.. container:: enumeratevisibleitemswithsquare + + It represents a 2D point, specified by its image coordinates :math:`x` and :math:`y`. We can define it as: .. code-block:: cpp @@ -51,7 +56,7 @@ Scalar Code ===== -* This code is in your OpenCV sample folder. Otherwise you can grab it from `here `_ +* This code is in your OpenCV sample folder. Otherwise you can grab it from `here `_ Explanation ============= @@ -126,11 +131,13 @@ Explanation As we can see, *MyLine* just call the function :line:`line <>`, which does the following: - * Draw a line from Point **start** to Point **end** - * The line is displayed in the image **img** - * The line color is defined by **Scalar( 0, 0, 0)** which is the RGB value correspondent to **Black** - * The line thickness is set to **thickness** (in this case 2) - * The line is a 8-connected one (**lineType** = 8) + .. container:: enumeratevisibleitemswithsquare + + * Draw a line from Point **start** to Point **end** + * The line is displayed in the image **img** + * The line color is defined by **Scalar( 0, 0, 0)** which is the RGB value correspondent to **Black** + * The line thickness is set to **thickness** (in this case 2) + * The line is a 8-connected one (**lineType** = 8) * *MyEllipse* @@ -153,13 +160,15 @@ Explanation } From the code above, we can observe that the function :ellipse:`ellipse <>` draws an ellipse such that: - - * The ellipse is displayed in the image **img** - * The ellipse center is located in the point **(w/2.0, w/2.0)** and is enclosed in a box of size **(w/4.0, w/16.0)** - * The ellipse is rotated **angle** degrees - * The ellipse extends an arc between **0** and **360** degrees - * The color of the figure will be **Scalar( 255, 255, 0)** which means blue in RGB value. - * The ellipse's **thickness** is 2. + + .. container:: enumeratevisibleitemswithsquare + + * The ellipse is displayed in the image **img** + * The ellipse center is located in the point **(w/2.0, w/2.0)** and is enclosed in a box of size **(w/4.0, w/16.0)** + * The ellipse is rotated **angle** degrees + * The ellipse extends an arc between **0** and **360** degrees + * The color of the figure will be **Scalar( 255, 255, 0)** which means blue in RGB value. + * The ellipse's **thickness** is 2. * *MyFilledCircle* @@ -181,11 +190,13 @@ Explanation Similar to the ellipse function, we can observe that *circle* receives as arguments: - * The image where the circle will be displayed (**img**) - * The center of the circle denoted as the Point **center** - * The radius of the circle: **w/32.0** - * The color of the circle: **Scalar(0, 0, 255)** which means *Red* in RGB - * Since **thickness** = -1, the circle will be drawn filled. + .. container:: enumeratevisibleitemswithsquare + + * The image where the circle will be displayed (**img**) + * The center of the circle denoted as the Point **center** + * The radius of the circle: **w/32.0** + * The color of the circle: **Scalar(0, 0, 255)** which means *Red* in BGR + * Since **thickness** = -1, the circle will be drawn filled. * *MyPolygon* @@ -230,12 +241,14 @@ Explanation } To draw a filled polygon we use the function :fill_poly:`fillPoly <>`. We note that: - - * The polygon will be drawn on **img** - * The vertices of the polygon are the set of points in **ppt** - * The total number of vertices to be drawn are **npt** - * The number of polygons to be drawn is only **1** - * The color of the polygon is defined by **Scalar( 255, 255, 255)**, which is the RGB value for *white* + + .. container:: enumeratevisibleitemswithsquare + + * The polygon will be drawn on **img** + * The vertices of the polygon are the set of points in **ppt** + * The total number of vertices to be drawn are **npt** + * The number of polygons to be drawn is only **1** + * The color of the polygon is defined by **Scalar( 255, 255, 255)**, which is the BGR value for *white* * *rectangle* @@ -250,10 +263,12 @@ Explanation Finally we have the :rectangle:`rectangle <>` function (we did not create a special function for this guy). We note that: - * The rectangle will be drawn on **rook_image** - * Two opposite vertices of the rectangle are defined by ** Point( 0, 7*w/8.0 )** and **Point( w, w)** - * The color of the rectangle is given by **Scalar(0, 255, 255)** which is the RGB value for *yellow* - * Since the thickness value is given by **-1**, the rectangle will be filled. + .. container:: enumeratevisibleitemswithsquare + + * The rectangle will be drawn on **rook_image** + * Two opposite vertices of the rectangle are defined by ** Point( 0, 7*w/8.0 )** and **Point( w, w)** + * The color of the rectangle is given by **Scalar(0, 255, 255)** which is the BGR value for *yellow* + * Since the thickness value is given by **-1**, the rectangle will be filled. Result ======= diff --git a/doc/tutorials/core/basic_linear_transform/basic_linear_transform.rst b/doc/tutorials/core/basic_linear_transform/basic_linear_transform.rst index cb605d0ccc..097e79e00b 100644 --- a/doc/tutorials/core/basic_linear_transform/basic_linear_transform.rst +++ b/doc/tutorials/core/basic_linear_transform/basic_linear_transform.rst @@ -18,8 +18,8 @@ In this tutorial you will learn how to: + Get some cool info about pixel transformations -Cool Theory -================= +Theory +======= .. note:: The explanation below belongs to the book `Computer Vision: Algorithms and Applications `_ by Richard Szeliski @@ -27,44 +27,52 @@ Cool Theory Image Processing -------------------- -* A general image processing operator is a function that takes one or more input images and produces an output image. +.. container:: enumeratevisibleitemswithsquare -* Image transforms can be seen as: + * A general image processing operator is a function that takes one or more input images and produces an output image. - * Point operators (pixel transforms) - * Neighborhood (area-based) operators + * Image transforms can be seen as: + + + Point operators (pixel transforms) + + Neighborhood (area-based) operators Pixel Transforms ^^^^^^^^^^^^^^^^^ -* In this kind of image processing transform, each output pixel's value depends on only the corresponding input pixel value (plus, potentially, some globally collected information or parameters). +.. container:: enumeratevisibleitemswithsquare -* Examples of such operators include *brightness and contrast adjustments* as well as color correction and transformations. + * In this kind of image processing transform, each output pixel's value depends on only the corresponding input pixel value (plus, potentially, some globally collected information or parameters). + + * Examples of such operators include *brightness and contrast adjustments* as well as color correction and transformations. Brightness and contrast adjustments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* Two commonly used point processes are *multiplication* and *addition* with a constant: - .. math:: +.. container:: enumeratevisibleitemswithsquare - g(x) = \alpha f(x) + \beta + * Two commonly used point processes are *multiplication* and *addition* with a constant: -* The parameters :math:`\alpha > 0` and :math:`\beta` are often called the *gain* and *bias* parameters; sometimes these parameters are said to control *contrast* and *brightness* respectively. - -* You can think of :math:`f(x)` as the source image pixels and :math:`g(x)` as the output image pixels. Then, more conveniently we can write the expression as: - - .. math:: - - g(i,j) = \alpha \cdot f(i,j) + \beta + .. math:: + + g(x) = \alpha f(x) + \beta - where :math:`i` and :math:`j` indicates that the pixel is located in the *i-th* row and *j-th* column. + * The parameters :math:`\alpha > 0` and :math:`\beta` are often called the *gain* and *bias* parameters; sometimes these parameters are said to control *contrast* and *brightness* respectively. + + * You can think of :math:`f(x)` as the source image pixels and :math:`g(x)` as the output image pixels. Then, more conveniently we can write the expression as: + + .. math:: + + g(i,j) = \alpha \cdot f(i,j) + \beta + + where :math:`i` and :math:`j` indicates that the pixel is located in the *i-th* row and *j-th* column. Code ===== -* The following code performs the operation :math:`g(i,j) = \alpha \cdot f(i,j) + \beta` -* Here it is: +.. container:: enumeratevisibleitemswithsquare + + * The following code performs the operation :math:`g(i,j) = \alpha \cdot f(i,j) + \beta` : .. code-block:: cpp @@ -132,8 +140,10 @@ Explanation #. Now, since we will make some transformations to this image, we need a new Mat object to store it. Also, we want this to have the following features: - * Initial pixel values equal to zero - * Same size and type as the original image + .. container:: enumeratevisibleitemswithsquare + + * Initial pixel values equal to zero + * Same size and type as the original image .. code-block:: cpp @@ -155,9 +165,11 @@ Explanation Notice the following: - * To access each pixel in the images we are using this syntax: *image.at(y,x)[c]* where *y* is the row, *x* is the column and *c* is R, G or B (0, 1 or 2). + .. container:: enumeratevisibleitemswithsquare - * Since the operation :math:`\alpha \cdot p(i,j) + \beta` can give values out of range or not integers (if :math:`\alpha` is float), we use :saturate_cast:`saturate_cast <>` to make sure the values are valid. + * To access each pixel in the images we are using this syntax: *image.at(y,x)[c]* where *y* is the row, *x* is the column and *c* is R, G or B (0, 1 or 2). + + * Since the operation :math:`\alpha \cdot p(i,j) + \beta` can give values out of range or not integers (if :math:`\alpha` is float), we use :saturate_cast:`saturate_cast <>` to make sure the values are valid. #. Finally, we create windows and show the images, the usual way. @@ -199,4 +211,4 @@ Result .. image:: images/Basic_Linear_Transform_Tutorial_Result_0.jpg :alt: Basic Linear Transform - Final Result - :align: center \ No newline at end of file + :align: center diff --git a/doc/tutorials/core/random_generator_and_text/random_generator_and_text.rst b/doc/tutorials/core/random_generator_and_text/random_generator_and_text.rst index fb487cab71..e463a57deb 100644 --- a/doc/tutorials/core/random_generator_and_text/random_generator_and_text.rst +++ b/doc/tutorials/core/random_generator_and_text/random_generator_and_text.rst @@ -8,16 +8,21 @@ Goals In this tutorial you will learn how to: -* Use the *Random Number generator class* (:rng:`RNG <>`) and how to get a random number from a uniform distribution. -* Display text on an OpenCV window by using the function :put_text:`putText <>` +.. container:: enumeratevisibleitemswithsquare + + * Use the *Random Number generator class* (:rng:`RNG <>`) and how to get a random number from a uniform distribution. + * Display text on an OpenCV window by using the function :put_text:`putText <>` Code ===== -* In the previous tutorial (:ref:`Drawing_1`) we drew diverse geometric figures, giving as input parameters such as coordinates (in the form of :point:`Points <>`), color, thickness, etc. You might have noticed that we gave specific values for these arguments. - -* In this tutorial, we intend to use *random* values for the drawing parameters. Also, we intend to populate our image with a big number of geometric figures. Since we will be initializing them in a random fashion, this process will be automatic and made by using *loops* . -* This code is in your OpenCV sample folder. Otherwise you can grab it from `here `_ . +.. container:: enumeratevisibleitemswithsquare + + * In the previous tutorial (:ref:`Drawing_1`) we drew diverse geometric figures, giving as input parameters such as coordinates (in the form of :point:`Points <>`), color, thickness, etc. You might have noticed that we gave specific values for these arguments. + + * In this tutorial, we intend to use *random* values for the drawing parameters. Also, we intend to populate our image with a big number of geometric figures. Since we will be initializing them in a random fashion, this process will be automatic and made by using *loops* . + + * This code is in your OpenCV sample folder. Otherwise you can grab it from `here `_ . Explanation ============ @@ -172,12 +177,14 @@ Explanation So, what does the function :put_text:`putText <>` do? In our example: - * Draws the text **"Testing text rendering"** in **image** - * The bottom-left corner of the text will be located in the Point **org** - * The font type is a random integer value in the range: :math:`[0, 8>`. - * The scale of the font is denoted by the expression **rng.uniform(0, 100)x0.05 + 0.1** (meaning its range is: :math:`[0.1, 5.1>`) - * The text color is random (denoted by **randomColor(rng)**) - * The text thickness ranges between 1 and 10, as specified by **rng.uniform(1,10)** + .. container:: enumeratevisibleitemswithsquare + + * Draws the text **"Testing text rendering"** in **image** + * The bottom-left corner of the text will be located in the Point **org** + * The font type is a random integer value in the range: :math:`[0, 8>`. + * The scale of the font is denoted by the expression **rng.uniform(0, 100)x0.05 + 0.1** (meaning its range is: :math:`[0.1, 5.1>`) + * The text color is random (denoted by **randomColor(rng)**) + * The text thickness ranges between 1 and 10, as specified by **rng.uniform(1,10)** As a result, we will get (analagously to the other drawing functions) **NUMBER** texts over our image, in random locations. @@ -257,4 +264,4 @@ As you just saw in the Code section, the program will sequentially execute diver .. image:: images/Drawing_2_Tutorial_Result_7.jpg :alt: Drawing Tutorial 2 - Final Result 7 - :align: center \ No newline at end of file + :align: center diff --git a/doc/tutorials/features2d/feature_description/feature_description.rst b/doc/tutorials/features2d/feature_description/feature_description.rst new file mode 100644 index 0000000000..ee139355a0 --- /dev/null +++ b/doc/tutorials/features2d/feature_description/feature_description.rst @@ -0,0 +1,104 @@ +.. _feature_description: + +Feature Description +******************* + +Goal +===== + +In this tutorial you will learn how to: + +.. container:: enumeratevisibleitemswithsquare + + * Use the :descriptor_extractor:`DescriptorExtractor<>` interface in order to find the feature vector correspondent to the keypoints. Specifically: + + * Use :surf_descriptor_extractor:`SurfDescriptorExtractor<>` and its function :descriptor_extractor:`compute<>` to perform the required calculations. + * Use a :brute_force_matcher:`BruteForceMatcher<>` to match the features vector + * Use the function :draw_matches:`drawMatches<>` to draw the detected matches. + + +Theory +====== + +Code +==== + +This tutorial code's is shown lines below. You can also download it from `here `_ + +.. code-block:: cpp + + #include + #include + #include "opencv2/core/core.hpp" + #include "opencv2/features2d/features2d.hpp" + #include "opencv2/highgui/highgui.hpp" + + using namespace cv; + + void readme(); + + /** @function main */ + int main( int argc, char** argv ) + { + if( argc != 3 ) + { return -1; } + + Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE ); + Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); + + if( !img_1.data || !img_2.data ) + { return -1; } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + + SurfFeatureDetector detector( minHessian ); + + std::vector keypoints_1, keypoints_2; + + detector.detect( img_1, keypoints_1 ); + detector.detect( img_2, keypoints_2 ); + + //-- Step 2: Calculate descriptors (feature vectors) + SurfDescriptorExtractor extractor; + + Mat descriptors_1, descriptors_2; + + extractor.compute( img_1, keypoints_1, descriptors_1 ); + extractor.compute( img_2, keypoints_2, descriptors_2 ); + + //-- Step 3: Matching descriptor vectors with a brute force matcher + BruteForceMatcher< L2 > matcher; + std::vector< DMatch > matches; + matcher.match( descriptors_1, descriptors_2, matches ); + + //-- Draw matches + Mat img_matches; + drawMatches( img_1, keypoints_1, img_2, keypoints_2, matches, img_matches ); + + //-- Show detected matches + imshow("Matches", img_matches ); + + waitKey(0); + + return 0; + } + + /** @function readme */ + void readme() + { std::cout << " Usage: ./SURF_descriptor " << std::endl; } + +Explanation +============ + +Result +====== + +#. Here is the result after applying the BruteForce matcher between the two original images: + + .. image:: images/Feature_Description_BruteForce_Result.jpg + :align: center + :height: 200pt + + + diff --git a/doc/tutorials/features2d/feature_description/images/Feature_Description_BruteForce_Result.jpg b/doc/tutorials/features2d/feature_description/images/Feature_Description_BruteForce_Result.jpg new file mode 100644 index 0000000000..975caa62ef Binary files /dev/null and b/doc/tutorials/features2d/feature_description/images/Feature_Description_BruteForce_Result.jpg differ diff --git a/doc/tutorials/features2d/feature_detection/feature_detection.rst b/doc/tutorials/features2d/feature_detection/feature_detection.rst new file mode 100644 index 0000000000..a208c83757 --- /dev/null +++ b/doc/tutorials/features2d/feature_detection/feature_detection.rst @@ -0,0 +1,97 @@ +.. _feature_detection: + +Feature Detection +****************** + +Goal +===== + +In this tutorial you will learn how to: + +.. container:: enumeratevisibleitemswithsquare + + * Use the :feature_detector:`FeatureDetector<>` interface in order to find interest points. Specifically: + + * Use the :surf_feature_detector:`SurfFeatureDetector<>` and its function :feature_detector_detect:`detect<>` to perform the detection process + * Use the function :draw_keypoints:`drawKeypoints<>` to draw the detected keypoints + + +Theory +====== + +Code +==== + +This tutorial code's is shown lines below. You can also download it from `here `_ + +.. code-block:: cpp + + #include + #include + #include "opencv2/core/core.hpp" + #include "opencv2/features2d/features2d.hpp" + #include "opencv2/highgui/highgui.hpp" + + using namespace cv; + + void readme(); + + /** @function main */ + int main( int argc, char** argv ) + { + if( argc != 3 ) + { readme(); return -1; } + + Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE ); + Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); + + if( !img_1.data || !img_2.data ) + { std::cout<< " --(!) Error reading images " << std::endl; return -1; } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + + SurfFeatureDetector detector( minHessian ); + + std::vector keypoints_1, keypoints_2; + + detector.detect( img_1, keypoints_1 ); + detector.detect( img_2, keypoints_2 ); + + //-- Draw keypoints + Mat img_keypoints_1; Mat img_keypoints_2; + + drawKeypoints( img_1, keypoints_1, img_keypoints_1, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); + drawKeypoints( img_2, keypoints_2, img_keypoints_2, Scalar::all(-1), DrawMatchesFlags::DEFAULT ); + + //-- Show detected (drawn) keypoints + imshow("Keypoints 1", img_keypoints_1 ); + imshow("Keypoints 2", img_keypoints_2 ); + + waitKey(0); + + return 0; + } + + /** @function readme */ + void readme() + { std::cout << " Usage: ./SURF_detector " << std::endl; } + +Explanation +============ + +Result +====== + +#. Here is the result of the feature detection applied to the first image: + + .. image:: images/Feature_Detection_Result_a.jpg + :align: center + :height: 125pt + +#. And here is the result for the second image: + + .. image:: images/Feature_Detection_Result_b.jpg + :align: center + :height: 200pt + diff --git a/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_a.jpg b/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_a.jpg new file mode 100644 index 0000000000..cca9a2b438 Binary files /dev/null and b/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_a.jpg differ diff --git a/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_b.jpg b/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_b.jpg new file mode 100644 index 0000000000..129450eb4f Binary files /dev/null and b/doc/tutorials/features2d/feature_detection/images/Feature_Detection_Result_b.jpg differ diff --git a/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.rst b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.rst new file mode 100644 index 0000000000..10135ff677 --- /dev/null +++ b/doc/tutorials/features2d/feature_flann_matcher/feature_flann_matcher.rst @@ -0,0 +1,132 @@ +.. _feature_flann_matcher: + +Feature Matching with FLANN +**************************** + +Goal +===== + +In this tutorial you will learn how to: + +.. container:: enumeratevisibleitemswithsquare + + * Use the :flann_based_matcher:`FlannBasedMatcher<>` interface in order to perform a quick and efficient matching by using the :flann:`FLANN<>` ( *Fast Approximate Nearest Neighbor Search Library* ) + + +Theory +====== + +Code +==== + +This tutorial code's is shown lines below. You can also download it from `here `_ + +.. code-block:: cpp + + #include + #include + #include "opencv2/core/core.hpp" + #include "opencv2/features2d/features2d.hpp" + #include "opencv2/highgui/highgui.hpp" + + using namespace cv; + + void readme(); + + /** @function main */ + int main( int argc, char** argv ) + { + if( argc != 3 ) + { readme(); return -1; } + + Mat img_1 = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE ); + Mat img_2 = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); + + if( !img_1.data || !img_2.data ) + { std::cout<< " --(!) Error reading images " << std::endl; return -1; } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + + SurfFeatureDetector detector( minHessian ); + + std::vector keypoints_1, keypoints_2; + + detector.detect( img_1, keypoints_1 ); + detector.detect( img_2, keypoints_2 ); + + //-- Step 2: Calculate descriptors (feature vectors) + SurfDescriptorExtractor extractor; + + Mat descriptors_1, descriptors_2; + + extractor.compute( img_1, keypoints_1, descriptors_1 ); + extractor.compute( img_2, keypoints_2, descriptors_2 ); + + //-- Step 3: Matching descriptor vectors using FLANN matcher + FlannBasedMatcher matcher; + std::vector< DMatch > matches; + matcher.match( descriptors_1, descriptors_2, matches ); + + double max_dist = 0; double min_dist = 100; + + //-- Quick calculation of max and min distances between keypoints + for( int i = 0; i < descriptors_1.rows; i++ ) + { double dist = matches[i].distance; + if( dist < min_dist ) min_dist = dist; + if( dist > max_dist ) max_dist = dist; + } + + printf("-- Max dist : %f \n", max_dist ); + printf("-- Min dist : %f \n", min_dist ); + + //-- Draw only "good" matches (i.e. whose distance is less than 2*min_dist ) + //-- PS.- radiusMatch can also be used here. + std::vector< DMatch > good_matches; + + for( int i = 0; i < descriptors_1.rows; i++ ) + { if( matches[i].distance < 2*min_dist ) + { good_matches.push_back( matches[i]); } + } + + //-- Draw only "good" matches + Mat img_matches; + drawMatches( img_1, keypoints_1, img_2, keypoints_2, + good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), + vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Show detected matches + imshow( "Good Matches", img_matches ); + + for( int i = 0; i < good_matches.size(); i++ ) + { printf( "-- Good Match [%d] Keypoint 1: %d -- Keypoint 2: %d \n", i, good_matches[i].queryIdx, good_matches[i].trainIdx ); } + + waitKey(0); + + return 0; + } + + /** @function readme */ + void readme() + { std::cout << " Usage: ./SURF_FlannMatcher " << std::endl; } + +Explanation +============ + +Result +====== + +#. Here is the result of the feature detection applied to the first image: + + .. image:: images/Featur_FlannMatcher_Result.jpg + :align: center + :height: 250pt + +#. Additionally, we get as console output the keypoints filtered: + + .. image:: images/Feature_FlannMatcher_Keypoints_Result.jpg + :align: center + :height: 250pt + + + diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Featur_FlannMatcher_Result.jpg b/doc/tutorials/features2d/feature_flann_matcher/images/Featur_FlannMatcher_Result.jpg new file mode 100644 index 0000000000..e3f66fa0d1 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Featur_FlannMatcher_Result.jpg differ diff --git a/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Keypoints_Result.jpg b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Keypoints_Result.jpg new file mode 100644 index 0000000000..7648e58607 Binary files /dev/null and b/doc/tutorials/features2d/feature_flann_matcher/images/Feature_FlannMatcher_Keypoints_Result.jpg differ diff --git a/doc/tutorials/features2d/feature_homography/feature_homography.rst b/doc/tutorials/features2d/feature_homography/feature_homography.rst new file mode 100644 index 0000000000..e04be717d0 --- /dev/null +++ b/doc/tutorials/features2d/feature_homography/feature_homography.rst @@ -0,0 +1,148 @@ +.. _feature_homography: + +Features2D + Homography to find a known object +********************************************** + +Goal +===== + +In this tutorial you will learn how to: + +.. container:: enumeratevisibleitemswithsquare + + * Use the function :find_homography:`findHomography<>` to find the transform between matched keypoints. + * Use the function :perspective_transform:`perspectiveTransform<>` to map the points. + + +Theory +====== + +Code +==== + +This tutorial code's is shown lines below. You can also download it from `here `_ + +.. code-block:: cpp + + #include + #include + #include "opencv2/core/core.hpp" + #include "opencv2/features2d/features2d.hpp" + #include "opencv2/highgui/highgui.hpp" + #include "opencv2/calib3d/calib3d.hpp" + + using namespace cv; + + void readme(); + + /** @function main */ + int main( int argc, char** argv ) + { + if( argc != 3 ) + { readme(); return -1; } + + Mat img_object = imread( argv[1], CV_LOAD_IMAGE_GRAYSCALE ); + Mat img_scene = imread( argv[2], CV_LOAD_IMAGE_GRAYSCALE ); + + if( !img_object.data || !img_scene.data ) + { std::cout<< " --(!) Error reading images " << std::endl; return -1; } + + //-- Step 1: Detect the keypoints using SURF Detector + int minHessian = 400; + + SurfFeatureDetector detector( minHessian ); + + std::vector keypoints_object, keypoints_scene; + + detector.detect( img_object, keypoints_object ); + detector.detect( img_scene, keypoints_scene ); + + //-- Step 2: Calculate descriptors (feature vectors) + SurfDescriptorExtractor extractor; + + Mat descriptors_object, descriptors_scene; + + extractor.compute( img_object, keypoints_object, descriptors_object ); + extractor.compute( img_scene, keypoints_scene, descriptors_scene ); + + //-- Step 3: Matching descriptor vectors using FLANN matcher + FlannBasedMatcher matcher; + std::vector< DMatch > matches; + matcher.match( descriptors_object, descriptors_scene, matches ); + + double max_dist = 0; double min_dist = 100; + + //-- Quick calculation of max and min distances between keypoints + for( int i = 0; i < descriptors_object.rows; i++ ) + { double dist = matches[i].distance; + if( dist < min_dist ) min_dist = dist; + if( dist > max_dist ) max_dist = dist; + } + + printf("-- Max dist : %f \n", max_dist ); + printf("-- Min dist : %f \n", min_dist ); + + //-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist ) + std::vector< DMatch > good_matches; + + for( int i = 0; i < descriptors_object.rows; i++ ) + { if( matches[i].distance < 3*min_dist ) + { good_matches.push_back( matches[i]); } + } + + Mat img_matches; + drawMatches( img_object, keypoints_object, img_scene, keypoints_scene, + good_matches, img_matches, Scalar::all(-1), Scalar::all(-1), + vector(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS ); + + //-- Localize the object + std::vector obj; + std::vector scene; + + for( int i = 0; i < good_matches.size(); i++ ) + { + //-- Get the keypoints from the good matches + obj.push_back( keypoints_object[ good_matches[i].queryIdx ].pt ); + scene.push_back( keypoints_scene[ good_matches[i].trainIdx ].pt ); + } + + Mat H = findHomography( obj, scene, CV_RANSAC ); + + //-- Get the corners from the image_1 ( the object to be "detected" ) + std::vector obj_corners(4); + obj_corners[0] = cvPoint(0,0); obj_corners[1] = cvPoint( img_object.cols, 0 ); + obj_corners[2] = cvPoint( img_object.cols, img_object.rows ); obj_corners[3] = cvPoint( 0, img_object.rows ); + std::vector scene_corners(4); + + perspectiveTransform( obj_corners, scene_corners, H); + + //-- Draw lines between the corners (the mapped object in the scene - image_2 ) + line( img_matches, scene_corners[0] + Point2f( img_object.cols, 0), scene_corners[1] + Point2f( img_object.cols, 0), Scalar(0, 255, 0), 4 ); + line( img_matches, scene_corners[1] + Point2f( img_object.cols, 0), scene_corners[2] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[2] + Point2f( img_object.cols, 0), scene_corners[3] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + line( img_matches, scene_corners[3] + Point2f( img_object.cols, 0), scene_corners[0] + Point2f( img_object.cols, 0), Scalar( 0, 255, 0), 4 ); + + //-- Show detected matches + imshow( "Good Matches & Object detection", img_matches ); + + waitKey(0); + return 0; + } + + /** @function readme */ + void readme() + { std::cout << " Usage: ./SURF_descriptor " << std::endl; } + +Explanation +============ + +Result +====== + + +#. And here is the result for the detected object (highlighted in green) + + .. image:: images/Feature_Homography_Result.jpg + :align: center + :height: 200pt + diff --git a/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg new file mode 100644 index 0000000000..d043a5a6e9 Binary files /dev/null and b/doc/tutorials/features2d/feature_homography/images/Feature_Homography_Result.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Description_Tutorial_Cover.jpg b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Description_Tutorial_Cover.jpg new file mode 100644 index 0000000000..975caa62ef Binary files /dev/null and b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Description_Tutorial_Cover.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Detection_Tutorial_Cover.jpg b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Detection_Tutorial_Cover.jpg new file mode 100644 index 0000000000..cca9a2b438 Binary files /dev/null and b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Detection_Tutorial_Cover.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Flann_Matcher_Tutorial_Cover.jpg b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Flann_Matcher_Tutorial_Cover.jpg new file mode 100644 index 0000000000..e3f66fa0d1 Binary files /dev/null and b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Flann_Matcher_Tutorial_Cover.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Homography_Tutorial_Cover.jpg b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Homography_Tutorial_Cover.jpg new file mode 100644 index 0000000000..d509cd9eb7 Binary files /dev/null and b/doc/tutorials/features2d/table_of_content_features2d/images/Feature_Homography_Tutorial_Cover.jpg differ diff --git a/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst b/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst index 062f03fd70..e0d4381910 100644 --- a/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst +++ b/doc/tutorials/features2d/table_of_content_features2d/table_of_content_features2d.rst @@ -72,7 +72,7 @@ Learn about how to use the feature points detectors, descriptors and matching f .. cssclass:: toctableopencv ===================== ============================================== - |Subpixel| **Title:** :ref:`corner_subpixeles` + |Subpixel| **Title:** :ref:`corner_subpixeles` *Compatibility:* > OpenCV 2.0 @@ -86,6 +86,84 @@ Learn about how to use the feature points detectors, descriptors and matching f :height: 90pt :width: 90pt ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |FeatureDetect| **Title:** :ref:`feature_detection` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_AnaH| + + In this tutorial, you will use *features2d* to detect interest points. + + ===================== ============================================== + + .. |FeatureDetect| image:: images/Feature_Detection_Tutorial_Cover.jpg + :height: 90pt + :width: 90pt + + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |FeatureDescript| **Title:** :ref:`feature_description` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_AnaH| + + In this tutorial, you will use *features2d* to calculate feature vectors. + + ===================== ============================================== + + .. |FeatureDescript| image:: images/Feature_Description_Tutorial_Cover.jpg + :height: 90pt + :width: 90pt + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |FeatureFlann| **Title:** :ref:`feature_flann_matcher` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_AnaH| + + In this tutorial, you will use the FLANN library to make a fast matching. + + ===================== ============================================== + + .. |FeatureFlann| image:: images/Feature_Flann_Matcher_Tutorial_Cover.jpg + :height: 90pt + :width: 90pt + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |FeatureHomo| **Title:** :ref:`feature_homography` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_AnaH| + + In this tutorial, you will use *features2d* and *calib3d* to detect an object in a scene. + + ===================== ============================================== + + .. |FeatureHomo| image:: images/Feature_Homography_Tutorial_Cover.jpg + :height: 90pt + :width: 90pt + + + .. tabularcolumns:: m{100pt} m{300pt} .. cssclass:: toctableopencv @@ -112,8 +190,17 @@ Learn about how to use the feature points detectors, descriptors and matching f .. toctree:: :hidden: + ../feature_description/feature_description + ../feature_detection/feature_detection ../trackingmotion/harris_detector/harris_detector + ../feature_flann_matcher/feature_flann_matcher + ../feature_homography/feature_homography ../trackingmotion/good_features_to_track/good_features_to_track.rst ../trackingmotion/generic_corner_detector/generic_corner_detector ../trackingmotion/corner_subpixeles/corner_subpixeles - ../detection_of_planar_objects/detection_of_planar_objects \ No newline at end of file + ../feature_detection/feature_detection + ../feature_detection/feature_description + ../feature_flann_matcher/feature_flann_matcher + ../feature_homography/feature_homography + ../detection_of_planar_objects/detection_of_planar_objects + diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.rst b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.rst index 00782364b2..7fad76e13a 100644 --- a/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.rst +++ b/doc/tutorials/features2d/trackingmotion/good_features_to_track/good_features_to_track.rst @@ -88,14 +88,14 @@ This tutorial code's is shown lines below. You can also download it from `here < /// Apply corner detection goodFeaturesToTrack( src_gray, - corners, - maxCorners, - qualityLevel, - minDistance, - Mat(), - blockSize, - useHarrisDetector, - k ); + corners, + maxCorners, + qualityLevel, + minDistance, + Mat(), + blockSize, + useHarrisDetector, + k ); /// Draw corners detected @@ -116,7 +116,7 @@ Explanation Result ====== -.. image:: images/Shi_Tomasi_Detector_Result.jpg +.. image:: images/Feature_Detection_Result_a.jpg :align: center diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg new file mode 100644 index 0000000000..cca9a2b438 Binary files /dev/null and b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_a.jpg differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg new file mode 100644 index 0000000000..129450eb4f Binary files /dev/null and b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Feature_Detection_Result_b.jpg differ diff --git a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Shi_Tomasi_Detector_Result.jpg b/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Shi_Tomasi_Detector_Result.jpg deleted file mode 100644 index abfad97291..0000000000 Binary files a/doc/tutorials/features2d/trackingmotion/good_features_to_track/images/Shi_Tomasi_Detector_Result.jpg and /dev/null differ diff --git a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.rst b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.rst index de849c74f7..63e35b26c3 100644 --- a/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.rst +++ b/doc/tutorials/features2d/trackingmotion/harris_detector/harris_detector.rst @@ -6,15 +6,46 @@ Harris corner detector Goal ===== -In this tutorial you will learn how to: +In this tutorial you will learn: .. container:: enumeratevisibleitemswithsquare + * What features are and why they are important * Use the function :corner_harris:`cornerHarris <>` to detect corners using the Harris-Stephens method. Theory ====== +What is a feature? +------------------- + +.. container:: enumeratevisibleitemswithsquare + + * In computer vision, usually we need to find matching points between different frames of an environment. Why? If we know how two images relate to each other, we can use *both* images to extract information of them. + + * When we say **matching points** we are referring, in a general sense, to *characteristics* in the scene that we can recognize easily. We call these characteristics **features**. + + * **So, what characteristics should a feature have?** + + * It must be *uniquely recognizable* + + +Types of Image Features +------------------------ + +To mention a few: + +.. container:: enumeratevisibleitemswithsquare + + * Edges + * Corner (also known as interest points) + * Blobs (also known as regions of interest ) + +In this tutorial we will study the *corner* features, specifically. + +Why is a corner so special? +---------------------------- + Code ==== diff --git a/doc/tutorials/gpu/gpu-basics-similarity/gpu-basics-similarity.rst b/doc/tutorials/gpu/gpu-basics-similarity/gpu-basics-similarity.rst new file mode 100644 index 0000000000..04ce2b2d77 --- /dev/null +++ b/doc/tutorials/gpu/gpu-basics-similarity/gpu-basics-similarity.rst @@ -0,0 +1 @@ +.. _gpuBasicsSimilarity: Similarity check (PNSR and SSIM) on the GPU ******************************************* Goal ==== In the :ref:`videoInputPSNRMSSIM` tutorial I already presented the PSNR and SSIM methods for checking the similarity between the two images. And as you could see there performing these takes quite some time, especially in the case of the SSIM. However, if the performance numbers of an OpenCV implementation for the CPU do not satisfy you and you happen to have an NVidia CUDA GPU device in your system all is not lost. You may try to port or write your algorithm for the video card. This tutorial will give a good grasp on how to approach coding by using the GPU module of OpenCV. As a prerequisite you should already know how to handle the core, highgui and imgproc modules. So, our goals are: .. container:: enumeratevisibleitemswithsquare + What's different compared to the CPU? + Create the GPU code for the PSNR and SSIM + Optimize the code for maximal performance The source code =============== You may also find the source code and these video file in the :file:`samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity` folder of the OpenCV source library or :download:`download it from here <../../../../samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp>`. The full source code is quite long (due to the controlling of the application via the command line arguments and performance measurement). Therefore, to avoid cluttering up these sections with those you'll find here only the functions itself. The PSNR returns a float number, that if the two inputs are similar between 30 and 50 (higher is better). .. literalinclude:: ../../../../samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp :language: cpp :linenos: :tab-width: 4 :lines: 165-210, 18-23, 210-235 The SSIM returns the MSSIM of the images. This is too a float number between zero and one (higher is better), however we have one for each channel. Therefore, we return a *Scalar* OpenCV data structure: .. literalinclude:: ../../../../samples/cpp/tutorial_code/gpu/gpu-basics-similarity/gpu-basics-similarity.cpp :language: cpp :linenos: :tab-width: 4 :lines: 235-355, 26-42, 357- How to do it? - The GPU ======================= Now as you can see we have three types of functions for each operation. One for the CPU and two for the GPU. The reason I made two for the GPU is too illustrate that often simple porting your CPU to GPU will actually make it slower. If you want some performance gain you will need to remember a few rules, whose I'm going to detail later on. The development of the GPU module was made so that it resembles as much as possible its CPU counterpart. This is to make porting easy. The first thing you need to do before writing any code is to link the GPU module to your project, and include the header file for the module. All the functions and data structures of the GPU are in a *gpu* sub namespace of the *cv* namespace. You may add this to the default one via the *use namespace* keyword, or mark it everywhere explicitly via the cv:: to avoid confusion. I'll do the later. .. code-block:: cpp #include // GPU structures and methods GPU stands for **g**\ raphics **p**\ rocessing **u**\ nit. It was originally build to render graphical scenes. These scenes somehow build on a lot of data. Nevertheless, these aren't all dependent one from another in a sequential way and as it is possible a parallel processing of them. Due to this a GPU will contain multiple smaller processing units. These aren't the state of the art processors and on a one on one test with a CPU it will fall behind. However, its strength lies in its numbers. In the last years there has been an increasing trend to harvest these massive parallel powers of the GPU in non-graphical scene rendering too. This gave birth to the general-purpose computation on graphics processing units (GPGPU). The GPU has its own memory. When you read data from the hard drive with OpenCV into a *Mat* object that takes place in your systems memory. The CPU works somehow directly on this (via its cache), however the GPU cannot. He has too transferred the information he will use for calculations from the system memory to its own. This is done via an upload process and takes time. In the end the result will have to be downloaded back to your system memory for your CPU to see it and use it. Porting small functions to GPU is not recommended as the upload/download time will be larger than the amount you gain by a parallel execution. *Mat* objects are stored **only** in the system memory (or the CPU cache). For getting an OpenCV matrix to the GPU you'll need to use its GPU counterpart :gpudatastructure:`GpuMat `. It works similar to the *Mat* with a 2D only limitation and no reference returning for its functions (cannot mix GPU references with CPU ones). To upload a *Mat* object to the *GPU* you need to call the *upload* function after creating an instance of the class. To download you may use simple assignment to a *Mat* object or use the *download* function. .. code-block:: cpp Mat I1; // Main memory item - read image into with imread for example gpu::GpuMat gI; // GPU matrix - for now empty gI1.upload(I1); // Upload a data from the system memory to the GPU memory I1 = gI1; // Download, gI1.download(I1) will work too Once you have your data up in the GPU memory you may call GPU enabled functions of OpenCV. Most of the functions keep the same name just as on the CPU, with the difference that they only accept *GpuMat* inputs. A full list of these you will find in the documentation: `online here `_ or the OpenCV reference manual that comes with the source code. Another thing to keep in mind is that not for all channel numbers you can make efficient algorithms on the GPU. Generally, I found that the input images for the GPU images need to be either one or four channel ones and one of the char or float type for the item sizes. No double support on the GPU, sorry. Passing other types of objects for some functions will result in an exception thrown, and an error message on the error output. The documentation details in most of the places the types accepted for the inputs. If you have three channel images as an input you can do two things: either adds a new channel (and use char elements) or split up the image and call the function for each image. The first one isn't really recommended as you waste memory. For some functions, where the position of the elements (neighbor items) doesn't matter quick solution is to just reshape it into a single channel image. This is the case for the PSNR implementation where for the *absdiff* method the value of the neighbors is not important. However, for the *GaussianBlur* this isn't an option and such need to use the split method for the SSIM. With this knowledge you can already make a GPU viable code (like mine GPU one) and run it. You'll be surprised to see that it might turn out slower than your CPU implementation. Optimization ============ The reason for this is that you're throwing out on the window the price for memory allocation and data transfer. And on the GPU this is damn high. Another possibility for optimization is to introduce asynchronous OpenCV GPU calls too with the help of the :gpudatastructure:`gpu::Stream `. 1. Memory allocation on the GPU is considerable. Therefore, if it’s possible allocate new memory as few times as possible. If you create a function what you intend to call multiple times it is a good idea to allocate any local parameters for the function only once, during the first call. To do this you create a data structure containing all the local variables you will use. For instance in case of the PSNR these are: .. code-block:: cpp struct BufferPSNR // Optimized GPU versions { // Data allocations are very expensive on GPU. Use a buffer to solve: allocate once reuse later. gpu::GpuMat gI1, gI2, gs, t1,t2; gpu::GpuMat buf; }; Then create an instance of this in the main program: .. code-block:: cpp BufferPSNR bufferPSNR; And finally pass this to the function each time you call it: .. code-block:: cpp double getPSNR_GPU_optimized(const Mat& I1, const Mat& I2, BufferPSNR& b) Now you access these local parameters as: *b.gI1*, *b.buf* and so on. The GpuMat will only reallocate itself on a new call if the new matrix size is different from the previous one. #. Avoid unnecessary function data transfers. Any small data transfer will be significant one once you go to the GPU. Therefore, if possible make all calculations in-place (in other words do not create new memory objects - for reasons explained at the previous point). For example, although expressing arithmetical operations may be easier to express in one line formulas, it will be slower. In case of the SSIM at one point I need to calculate: .. code-block:: cpp b.t1 = 2 * b.mu1_mu2 + C1; Although the upper call will succeed observe that there is a hidden data transfer present. Before it makes the addition it needs to store somewhere the multiplication. Therefore, it will create a local matrix in the background, add to that the *C1* value and finally assign that to *t1*. To avoid this we use the gpu functions, instead of the arithmetic operators: .. code-block:: cpp gpu::multiply(b.mu1_mu2, 2, b.t1); //b.t1 = 2 * b.mu1_mu2 + C1; gpu::add(b.t1, C1, b.t1); #. Use asynchronous calls (the :gpudatastructure:`gpu::Stream `). By default whenever you call a gpu function it will wait for the call to finish and return with the result afterwards. However, it is possible to make asynchronous calls, meaning it will call for the operation execution, make the costly data allocations for the algorithm and return back right away. Now you can call another function if you wish to do so. For the MSSIM this is a small optimization point. In our default implementation we split up the image into channels and call then for each channel the gpu functions. A small degree of parallelization is possible with the stream. By using a stream we can make the data allocation, upload operations while the GPU is already executing a given method. For example we need to upload two images. We queue these one after another and call already the function that processes it. The functions will wait for the upload to finish, however while that happens makes the output buffer allocations for the function to be executed next. .. code-block:: cpp gpu::Stream stream; stream.enqueueConvert(b.gI1, b.t1, CV_32F); // Upload gpu::split(b.t1, b.vI1, stream); // Methods (pass the stream as final parameter). gpu::multiply(b.vI1[i], b.vI1[i], b.I1_2, stream); // I1^2 Result and conclusion ===================== On an Intel P8700 laptop CPU paired with a low end NVidia GT220M here are the performance numbers: .. code-block:: cpp Time of PSNR CPU (averaged for 10 runs): 41.4122 milliseconds. With result of: 19.2506 Time of PSNR GPU (averaged for 10 runs): 158.977 milliseconds. With result of: 19.2506 Initial call GPU optimized: 31.3418 milliseconds. With result of: 19.2506 Time of PSNR GPU OPTIMIZED ( / 10 runs): 24.8171 milliseconds. With result of: 19.2506 Time of MSSIM CPU (averaged for 10 runs): 484.343 milliseconds. With result of B0.890964 G0.903845 R0.936934 Time of MSSIM GPU (averaged for 10 runs): 745.105 milliseconds. With result of B0.89922 G0.909051 R0.968223 Time of MSSIM GPU Initial Call 357.746 milliseconds. With result of B0.890964 G0.903845 R0.936934 Time of MSSIM GPU OPTIMIZED ( / 10 runs): 203.091 milliseconds. With result of B0.890964 G0.903845 R0.936934 In both cases we managed a performance increase of almost 100% compared to the CPU implementation. It may be just the improvement needed for your application to work. You may observe a runtime instance of this on the `YouTube here `_. .. raw:: html
\ No newline at end of file diff --git a/doc/tutorials/gpu/table_of_content_gpu/images/gpu-basics-similarity.png b/doc/tutorials/gpu/table_of_content_gpu/images/gpu-basics-similarity.png new file mode 100644 index 0000000000..3d6cca4bfb Binary files /dev/null and b/doc/tutorials/gpu/table_of_content_gpu/images/gpu-basics-similarity.png differ diff --git a/doc/tutorials/gpu/table_of_content_gpu/table_of_content_gpu.rst b/doc/tutorials/gpu/table_of_content_gpu/table_of_content_gpu.rst index cadbcfd384..a5935fba6c 100644 --- a/doc/tutorials/gpu/table_of_content_gpu/table_of_content_gpu.rst +++ b/doc/tutorials/gpu/table_of_content_gpu/table_of_content_gpu.rst @@ -1,12 +1,36 @@ .. _Table-Of-Content-GPU: *gpu* module. GPU-Accelerated Computer Vision ------------------------------------------------------------ +--------------------------------------------- Squeeze out every little computation power from your system by using the power of your video card to run the OpenCV algorithms. -.. include:: ../../definitions/noContent.rst +.. include:: ../../definitions/tocDefinitions.rst + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + =============== ====================================================== + |hVideoWrite| *Title:* :ref:`gpuBasicsSimilarity` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_BernatG| + + This will give a good grasp on how to approach coding on the GPU module, once you already know how to handle the other modules. As a test case it will port the similarity methods from the tutorial :ref:`videoInputPSNRMSSIM` to the GPU. + + =============== ====================================================== + + .. |hVideoWrite| image:: images/gpu-basics-similarity.png + :height: 90pt + :width: 90pt .. raw:: latex \pagebreak + +.. toctree:: + :hidden: + + ../gpu-basics-similarity/gpu-basics-similarity diff --git a/doc/tutorials/highgui/table_of_content_highgui/images/video-write.png b/doc/tutorials/highgui/table_of_content_highgui/images/video-write.png new file mode 100644 index 0000000000..9413d80836 Binary files /dev/null and b/doc/tutorials/highgui/table_of_content_highgui/images/video-write.png differ diff --git a/doc/tutorials/highgui/table_of_content_highgui/table_of_content_highgui.rst b/doc/tutorials/highgui/table_of_content_highgui/table_of_content_highgui.rst index b03f1a66c7..0cd6c63872 100644 --- a/doc/tutorials/highgui/table_of_content_highgui/table_of_content_highgui.rst +++ b/doc/tutorials/highgui/table_of_content_highgui/table_of_content_highgui.rst @@ -1,7 +1,7 @@ .. _Table-Of-Content-HighGui: *highgui* module. High Level GUI and Media ------------------------------------------------------------ +------------------------------------------ This section contains valuable tutorials about how to read/save your image/video files and how to use the built-in graphical user interface of the library. @@ -45,6 +45,26 @@ This section contains valuable tutorials about how to read/save your image/video :height: 90pt :width: 90pt ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + =============== ====================================================== + |hVideoWrite| *Title:* :ref:`videoWriteHighGui` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_BernatG| + + Whenever you work with video feeds you may eventually want to save your image processing result in a form of a new video file. Here's how to do it. + + =============== ====================================================== + + .. |hVideoWrite| image:: images/video-write.png + :height: 90pt + :width: 90pt + + .. raw:: latex \pagebreak @@ -54,3 +74,4 @@ This section contains valuable tutorials about how to read/save your image/video ../trackbar/trackbar ../video-input-psnr-ssim/video-input-psnr-ssim + ../video-write/video-write \ No newline at end of file diff --git a/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst b/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst index 4bde0be6bb..cb45a5b0a3 100644 --- a/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst +++ b/doc/tutorials/highgui/video-input-psnr-ssim/video-input-psnr-ssim.rst @@ -18,7 +18,7 @@ The source code As a test case where to show off these using OpenCV I've created a small program that reads in two video files and performs a similarity check between them. This is something you could use to check just how well a new video compressing algorithms works. Let there be a reference (original) video like :download:`this small Megamind clip <../../../../samples/cpp/tutorial_code/highgui/video-input-psnr-ssim/video/Megamind.avi>` and :download:`a compressed version of it <../../../../samples/cpp/tutorial_code/highgui/video-input-psnr-ssim/video/Megamind_bugy.avi>`. You may also find the source code and these video file in the :file:`samples/cpp/tutorial_code/highgui/video-input-psnr-ssim/` folder of the OpenCV source library. -.. literalinclude:: ../../../../samples/cpp/tutorial_code/HighGUI\video-input-psnr-ssim\video-input-psnr-ssim.cpp +.. literalinclude:: ../../../../samples/cpp/tutorial_code/HighGUI/video-input-psnr-ssim/video-input-psnr-ssim.cpp :language: cpp :linenos: :tab-width: 4 diff --git a/doc/tutorials/highgui/video-write/images/resultOutputWideoWrite.png b/doc/tutorials/highgui/video-write/images/resultOutputWideoWrite.png new file mode 100644 index 0000000000..bea15a35bd Binary files /dev/null and b/doc/tutorials/highgui/video-write/images/resultOutputWideoWrite.png differ diff --git a/doc/tutorials/highgui/video-write/images/videoCompressSelect.png b/doc/tutorials/highgui/video-write/images/videoCompressSelect.png new file mode 100644 index 0000000000..a72fc01c31 Binary files /dev/null and b/doc/tutorials/highgui/video-write/images/videoCompressSelect.png differ diff --git a/doc/tutorials/highgui/video-write/images/videoFileStructure.png b/doc/tutorials/highgui/video-write/images/videoFileStructure.png new file mode 100644 index 0000000000..cef586d96b Binary files /dev/null and b/doc/tutorials/highgui/video-write/images/videoFileStructure.png differ diff --git a/doc/tutorials/highgui/video-write/video-write.rst b/doc/tutorials/highgui/video-write/video-write.rst new file mode 100644 index 0000000000..0bebf82381 --- /dev/null +++ b/doc/tutorials/highgui/video-write/video-write.rst @@ -0,0 +1 @@ +.. _videoWriteHighGui: Creating a video with OpenCV **************************** Goal ==== Whenever you work with video feeds you may eventually want to save your image processing result in a form of a new video file. For simple video outputs you can use the OpenCV built-in :huivideo:`VideoWriter ` class, designed for this. .. container:: enumeratevisibleitemswithsquare + How to create a video file with OpenCV + What type of video files you can create with OpenCV + How to extract a given color channel from a video As a simple demonstration I'll just extract one of the RGB color channels of an input video file into a new video. You can control the flow of the application from its console line arguments: .. container:: enumeratevisibleitemswithsquare + The first argument points to the video file to work on + The second argument may be one of the characters: R G B. This will specify which of the channels to extract. + The last argument is the character Y (Yes) or N (No). If this is no, the codec used for the input video file will be the same as for the output. Otherwise, a window will pop up and allow you to select yourself the codec to use. For example, a valid command line would look like: .. code-block:: bash video-write.exe video/Megamind.avi R Y The source code =============== You may also find the source code and these video file in the :file:`samples/cpp/tutorial_code/highgui/video-write/` folder of the OpenCV source library or :download:`download it from here <../../../../samples/cpp/tutorial_code/HighGUI/video-write/video-write.cpp>`. .. literalinclude:: ../../../../samples/cpp/tutorial_code/HighGUI/video-write/video-write.cpp :language: cpp :linenos: :tab-width: 4 :lines: 1-8, 21-22, 24-97 The structure of a video ======================== For start, you should have an idea of just how a video file looks. Every video file in itself is a container. The type of the container is expressed in the files extension (for example *avi*, *mov* or *mkv*). This contains multiple elements like: video feeds, audio feeds or other tracks (like for example subtitles). How these feeds are stored is determined by the codec used for each one of them. In case of the audio tracks commonly used codecs are *mp3* or *aac*. For the video files the list is somehow longer and includes names such as *XVID*, *DIVX*, *H264* or *LAGS* (*Lagarith Lossless Codec*). The full list of codecs you may use on a system depends on just what one you have installed. .. image:: images/videoFileStructure.png :alt: The Structure of the video :align: center As you can see things can get really complicated with videos. However, OpenCV is mainly a computer vision library, not a video stream, codec and write one. Therefore, the developers tried to keep this part as simple as possible. Due to this OpenCV for video containers supports only the *avi* extension, its first version. A direct limitation of this is that you cannot save a video file larger than 2 GB. Furthermore you can only create and expand a single video track inside the container. No audio or other track editing support here. Nevertheless, any video codec present on your system might work. If you encounter some of these limitations you will need to look into more specialized video writing libraries such as *FFMpeg* or codecs as *HuffYUV*, *CorePNG* and *LCL*. As an alternative, create the video track with OpenCV and expand it with sound tracks or convert it to other formats by using video manipulation programs such as *VirtualDub* or *AviSynth*. The *VideoWriter* class ======================= The content written here builds on the assumption you already read the :ref:`videoInputPSNRMSSIM` tutorial and you know how to read video files. To create a video file you just need to create an instance of the :huivideo:`VideoWriter ` class. You can specify its properties either via parameters in the constructor or later on via the :huivideo:`open ` function. Either way, the parameters are the same: 1. The name of the output that contains the container type in its extension. At the moment only *avi* is supported. We construct this from the input file, add to this the name of the channel to use, and finish it off with the container extension. .. code-block:: cpp const string source = argv[1]; // the source file name string::size_type pAt = source.find_last_of('.'); // Find extension point const string NAME = source.substr(0, pAt) + argv[2][0] + ".avi"; // Form the new name with container #. The codec to use for the video track. Now all the video codecs have a unique short name of maximum four characters. Hence, the *XVID*, *DIVX* or *H264* names. This is called a four character code. You may also ask this from an input video by using its *get* function. Because the *get* function is a general function it always returns double values. A double value is stored on 64 bits. Four characters are four bytes, meaning 32 bits. These four characters are coded in the lower 32 bits of the *double*. A simple way to throw away the upper 32 bits would be to just convert this value to *int*: .. code-block:: cpp VideoCapture inputVideo(source); // Open input int ex = static_cast(inputVideo.get(CV_CAP_PROP_FOURCC)); // Get Codec Type- Int form OpenCV internally works with this integer type and expect this as its second parameter. Now to convert from the integer form to string we may use two methods: a bitwise operator and a union method. The first one extracting from an int the characters looks like (an "and" operation, some shifting and adding a 0 at the end to close the string): .. code-block:: cpp char EXT[] = {ex & 0XFF , (ex & 0XFF00) >> 8,(ex & 0XFF0000) >> 16,(ex & 0XFF000000) >> 24, 0}; You can do the same thing with the *union* as: .. code-block:: cpp union { int v; char c[5];} uEx ; uEx.v = ex; // From Int to char via union uEx.c[4]='\0'; The advantage of this is that the conversion is done automatically after assigning, while for the bitwise operator you need to do the operations whenever you change the codec type. In case you know the codecs four character code beforehand, you can use the *CV_FOURCC* macro to build the integer: .. code-block::cpp CV_FOURCC('P','I','M,'1') // this is an MPEG1 codec from the characters to integer If you pass for this argument minus one than a window will pop up at runtime that contains all the codec installed on your system and ask you to select the one to use: .. image:: images/videoCompressSelect.png :alt: Select the codec type to use :align: center #. The frame per second for the output video. Again, here I keep the input videos frame per second by using the *get* function. #. The size of the frames for the output video. Here too I keep the input videos frame size per second by using the *get* function. #. The final argument is an optional one. By default is true and says that the output will be a colorful one (so for write you will send three channel images). To create a gray scale video pass a false parameter here. Here it is, how I use it in the sample: .. code-block:: cpp VideoWriter outputVideo; Size S = Size((int) inputVideo.get(CV_CAP_PROP_FRAME_WIDTH), //Acquire input size (int) inputVideo.get(CV_CAP_PROP_FRAME_HEIGHT)); outputVideo.open(NAME , ex, inputVideo.get(CV_CAP_PROP_FPS),S, true); Afterwards, you use the :huivideo:`isOpened() ` function to find out if the open operation succeeded or not. The video file automatically closes when the *VideoWriter* object is destroyed. After you open the object with success you can send the frames of the video in a sequential order by using the :huivideo:`write` function of the class. Alternatively, you can use its overloaded operator << : .. code-block:: cpp outputVideo.write(res); //or outputVideo << res; Extracting a color channel from an RGB image means to set to zero the RGB values of the other channels. You can either do this with image scanning operations or by using the split and merge operations. You first split the channels up into different images, set the other channels to zero images of the same size and type and finally merge them back: .. code-block:: cpp split(src, spl); // process - extract only the correct channel for( int i =0; i < 3; ++i) if (i != channel) spl[i] = Mat::zeros(S, spl[0].type()); merge(spl, res); Put all this together and you'll get the upper source code, whose runtime result will show something around the idea: .. image:: images/resultOutputWideoWrite.png :alt: A sample output :align: center You may observe a runtime instance of this on the `YouTube here `_. .. raw:: html
\ No newline at end of file diff --git a/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.rst b/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.rst index 601abb893b..49639060ac 100644 --- a/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.rst +++ b/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.rst @@ -70,7 +70,7 @@ Erosion Code ====== -This tutorial code's is shown lines below. You can also download it from `here `_ +This tutorial code's is shown lines below. You can also download it from `here `_ .. code-block:: cpp @@ -175,16 +175,16 @@ Explanation #. Most of the stuff shown is known by you (if you have any doubt, please refer to the tutorials in previous sections). Let's check the general structure of the program: - * Load an image (can be RGB or grayscale) + .. container:: enumeratevisibleitemswithsquare - * Create two windows (one for dilation output, the other for erosion) + * Load an image (can be RGB or grayscale) + * Create two windows (one for dilation output, the other for erosion) + * Create a set of 02 Trackbars for each operation: - * Create a set of 02 Trackbars for each operation: + * The first trackbar "Element" returns either **erosion_elem** or **dilation_elem** + * The second trackbar "Kernel size" return **erosion_size** or **dilation_size** for the corresponding operation. - * The first trackbar "Element" returns either **erosion_elem** or **dilation_elem** - * The second trackbar "Kernel size" return **erosion_size** or **dilation_size** for the corresponding operation. - - * Every time we move any slider, the user's function **Erosion** or **Dilation** will be called and it will update the output image based on the current trackbar values. + * Every time we move any slider, the user's function **Erosion** or **Dilation** will be called and it will update the output image based on the current trackbar values. Let's analyze these two functions: @@ -220,13 +220,15 @@ Explanation Size( 2*erosion_size + 1, 2*erosion_size+1 ), Point( erosion_size, erosion_size ) ); - We can choose any of three shapes for our kernel: + We can choose any of three shapes for our kernel: - * Rectangular box: MORPH_RECT - * Cross: MORPH_CROSS - * Ellipse: MORPH_ELLIPSE + .. container:: enumeratevisibleitemswithsquare - Then, we just have to specify the size of our kernel and the *anchor point*. If not specified, it is assumed to be in the center. + + Rectangular box: MORPH_RECT + + Cross: MORPH_CROSS + + Ellipse: MORPH_ELLIPSE + + Then, we just have to specify the size of our kernel and the *anchor point*. If not specified, it is assumed to be in the center. * That is all. We are ready to perform the erosion of our image. @@ -271,4 +273,4 @@ Results .. image:: images/Morphology_1_Tutorial_Cover.jpg :alt: Dilation and Erosion application - :align: center \ No newline at end of file + :align: center diff --git a/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.rst b/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.rst index 73a25382f6..e7fe4dc697 100644 --- a/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.rst +++ b/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.rst @@ -8,7 +8,9 @@ Goal In this tutorial you will learn how to: -a. Use the OpenCV function :canny:`Canny <>` to implement the Canny Edge Detector. +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV function :canny:`Canny <>` to implement the Canny Edge Detector. Theory ======= @@ -265,21 +267,21 @@ Explanation Result ======= -#. After compiling the code above, we can run it giving as argument the path to an image. For example, using as an input the following image: +* After compiling the code above, we can run it giving as argument the path to an image. For example, using as an input the following image: .. image:: images/Canny_Detector_Tutorial_Original_Image.jpg :alt: Original test image :width: 200pt :align: center - and moving the slider, trying different threshold, we obtain the following result: +* Moving the slider, trying different threshold, we obtain the following result: .. image:: images/Canny_Detector_Tutorial_Result.jpg :alt: Result after running Canny :width: 200pt :align: center - Notice how the image is superposed to the black background on the edge regions. +* Notice how the image is superposed to the black background on the edge regions. diff --git a/doc/tutorials/imgproc/imgtrans/copyMakeBorder/copyMakeBorder.rst b/doc/tutorials/imgproc/imgtrans/copyMakeBorder/copyMakeBorder.rst index 5558b35013..877accd529 100644 --- a/doc/tutorials/imgproc/imgtrans/copyMakeBorder/copyMakeBorder.rst +++ b/doc/tutorials/imgproc/imgtrans/copyMakeBorder/copyMakeBorder.rst @@ -8,10 +8,12 @@ Goal In this tutorial you will learn how to: -#. Use the OpenCV function :copy_make_border:`copyMakeBorder <>` to set the borders (extra padding to your image). +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV function :copy_make_border:`copyMakeBorder <>` to set the borders (extra padding to your image). Theory -============ +======== .. note:: The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. @@ -208,10 +210,12 @@ Results #. After compiling the code above, you can execute it giving as argument the path of an image. The result should be: - * By default, it begins with the border set to BORDER_CONSTANT. Hence, a succession of random colored borders will be shown. - * If you press 'r', the border will become a replica of the edge pixels. - * If you press 'c', the random colored borders will appear again - * If you press 'ESC' the program will exit. + .. container:: enumeratevisibleitemswithsquare + + * By default, it begins with the border set to BORDER_CONSTANT. Hence, a succession of random colored borders will be shown. + * If you press 'r', the border will become a replica of the edge pixels. + * If you press 'c', the random colored borders will appear again + * If you press 'ESC' the program will exit. Below some screenshot showing how the border changes color and how the *BORDER_REPLICATE* option looks: diff --git a/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.rst b/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.rst index 7fad4b56a7..bee8f3c604 100644 --- a/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.rst +++ b/doc/tutorials/imgproc/imgtrans/filter_2d/filter_2d.rst @@ -8,10 +8,12 @@ Goal In this tutorial you will learn how to: -* Use the OpenCV function :filter2d:`filter2D <>` to create your own linear filters. +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV function :filter2d:`filter2D <>` to create your own linear filters. Theory -============ +======= .. note:: The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. diff --git a/doc/tutorials/imgproc/imgtrans/laplace_operator/laplace_operator.rst b/doc/tutorials/imgproc/imgtrans/laplace_operator/laplace_operator.rst index 99bb326ac5..764b119669 100644 --- a/doc/tutorials/imgproc/imgtrans/laplace_operator/laplace_operator.rst +++ b/doc/tutorials/imgproc/imgtrans/laplace_operator/laplace_operator.rst @@ -9,7 +9,9 @@ Goal In this tutorial you will learn how to: -a. Use the OpenCV function :laplacian:`Laplacian <>` to implement a discrete analog of the *Laplacian operator*. +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV function :laplacian:`Laplacian <>` to implement a discrete analog of the *Laplacian operator*. Theory diff --git a/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.rst b/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.rst index d544b109cd..007746e39a 100644 --- a/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.rst +++ b/doc/tutorials/imgproc/imgtrans/sobel_derivatives/sobel_derivatives.rst @@ -9,8 +9,10 @@ Goal In this tutorial you will learn how to: -#. Use the OpenCV function :sobel:`Sobel <>` to calculate the derivatives from an image. -#. Use the OpenCV function :scharr:`Scharr <>` to calculate a more accurate derivative for a kernel of size :math:`3 \cdot 3` +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV function :sobel:`Sobel <>` to calculate the derivatives from an image. + * Use the OpenCV function :scharr:`Scharr <>` to calculate a more accurate derivative for a kernel of size :math:`3 \cdot 3` Theory ======== diff --git a/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.rst b/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.rst index 40b06a5ea5..5ce49d305a 100644 --- a/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.rst +++ b/doc/tutorials/imgproc/opening_closing_hats/opening_closing_hats.rst @@ -8,25 +8,28 @@ Goal In this tutorial you will learn how to: -* Use the OpenCV function :morphology_ex:`morphologyEx <>` to apply Morphological Transformation such as: - - * Opening - * Closing - * Morphological Gradient - * Top Hat - * Black Hat +.. container:: enumeratevisibleitemswithsquare -Cool Theory -============ + * Use the OpenCV function :morphology_ex:`morphologyEx <>` to apply Morphological Transformation such as: + + + Opening + + Closing + + Morphological Gradient + + Top Hat + + Black Hat + +Theory +======= .. note:: The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. In the previous tutorial we covered two basic Morphology operations: -* Erosion +.. container:: enumeratevisibleitemswithsquare -* Dilation. + * Erosion + * Dilation. Based on these two we can effectuate more sophisticated transformations to our images. Here we discuss briefly 05 operations offered by OpenCV: @@ -246,11 +249,11 @@ Explanation * **dst**: Output image * **operation**: The kind of morphology transformation to be performed. Note that we have 5 alternatives: - * *Opening*: MORPH_OPEN : 2 - * *Closing*: MORPH_CLOSE: 3 - * *Gradient*: MORPH_GRADIENT: 4 - * *Top Hat*: MORPH_TOPHAT: 5 - * *Black Hat*: MORPH_BLACKHAT: 6 + + *Opening*: MORPH_OPEN : 2 + + *Closing*: MORPH_CLOSE: 3 + + *Gradient*: MORPH_GRADIENT: 4 + + *Top Hat*: MORPH_TOPHAT: 5 + + *Black Hat*: MORPH_BLACKHAT: 6 As you can see the values range from <2-6>, that is why we add (+2) to the values entered by the Trackbar: diff --git a/doc/tutorials/imgproc/pyramids/pyramids.rst b/doc/tutorials/imgproc/pyramids/pyramids.rst index 41c734742f..43b03e45b3 100644 --- a/doc/tutorials/imgproc/pyramids/pyramids.rst +++ b/doc/tutorials/imgproc/pyramids/pyramids.rst @@ -8,7 +8,9 @@ Goal In this tutorial you will learn how to: -* Use the OpenCV functions :pyr_up:`pyrUp <>` and :pyr_down:`pyrDown <>` to downsample or upsample a given image. +.. container:: enumeratevisibleitemswithsquare + + * Use the OpenCV functions :pyr_up:`pyrUp <>` and :pyr_down:`pyrDown <>` to downsample or upsample a given image. Theory ======= @@ -16,25 +18,30 @@ Theory .. note:: The explanation below belongs to the book **Learning OpenCV** by Bradski and Kaehler. -* Usually we need to convert an image to a size different than its original. For this, there are two possible options: - - * *Upsize* the image (zoom in) or - * *Downsize* it (zoom out). +.. container:: enumeratevisibleitemswithsquare + + * Usually we need to convert an image to a size different than its original. For this, there are two possible options: + + #. *Upsize* the image (zoom in) or + #. *Downsize* it (zoom out). + + * Although there is a *geometric transformation* function in OpenCV that -literally- resize an image (:resize:`resize <>`, which we will show in a future tutorial), in this section we analyze first the use of **Image Pyramids**, which are widely applied in a huge range of vision applications. -* Although there is a *geometric transformation* function in OpenCV that -literally- resize an image (:resize:`resize <>`, which we will show in a future tutorial), in this section we analyze first the use of **Image Pyramids**, which are widely applied in a huge range of vision applications. Image Pyramid -------------- -* An image pyramid is a collection of images - all arising from a single original image - that are successively downsampled until some desired stopping point is reached. +.. container:: enumeratevisibleitemswithsquare -* There are two common kinds of image pyramids: + * An image pyramid is a collection of images - all arising from a single original image - that are successively downsampled until some desired stopping point is reached. - * **Gaussian pyramid:** Used to downsample images + * There are two common kinds of image pyramids: - * **Laplacian pyramid:** Used to reconstruct an upsampled image from an image lower in the pyramid (with less resolution) + * **Gaussian pyramid:** Used to downsample images -* In this tutorial we'll use the *Gaussian pyramid*. + * **Laplacian pyramid:** Used to reconstruct an upsampled image from an image lower in the pyramid (with less resolution) + + * In this tutorial we'll use the *Gaussian pyramid*. Gaussian Pyramid ^^^^^^^^^^^^^^^^^ diff --git a/doc/tutorials/imgproc/threshold/threshold.rst b/doc/tutorials/imgproc/threshold/threshold.rst index 1ac4919bd0..9b164b69c6 100644 --- a/doc/tutorials/imgproc/threshold/threshold.rst +++ b/doc/tutorials/imgproc/threshold/threshold.rst @@ -8,7 +8,9 @@ Goal In this tutorial you will learn how to: -* Perform basic thresholding operations using OpenCV function :threshold:`threshold <>` +.. container:: enumeratevisibleitemswithsquare + + * Perform basic thresholding operations using OpenCV function :threshold:`threshold <>` Cool Theory @@ -305,4 +307,4 @@ Results .. image:: images/Threshold_Tutorial_Result_Zero.jpg :alt: Threshold Result Zero - :align: center \ No newline at end of file + :align: center diff --git a/doc/tutorials/introduction/display_image/display_image.rst b/doc/tutorials/introduction/display_image/display_image.rst index a8be339867..af07b7f7f4 100644 --- a/doc/tutorials/introduction/display_image/display_image.rst +++ b/doc/tutorials/introduction/display_image/display_image.rst @@ -17,7 +17,7 @@ In this tutorial you will learn how to: Source Code =========== -Download the :download:`source code from here <../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp>` or look it up in our library at :file:`samples/cpp/tutorial_code/introduction/display_image/display_image.cpp`. +Download the source code from `here `_. .. literalinclude:: ../../../../samples/cpp/tutorial_code/introduction/display_image/display_image.cpp :language: cpp @@ -108,20 +108,22 @@ Because we want our window to be displayed until the user presses a key (otherwi Result ======= -* Compile your code and then run the executable giving an image path as argument. If you're on Windows the executable will of course contain an *exe* extension too. Of course assure the image file is near your program file. +.. container:: enumeratevisibleitemswithsquare - .. code-block:: bash + * Compile your code and then run the executable giving an image path as argument. If you're on Windows the executable will of course contain an *exe* extension too. Of course assure the image file is near your program file. - ./DisplayImage HappyFish.jpg + .. code-block:: bash -* You should get a nice window as the one shown below: + ./DisplayImage HappyFish.jpg - .. image:: images/Display_Image_Tutorial_Result.jpg - :alt: Display Image Tutorial - Final Result - :align: center + * You should get a nice window as the one shown below: -.. raw:: html + .. image:: images/Display_Image_Tutorial_Result.jpg + :alt: Display Image Tutorial - Final Result + :align: center -
- -
+ .. raw:: html + +
+ +
diff --git a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst index d5e039134a..3e3191735f 100644 --- a/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst +++ b/doc/tutorials/introduction/linux_gcc_cmake/linux_gcc_cmake.rst @@ -6,12 +6,14 @@ Using OpenCV with gcc and CMake .. note:: We assume that you have successfully installed OpenCV in your workstation. -The easiest way of using OpenCV in your code is to use `CMake `_. A few advantages (taken from the Wiki): +.. container:: enumeratevisibleitemswithsquare -* No need to change anything when porting between Linux and Windows -* Can easily be combined with other tools by CMake( i.e. Qt, ITK and VTK ) + * The easiest way of using OpenCV in your code is to use `CMake `_. A few advantages (taken from the Wiki): -If you are not familiar with CMake, checkout the `tutorial `_ on its website. + #. No need to change anything when porting between Linux and Windows + #. Can easily be combined with other tools by CMake( i.e. Qt, ITK and VTK ) + + * If you are not familiar with CMake, checkout the `tutorial `_ on its website. Steps ====== diff --git a/doc/tutorials/ml/non_linear_svms/images/result.png b/doc/tutorials/ml/non_linear_svms/images/result.png new file mode 100644 index 0000000000..bfecae9a1b Binary files /dev/null and b/doc/tutorials/ml/non_linear_svms/images/result.png differ diff --git a/doc/tutorials/ml/non_linear_svms/images/sample-errors-dist.png b/doc/tutorials/ml/non_linear_svms/images/sample-errors-dist.png new file mode 100644 index 0000000000..1379a56d52 Binary files /dev/null and b/doc/tutorials/ml/non_linear_svms/images/sample-errors-dist.png differ diff --git a/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst b/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst new file mode 100644 index 0000000000..8fbcc563a7 --- /dev/null +++ b/doc/tutorials/ml/non_linear_svms/non_linear_svms.rst @@ -0,0 +1 @@ +.. _nonLinearSvmS: Support Vector Machines for Non-Linearly Separable Data ******************************************************* Goal ==== In this tutorial you will learn how to: .. container:: enumeratevisibleitemswithsquare + Define the optimization problem for SVMs when it is not possible to separate linearly the training data. + How to configure the parameters in :svms:`CvSVMParams ` to adapt your SVM for this class of problems. Motivation ========== Why is it interesting to extend the SVM optimation problem in order to handle non-linearly separable training data? Most of the applications in which SVMs are used in computer vision require a more powerful tool than a simple linear classifier. This stems from the fact that in these tasks **the training data can be rarely separated using an hyperplane**. Consider one of these tasks, for example, face detection. The training data in this case is composed by a set of images that are faces and another set of images that are non-faces (*every other thing in the world except from faces*). This training data is too complex so as to find a representation of each sample (*feature vector*) that could make the whole set of faces linearly separable from the whole set of non-faces. Extension of the Optimization Problem ===================================== Remember that using SVMs we obtain a separating hyperplane. Therefore, since the training data is now non-linearly separable, we must admit that the hyperplane found will misclassify some of the samples. This *misclassification* is a new variable in the optimization that must be taken into account. The new model has to include both the old requirement of finding the hyperplane that gives the biggest margin and the new one of generalizing the training data correctly by not allowing too many classification errors. We start here from the formulation of the optimization problem of finding the hyperplane which maximizes the **margin** (this is explained in the :ref:`previous tutorial `): .. math:: \min_{\beta, \beta_{0}} L(\beta) = \frac{1}{2}||\beta||^{2} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 \text{ } \forall i There are multiple ways in which this model can be modified so it takes into account the misclassification errors. For example, one could think of minimizing the same quantity plus a constant times the number of misclassification errors in the training data, i.e.: .. math:: \min ||\beta||^{2} + C \text{(\# misclassication errors)} However, this one is not a very good solution since, among some other reasons, we do not distinguish between samples that are misclassified with a small distance to their appropriate decision region or samples that are not. Therefore, a better solution will take into account the *distance of the misclassified samples to their correct decision regions*, i.e.: .. math:: \min ||\beta||^{2} + C \text{(distance of misclassified samples to their correct regions)} For each sample of the training data a new parameter :math:`\xi_{i}` is defined. Each one of these parameters contains the distance from its corresponding training sample to their correct decision region. The following picture shows non-linearly separable training data from two classes, a separating hyperplane and the distances to their correct regions of the samples that are misclassified. .. image:: images/sample-errors-dist.png :alt: Samples misclassified and their distances to their correct regions :align: center .. note:: Only the distances of the samples that are misclassified are shown in the picture. The distances of the rest of the samples are zero since they lay already in their correct decision region. The red and blue lines that appear on the picture are the margins to each one of the decision regions. It is very **important** to realize that each of the :math:`\xi_{i}` goes from a misclassified training sample to the margin of its appropriate region. Finally, the new formulation for the optimization problem is: .. math:: \min_{\beta, \beta_{0}} L(\beta) = ||\beta||^{2} + C \sum_{i} {\xi_{i}} \text{ subject to } y_{i}(\beta^{T} x_{i} + \beta_{0}) \geq 1 - \xi_{i} \text{ and } \xi_{i} \geq 0 \text{ } \forall i How should the parameter C be chosen? It is obvious that the answer to this question depends on how the training data is distributed. Although there is no general answer, it is useful to take into account these rules: .. container:: enumeratevisibleitemswithsquare * Large values of C give solutions with *less misclassification errors* but a *smaller margin*. Consider that in this case it is expensive to make misclassification errors. Since the aim of the optimization is to minimize the argument, few misclassifications errors are allowed. * Small values of C give solutions with *bigger margin* and *more classification errors*. In this case the minimization does not consider that much the term of the sum so it focuses more on finding a hyperplane with big margin. Source Code =========== You may also find the source code and these video file in the :file:`samples/cpp/tutorial_code/gpu/non_linear_svms/non_linear_svms` folder of the OpenCV source library or :download:`download it from here <../../../../samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp>`. .. literalinclude:: ../../../../samples/cpp/tutorial_code/ml/non_linear_svms/non_linear_svms.cpp :language: cpp :linenos: :tab-width: 4 :lines: 1-11, 22-23, 26- Explanation =========== 1. **Set up the training data** The training data of this exercise is formed by a set of labeled 2D-points that belong to one of two different classes. To make the exercise more appealing, the training data is generated randomly using a uniform probability density functions (PDFs). We have divided the generation of the training data into two main parts. In the first part we generate data for both classes that is linearly separable. .. code-block:: cpp // Generate random points for the class 1 Mat trainClass = trainData.rowRange(0, nLinearSamples); // The x coordinate of the points is in [0, 0.4) Mat c = trainClass.colRange(0, 1); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH)); // The y coordinate of the points is in [0, 1) c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); // Generate random points for the class 2 trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES); // The x coordinate of the points is in [0.6, 1] c = trainClass.colRange(0 , 1); rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH)); // The y coordinate of the points is in [0, 1) c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); In the second part we create data for both classes that is non-linearly separable, data that overlaps. .. code-block:: cpp // Generate random points for the classes 1 and 2 trainClass = trainData.rowRange( nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples); // The x coordinate of the points is in [0.4, 0.6) c = trainClass.colRange(0,1); rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH)); // The y coordinate of the points is in [0, 1) c = trainClass.colRange(1,2); rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT)); 2. **Set up SVM's parameters** .. seealso:: In the previous tutorial :ref:`introductiontosvms` there is an explanation of the atributes of the class :svms:`CvSVMParams ` that we configure here before training the SVM. .. code-block:: cpp CvSVMParams params; params.svm_type = SVM::C_SVC; params.C = 0.1; params.kernel_type = SVM::LINEAR; params.term_crit = TermCriteria(CV_TERMCRIT_ITER, (int)1e7, 1e-6); There are just two differences between the configuration we do here and the one that was done in the :ref:`previous tutorial ` that we use as reference. * *CvSVM::C_SVC*. We chose here a small value of this parameter in order not to punish too much the misclassification errors in the optimization. The idea of doing this stems from the will of obtaining a solution close to the one intuitively expected. However, we recommend to get a better insight of the problem by making adjustments to this parameter. .. note:: Here there are just very few points in the overlapping region between classes, giving a smaller value to **FRAC_LINEAR_SEP** the density of points can be incremented and the impact of the parameter **CvSVM::C_SVC** explored deeply. * *Termination Criteria of the algorithm*. The maximum number of iterations has to be increased considerably in order to solve correctly a problem with non-linearly separable training data. In particular, we have increased in five orders of magnitude this value. 3. **Train the SVM** We call the method :svms:`CvSVM::train ` to build the SVM model. Watch out that the training process may take a quite long time. Have patiance when your run the program. .. code-block:: cpp CvSVM svm; svm.train(trainData, labels, Mat(), Mat(), params); 4. **Show the Decision Regions** The method :svms:`CvSVM::predict ` is used to classify an input sample using a trained SVM. In this example we have used this method in order to color the space depending on the prediction done by the SVM. In other words, an image is traversed interpreting its pixels as points of the Cartesian plane. Each of the points is colored depending on the class predicted by the SVM; in dark green if it is the class with label 1 and in dark blue if it is the class with label 2. .. code-block:: cpp Vec3b green(0,100,0), blue (100,0,0); for (int i = 0; i < I.rows; ++i) for (int j = 0; j < I.cols; ++j) { Mat sampleMat = (Mat_(1,2) << i, j); float response = svm.predict(sampleMat); if (response == 1) I.at(j, i) = green; else if (response == 2) I.at(j, i) = blue; } 5. **Show the training data** The method :drawingFunc:`circle ` is used to show the samples that compose the training data. The samples of the class labeled with 1 are shown in light green and in light blue the samples of the class labeled with 2. .. code-block:: cpp int thick = -1; int lineType = 8; float px, py; // Class 1 for (int i = 0; i < NTRAINING_SAMPLES; ++i) { px = trainData.at(i,0); py = trainData.at(i,1); circle(I, Point( (int) px, (int) py ), 3, Scalar(0, 255, 0), thick, lineType); } // Class 2 for (int i = NTRAINING_SAMPLES; i <2*NTRAINING_SAMPLES; ++i) { px = trainData.at(i,0); py = trainData.at(i,1); circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType); } 6. **Support vectors** We use here a couple of methods to obtain information about the support vectors. The method :svms:`CvSVM::get_support_vector_count ` outputs the total number of support vectors used in the problem and with the method :svms:`CvSVM::get_support_vector ` we obtain each of the support vectors using an index. We have used this methods here to find the training examples that are support vectors and highlight them. .. code-block:: cpp thick = 2; lineType = 8; int x = svm.get_support_vector_count(); for (int i = 0; i < x; ++i) { const float* v = svm.get_support_vector(i); circle( I, Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType); } Results ======= .. container:: enumeratevisibleitemswithsquare * The code opens an image and shows the training examples of both classes. The points of one class are represented with light green and light blue ones are used for the other class. * The SVM is trained and used to classify all the pixels of the image. This results in a division of the image in a blue region and a green region. The boundary between both regions is the separating hyperplane. Since the training data is non-linearly separable, it can be seen that some of the examples of both classes are misclassified; some green points lay on the blue region and some blue points lay on the green one. * Finally the support vectors are shown using gray rings around the training examples. .. image:: images/result.png :alt: Training data and decision regions given by the SVM :width: 300pt :align: center You may observe a runtime instance of this on the `YouTube here `_. .. raw:: html
\ No newline at end of file diff --git a/doc/tutorials/ml/table_of_content_ml/images/non_linear_svms.png b/doc/tutorials/ml/table_of_content_ml/images/non_linear_svms.png new file mode 100644 index 0000000000..bd185d4c74 Binary files /dev/null and b/doc/tutorials/ml/table_of_content_ml/images/non_linear_svms.png differ diff --git a/doc/tutorials/ml/table_of_content_ml/table_of_content_ml.rst b/doc/tutorials/ml/table_of_content_ml/table_of_content_ml.rst index b2b581afa9..34bd446bb2 100644 --- a/doc/tutorials/ml/table_of_content_ml/table_of_content_ml.rst +++ b/doc/tutorials/ml/table_of_content_ml/table_of_content_ml.rst @@ -26,6 +26,25 @@ Use the powerfull machine learning classes for statistical classification, regre :height: 90pt :width: 90pt ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ============ ============================================== + |NonLinSVM| **Title:** :ref:`nonLinearSvmS` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_FernandoI| + + Here you will learn how to define the optimization problem for SVMs when it is not possible to separate linearly the training data. + + ============ ============================================== + + .. |NonLinSVM| image:: images/non_linear_svms.png + :height: 90pt + :width: 90pt + .. raw:: latex \pagebreak @@ -34,3 +53,4 @@ Use the powerfull machine learning classes for statistical classification, regre :hidden: ../introduction_to_svm/introduction_to_svm + ../non_linear_svms/non_linear_svms diff --git a/doc/tutorials/objdetect/cascade_classifier/cascade_classifier.rst b/doc/tutorials/objdetect/cascade_classifier/cascade_classifier.rst new file mode 100644 index 0000000000..7dd36fb56a --- /dev/null +++ b/doc/tutorials/objdetect/cascade_classifier/cascade_classifier.rst @@ -0,0 +1,134 @@ +.. _cascade_classifier: + +Cascade Classifier +******************* + +Goal +===== + +In this tutorial you will learn how to: + +.. container:: enumeratevisibleitemswithsquare + + * Use the :cascade_classifier:`CascadeClassifier <>` class to detect objects in a video stream. Particularly, we will use the functions: + + * :cascade_classifier_load:`load <>` to load a .xml classifier file. It can be either a Haar or a LBP classifer + * :cascade_classifier_detect_multiscale:`detectMultiScale <>` to perform the detection. + + +Theory +====== + +Code +==== + +This tutorial code's is shown lines below. You can also download it from `here `_ . The second version (using LBP for face detection) can be found `here `_ + +.. code-block:: cpp + + #include "opencv2/objdetect/objdetect.hpp" + #include "opencv2/highgui/highgui.hpp" + #include "opencv2/imgproc/imgproc.hpp" + + #include + #include + + using namespace std; + using namespace cv; + + /** Function Headers */ + void detectAndDisplay( Mat frame ); + + /** Global variables */ + String face_cascade_name = "haarcascade_frontalface_alt.xml"; + String eyes_cascade_name = "haarcascade_eye_tree_eyeglasses.xml"; + CascadeClassifier face_cascade; + CascadeClassifier eyes_cascade; + string window_name = "Capture - Face detection"; + RNG rng(12345); + + /** @function main */ + int main( int argc, const char** argv ) + { + CvCapture* capture; + Mat frame; + + //-- 1. Load the cascades + if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error loading\n"); return -1; }; + if( !eyes_cascade.load( eyes_cascade_name ) ){ printf("--(!)Error loading\n"); return -1; }; + + //-- 2. Read the video stream + capture = cvCaptureFromCAM( -1 ); + if( capture ) + { + while( true ) + { + frame = cvQueryFrame( capture ); + + //-- 3. Apply the classifier to the frame + if( !frame.empty() ) + { detectAndDisplay( frame ); } + else + { printf(" --(!) No captured frame -- Break!"); break; } + + int c = waitKey(10); + if( (char)c == 'c' ) { break; } + } + } + return 0; + } + + /** @function detectAndDisplay */ + void detectAndDisplay( Mat frame ) + { + std::vector faces; + Mat frame_gray; + + cvtColor( frame, frame_gray, CV_BGR2GRAY ); + equalizeHist( frame_gray, frame_gray ); + + //-- Detect faces + face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) ); + + for( int i = 0; i < faces.size(); i++ ) + { + Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 ); + ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 ); + + Mat faceROI = frame_gray( faces[i] ); + std::vector eyes; + + //-- In each face, detect eyes + eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CV_HAAR_SCALE_IMAGE, Size(30, 30) ); + + for( int j = 0; j < eyes.size(); j++ ) + { + Point center( faces[i].x + eyes[j].x + eyes[j].width*0.5, faces[i].y + eyes[j].y + eyes[j].height*0.5 ); + int radius = cvRound( (eyes[j].width + eyes[i].height)*0.25 ); + circle( frame, center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 ); + } + } + //-- Show what you got + imshow( window_name, frame ); + } + +Explanation +============ + +Result +====== + +#. Here is the result of running the code above and using as input the video stream of a build-in webcam: + + .. image:: images/Cascade_Classifier_Tutorial_Result_Haar.jpg + :align: center + :height: 300pt + + Remember to copy the files *haarcascade_frontalface_alt.xml* and *haarcascade_eye_tree_eyeglasses.xml* in your current directory. They are located in *opencv/data/haarcascades* + +#. This is the result of using the file *lbpcascade_frontalface.xml* (LBP trained) for the face detection. For the eyes we keep using the file used in the tutorial. + + .. image:: images/Cascade_Classifier_Tutorial_Result_LBP.jpg + :align: center + :height: 300pt + diff --git a/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_Haar.jpg b/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_Haar.jpg new file mode 100644 index 0000000000..917714456b Binary files /dev/null and b/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_Haar.jpg differ diff --git a/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_LBP.jpg b/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_LBP.jpg new file mode 100644 index 0000000000..dc977344ac Binary files /dev/null and b/doc/tutorials/objdetect/cascade_classifier/images/Cascade_Classifier_Tutorial_Result_LBP.jpg differ diff --git a/doc/tutorials/objdetect/table_of_content_objdetect/images/Cascade_Classifier_Tutorial_Cover.jpg b/doc/tutorials/objdetect/table_of_content_objdetect/images/Cascade_Classifier_Tutorial_Cover.jpg new file mode 100644 index 0000000000..cfa5de67e5 Binary files /dev/null and b/doc/tutorials/objdetect/table_of_content_objdetect/images/Cascade_Classifier_Tutorial_Cover.jpg differ diff --git a/doc/tutorials/objdetect/table_of_content_objdetect/table_of_content_objdetect.rst b/doc/tutorials/objdetect/table_of_content_objdetect/table_of_content_objdetect.rst index e9b37f5eb2..64ed109450 100644 --- a/doc/tutorials/objdetect/table_of_content_objdetect/table_of_content_objdetect.rst +++ b/doc/tutorials/objdetect/table_of_content_objdetect/table_of_content_objdetect.rst @@ -5,4 +5,32 @@ Ever wondered how your digital camera detects peoples and faces? Look here to find out! -.. include:: ../../definitions/noContent.rst \ No newline at end of file +.. include:: ../../definitions/tocDefinitions.rst + ++ + .. tabularcolumns:: m{100pt} m{300pt} + .. cssclass:: toctableopencv + + ===================== ============================================== + |CascadeClassif| **Title:** :ref:`cascade_classifier` + + *Compatibility:* > OpenCV 2.0 + + *Author:* |Author_AnaH| + + Here we learn how to use *objdetect* to find objects in our images or videos + + ===================== ============================================== + + .. |CascadeClassif| image:: images/Cascade_Classifier_Tutorial_Cover.jpg + :height: 90pt + :width: 90pt + +.. raw:: latex + + \pagebreak + +.. toctree:: + :hidden: + + ../cascade_classifier/cascade_classifier diff --git a/samples/python2/find_obj.py b/samples/python2/find_obj.py index 1dc9c3c842..c5c88dbf7e 100644 --- a/samples/python2/find_obj.py +++ b/samples/python2/find_obj.py @@ -1,14 +1,19 @@ import numpy as np import cv2 from common import anorm +from functools import partial help_message = '''SURF image match USAGE: findobj.py [ ] ''' +FLANN_INDEX_KDTREE = 1 # bug: flann enums are missing -def match(desc1, desc2, r_threshold = 0.75): +flann_params = dict(algorithm = FLANN_INDEX_KDTREE, + trees = 4) + +def match_bruteforce(desc1, desc2, r_threshold = 0.75): res = [] for i in xrange(len(desc1)): dist = anorm( desc2 - desc1[i] ) @@ -18,6 +23,14 @@ def match(desc1, desc2, r_threshold = 0.75): res.append((i, n1)) return np.array(res) +def match_flann(desc1, desc2, r_threshold = 0.6): + flann = cv2.flann_Index(desc2, flann_params) + idx2, dist = flann.knnSearch(desc1, 2, params = {}) # bug: need to provide empty dict + mask = dist[:,0] / dist[:,1] < r_threshold + idx1 = np.arange(len(desc1)) + pairs = np.int32( zip(idx1, idx2[:,0]) ) + return pairs[mask] + def draw_match(img1, img2, p1, p2, status = None, H = None): h1, w1 = img1.shape[:2] h2, w2 = img2.shape[:2] @@ -50,6 +63,7 @@ def draw_match(img1, img2, p1, p2, status = None, H = None): cv2.line(vis, (x2+w1-r, y2+r), (x2+w1+r, y2-r), col, thickness) return vis + if __name__ == '__main__': import sys try: fn1, fn2 = sys.argv[1:3] @@ -68,12 +82,21 @@ if __name__ == '__main__': desc2.shape = (-1, surf.descriptorSize()) print 'img1 - %d features, img2 - %d features' % (len(kp1), len(kp2)) - m = match(desc1, desc2) - matched_p1 = np.array([kp1[i].pt for i, j in m]) - matched_p2 = np.array([kp2[j].pt for i, j in m]) - H, status = cv2.findHomography(matched_p1, matched_p2, cv2.RANSAC, 10.0) - print '%d / %d inliers/matched' % (np.sum(status), len(status)) + def match_and_draw(match, r_threshold): + m = match(desc1, desc2, r_threshold) + matched_p1 = np.array([kp1[i].pt for i, j in m]) + matched_p2 = np.array([kp2[j].pt for i, j in m]) + H, status = cv2.findHomography(matched_p1, matched_p2, cv2.RANSAC, 5.0) + print '%d / %d inliers/matched' % (np.sum(status), len(status)) - vis = draw_match(img1, img2, matched_p1, matched_p2, status, H) - cv2.imshow('find_obj SURF', vis) + vis = draw_match(img1, img2, matched_p1, matched_p2, status, H) + return vis + + print 'bruteforce match:', + vis_brute = match_and_draw( match_bruteforce, 0.75 ) + print 'flann match:', + vis_flann = match_and_draw( match_flann, 0.6 ) # flann tends to find more distant second + # neighbours, so r_threshold is decreased + cv2.imshow('find_obj SURF', vis_brute) + cv2.imshow('find_obj SURF flann', vis_flann) cv2.waitKey() \ No newline at end of file