Merge pull request #17839 from malliaridis:master

This commit is contained in:
Alexander Alekhin 2020-11-18 16:48:36 +00:00
commit 87ed750510
4 changed files with 250 additions and 77 deletions

View File

@ -84,57 +84,198 @@ This tutorial's code is shown below. You can also download it
Explanation Explanation
----------- -----------
-# Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in @add_toggle_cpp
previous sections). Let's check the general structure of the C++ program: Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
previous sections). Let's check the general structure of the C++ program:
- Load an image (can be BGR or grayscale) @snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp main
- Create two windows (one for dilation output, the other for erosion)
- Create a set of two 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.
- 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: -# Load an image (can be BGR or grayscale)
-# Create two windows (one for dilation output, the other for erosion)
-# Create a set of two 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.
-# Call once erosion and dilation to show the initial image.
-# **erosion:**
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
- The function that performs the *erosion* operation is @ref cv::erode . As we can see, it Every time we move any slider, the user's function **Erosion** or **Dilation** will be
receives three arguments: called and it will update the output image based on the current trackbar values.
- *src*: The source image
- *erosion_dst*: The output image
- *element*: This is the kernel we will use to perform the operation. If we do not
specify, the default is a simple `3x3` matrix. Otherwise, we can specify its
shape. For this, we need to use the function cv::getStructuringElement :
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp kernel
We can choose any of three shapes for our kernel: Let's analyze these two functions:
- Rectangular box: MORPH_RECT #### The erosion function
- Cross: MORPH_CROSS
- Ellipse: MORPH_ELLIPSE
Then, we just have to specify the size of our kernel and the *anchor point*. If not @snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp erosion
specified, it is assumed to be in the center.
- That is all. We are ready to perform the erosion of our image. The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
@note Additionally, there is another parameter that allows you to perform multiple erosions receives three arguments:
(iterations) at once. However, We haven't used it in this simple tutorial. You can check out the - *src*: The source image
reference for more details. - *erosion_dst*: The output image
- *element*: This is the kernel we will use to perform the operation. If we do not
specify, the default is a simple `3x3` matrix. Otherwise, we can specify its
shape. For this, we need to use the function cv::getStructuringElement :
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp kernel
-# **dilation:** We can choose any of three shapes for our kernel:
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**. - Rectangular box: MORPH_RECT
Here we also have the option of defining our kernel, its anchor point and the size of the operator - Cross: MORPH_CROSS
to be used. - Ellipse: MORPH_ELLIPSE
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
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.
#### The dilation function
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
Here we also have the option of defining our kernel, its anchor point and the size of the operator
to be used.
@snippet cpp/tutorial_code/ImgProc/Morphology_1.cpp dilation
@end_toggle
@add_toggle_java
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
previous sections). Let's check however the general structure of the java class. There are 4 main
parts in the java class:
- the class constructor which setups the window that will be filled with window components
- the `addComponentsToPane` method, which fills out the window
- the `update` method, which determines what happens when the user changes any value
- the `main` method, which is the entry point of the program
In this tutorial we will focus on the `addComponentsToPane` and `update` methods. However, for completion the
steps followed in the constructor are:
-# Load an image (can be BGR or grayscale)
-# Create a window
-# Add various control components with `addComponentsToPane`
-# show the window
The components were added by the following method:
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java components
In short we
-# create a panel for the sliders
-# create a combo box for the element types
-# create a slider for the kernel size
-# create a combo box for the morphology function to use (erosion or dilation)
The action and state changed listeners added call at the end the `update` method which updates
the image based on the current slider values. So every time we move any slider, the `update` method is triggered.
#### Updating the image
To update the image we used the following implementation:
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java update
In other words we
-# get the structuring element the user chose
-# execute the **erosion** or **dilation** function based on `doErosion`
-# reload the image with the morphology applied
-# repaint the frame
Let's analyze the `erode` and `dilate` methods:
#### The erosion method
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java erosion
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
receives three arguments:
- *src*: The source image
- *erosion_dst*: The output image
- *element*: This is the kernel we will use to perform the operation. For specifying the shape, we need to use
the function cv::getStructuringElement :
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java kernel
We can choose any of three shapes for our kernel:
- Rectangular box: CV_SHAPE_RECT
- Cross: CV_SHAPE_CROSS
- Ellipse: CV_SHAPE_ELLIPSE
Together with the shape we specify the size of our kernel and the *anchor point*. If the anchor point is not
specified, it is assumed to be in the center.
That is all. We are ready to perform the erosion of our image.
#### The dilation function
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
Here we also have the option of defining our kernel, its anchor point and the size of the operator
to be used.
@snippet java/tutorial_code/ImgProc/erosion_dilatation/MorphologyDemo1.java dilation
@end_toggle
@add_toggle_python
Most of the material shown here is trivial (if you have any doubt, please refer to the tutorials in
previous sections). Let's check the general structure of the python script:
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py main
-# Load an image (can be BGR or grayscale)
-# Create two windows (one for erosion output, the other for dilation) with a set of trackbars each
- The first trackbar "Element" returns the value for the morphological type that will be mapped
(1 = rectangle, 2 = cross, 3 = ellipse)
- The second trackbar "Kernel size" returns the size of the element for the
corresponding operation
-# Call once erosion and dilation to show the initial image
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:
#### The erosion function
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py erosion
The function that performs the *erosion* operation is @ref cv::erode . As we can see, it
receives two arguments and returns the processed image:
- *src*: The source image
- *element*: The kernel we will use to perform the operation. We can specify its
shape by using the function cv::getStructuringElement :
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py kernel
We can choose any of three shapes for our kernel:
- 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 the anchor point not
specified, it is assumed to be in the center.
That is all. We are ready to perform the erosion of our image.
#### The dilation function
The code is below. As you can see, it is completely similar to the snippet of code for **erosion**.
Here we also have the option of defining our kernel, its anchor point and the size of the operator
to be used.
@snippet python/tutorial_code/imgProc/erosion_dilatation/morphology_1.py dilation
@end_toggle
@note Additionally, there are further parameters that allow you to perform multiple erosions/dilations
(iterations) at once and also set the border type and value. However, We haven't used those
in this simple tutorial. You can check out the reference for more details.
Results Results
------- -------
Compile the code above and execute it with an image as argument. For instance, using this image: Compile the code above and execute it (or run the script if using python) with an image as argument.
If you do not provide an image as argument the default sample image
([LinuxLogo.jpg](https://github.com/opencv/opencv/tree/master/samples/data/LinuxLogo.jpg)) will be used.
For instance, using this image:
![](images/Morphology_1_Tutorial_Original_Image.jpg) ![](images/Morphology_1_Tutorial_Original_Image.jpg)
@ -143,3 +284,4 @@ naturally. Try them out! You can even try to add a third Trackbar to control the
iterations. iterations.
![](images/Morphology_1_Result.jpg) ![](images/Morphology_1_Result.jpg)
(depending on the programming language the output might vary a little or be only 1 window)

View File

@ -25,6 +25,7 @@ int const max_kernel_size = 21;
void Erosion( int, void* ); void Erosion( int, void* );
void Dilation( int, void* ); void Dilation( int, void* );
//![main]
/** /**
* @function main * @function main
*/ */
@ -70,6 +71,7 @@ int main( int argc, char** argv )
waitKey(0); waitKey(0);
return 0; return 0;
} }
//![main]
//![erosion] //![erosion]
/** /**

View File

@ -34,6 +34,7 @@ public class MorphologyDemo1 {
private JFrame frame; private JFrame frame;
private JLabel imgLabel; private JLabel imgLabel;
//! [constructor]
public MorphologyDemo1(String[] args) { public MorphologyDemo1(String[] args) {
String imagePath = args.length > 0 ? args[0] : "../data/LinuxLogo.jpg"; String imagePath = args.length > 0 ? args[0] : "../data/LinuxLogo.jpg";
matImgSrc = Imgcodecs.imread(imagePath); matImgSrc = Imgcodecs.imread(imagePath);
@ -54,7 +55,9 @@ public class MorphologyDemo1 {
frame.pack(); frame.pack();
frame.setVisible(true); frame.setVisible(true);
} }
//! [constructor]
//! [components]
private void addComponentsToPane(Container pane, Image img) { private void addComponentsToPane(Container pane, Image img) {
if (!(pane.getLayout() instanceof BorderLayout)) { if (!(pane.getLayout() instanceof BorderLayout)) {
pane.add(new JLabel("Container doesn't use BorderLayout!")); pane.add(new JLabel("Container doesn't use BorderLayout!"));
@ -114,21 +117,31 @@ public class MorphologyDemo1 {
imgLabel = new JLabel(new ImageIcon(img)); imgLabel = new JLabel(new ImageIcon(img));
pane.add(imgLabel, BorderLayout.CENTER); pane.add(imgLabel, BorderLayout.CENTER);
} }
//! [components]
//! [update]
private void update() { private void update() {
//! [kernel]
Mat element = Imgproc.getStructuringElement(elementType, new Size(2 * kernelSize + 1, 2 * kernelSize + 1), Mat element = Imgproc.getStructuringElement(elementType, new Size(2 * kernelSize + 1, 2 * kernelSize + 1),
new Point(kernelSize, kernelSize)); new Point(kernelSize, kernelSize));
//! [kernel]
if (doErosion) { if (doErosion) {
//! [erosion]
Imgproc.erode(matImgSrc, matImgDst, element); Imgproc.erode(matImgSrc, matImgDst, element);
//! [erosion]
} else { } else {
//! [dilation]
Imgproc.dilate(matImgSrc, matImgDst, element); Imgproc.dilate(matImgSrc, matImgDst, element);
//! [dilation]
} }
Image img = HighGui.toBufferedImage(matImgDst); Image img = HighGui.toBufferedImage(matImgDst);
imgLabel.setIcon(new ImageIcon(img)); imgLabel.setIcon(new ImageIcon(img));
frame.repaint(); frame.repaint();
} }
//! [update]
//! [main]
public static void main(String[] args) { public static void main(String[] args) {
// Load the native OpenCV library // Load the native OpenCV library
System.loadLibrary(Core.NATIVE_LIBRARY_NAME); System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
@ -142,4 +155,5 @@ public class MorphologyDemo1 {
} }
}); });
} }
//! [main]
} }

View File

@ -3,61 +3,76 @@ import cv2 as cv
import numpy as np import numpy as np
import argparse import argparse
src = None
erosion_size = 0 erosion_size = 0
max_elem = 2 max_elem = 2
max_kernel_size = 21 max_kernel_size = 21
title_trackbar_element_type = 'Element:\n 0: Rect \n 1: Cross \n 2: Ellipse' title_trackbar_element_shape = 'Element:\n 0: Rect \n 1: Cross \n 2: Ellipse'
title_trackbar_kernel_size = 'Kernel size:\n 2n +1' title_trackbar_kernel_size = 'Kernel size:\n 2n +1'
title_erosion_window = 'Erosion Demo' title_erosion_window = 'Erosion Demo'
title_dilatation_window = 'Dilation Demo' title_dilation_window = 'Dilation Demo'
## [main]
def main(image):
global src
src = cv.imread(cv.samples.findFile(image))
if src is None:
print('Could not open or find the image: ', image)
exit(0)
cv.namedWindow(title_erosion_window)
cv.createTrackbar(title_trackbar_element_shape, title_erosion_window, 0, max_elem, erosion)
cv.createTrackbar(title_trackbar_kernel_size, title_erosion_window, 0, max_kernel_size, erosion)
cv.namedWindow(title_dilation_window)
cv.createTrackbar(title_trackbar_element_shape, title_dilation_window, 0, max_elem, dilatation)
cv.createTrackbar(title_trackbar_kernel_size, title_dilation_window, 0, max_kernel_size, dilatation)
erosion(0)
dilatation(0)
cv.waitKey()
## [main]
# optional mapping of values with morphological shapes
def morph_shape(val):
if val == 0:
return cv.MORPH_RECT
elif val == 1:
return cv.MORPH_CROSS
elif val == 2:
return cv.MORPH_ELLIPSE
## [erosion]
def erosion(val): def erosion(val):
erosion_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_erosion_window) erosion_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_erosion_window)
erosion_type = 0 erosion_shape = morph_shape(cv.getTrackbarPos(title_trackbar_element_shape, title_erosion_window))
val_type = cv.getTrackbarPos(title_trackbar_element_type, title_erosion_window)
if val_type == 0:
erosion_type = cv.MORPH_RECT
elif val_type == 1:
erosion_type = cv.MORPH_CROSS
elif val_type == 2:
erosion_type = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(erosion_type, (2*erosion_size + 1, 2*erosion_size+1), (erosion_size, erosion_size)) ## [kernel]
element = cv.getStructuringElement(erosion_shape, (2 * erosion_size + 1, 2 * erosion_size + 1),
(erosion_size, erosion_size))
## [kernel]
erosion_dst = cv.erode(src, element) erosion_dst = cv.erode(src, element)
cv.imshow(title_erosion_window, erosion_dst) cv.imshow(title_erosion_window, erosion_dst)
## [erosion]
## [dilation]
def dilatation(val): def dilatation(val):
dilatation_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_dilatation_window) dilatation_size = cv.getTrackbarPos(title_trackbar_kernel_size, title_dilation_window)
dilatation_type = 0 dilation_shape = morph_shape(cv.getTrackbarPos(title_trackbar_element_shape, title_dilation_window))
val_type = cv.getTrackbarPos(title_trackbar_element_type, title_dilatation_window)
if val_type == 0:
dilatation_type = cv.MORPH_RECT
elif val_type == 1:
dilatation_type = cv.MORPH_CROSS
elif val_type == 2:
dilatation_type = cv.MORPH_ELLIPSE
element = cv.getStructuringElement(dilatation_type, (2*dilatation_size + 1, 2*dilatation_size+1), (dilatation_size, dilatation_size)) element = cv.getStructuringElement(dilation_shape, (2 * dilatation_size + 1, 2 * dilatation_size + 1),
(dilatation_size, dilatation_size))
dilatation_dst = cv.dilate(src, element) dilatation_dst = cv.dilate(src, element)
cv.imshow(title_dilatation_window, dilatation_dst) cv.imshow(title_dilation_window, dilatation_dst)
## [dilation]
parser = argparse.ArgumentParser(description='Code for Eroding and Dilating tutorial.')
parser.add_argument('--input', help='Path to input image.', default='LinuxLogo.jpg')
args = parser.parse_args()
src = cv.imread(cv.samples.findFile(args.input)) if __name__ == "__main__":
if src is None: parser = argparse.ArgumentParser(description='Code for Eroding and Dilating tutorial.')
print('Could not open or find the image: ', args.input) parser.add_argument('--input', help='Path to input image.', default='LinuxLogo.jpg')
exit(0) args = parser.parse_args()
cv.namedWindow(title_erosion_window) main(args.input)
cv.createTrackbar(title_trackbar_element_type, title_erosion_window , 0, max_elem, erosion)
cv.createTrackbar(title_trackbar_kernel_size, title_erosion_window , 0, max_kernel_size, erosion)
cv.namedWindow(title_dilatation_window)
cv.createTrackbar(title_trackbar_element_type, title_dilatation_window , 0, max_elem, dilatation)
cv.createTrackbar(title_trackbar_kernel_size, title_dilatation_window , 0, max_kernel_size, dilatation)
erosion(0)
dilatation(0)
cv.waitKey()