Added the "Mask operations on matrices" tutorial (with its sample). Plus modified...
authorBernat Gabor <no@email>
Wed, 27 Jul 2011 17:52:27 +0000 (17:52 +0000)
committerBernat Gabor <no@email>
Wed, 27 Jul 2011 17:52:27 +0000 (17:52 +0000)
doc/conf.py
doc/tutorials/core/basic_linear_transform/basic_linear_transform.rst
doc/tutorials/core/how_to_scan_images/how_to_scan_images.rst
doc/tutorials/core/mat-mask-operations/images/resultMatMaskFilter2D.png [new file with mode: 0644]
doc/tutorials/core/mat-mask-operations/mat-mask-operations.rst
doc/tutorials/core/random_generator_and_text/random_generator_and_text.rst
doc/tutorials/core/table_of_content_core/images/matMaskFilter2DOp.png [new file with mode: 0644]
doc/tutorials/core/table_of_content_core/table_of_content_core.rst
doc/tutorials/imgproc/gausian_median_blur_bilateral_filter/gausian_median_blur_bilateral_filter.rst
samples/cpp/tutorial_code/core/discrete_fourier_transform/discrete_fourier_transform.cpp
samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp [new file with mode: 0644]

index f54919f..e6ea396 100644 (file)
@@ -355,6 +355,7 @@ extlinks = {'cvt_color': ('http://opencv.willowgarage.com/documentation/cpp/imgp
             '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),
             'xmlymlpers':('http://opencv.itseez.com/modules/core/doc/xml_yaml_persistence.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)
            }
 
index d5c9a26..cb605d0 100644 (file)
@@ -8,13 +8,15 @@ Goal
 
 In this tutorial you will learn how to:
 
-* Access pixel values 
+.. container:: enumeratevisibleitemswithsquare
 
-* Initialize a matrix with zeros
+   + Access pixel values 
 
-* Learn what :saturate_cast:`saturate_cast <>` does and why it is useful
+   + Initialize a matrix with zeros
 
-* Get some cool info about pixel transformations
+   + Learn what :saturate_cast:`saturate_cast <>` does and why it is useful
+
+   + Get some cool info about pixel transformations
 
 Cool Theory
 =================
index 675f80c..e521b82 100644 (file)
@@ -58,6 +58,8 @@ Another issue is how do we measure time? Well OpenCV offers two simple functions
    t = ((double)getTickCount() - t)/getTickFrequency(); \r
    cout << "Times passed in seconds: " << t << endl;\r
 \r
+.. _How_Image_Stored_Memory: \r
+\r
 How the image matrix is stored in the memory?\r
 =============================================\r
 \r
@@ -175,5 +177,5 @@ Finally, you may watch a sample run of the program on the `video posted <https:/
 .. raw:: html\r
 \r
   <div align="center">\r
-  <iframe title="Install OpenCV by using its source files - Part 1" width="560" height="349" src="http://www.youtube.com/embed/fB3AN5fjgwc?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>\r
+  <iframe title="How to scan images in OpenCV?" width="560" height="349" src="http://www.youtube.com/embed/fB3AN5fjgwc?rel=0&loop=1" frameborder="0" allowfullscreen align="middle"></iframe>\r
   </div>\r
diff --git a/doc/tutorials/core/mat-mask-operations/images/resultMatMaskFilter2D.png b/doc/tutorials/core/mat-mask-operations/images/resultMatMaskFilter2D.png
new file mode 100644 (file)
index 0000000..cebbb4c
Binary files /dev/null and b/doc/tutorials/core/mat-mask-operations/images/resultMatMaskFilter2D.png differ
index dd2b15e..135a2e4 100644 (file)
-.. _maskOperations:\r
+.. _maskOperationsFilter:\r
 \r
-Mask operations on matrixes\r
+Mask operations on matrices\r
 ***************************\r
 \r
-Mask operations on matrixes are quite simple. The idea is that we recalculate each pixels value in an image according to a matrix mask. This mask holds values that will just how much influence have neighbor pixel values (and the pixel value itself) to the new pixel value. From a mathematical point of view we make a weighted average, with our specified values. \r
+Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in an image according to a mask matrix (also known as kernel). This mask holds values that will just how much influence have neighbor pixel values (and the pixel value itself) to the new pixel value. From a mathematical point of view we make a weighted average, with our specified values. \r
 \r
 Our test case\r
 =============\r
 \r
-Let us consider the issue of an image contrast enchancement method. Basically we want to apply for every pixel of the image the following formula: \r
+Let us consider the issue of an image contrast enhancement method. Basically we want to apply for every pixel of the image the following formula: \r
 \r
 .. math::\r
 \r
    I(i,j) = 5*I(i,j) - [ I(i-1,j) + I(i+1,j) + I(i,j-1) + I(i,j+1)] \r
 \r
-       \iff I(i,j)*M, \text{where }\r
-       M = \bordermatrix{ _i\backslash ^j  & -1 &  0 & -1 \cr\r
-                                               -1 &  0 & -1 &  0 \cr\r
-                                                0 & -1 &  5 & -1 \cr\r
-                                               +1 &  0 & -1 &  0 \cr\r
-                                         }\r
+   \iff I(i,j)*M, \text{where }\r
+   M = \bordermatrix{ _i\backslash ^j  & -1 &  0 & -1 \cr\r
+                        -1 &  0 & -1 &  0 \cr\r
+                         0 & -1 &  5 & -1 \cr\r
+                        +1 &  0 & -1 &  0 \cr\r
+                    }\r
 \r
-The first notation is by using a formula, while the second is a compacted version of the first by using a mask. You use the mask by puting the center of the mask matrix (in the upper case noted by the zero-zero index) on the pixel you want to calculate and sum up the pixel values multiplicated with the overlapped matrix values. It's the same thing, however in case of large matrices the later notation is a lot easier to look over. \r
+The first notation is by using a formula, while the second is a compacted version of the first by using a mask. You use the mask by putting the center of the mask matrix (in the upper case noted by the zero-zero index) on the pixel you want to calculate and sum up the pixel values multiplicity with the overlapped matrix values. It's the same thing, however in case of large matrices the later notation is a lot easier to look over. \r
+\r
+Now let us see how we can make this happen by using the basic pixel access method or by using the :filtering:`filter2D <filter2d>` function. \r
+\r
+The Basic Method\r
+================\r
+\r
+Here's a function that will do this: \r
+\r
+.. code-block:: cpp\r
+\r
+   void Sharpen(const Mat& myImage,Mat& Result)\r
+   {\r
+       CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images\r
+\r
+       Result.create(myImage.size(),myImage.type());\r
+       const int nChannels = myImage.channels();\r
+\r
+       for(int j = 1 ; j < myImage.rows-1; ++j)\r
+       {\r
+           const uchar* previous = myImage.ptr<uchar>(j - 1);\r
+           const uchar* current  = myImage.ptr<uchar>(j    );\r
+           const uchar* next     = myImage.ptr<uchar>(j + 1);\r
+\r
+           uchar* output = Result.ptr<uchar>(j);\r
+\r
+           for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)\r
+           {\r
+               *output++ = saturate_cast<uchar>(5*current[i] \r
+                            -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);\r
+           }\r
+       }\r
+\r
+       Result.row(0).setTo(Scalar(0));\r
+       Result.row(Result.rows-1).setTo(Scalar(0));\r
+       Result.col(0).setTo(Scalar(0));\r
+       Result.col(Result.cols-1).setTo(Scalar(0));\r
+   }\r
+\r
+At first we make sure that the input images data is in unsigned char format. For this we use the :utilitysystemfunctions:`CV_Assert <cv-assert>` function that throws an error when the expression inside it is false.\r
+\r
+.. code-block:: cpp\r
+\r
+   CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images\r
+\r
+We create an output image with the same size and the same type than our input. As you can see at the :ref:`How_Image_Stored_Memory` section, depending on the number of channels we may have one or more subcolumns. We will iterate through them via pointers so the total number of elements depends from this number.\r
+\r
+.. code-block:: cpp\r
+\r
+   Result.create(myImage.size(),myImage.type());\r
+   const int nChannels = myImage.channels();\r
+\r
+We'll use the plain C [] operator to access pixels. Because we need to access multiple rows at the same time we'll acquire the pointers for each of them (a previous, a current and a next line). We need another pointer to where we're going to save the calculation. Then simply access the right items with the [] operator. For moving the output pointer ahead we simply increase this (with one byte) after each operation:\r
+\r
+.. code-block:: cpp\r
+\r
+   for(int j = 1 ; j < myImage.rows-1; ++j)\r
+   {\r
+       const uchar* previous = myImage.ptr<uchar>(j - 1);\r
+       const uchar* current  = myImage.ptr<uchar>(j    );\r
+       const uchar* next     = myImage.ptr<uchar>(j + 1);\r
+\r
+       uchar* output = Result.ptr<uchar>(j);\r
+\r
+       for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)\r
+       {\r
+           *output++ = saturate_cast<uchar>(5*current[i] \r
+                        -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);\r
+       }\r
+   }\r
+\r
+On the borders of the image the upper notation results inexistent pixel locations (like minus one - minus one). In these points our formula is undefined. A simple solution is to do not apply the mask in these points and, for example, maintain the images previous values by copying these values over to these locations:\r
+\r
+.. code-block:: cpp\r
+\r
+   Result.row(0).setTo(Scalar(0));             // The top row \r
+   Result.row(Result.rows-1).setTo(Scalar(0)); // The left column\r
+   Result.col(0).setTo(Scalar(0));             // The right column\r
+   Result.col(Result.cols-1).setTo(Scalar(0)); // The bottom row\r
+\r
+The filter2D function\r
+=====================\r
+\r
+Applying such filters are so common in image processing that in OpenCV there exist a function that will take care of applying the mask (also called a kernel in some places). For this you first need to define a *Mat* object that holds the mask:\r
+\r
+.. code-block:: cpp\r
+\r
+   Mat kern = (Mat_<char>(3,3) <<  0, -1,  0, \r
+                                  -1,  5, -1,\r
+                                   0, -1,  0);\r
+\r
+Then call the :filtering:`filter2D <filter2d>` function specifying the input, the output image and the kernell to use: \r
+\r
+.. code-block:: cpp\r
+\r
+   filter2D(I, K, I.depth(), kern ); \r
+\r
+The function even has a fifth optional argument to specify the center of the kernel, and a sixth one for determining what to do in the regions where the operation is undefined (borders). Using this function has the advantage that it's shorter, more verbose and that because they are some optimization techniques implemented usually faster than the *hand method*. For example in my while the first one took only 13 milliseconds the second took around 31 milliseconds. Quite some difference.\r
+\r
+For example: \r
+\r
+.. image:: images/resultMatMaskFilter2D.png\r
+   :alt: A sample output of the program\r
+   :align: center\r
+\r
+You can download this source code from :download:`here <../../../../samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp>` or look in the OpenCV source code libraries sample directory at :file:`samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp`.\r
+\r
+Check out an instance of running the program on our `YouTube channel <http://www.youtube.com/watch?v=7PF1tAU9se4>`_ . \r
+\r
+.. raw:: html\r
+\r
+  <div align="center">\r
+ <iframe width="560" height="349" src="https://www.youtube.com/embed/7PF1tAU9se4?hd=1" frameborder="0" allowfullscreen></iframe>\r
+  </div>\r
index 5e3f1de..fb487ca 100644 (file)
@@ -1,7 +1,7 @@
 .. _Drawing_2:
 
-Fancy Drawing!
-****************
+Random generator and text with OpenCV
+*************************************
 
 Goals
 ======
@@ -9,15 +9,15 @@ 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 <>`
+* Display text on an OpenCV window by using the function :put_text:`putText <>`
 
 Code
 =====
-* In the previous tutorial 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 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*
+* 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 <https://code.ros.org/svn/opencv/trunk/opencv/samples/cpp/tutorial_code/Basic/Drawing_2.cpp>`_
+* This code is in your OpenCV sample folder. Otherwise you can grab it from `here <https://code.ros.org/svn/opencv/trunk/opencv/samples/cpp/tutorial_code/Basic/Drawing_2.cpp>`_ .
 
 Explanation
 ============
@@ -81,27 +81,23 @@ Explanation
 
    .. code-block:: cpp
 
-      /**
-       * @function Drawing_Random_Lines
-       */
       int Drawing_Random_Lines( Mat image, char* window_name, RNG rng )
       {
         int lineType = 8;
         Point pt1, pt2;
 
-       for( int i = 0; i < NUMBER; i++ )
-       {
-        pt1.x = rng.uniform( x_1, x_2 );
-        pt1.y = rng.uniform( y_1, y_2 );
-        pt2.x = rng.uniform( x_1, x_2 );
-        pt2.y = rng.uniform( y_1, y_2 );
+        for( int i = 0; i < NUMBER; i++ )
+        {
+         pt1.x = rng.uniform( x_1, x_2 );
+         pt1.y = rng.uniform( y_1, y_2 );
+         pt2.x = rng.uniform( x_1, x_2 );
+         pt2.y = rng.uniform( y_1, y_2 );
 
          line( image, pt1, pt2, randomColor(rng), rng.uniform(1, 10), 8 );
-        imshow( window_name, image );
+         imshow( window_name, image );
          if( waitKey( DELAY ) >= 0 )
-          { return -1; }
+         { return -1; }
         }
-
         return 0;
       }
 
@@ -122,18 +118,18 @@ Explanation
      * As another observation, we notice that in the :line:`line <>` arguments, for the *color* input we enter:
 
        .. code-block:: cpp
-          
-         randomColor(rng)           
-          
+      
+          randomColor(rng)           
+      
        Let's check the function implementation:
 
        .. code-block:: cpp
 
-         static Scalar randomColor( RNG& rng )
-          {
+          static Scalar randomColor( RNG& rng )
+            {
             int icolor = (unsigned) rng;
             return Scalar( icolor&255, (icolor>>8)&255, (icolor>>16)&255 );
-         }
+            }
 
        As we can see, the return value is an *Scalar* with 3 randomly initialized values, which are used as the *R*, *G* and *B* parameters for the line color. Hence, the color of the lines will be random too!
 
@@ -192,21 +188,21 @@ Explanation
       int Displaying_Big_End( Mat image, char* window_name, RNG rng )
       {
         Size textsize = getTextSize("OpenCV forever!", CV_FONT_HERSHEY_COMPLEX, 3, 5, 0);
-       Point org((window_width - textsize.width)/2, (window_height - textsize.height)/2);
-       int lineType = 8;
+        Point org((window_width - textsize.width)/2, (window_height - textsize.height)/2);
+        int lineType = 8;
     
-       Mat image2;
+        Mat image2;
 
-       for( int i = 0; i < 255; i += 2 )
-       {
-         image2 = image - Scalar::all(i);
-         putText( image2, "OpenCV forever!", org, CV_FONT_HERSHEY_COMPLEX, 3,
-                  Scalar(i, i, 255), 5, lineType );
+        for( int i = 0; i < 255; i += 2 )
+        {
+          image2 = image - Scalar::all(i);
+          putText( image2, "OpenCV forever!", org, CV_FONT_HERSHEY_COMPLEX, 3,
+                 Scalar(i, i, 255), 5, lineType );
         
-         imshow( window_name, image2 );
-         if( waitKey(DELAY) >= 0 )
-                 { return -1; }
-       }
+          imshow( window_name, image2 );
+          if( waitKey(DELAY) >= 0 )
+            { return -1; }
+        }
 
         return 0;
       }
diff --git a/doc/tutorials/core/table_of_content_core/images/matMaskFilter2DOp.png b/doc/tutorials/core/table_of_content_core/images/matMaskFilter2DOp.png
new file mode 100644 (file)
index 0000000..6795921
Binary files /dev/null and b/doc/tutorials/core/table_of_content_core/images/matMaskFilter2DOp.png differ
index 845fe62..92c0acd 100644 (file)
@@ -51,6 +51,25 @@ Here you will learn the about the basic building blocks of the library. A must r
   .. cssclass:: toctableopencv
 
   =============== ======================================================
+   |HowFilterIm|  **Title:** :ref:`maskOperationsFilter`
+
+                  *Compatibility:* > OpenCV 2.0
+
+                  *Author:* |Author_BernatG|
+
+                  You'll find out how to scan images with neighbor access and use the :filtering:`filter2D <filter2d>` function to apply kernel filters on images.
+
+  =============== ======================================================
+
+  .. |HowFilterIm| image:: images/matMaskFilter2DOp.png
+                   :height: 90pt
+                   :width:  90pt
+
++
+  .. tabularcolumns:: m{100pt} m{300pt}
+  .. cssclass:: toctableopencv
+
+  =============== ======================================================
    |Beginners_4|  **Title:** :ref:`Adding_Images`
 
                   *Compatibility:* > OpenCV 2.0
@@ -171,10 +190,10 @@ Here you will learn the about the basic building blocks of the library. A must r
    
    ../mat - the basic image container/mat - the basic image container
    ../how_to_scan_images/how_to_scan_images
+   ../mat-mask-operations/mat-mask-operations
    ../adding_images/adding_images
    ../basic_linear_transform/basic_linear_transform
    ../basic_geometric_drawing/basic_geometric_drawing
    ../random_generator_and_text/random_generator_and_text
-   ../mat-mask-operations/mat-mask-operations
    ../discrete_fourier_transform/discrete_fourier_transform
    ../file_input_output_with_xml_yml/file_input_output_with_xml_yml
\ No newline at end of file
index 379ce0c..ef85008 100644 (file)
@@ -216,7 +216,7 @@ Code
 Explanation
 =============
 
-#. Let's check the OpenCV functions that involve only the smoothing procedure, since the rest is already known by now.
+#. Let's check the OpenCV functions that involve only the smoothing procedure, since the rest is already known by now. 
 
 #. **Normalized Block Filter:**
 
index e9eee6e..628b974 100644 (file)
@@ -18,7 +18,7 @@ void help(char* progName)
 \r
 int main(int argc, char ** argv)\r
 {\r
-    help(argv[0]);\r
+       help(argv[0]);\r
 \r
     const char* filename = argc >=2 ? argv[1] : "lena.jpg";\r
 \r
diff --git a/samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp b/samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp
new file mode 100644 (file)
index 0000000..6b18f95
--- /dev/null
@@ -0,0 +1,86 @@
+#include <opencv2/core/core.hpp>\r
+#include <opencv2/highgui/highgui.hpp>\r
+#include <opencv2/imgproc/imgproc.hpp>\r
+#include <iostream>\r
+\r
+using namespace std; \r
+using namespace cv;\r
+\r
+void help(char* progName)\r
+{\r
+    cout << endl \r
+        <<  "This program shows how to filter images with mask: the write it yourself and the"\r
+        << "filter2d way. " << endl\r
+        <<  "Usage:"                                                                        << endl\r
+        << progName << " [image_name -- default lena.jpg] [G -- grayscale] "        << endl << endl;\r
+}\r
+\r
+\r
+void Sharpen(const Mat& myImage,Mat& Result);\r
+\r
+int main( int argc, char* argv[])\r
+{\r
+    help(argv[0]);\r
+    const char* filename = argc >=2 ? argv[1] : "lena.jpg";\r
+\r
+    Mat I, J, K;\r
+\r
+    if (argc >= 3 && !strcmp("G", argv[2]))\r
+        I = imread( filename, CV_LOAD_IMAGE_GRAYSCALE);\r
+    else\r
+        I = imread( filename, CV_LOAD_IMAGE_COLOR);\r
+\r
+    namedWindow("Input", CV_WINDOW_AUTOSIZE);\r
+    namedWindow("Output", CV_WINDOW_AUTOSIZE);\r
+\r
+    imshow("Input", I);\r
+    double t = (double)getTickCount();\r
+    \r
+    Sharpen(I, J); \r
+    \r
+    t = ((double)getTickCount() - t)/getTickFrequency();\r
+    cout << "Hand written function times passed in seconds: " << t << endl;\r
+\r
+    imshow("Output", J);\r
+    cvWaitKey(0);\r
+\r
+    Mat kern = (Mat_<char>(3,3) <<  0, -1,  0, \r
+                                   -1,  5, -1,\r
+                                    0, -1,  0);\r
+    t = (double)getTickCount();\r
+    filter2D(I, K, I.depth(), kern ); \r
+    t = ((double)getTickCount() - t)/getTickFrequency();\r
+    cout << "Built-in filter2D time passed in seconds:      " << t << endl;\r
+\r
+    imshow("Output", K);\r
+\r
+    cvWaitKey(0);\r
+    return 0; \r
+}\r
+void Sharpen(const Mat& myImage,Mat& Result)\r
+{\r
+    CV_Assert(myImage.depth() == CV_8U);  // accept only uchar images\r
+\r
+    const int nChannels = myImage.channels();\r
+    Result.create(myImage.size(),myImage.type());\r
+    \r
+    for(int j = 1 ; j < myImage.rows-1; ++j)\r
+    {\r
+        const uchar* previous = myImage.ptr<uchar>(j - 1);\r
+        const uchar* current  = myImage.ptr<uchar>(j    );\r
+        const uchar* next     = myImage.ptr<uchar>(j + 1);\r
+\r
+        uchar* output = Result.ptr<uchar>(j);\r
+\r
+        for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)\r
+        {\r
+            *output++ = saturate_cast<uchar>(5*current[i] \r
+                         -current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);\r
+        }\r
+    }\r
+\r
+    Result.row(0).setTo(Scalar(0));\r
+    Result.row(Result.rows-1).setTo(Scalar(0));\r
+    Result.col(0).setTo(Scalar(0));\r
+    Result.col(Result.cols-1).setTo(Scalar(0));\r
+}
\ No newline at end of file