From d068e2742018342a2401a90bfd97b40ab8bce3cf Mon Sep 17 00:00:00 2001 From: tribta Date: Mon, 21 Aug 2017 18:51:50 +0100 Subject: [PATCH] Tutorial Morph Lines Detection --- .../morph_lines_detection/moprh_lines_detection.md | 86 --------- .../morph_lines_detection/morph_lines_detection.md | 194 +++++++++++++++++++++ .../imgproc/table_of_content_imgproc.markdown | 4 +- .../{ => morph_lines_detection}/Morphology_3.cpp | 74 ++++---- .../morph_lines_detection/Morphology_3.java | 152 ++++++++++++++++ .../morph_lines_detection/morph_lines_detection.py | 136 +++++++++++++++ 6 files changed, 527 insertions(+), 119 deletions(-) delete mode 100644 doc/tutorials/imgproc/morph_lines_detection/moprh_lines_detection.md create mode 100644 doc/tutorials/imgproc/morph_lines_detection/morph_lines_detection.md rename samples/cpp/tutorial_code/ImgProc/{ => morph_lines_detection}/Morphology_3.cpp (68%) create mode 100644 samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java create mode 100644 samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py diff --git a/doc/tutorials/imgproc/morph_lines_detection/moprh_lines_detection.md b/doc/tutorials/imgproc/morph_lines_detection/moprh_lines_detection.md deleted file mode 100644 index 23b748d..0000000 --- a/doc/tutorials/imgproc/morph_lines_detection/moprh_lines_detection.md +++ /dev/null @@ -1,86 +0,0 @@ -Extract horizontal and vertical lines by using morphological operations {#tutorial_moprh_lines_detection} -============= - -Goal ----- - -In this tutorial you will learn how to: - -- Apply two very common morphology operators (i.e. Dilation and Erosion), with the creation of custom kernels, in order to extract straight lines on the horizontal and vertical axes. For this purpose, you will use the following OpenCV functions: - - @ref cv::erode - - @ref cv::dilate - - @ref cv::getStructuringElement - - in an example where your goal will be to extract the music notes from a music sheet. - -Theory ------- - -### Morphology Operations -Morphology is a set of image processing operations that process images based on predefined *structuring elements* known also as kernels. The value of each pixel in the output image is based on a comparison of the corresponding pixel in the input image with its neighbors. By choosing the size and shape of the kernel, you can construct a morphological operation that is sensitive to specific shapes regarding the input image. - -Two of the most basic morphological operations are dilation and erosion. Dilation adds pixels to the boundaries of the object in an image, while erosion does exactly the opposite. The amount of pixels added or removed, respectively depends on the size and shape of the structuring element used to process the image. In general the rules followed from these two operations have as follows: - -- __Dilation__: The value of the output pixel is the maximum value of all the pixels that fall within the structuring element's size and shape. For example in a binary image, if any of the pixels of the input image falling within the range of the kernel is set to the value 1, the corresponding pixel of the output image will be set to 1 as well. The latter applies to any type of image (e.g. grayscale, bgr, etc). - - ![Dilation on a Binary Image](images/morph21.gif) - - ![Dilation on a Grayscale Image](images/morph6.gif) - -- __Erosion__: The vise versa applies for the erosion operation. The value of the output pixel is the minimum value of all the pixels that fall within the structuring element's size and shape. Look the at the example figures below: - - ![Erosion on a Binary Image](images/morph211.png) - - ![Erosion on a Grayscale Image](images/morph61.png) - -### Structuring Elements - -As it can be seen above and in general in any morphological operation the structuring element used to probe the input image, is the most important part. - -A structuring element is a matrix consisting of only 0's and 1's that can have any arbitrary shape and size. Typically are much smaller than the image being processed, while the pixels with values of 1 define the neighborhood. The center pixel of the structuring element, called the origin, identifies the pixel of interest -- the pixel being processed. - -For example, the following illustrates a diamond-shaped structuring element of 7x7 size. - -![A Diamond-Shaped Structuring Element and its Origin](images/morph12.gif) - -A structuring element can have many common shapes, such as lines, diamonds, disks, periodic lines, and circles and sizes. You typically choose a structuring element the same size and shape as the objects you want to process/extract in the input image. For example, to find lines in an image, create a linear structuring element as you will see later. - -Code ----- - -This tutorial code's is shown lines below. You can also download it from [here](https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp). -@include samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp - -Explanation / Result --------------------- - --# Load the source image and check if it is loaded without any problem, then show it: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp load_image - ![](images/src.png) - --# Then transform image to grayscale if it not already: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp gray - ![](images/gray.png) - --# Afterwards transform grayscale image to binary. Notice the ~ symbol which indicates that we use the inverse (i.e. bitwise_not) version of it: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp bin - ![](images/binary.png) - --# Now we are ready to apply morphological operations in order to extract the horizontal and vertical lines and as a consequence to separate the the music notes from the music sheet, but first let's initialize the output images that we will use for that reason: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp init - --# As we specified in the theory in order to extract the object that we desire, we need to create the corresponding structure element. Since here we want to extract the horizontal lines, a corresponding structure element for that purpose will have the following shape: - ![](images/linear_horiz.png) - and in the source code this is represented by the following code snippet: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp horiz - ![](images/horiz.png) - --# The same applies for the vertical lines, with the corresponding structure element: - ![](images/linear_vert.png) - and again this is represented as follows: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp vert - ![](images/vert.png) - --# As you can see we are almost there. However, at that point you will notice that the edges of the notes are a bit rough. For that reason we need to refine the edges in order to obtain a smoother result: - @snippet samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp smooth - ![](images/smooth.png) diff --git a/doc/tutorials/imgproc/morph_lines_detection/morph_lines_detection.md b/doc/tutorials/imgproc/morph_lines_detection/morph_lines_detection.md new file mode 100644 index 0000000..4b0d3fa --- /dev/null +++ b/doc/tutorials/imgproc/morph_lines_detection/morph_lines_detection.md @@ -0,0 +1,194 @@ +Extract horizontal and vertical lines by using morphological operations {#tutorial_morph_lines_detection} +============= + +@prev_tutorial{tutorial_hitOrMiss} +@next_tutorial{tutorial_pyramids} + +Goal +---- + +In this tutorial you will learn how to: + +- Apply two very common morphology operators (i.e. Dilation and Erosion), with the creation of custom kernels, in order to extract straight lines on the horizontal and vertical axes. For this purpose, you will use the following OpenCV functions: + - **erode()** + - **dilate()** + - **getStructuringElement()** + + in an example where your goal will be to extract the music notes from a music sheet. + +Theory +------ + +### Morphology Operations +Morphology is a set of image processing operations that process images based on predefined *structuring elements* known also as kernels. The value of each pixel in the output image is based on a comparison of the corresponding pixel in the input image with its neighbors. By choosing the size and shape of the kernel, you can construct a morphological operation that is sensitive to specific shapes regarding the input image. + +Two of the most basic morphological operations are dilation and erosion. Dilation adds pixels to the boundaries of the object in an image, while erosion does exactly the opposite. The amount of pixels added or removed, respectively depends on the size and shape of the structuring element used to process the image. In general the rules followed from these two operations have as follows: + +- __Dilation__: The value of the output pixel is the maximum value of all the pixels that fall within the structuring element's size and shape. For example in a binary image, if any of the pixels of the input image falling within the range of the kernel is set to the value 1, the corresponding pixel of the output image will be set to 1 as well. The latter applies to any type of image (e.g. grayscale, bgr, etc). + + ![Dilation on a Binary Image](images/morph21.gif) + + ![Dilation on a Grayscale Image](images/morph6.gif) + +- __Erosion__: The vise versa applies for the erosion operation. The value of the output pixel is the minimum value of all the pixels that fall within the structuring element's size and shape. Look the at the example figures below: + + ![Erosion on a Binary Image](images/morph211.png) + + ![Erosion on a Grayscale Image](images/morph61.png) + +### Structuring Elements + +As it can be seen above and in general in any morphological operation the structuring element used to probe the input image, is the most important part. + +A structuring element is a matrix consisting of only 0's and 1's that can have any arbitrary shape and size. Typically are much smaller than the image being processed, while the pixels with values of 1 define the neighborhood. The center pixel of the structuring element, called the origin, identifies the pixel of interest -- the pixel being processed. + +For example, the following illustrates a diamond-shaped structuring element of 7x7 size. + +![A Diamond-Shaped Structuring Element and its Origin](images/morph12.gif) + +A structuring element can have many common shapes, such as lines, diamonds, disks, periodic lines, and circles and sizes. You typically choose a structuring element the same size and shape as the objects you want to process/extract in the input image. For example, to find lines in an image, create a linear structuring element as you will see later. + +Code +---- + +This tutorial code's is shown lines below. + +@add_toggle_cpp +You can also download it from [here](https://raw.githubusercontent.com/opencv/opencv/master/samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp). +@include samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp +@end_toggle + +@add_toggle_java +You can also download it from [here](https://raw.githubusercontent.com/opencv/opencv/master/samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java). +@include samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java +@end_toggle + +@add_toggle_python +You can also download it from [here](https://raw.githubusercontent.com/opencv/opencv/master/samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py). +@include samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py +@end_toggle + +Explanation / Result +-------------------- + +Get image from [here](https://raw.githubusercontent.com/opencv/opencv/master/doc/tutorials/imgproc/morph_lines_detection/images/src.png) . + +#### Load Image + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp load_image +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java load_image +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py load_image +@end_toggle + +![](images/src.png) + +#### Grayscale + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp gray +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java gray +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py gray +@end_toggle + +![](images/gray.png) + +#### Grayscale to Binary image + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp bin +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java bin +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py bin +@end_toggle + +![](images/binary.png) + +#### Output images + +Now we are ready to apply morphological operations in order to extract the horizontal and vertical lines and as a consequence to separate the the music notes from the music sheet, but first let's initialize the output images that we will use for that reason: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp init +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java init +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py init +@end_toggle + +#### Structure elements + +As we specified in the theory in order to extract the object that we desire, we need to create the corresponding structure element. Since we want to extract the horizontal lines, a corresponding structure element for that purpose will have the following shape: +![](images/linear_horiz.png) +and in the source code this is represented by the following code snippet: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp horiz +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java horiz +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py horiz +@end_toggle + +![](images/horiz.png) + +The same applies for the vertical lines, with the corresponding structure element: +![](images/linear_vert.png) +and again this is represented as follows: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp vert +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java vert +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py vert +@end_toggle + +![](images/vert.png) + +#### Refine edges / Result + +As you can see we are almost there. However, at that point you will notice that the edges of the notes are a bit rough. For that reason we need to refine the edges in order to obtain a smoother result: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp smooth +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java smooth +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py smooth +@end_toggle + +![](images/smooth.png) diff --git a/doc/tutorials/imgproc/table_of_content_imgproc.markdown b/doc/tutorials/imgproc/table_of_content_imgproc.markdown index 59f7725..b83d0a6 100644 --- a/doc/tutorials/imgproc/table_of_content_imgproc.markdown +++ b/doc/tutorials/imgproc/table_of_content_imgproc.markdown @@ -39,7 +39,9 @@ In this section you will learn about the image processing (manipulation) functio Learn how to find patterns in binary images using the Hit-or-Miss operation -- @subpage tutorial_moprh_lines_detection +- @subpage tutorial_morph_lines_detection + + *Languages:* C++, Java, Python *Compatibility:* \> OpenCV 2.0 diff --git a/samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp b/samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp similarity index 68% rename from samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp rename to samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp index fa70e69..1b0b4b7 100644 --- a/samples/cpp/tutorial_code/ImgProc/Morphology_3.cpp +++ b/samples/cpp/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.cpp @@ -4,28 +4,32 @@ * @author OpenCV team */ -#include #include +void show_wait_destroy(const char* winname, cv::Mat img); + using namespace std; using namespace cv; int main(int, char** argv) { -//! [load_image] + //! [load_image] // Load the image Mat src = imread(argv[1]); // Check if image is loaded fine - if(!src.data) - cerr << "Problem loading image!!!" << endl; + if(src.empty()){ + printf(" Error opening image\n"); + printf(" Program Arguments: [image_path]\n"); + return -1; + } // Show source image imshow("src", src); -//! [load_image] + //! [load_image] -//! [gray] - // Transform source image to gray if it is not + //! [gray] + // Transform source image to gray if it is not already Mat gray; if (src.channels() == 3) @@ -38,58 +42,58 @@ int main(int, char** argv) } // Show gray image - imshow("gray", gray); -//! [gray] + show_wait_destroy("gray", gray); + //! [gray] -//! [bin] + //! [bin] // Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol Mat bw; adaptiveThreshold(~gray, bw, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2); // Show binary image - imshow("binary", bw); -//! [bin] + show_wait_destroy("binary", bw); + //! [bin] -//! [init] + //! [init] // Create the images that will use to extract the horizontal and vertical lines Mat horizontal = bw.clone(); Mat vertical = bw.clone(); -//! [init] + //! [init] -//! [horiz] + //! [horiz] // Specify size on horizontal axis - int horizontalsize = horizontal.cols / 30; + int horizontal_size = horizontal.cols / 30; // Create structure element for extracting horizontal lines through morphology operations - Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontalsize,1)); + Mat horizontalStructure = getStructuringElement(MORPH_RECT, Size(horizontal_size, 1)); // Apply morphology operations erode(horizontal, horizontal, horizontalStructure, Point(-1, -1)); dilate(horizontal, horizontal, horizontalStructure, Point(-1, -1)); // Show extracted horizontal lines - imshow("horizontal", horizontal); -//! [horiz] + show_wait_destroy("horizontal", horizontal); + //! [horiz] -//! [vert] + //! [vert] // Specify size on vertical axis - int verticalsize = vertical.rows / 30; + int vertical_size = vertical.rows / 30; // Create structure element for extracting vertical lines through morphology operations - Mat verticalStructure = getStructuringElement(MORPH_RECT, Size( 1,verticalsize)); + Mat verticalStructure = getStructuringElement(MORPH_RECT, Size(1, vertical_size)); // Apply morphology operations erode(vertical, vertical, verticalStructure, Point(-1, -1)); dilate(vertical, vertical, verticalStructure, Point(-1, -1)); // Show extracted vertical lines - imshow("vertical", vertical); -//! [vert] + show_wait_destroy("vertical", vertical); + //! [vert] -//! [smooth] + //! [smooth] // Inverse vertical image bitwise_not(vertical, vertical); - imshow("vertical_bit", vertical); + show_wait_destroy("vertical_bit", vertical); // Extract edges and smooth image according to the logic // 1. extract edges @@ -101,12 +105,12 @@ int main(int, char** argv) // Step 1 Mat edges; adaptiveThreshold(vertical, edges, 255, CV_ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 3, -2); - imshow("edges", edges); + show_wait_destroy("edges", edges); // Step 2 Mat kernel = Mat::ones(2, 2, CV_8UC1); dilate(edges, edges, kernel); - imshow("dilate", edges); + show_wait_destroy("dilate", edges); // Step 3 Mat smooth; @@ -119,9 +123,15 @@ int main(int, char** argv) smooth.copyTo(vertical, edges); // Show final result - imshow("smooth", vertical); -//! [smooth] + show_wait_destroy("smooth - final", vertical); + //! [smooth] - waitKey(0); return 0; -} \ No newline at end of file +} + +void show_wait_destroy(const char* winname, cv::Mat img) { + imshow(winname, img); + moveWindow(winname, 500, 0); + waitKey(0); + destroyWindow(winname); +} diff --git a/samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java b/samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java new file mode 100644 index 0000000..23f7299 --- /dev/null +++ b/samples/java/tutorial_code/ImgProc/morph_lines_detection/Morphology_3.java @@ -0,0 +1,152 @@ +/** + * @file Morphology_3.java + * @brief Use morphology transformations for extracting horizontal and vertical lines sample code + */ + +import org.opencv.core.*; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +class Morphology_3Run { + + public void run(String[] args) { + + //! [load_image] + // Check number of arguments + if (args.length == 0){ + System.out.println("Not enough parameters!"); + System.out.println("Program Arguments: [image_path]"); + System.exit(-1); + } + + // Load the image + Mat src = Imgcodecs.imread(args[0]); + + // Check if image is loaded fine + if( src.empty() ) { + System.out.println("Error opening image: " + args[0]); + System.exit(-1); + } + + // Show source image + HighGui.imshow("src", src); + //! [load_image] + + //! [gray] + // Transform source image to gray if it is not already + Mat gray = new Mat(); + + if (src.channels() == 3) + { + Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY); + } + else + { + gray = src; + } + + // Show gray image + showWaitDestroy("gray" , gray); + //! [gray] + + //! [bin] + // Apply adaptiveThreshold at the bitwise_not of gray + Mat bw = new Mat(); + Core.bitwise_not(gray, gray); + Imgproc.adaptiveThreshold(gray, bw, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 15, -2); + + // Show binary image + showWaitDestroy("binary" , bw); + //! [bin] + + //! [init] + // Create the images that will use to extract the horizontal and vertical lines + Mat horizontal = bw.clone(); + Mat vertical = bw.clone(); + //! [init] + + //! [horiz] + // Specify size on horizontal axis + int horizontal_size = horizontal.cols() / 30; + + // Create structure element for extracting horizontal lines through morphology operations + Mat horizontalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(horizontal_size,1)); + + // Apply morphology operations + Imgproc.erode(horizontal, horizontal, horizontalStructure); + Imgproc.dilate(horizontal, horizontal, horizontalStructure); + + // Show extracted horizontal lines + showWaitDestroy("horizontal" , horizontal); + //! [horiz] + + //! [vert] + // Specify size on vertical axis + int vertical_size = vertical.rows() / 30; + + // Create structure element for extracting vertical lines through morphology operations + Mat verticalStructure = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size( 1,vertical_size)); + + // Apply morphology operations + Imgproc.erode(vertical, vertical, verticalStructure); + Imgproc.dilate(vertical, vertical, verticalStructure); + + // Show extracted vertical lines + showWaitDestroy("vertical", vertical); + //! [vert] + + //! [smooth] + // Inverse vertical image + Core.bitwise_not(vertical, vertical); + showWaitDestroy("vertical_bit" , vertical); + + // Extract edges and smooth image according to the logic + // 1. extract edges + // 2. dilate(edges) + // 3. src.copyTo(smooth) + // 4. blur smooth img + // 5. smooth.copyTo(src, edges) + + // Step 1 + Mat edges = new Mat(); + Imgproc.adaptiveThreshold(vertical, edges, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 3, -2); + showWaitDestroy("edges", edges); + + // Step 2 + Mat kernel = Mat.ones(2, 2, CvType.CV_8UC1); + Imgproc.dilate(edges, edges, kernel); + showWaitDestroy("dilate", edges); + + // Step 3 + Mat smooth = new Mat(); + vertical.copyTo(smooth); + + // Step 4 + Imgproc.blur(smooth, smooth, new Size(2, 2)); + + // Step 5 + smooth.copyTo(vertical, edges); + + // Show final result + showWaitDestroy("smooth - final", vertical); + //! [smooth] + + System.exit(0); + } + + private void showWaitDestroy(String winname, Mat img) { + HighGui.imshow(winname, img); + HighGui.moveWindow(winname, 500, 0); + HighGui.waitKey(0); + HighGui.destroyWindow(winname); + } +} + +public class Morphology_3 { + public static void main(String[] args) { + // Load the native library. + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + new Morphology_3Run().run(args); + } +} diff --git a/samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py b/samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py new file mode 100644 index 0000000..24965e5 --- /dev/null +++ b/samples/python/tutorial_code/imgProc/morph_lines_detection/morph_lines_detection.py @@ -0,0 +1,136 @@ +""" +@file morph_lines_detection.py +@brief Use morphology transformations for extracting horizontal and vertical lines sample code +""" +import numpy as np +import sys +import cv2 + + +def show_wait_destroy(winname, img): + cv2.imshow(winname, img) + cv2.moveWindow(winname, 500, 0) + cv2.waitKey(0) + cv2.destroyWindow(winname) + + +def main(argv): + # [load_image] + # Check number of arguments + if len(argv) < 1: + print ('Not enough parameters') + print ('Usage:\nmorph_lines_detection.py < path_to_image >') + return -1 + + # Load the image + src = cv2.imread(argv[0], cv2.IMREAD_COLOR) + + # Check if image is loaded fine + if src is None: + print ('Error opening image: ' + argv[0]) + return -1 + + # Show source image + cv2.imshow("src", src) + # [load_image] + + # [gray] + # Transform source image to gray if it is not already + if len(src.shape) != 2: + gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY) + else: + gray = src + + # Show gray image + show_wait_destroy("gray", gray) + # [gray] + + # [bin] + # Apply adaptiveThreshold at the bitwise_not of gray, notice the ~ symbol + gray = cv2.bitwise_not(gray) + bw = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, \ + cv2.THRESH_BINARY, 15, -2) + # Show binary image + show_wait_destroy("binary", bw) + # [bin] + + # [init] + # Create the images that will use to extract the horizontal and vertical lines + horizontal = np.copy(bw) + vertical = np.copy(bw) + # [init] + + # [horiz] + # Specify size on horizontal axis + cols = horizontal.shape[1] + horizontal_size = cols / 30 + + # Create structure element for extracting horizontal lines through morphology operations + horizontalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1)) + + # Apply morphology operations + horizontal = cv2.erode(horizontal, horizontalStructure) + horizontal = cv2.dilate(horizontal, horizontalStructure) + + # Show extracted horizontal lines + show_wait_destroy("horizontal", horizontal) + # [horiz] + + # [vert] + # Specify size on vertical axis + rows = vertical.shape[0] + verticalsize = rows / 30 + + # Create structure element for extracting vertical lines through morphology operations + verticalStructure = cv2.getStructuringElement(cv2.MORPH_RECT, (1, verticalsize)) + + # Apply morphology operations + vertical = cv2.erode(vertical, verticalStructure) + vertical = cv2.dilate(vertical, verticalStructure) + + # Show extracted vertical lines + show_wait_destroy("vertical", vertical) + # [vert] + + # [smooth] + # Inverse vertical image + vertical = cv2.bitwise_not(vertical) + show_wait_destroy("vertical_bit", vertical) + + ''' + Extract edges and smooth image according to the logic + 1. extract edges + 2. dilate(edges) + 3. src.copyTo(smooth) + 4. blur smooth img + 5. smooth.copyTo(src, edges) + ''' + + # Step 1 + edges = cv2.adaptiveThreshold(vertical, 255, cv2.ADAPTIVE_THRESH_MEAN_C, \ + cv2.THRESH_BINARY, 3, -2) + show_wait_destroy("edges", edges) + + # Step 2 + kernel = np.ones((2, 2), np.uint8) + edges = cv2.dilate(edges, kernel) + show_wait_destroy("dilate", edges) + + # Step 3 + smooth = np.copy(vertical) + + # Step 4 + smooth = cv2.blur(smooth, (2, 2)) + + # Step 5 + (rows, cols) = np.where(edges != 0) + vertical[rows, cols] = smooth[rows, cols] + + # Show final result + show_wait_destroy("smooth - final", vertical) + # [smooth] + + return 0 + +if __name__ == "__main__": + main(sys.argv[1:]) -- 2.7.4