Skip to content

Commit fcddfa4

Browse files
Cartuchomshabunin
authored andcommitted
GSoC 2016 - Adding ALIASES for tutorial (opencv#7041)
* GSoC 2016 - Adding toggle files to be used by tutorials. Add a toggle option for tutorials. * adds a button on the HTML tutorial pages to switch between blocks * the default option is for languages: one can write a block for C++ and another one for Python without re-writing the tutorial Add aliases to the doxyfile. * adding alises to make a link to previous and next tutorial. * adding alias to specify the toggle options in the tutorials index. * adding alias to add a youtube video directly from link. Add a sample tutorial (mat_mask_opertaions) using the developed aliases: * youtube alias * previous and next tutorial alias * buttons * languages info for tutorial table of content * code referances with snippets (and associated sample code files) * Removing the automatic ordering. Adding specific toggles for cpp, java and python. Move all the code to the footer / header and Doxyfile. Updating documentation.
1 parent 36b5abf commit fcddfa4

File tree

13 files changed

+567
-92
lines changed

13 files changed

+567
-92
lines changed

doc/Doxyfile.in

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,17 @@ MULTILINE_CPP_IS_BRIEF = NO
3131
INHERIT_DOCS = YES
3232
SEPARATE_MEMBER_PAGES = NO
3333
TAB_SIZE = 4
34-
ALIASES =
34+
ALIASES += add_toggle{1}="@htmlonly[block] <div class='newInnerHTML'>\1</div><div> <script type="text/javascript"> addToggle(); </script>@endhtmlonly"
35+
ALIASES += add_toggle_cpp="@htmlonly[block] <div class='newInnerHTML' title='cpp' style='display: none;'>C++</div><div class='toggleable_div label_cpp' style='display: none;'>@endhtmlonly"
36+
ALIASES += add_toggle_java="@htmlonly[block] <div class='newInnerHTML' title='java' style='display: none;'>Java</div><div class='toggleable_div label_java' style='display: none;'>@endhtmlonly"
37+
ALIASES += add_toggle_python="@htmlonly[block] <div class='newInnerHTML' title='python' style='display: none;'>Python</div><div class='toggleable_div label_python' style='display: none;'>@endhtmlonly"
38+
ALIASES += end_toggle="@htmlonly[block] </div> @endhtmlonly"
39+
ALIASES += prev_tutorial{1}="**Prev Tutorial:** \ref \1 \n"
40+
ALIASES += next_tutorial{1}="**Next Tutorial:** \ref \1 \n"
41+
ALIASES += youtube{1}="@htmlonly[block]<div align='center'><iframe title='my title' width='560' height='349' src='http://www.youtube.com/embed/\1?rel=0' frameborder='0' align='middle' allowfullscreen></iframe></div>@endhtmlonly"
3542
TCL_SUBST =
3643
OPTIMIZE_OUTPUT_FOR_C = NO
37-
OPTIMIZE_OUTPUT_JAVA = NO
44+
OPTIMIZE_OUTPUT_JAVA = YES
3845
OPTIMIZE_FOR_FORTRAN = NO
3946
OPTIMIZE_OUTPUT_VHDL = NO
4047
EXTENSION_MAPPING =

doc/footer.html

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,74 @@
1717
</a> $doxygenversion
1818
</small></address>
1919
<!--END !GENERATE_TREEVIEW-->
20+
<script type="text/javascript">
21+
//<![CDATA[
22+
function addButton(label, buttonName) {
23+
var b = document.createElement("BUTTON");
24+
b.innerHTML = buttonName;
25+
b.setAttribute('class', 'toggleable_button label_' + label);
26+
b.onclick = function() {
27+
$('.toggleable_button').css({
28+
border: '2px outset',
29+
'border-radius': '4px'
30+
});
31+
$('.toggleable_button.label_' + label).css({
32+
border: '2px inset',
33+
'border-radius': '4px'
34+
});
35+
$('.toggleable_div').css('display', 'none');
36+
$('.toggleable_div.label_' + label).css('display', 'block');
37+
};
38+
b.style.border = '2px outset';
39+
b.style.borderRadius = '4px';
40+
b.style.margin = '2px';
41+
return b;
42+
}
43+
function buttonsToAdd($elements, $heading, $type) {
44+
if ($elements.length === 0) {
45+
$elements = $("" + $type + ":contains(" + $heading.html() + ")").parent().prev("div.newInnerHTML");
46+
}
47+
var arr = jQuery.makeArray($elements);
48+
var seen = {};
49+
arr.forEach(function(e) {
50+
var txt = e.innerHTML;
51+
if (!seen[txt]) {
52+
$button = addButton(e.title, txt);
53+
if (Object.keys(seen).length == 0) {
54+
var linebreak1 = document.createElement("br");
55+
var linebreak2 = document.createElement("br");
56+
($heading).append(linebreak1);
57+
($heading).append(linebreak2);
58+
}
59+
($heading).append($button);
60+
seen[txt] = true;
61+
}
62+
});
63+
return;
64+
}
65+
$("h2").each(function() {
66+
$heading = $(this);
67+
$smallerHeadings = $(this).nextUntil("h2").filter("h3").add($(this).nextUntil("h2").find("h3"));
68+
if ($smallerHeadings.length) {
69+
$smallerHeadings.each(function() {
70+
var $elements = $(this).nextUntil("h3").filter("div.newInnerHTML");
71+
buttonsToAdd($elements, $(this), "h3");
72+
});
73+
} else {
74+
var $elements = $(this).nextUntil("h2").filter("div.newInnerHTML");
75+
buttonsToAdd($elements, $heading, "h2");
76+
}
77+
});
78+
$(".toggleable_button").first().click();
79+
var $clickDefault = $('.toggleable_button.label_python').first();
80+
if ($clickDefault.length) {
81+
$clickDefault.click();
82+
}
83+
$clickDefault = $('.toggleable_button.label_cpp').first();
84+
if ($clickDefault.length) {
85+
$clickDefault.click();
86+
}
87+
//]]>
88+
</script>
2089
</body>
2190
</html>

doc/header.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,34 @@
5454
</table>
5555
</div>
5656
<!--END TITLEAREA-->
57+
<script type="text/javascript">
58+
//<![CDATA[
59+
function getLabelName(innerHTML) {
60+
var str = innerHTML.toLowerCase();
61+
// Replace all '+' with 'p'
62+
str = str.split('+').join('p');
63+
// Replace all ' ' with '_'
64+
str = str.split(' ').join('_');
65+
// Replace all '#' with 'sharp'
66+
str = str.split('#').join('sharp');
67+
// Replace other special characters with 'ascii' + code
68+
for (var i = 0; i < str.length; i++) {
69+
var charCode = str.charCodeAt(i);
70+
if (!(charCode == 95 || (charCode > 96 && charCode < 123) || (charCode > 47 && charCode < 58)))
71+
str = str.substr(0, i) + 'ascii' + charCode + str.substr(i + 1);
72+
}
73+
return str;
74+
}
75+
function addToggle() {
76+
var $getDiv = $('div.newInnerHTML').last();
77+
var buttonName = $getDiv.html();
78+
var label = getLabelName(buttonName.trim());
79+
$getDiv.attr("title", label);
80+
$getDiv.hide();
81+
$getDiv = $getDiv.next();
82+
$getDiv.attr("class", "toggleable_div label_" + label);
83+
$getDiv.hide();
84+
}
85+
//]]>
86+
</script>
5787
<!-- end header part -->

doc/mymath.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
//<![CDATA[
22
MathJax.Hub.Config(
33
{
44
TeX: {
@@ -15,3 +15,4 @@ MathJax.Hub.Config(
1515
}
1616
}
1717
);
18+
//]]>
Lines changed: 125 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Mask operations on matrices {#tutorial_mat_mask_operations}
22
===========================
33

4+
@prev_tutorial{tutorial_how_to_scan_images}
5+
@next_tutorial{tutorial_mat_operations}
6+
47
Mask operations on matrices are quite simple. The idea is that we recalculate each pixels value in
58
an image according to a mask matrix (also known as kernel). This mask holds values that will adjust
69
how 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
2528
the overlapped matrix values. It's the same thing, however in case of large matrices the latter
2629
notation is a lot easier to look over.
2730

31+
@add_toggle_cpp
2832
Now 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

3146
The Basic Method
3247
----------------
3348

3449
Here'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+
6453
At 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+
6975
We 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
7281
this 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
7797
We'll use the plain C [] operator to access pixels. Because we need to access multiple rows at the
7898
same time we'll acquire the pointers for each of them (a previous, a current and a next line). We
7999
need another pointer to where we're going to save the calculation. Then simply access the right
80100
items with the [] operator. For moving the output pointer ahead we simply increase this (with one
81101
byte) 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+
98104
On the borders of the image the upper notation results inexistent pixel locations (like minus one -
99105
minus one). In these points our formula is undefined. A simple solution is to not apply the kernel
100106
in 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

108129
The filter2D function
109130
---------------------
110131

111132
Applying such filters are so common in image processing that in OpenCV there exist a function that
112133
will 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
120139
use:
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

131170
For example:
132171

133172
![](images/resultMatMaskFilter2D.png)
134173

174+
@add_toggle_cpp
135175
You 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
137177
OpenCV source code libraries sample directory at
138178
`samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp`.
139179

140180
Check out an instance of running the program on our [YouTube
141181
channel](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

Comments
 (0)