11Mask operations on matrices {#tutorial_mat_mask_operations}
22===========================
33
4+ @prev_tutorial{tutorial_how_to_scan_images}
5+ @next_tutorial{tutorial_mat_operations}
6+
47Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in
58an image according to a mask matrix (also known as kernel). This mask holds values that will adjust
69how much influence neighboring pixels (and the current pixel) have on the new pixel value. From a
@@ -25,123 +28,166 @@ the zero-zero index) on the pixel you want to calculate and sum up the pixel val
2528the overlapped matrix values. It's the same thing, however in case of large matrices the latter
2629notation is a lot easier to look over.
2730
31+ @add_toggle_cpp
2832Now let us see how we can make this happen by using the basic pixel access method or by using the
2933@ref cv::filter2D function.
34+ @end_toggle
35+
36+ @add_toggle_java
37+ Now let us see how we can make this happen by using the basic pixel access method or by using the
38+ ** Imgproc.filter2D()** function.
39+ @end_toggle
40+
41+ @add_toggle_python
42+ Now let us see how we can make this happen by using the basic pixel access method or by using the
43+ ** cv2.filter2D()** function.
44+ @end_toggle
3045
3146The Basic Method
3247----------------
3348
3449Here's a function that will do this:
35- @code {.cpp}
36- void Sharpen(const Mat& myImage, Mat& Result)
37- {
38- CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
39-
40- Result.create(myImage.size(), myImage.type());
41- const int nChannels = myImage.channels();
42-
43- for(int j = 1; j < myImage.rows - 1; ++j)
44- {
45- const uchar* previous = myImage.ptr<uchar>(j - 1);
46- const uchar* current = myImage.ptr<uchar>(j );
47- const uchar* next = myImage.ptr<uchar>(j + 1);
48-
49- uchar* output = Result.ptr<uchar>(j);
50-
51- for(int i = nChannels; i < nChannels * (myImage.cols - 1); ++i)
52- {
53- *output++ = saturate_cast<uchar>(5 * current[i]
54- -current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
55- }
56- }
57-
58- Result.row(0).setTo(Scalar(0));
59- Result.row(Result.rows - 1).setTo(Scalar(0));
60- Result.col(0).setTo(Scalar(0));
61- Result.col(Result.cols - 1).setTo(Scalar(0));
62- }
63- @endcode
50+ @add_toggle_cpp
51+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp basic_method
52+
6453At first we make sure that the input images data is in unsigned char format. For this we use the
6554@ref cv::CV_Assert function that throws an error when the expression inside it is false.
66- @code {.cpp}
67- CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
55+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp 8_bit
56+ @end_toggle
57+
58+ @add_toggle_java
59+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java basic_method
60+
61+ At first we make sure that the input images data in unsigned 8 bit format.
62+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java 8_bit
63+ @end_toggle
64+
65+ @add_toggle_python
66+ @snippet samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py basic_method
67+
68+ At first we make sure that the input images data in unsigned 8 bit format.
69+ @code {.py}
70+ my_image = cv2.cvtColor(my_image, cv2.CV_8U)
6871@endcode
72+
73+ @end_toggle
74+
6975We create an output image with the same size and the same type as our input. As you can see in the
7076@ref tutorial_how_to_scan_images_storing "storing" section, depending on the number of channels we may have one or more
71- subcolumns. We will iterate through them via pointers so the total number of elements depends from
77+ subcolumns.
78+
79+ @add_toggle_cpp
80+ We will iterate through them via pointers so the total number of elements depends on
7281this number.
73- @code {.cpp}
74- Result.create(myImage.size(), myImage.type());
75- const int nChannels = myImage.channels();
82+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp create_channels
83+ @end_toggle
84+
85+ @add_toggle_java
86+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java create_channels
87+ @end_toggle
88+
89+ @add_toggle_python
90+ @code {.py}
91+ height, width, n_channels = my_image.shape
92+ result = np.zeros(my_image.shape, my_image.dtype)
7693@endcode
94+ @end_toggle
95+
96+ @add_toggle_cpp
7797We'll use the plain C [ ] operator to access pixels. Because we need to access multiple rows at the
7898same time we'll acquire the pointers for each of them (a previous, a current and a next line). We
7999need another pointer to where we're going to save the calculation. Then simply access the right
80100items with the [ ] operator. For moving the output pointer ahead we simply increase this (with one
81101byte) after each operation:
82- @code {.cpp}
83- for(int j = 1; j < myImage.rows - 1; ++j)
84- {
85- const uchar* previous = myImage.ptr<uchar >(j - 1);
86- const uchar* current = myImage.ptr<uchar >(j );
87- const uchar* next = myImage.ptr<uchar >(j + 1);
88-
89- uchar* output = Result.ptr<uchar>(j);
90-
91- for(int i = nChannels; i < nChannels * (myImage.cols - 1); ++i)
92- {
93- *output++ = saturate_cast<uchar>(5 * current[i]
94- -current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
95- }
96- }
97- @endcode
102+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp basic_method_loop
103+
98104On the borders of the image the upper notation results inexistent pixel locations (like minus one -
99105minus one). In these points our formula is undefined. A simple solution is to not apply the kernel
100106in these points and, for example, set the pixels on the borders to zeros:
101- @code {.cpp}
102- Result.row(0).setTo(Scalar(0)); // The top row
103- Result.row(Result.rows - 1).setTo(Scalar(0)); // The bottom row
104- Result.col(0).setTo(Scalar(0)); // The left column
105- Result.col(Result.cols - 1).setTo(Scalar(0)); // The right column
106- @endcode
107+
108+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp borders
109+ @end_toggle
110+
111+ @add_toggle_java
112+ We need to access multiple rows and columns which can be done by adding or subtracting 1 to the current center (i,j).
113+ Then we apply the sum and put the new value in the Result matrix.
114+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java basic_method_loop
115+
116+ On the borders of the image the upper notation results in inexistent pixel locations (like (-1,-1)).
117+ In these points our formula is undefined. A simple solution is to not apply the kernel
118+ in these points and, for example, set the pixels on the borders to zeros:
119+
120+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java borders
121+ @end_toggle
122+
123+ @add_toggle_python
124+ We need to access multiple rows and columns which can be done by adding or subtracting 1 to the current center (i,j).
125+ Then we apply the sum and put the new value in the Result matrix.
126+ @snippet samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py basic_method_loop
127+ @end_toggle
107128
108129The filter2D function
109130---------------------
110131
111132Applying such filters are so common in image processing that in OpenCV there exist a function that
112133will take care of applying the mask (also called a kernel in some places). For this you first need
113- to define a * Mat* object that holds the mask:
114- @code {.cpp}
115- Mat kern = (Mat_ <char >(3,3) << 0, -1, 0,
116- -1, 5, -1,
117- 0, -1, 0);
118- @endcode
119- Then call the @ref cv::filter2D function specifying the input, the output image and the kernell to
134+ to define an object that holds the mask:
135+ @add_toggle_cpp
136+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp kern
137+
138+ Then call the @ref cv::filter2D function specifying the input, the output image and the kernel to
120139use:
121- @code {.cpp}
122- filter2D(I, K, I.depth(), kern);
123- @endcode
124- The function even has a fifth optional argument to specify the center of the kernel, and a sixth one
125- for determining what to do in the regions where the operation is undefined (borders). Using this
126- function has the advantage that it's shorter, less verbose and because there are some optimization
127- techniques implemented it is usually faster than the * hand-coded method* . For example in my test
128- while the second one took only 13 milliseconds the first took around 31 milliseconds. Quite some
129- difference.
140+ @snippet samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp filter2D
141+
142+ The function even has a fifth optional argument to specify the center of the kernel, a sixth
143+ for adding an optional value to the filtered pixels before storing them in K and a seventh one
144+ for determining what to do in the regions where the operation is undefined (borders).
145+ @end_toggle
146+
147+ @add_toggle_java
148+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java kern
149+
150+ Then call the ** Imgproc.filter2D()** function specifying the input, the output image and the kernel to
151+ use:
152+ @snippet samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java filter2D
153+ The function even has a fifth optional argument to specify the center of the kernel, a sixth
154+ for adding an optional value to the filtered pixels before storing them in K and a seventh one
155+ for determining what to do in the regions where the operation is undefined (borders).
156+ @end_toggle
157+
158+ @add_toggle_python
159+ @snippet samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py kern
160+
161+ Then call the ** cv2.filter2D()** function specifying the input, the output image and the kernell to
162+ use:
163+ @snippet samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py filter2D
164+ @end_toggle
165+
166+ This function is shorter, less verbose and, because there are some optimizations, it is usually faster
167+ than the * hand-coded method* . For example in my test while the second one took only 13
168+ milliseconds the first took around 31 milliseconds. Quite some difference.
130169
131170For example:
132171
133172![ ] ( images/resultMatMaskFilter2D.png )
134173
174+ @add_toggle_cpp
135175You can download this source code from [ here
136176] ( https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp ) or look in the
137177OpenCV source code libraries sample directory at
138178` samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp ` .
139179
140180Check out an instance of running the program on our [ YouTube
141181channel] ( http://www.youtube.com/watch?v=7PF1tAU9se4 ) .
142-
143- \htmlonly
144- <div align =" center " >
145- <iframe width =" 560 " height =" 349 " src =" https://www.youtube.com/embed/7PF1tAU9se4?hd=1 " frameborder =" 0 " allowfullscreen ></iframe >
146- </div >
147- \endhtmlonly
182+ @youtube {7PF1tAU9se4}
183+ @end_toggle
184+
185+ @add_toggle_java
186+ You can look in the OpenCV source code libraries sample directory at
187+ ` samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java ` .
188+ @end_toggle
189+
190+ @add_toggle_python
191+ You can look in the OpenCV source code libraries sample directory at
192+ ` samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py ` .
193+ @end_toggle
0 commit comments