From 7469981d1a5d31b49e8200f703e1afc50c63f336 Mon Sep 17 00:00:00 2001 From: catree Date: Wed, 27 Jun 2018 18:48:32 +0200 Subject: Add Java and Python code for Image Segmentation with Distance Transform and Watershed Algorithm tutorial. Use more Pythonic code. --- .../distance_transform.markdown | 162 +++++++++++++--- .../imgproc/table_of_content_imgproc.markdown | 2 + .../tutorial_code/ImgTrans/imageSegmentation.cpp | 113 +++++------ .../ImageSegmentationDemo.java | 215 +++++++++++++++++++++ .../distance_transformation/imageSegmentation.py | 138 +++++++++++++ .../SURF_FLANN_matching_Demo.py | 7 +- .../SURF_FLANN_matching_homography_Demo.py | 7 +- 7 files changed, 555 insertions(+), 89 deletions(-) create mode 100644 samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java create mode 100644 samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py diff --git a/doc/tutorials/imgproc/imgtrans/distance_transformation/distance_transform.markdown b/doc/tutorials/imgproc/imgtrans/distance_transformation/distance_transform.markdown index 12ef87fc7d..ca1ec47258 100644 --- a/doc/tutorials/imgproc/imgtrans/distance_transformation/distance_transform.markdown +++ b/doc/tutorials/imgproc/imgtrans/distance_transformation/distance_transform.markdown @@ -16,42 +16,152 @@ Theory Code ---- +@add_toggle_cpp This tutorial code's is shown lines below. You can also download it from - [here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp). +[here](https://github.com/opencv/opencv/tree/3.4/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp). @include samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp +@end_toggle + +@add_toggle_java +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/3.4/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java) +@include samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java +@end_toggle + +@add_toggle_python +This tutorial code's is shown lines below. You can also download it from +[here](https://github.com/opencv/opencv/tree/3.4/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py) +@include samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py +@end_toggle Explanation / Result -------------------- --# Load the source image and check if it is loaded without any problem, then show it: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp load_image - ![](images/source.jpeg) +- Load the source image and check if it is loaded without any problem, then show it: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp load_image +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java load_image +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py load_image +@end_toggle + +![](images/source.jpeg) + +- Then if we have an image with a white background, it is good to transform it to black. This will help us to discriminate the foreground objects easier when we will apply the Distance Transform: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp black_bg +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java black_bg +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py black_bg +@end_toggle + +![](images/black_bg.jpeg) + +- Afterwards we will sharpen our image in order to acute the edges of the foreground objects. We will apply a laplacian filter with a quite strong filter (an approximation of second derivative): + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp sharp +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java sharp +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py sharp +@end_toggle + +![](images/laplace.jpeg) +![](images/sharp.jpeg) + +- Now we transform our new sharpened source image to a grayscale and a binary one, respectively: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp bin +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java bin +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py bin +@end_toggle + +![](images/bin.jpeg) + +- We are ready now to apply the Distance Transform on the binary image. Moreover, we normalize the output image in order to be able visualize and threshold the result: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java dist +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py dist +@end_toggle + +![](images/dist_transf.jpeg) + +- We threshold the *dist* image and then perform some morphology operation (i.e. dilation) in order to extract the peaks from the above image: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp peaks +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java peaks +@end_toggle + +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py peaks +@end_toggle + +![](images/peaks.jpeg) + +- From each blob then we create a seed/marker for the watershed algorithm with the help of the @ref cv::findContours function: + +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp seeds +@end_toggle + +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java seeds +@end_toggle --# Then if we have an image with a white background, it is good to transform it to black. This will help us to discriminate the foreground objects easier when we will apply the Distance Transform: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp black_bg - ![](images/black_bg.jpeg) +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py seeds +@end_toggle --# Afterwards we will sharpen our image in order to acute the edges of the foreground objects. We will apply a laplacian filter with a quite strong filter (an approximation of second derivative): - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp sharp - ![](images/laplace.jpeg) - ![](images/sharp.jpeg) +![](images/markers.jpeg) --# Now we transform our new sharpened source image to a grayscale and a binary one, respectively: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp bin - ![](images/bin.jpeg) +- Finally, we can apply the watershed algorithm, and visualize the result: --# We are ready now to apply the Distance Transform on the binary image. Moreover, we normalize the output image in order to be able visualize and threshold the result: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp dist - ![](images/dist_transf.jpeg) +@add_toggle_cpp +@snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp watershed +@end_toggle --# We threshold the *dist* image and then perform some morphology operation (i.e. dilation) in order to extract the peaks from the above image: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp peaks - ![](images/peaks.jpeg) +@add_toggle_java +@snippet samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java watershed +@end_toggle --# From each blob then we create a seed/marker for the watershed algorithm with the help of the @ref cv::findContours function: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp seeds - ![](images/markers.jpeg) +@add_toggle_python +@snippet samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py watershed +@end_toggle --# Finally, we can apply the watershed algorithm, and visualize the result: - @snippet samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp watershed - ![](images/final.jpeg) \ No newline at end of file +![](images/final.jpeg) diff --git a/doc/tutorials/imgproc/table_of_content_imgproc.markdown b/doc/tutorials/imgproc/table_of_content_imgproc.markdown index e3fac55924..59c985e1dd 100644 --- a/doc/tutorials/imgproc/table_of_content_imgproc.markdown +++ b/doc/tutorials/imgproc/table_of_content_imgproc.markdown @@ -285,6 +285,8 @@ In this section you will learn about the image processing (manipulation) functio - @subpage tutorial_distance_transform + *Languages:* C++, Java, Python + *Compatibility:* \> OpenCV 2.0 *Author:* Theodore Tsesmelis diff --git a/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp b/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp index 87a5436a6d..d038cbd874 100644 --- a/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp +++ b/samples/cpp/tutorial_code/ImgTrans/imageSegmentation.cpp @@ -1,5 +1,4 @@ /** - * @function Watershed_and_Distance_Transform.cpp * @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed and Distance Transformation * @author OpenCV Team */ @@ -12,43 +11,47 @@ using namespace std; using namespace cv; -int main() +int main(int argc, char *argv[]) { -//! [load_image] + //! [load_image] // Load the image - Mat src = imread("../data/cards.png"); - - // Check if everything was fine - if (!src.data) + CommandLineParser parser( argc, argv, "{@input | ../data/cards.png | input image}" ); + Mat src = imread( parser.get( "@input" ) ); + if( src.empty() ) + { + cout << "Could not open or find the image!\n" << endl; + cout << "Usage: " << argv[0] << " " << endl; return -1; + } // Show source image imshow("Source Image", src); -//! [load_image] + //! [load_image] -//! [black_bg] + //! [black_bg] // Change the background from white to black, since that will help later to extract // better results during the use of Distance Transform - for( int x = 0; x < src.rows; x++ ) { - for( int y = 0; y < src.cols; y++ ) { - if ( src.at(x, y) == Vec3b(255,255,255) ) { - src.at(x, y)[0] = 0; - src.at(x, y)[1] = 0; - src.at(x, y)[2] = 0; - } + for ( int i = 0; i < src.rows; i++ ) { + for ( int j = 0; j < src.cols; j++ ) { + if ( src.at(i, j) == Vec3b(255,255,255) ) + { + src.at(i, j)[0] = 0; + src.at(i, j)[1] = 0; + src.at(i, j)[2] = 0; + } } } // Show output image imshow("Black Background Image", src); -//! [black_bg] + //! [black_bg] -//! [sharp] - // Create a kernel that we will use for accuting/sharpening our image + //! [sharp] + // Create a kernel that we will use to sharpen our image Mat kernel = (Mat_(3,3) << - 1, 1, 1, - 1, -8, 1, - 1, 1, 1); // an approximation of second derivative, a quite strong kernel + 1, 1, 1, + 1, -8, 1, + 1, 1, 1); // an approximation of second derivative, a quite strong kernel // do the laplacian filtering as it is // well, we need to convert everything in something more deeper then CV_8U @@ -57,8 +60,8 @@ int main() // BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255 // so the possible negative number will be truncated Mat imgLaplacian; - Mat sharp = src; // copy source image to another temporary one - filter2D(sharp, imgLaplacian, CV_32F, kernel); + filter2D(src, imgLaplacian, CV_32F, kernel); + Mat sharp; src.convertTo(sharp, CV_32F); Mat imgResult = sharp - imgLaplacian; @@ -68,41 +71,39 @@ int main() // imshow( "Laplace Filtered Image", imgLaplacian ); imshow( "New Sharped Image", imgResult ); -//! [sharp] + //! [sharp] - src = imgResult; // copy back - -//! [bin] + //! [bin] // Create binary image from source image Mat bw; - cvtColor(src, bw, COLOR_BGR2GRAY); + cvtColor(imgResult, bw, COLOR_BGR2GRAY); threshold(bw, bw, 40, 255, THRESH_BINARY | THRESH_OTSU); imshow("Binary Image", bw); -//! [bin] + //! [bin] -//! [dist] + //! [dist] // Perform the distance transform algorithm Mat dist; distanceTransform(bw, dist, DIST_L2, 3); // Normalize the distance image for range = {0.0, 1.0} // so we can visualize and threshold it - normalize(dist, dist, 0, 1., NORM_MINMAX); + normalize(dist, dist, 0, 1.0, NORM_MINMAX); imshow("Distance Transform Image", dist); -//! [dist] + //! [dist] -//! [peaks] + //! [peaks] // Threshold to obtain the peaks // This will be the markers for the foreground objects - threshold(dist, dist, .4, 1., THRESH_BINARY); + threshold(dist, dist, 0.4, 1.0, THRESH_BINARY); // Dilate a bit the dist image - Mat kernel1 = Mat::ones(3, 3, CV_8UC1); + Mat kernel1 = Mat::ones(3, 3, CV_8U); dilate(dist, dist, kernel1); imshow("Peaks", dist); -//! [peaks] + //! [peaks] -//! [seeds] + //! [seeds] // Create the CV_8U version of the distance image // It is needed for findContours() Mat dist_8u; @@ -113,34 +114,36 @@ int main() findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); // Create the marker image for the watershed algorithm - Mat markers = Mat::zeros(dist.size(), CV_32SC1); + Mat markers = Mat::zeros(dist.size(), CV_32S); // Draw the foreground markers for (size_t i = 0; i < contours.size(); i++) - drawContours(markers, contours, static_cast(i), Scalar::all(static_cast(i)+1), -1); + { + drawContours(markers, contours, static_cast(i), Scalar(static_cast(i)+1), -1); + } // Draw the background marker - circle(markers, Point(5,5), 3, CV_RGB(255,255,255), -1); + circle(markers, Point(5,5), 3, Scalar(255), -1); imshow("Markers", markers*10000); -//! [seeds] + //! [seeds] -//! [watershed] + //! [watershed] // Perform the watershed algorithm - watershed(src, markers); + watershed(imgResult, markers); - Mat mark = Mat::zeros(markers.size(), CV_8UC1); - markers.convertTo(mark, CV_8UC1); + Mat mark; + markers.convertTo(mark, CV_8U); bitwise_not(mark, mark); -// imshow("Markers_v2", mark); // uncomment this if you want to see how the mark - // image looks like at that point + // imshow("Markers_v2", mark); // uncomment this if you want to see how the mark + // image looks like at that point // Generate random colors vector colors; for (size_t i = 0; i < contours.size(); i++) { - int b = theRNG().uniform(0, 255); - int g = theRNG().uniform(0, 255); - int r = theRNG().uniform(0, 255); + int b = theRNG().uniform(0, 256); + int g = theRNG().uniform(0, 256); + int r = theRNG().uniform(0, 256); colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r)); } @@ -155,16 +158,16 @@ int main() { int index = markers.at(i,j); if (index > 0 && index <= static_cast(contours.size())) + { dst.at(i,j) = colors[index-1]; - else - dst.at(i,j) = Vec3b(0,0,0); + } } } // Visualize the final image imshow("Final Result", dst); -//! [watershed] + //! [watershed] - waitKey(0); + waitKey(); return 0; } diff --git a/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java b/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java new file mode 100644 index 0000000000..1a26092f64 --- /dev/null +++ b/samples/java/tutorial_code/ImgTrans/distance_transformation/ImageSegmentationDemo.java @@ -0,0 +1,215 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import org.opencv.core.Core; +import org.opencv.core.CvType; +import org.opencv.core.Mat; +import org.opencv.core.MatOfPoint; +import org.opencv.core.Point; +import org.opencv.core.Scalar; +import org.opencv.highgui.HighGui; +import org.opencv.imgcodecs.Imgcodecs; +import org.opencv.imgproc.Imgproc; + +/** + * + * @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed + * and Distance Transformation + * + */ +class ImageSegmentation { + public void run(String[] args) { + //! [load_image] + // Load the image + String filename = args.length > 0 ? args[0] : "../data/cards.png"; + Mat srcOriginal = Imgcodecs.imread(filename); + if (srcOriginal.empty()) { + System.err.println("Cannot read image: " + filename); + System.exit(0); + } + + // Show source image + HighGui.imshow("Source Image", srcOriginal); + //! [load_image] + + //! [black_bg] + // Change the background from white to black, since that will help later to + // extract + // better results during the use of Distance Transform + Mat src = srcOriginal.clone(); + byte[] srcData = new byte[(int) (src.total() * src.channels())]; + src.get(0, 0, srcData); + for (int i = 0; i < src.rows(); i++) { + for (int j = 0; j < src.cols(); j++) { + if (srcData[(i * src.cols() + j) * 3] == (byte) 255 && srcData[(i * src.cols() + j) * 3 + 1] == (byte) 255 + && srcData[(i * src.cols() + j) * 3 + 2] == (byte) 255) { + srcData[(i * src.cols() + j) * 3] = 0; + srcData[(i * src.cols() + j) * 3 + 1] = 0; + srcData[(i * src.cols() + j) * 3 + 2] = 0; + } + } + } + src.put(0, 0, srcData); + + // Show output image + HighGui.imshow("Black Background Image", src); + //! [black_bg] + + //! [sharp] + // Create a kernel that we will use to sharpen our image + Mat kernel = new Mat(3, 3, CvType.CV_32F); + // an approximation of second derivative, a quite strong kernel + float[] kernelData = new float[(int) (kernel.total() * kernel.channels())]; + kernelData[0] = 1; kernelData[1] = 1; kernelData[2] = 1; + kernelData[3] = 1; kernelData[4] = -8; kernelData[5] = 1; + kernelData[6] = 1; kernelData[7] = 1; kernelData[8] = 1; + kernel.put(0, 0, kernelData); + + // do the laplacian filtering as it is + // well, we need to convert everything in something more deeper then CV_8U + // because the kernel has some negative values, + // and we can expect in general to have a Laplacian image with negative values + // BUT a 8bits unsigned int (the one we are working with) can contain values + // from 0 to 255 + // so the possible negative number will be truncated + Mat imgLaplacian = new Mat(); + Imgproc.filter2D(src, imgLaplacian, CvType.CV_32F, kernel); + Mat sharp = new Mat(); + src.convertTo(sharp, CvType.CV_32F); + Mat imgResult = new Mat(); + Core.subtract(sharp, imgLaplacian, imgResult); + + // convert back to 8bits gray scale + imgResult.convertTo(imgResult, CvType.CV_8UC3); + imgLaplacian.convertTo(imgLaplacian, CvType.CV_8UC3); + + // imshow( "Laplace Filtered Image", imgLaplacian ); + HighGui.imshow("New Sharped Image", imgResult); + //! [sharp] + + //! [bin] + // Create binary image from source image + Mat bw = new Mat(); + Imgproc.cvtColor(imgResult, bw, Imgproc.COLOR_BGR2GRAY); + Imgproc.threshold(bw, bw, 40, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU); + HighGui.imshow("Binary Image", bw); + //! [bin] + + //! [dist] + // Perform the distance transform algorithm + Mat dist = new Mat(); + Imgproc.distanceTransform(bw, dist, Imgproc.DIST_L2, 3); + + // Normalize the distance image for range = {0.0, 1.0} + // so we can visualize and threshold it + Core.normalize(dist, dist, 0, 1., Core.NORM_MINMAX); + Mat distDisplayScaled = dist.mul(dist, 255); + Mat distDisplay = new Mat(); + distDisplayScaled.convertTo(distDisplay, CvType.CV_8U); + HighGui.imshow("Distance Transform Image", distDisplay); + //! [dist] + + //! [peaks] + // Threshold to obtain the peaks + // This will be the markers for the foreground objects + Imgproc.threshold(dist, dist, .4, 1., Imgproc.THRESH_BINARY); + + // Dilate a bit the dist image + Mat kernel1 = Mat.ones(3, 3, CvType.CV_8U); + Imgproc.dilate(dist, dist, kernel1); + Mat distDisplay2 = new Mat(); + dist.convertTo(distDisplay2, CvType.CV_8U); + distDisplay2 = distDisplay2.mul(distDisplay2, 255); + HighGui.imshow("Peaks", distDisplay2); + //! [peaks] + + //! [seeds] + // Create the CV_8U version of the distance image + // It is needed for findContours() + Mat dist_8u = new Mat(); + dist.convertTo(dist_8u, CvType.CV_8U); + + // Find total markers + List contours = new ArrayList<>(); + Mat hierarchy = new Mat(); + Imgproc.findContours(dist_8u, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); + + // Create the marker image for the watershed algorithm + Mat markers = Mat.zeros(dist.size(), CvType.CV_32S); + + // Draw the foreground markers + for (int i = 0; i < contours.size(); i++) { + Imgproc.drawContours(markers, contours, i, new Scalar(i + 1), -1); + } + + // Draw the background marker + Imgproc.circle(markers, new Point(5, 5), 3, new Scalar(255, 255, 255), -1); + Mat markersScaled = markers.mul(markers, 10000); + Mat markersDisplay = new Mat(); + markersScaled.convertTo(markersDisplay, CvType.CV_8U); + HighGui.imshow("Markers", markersDisplay); + //! [seeds] + + //! [watershed] + // Perform the watershed algorithm + Imgproc.watershed(imgResult, markers); + + Mat mark = Mat.zeros(markers.size(), CvType.CV_8U); + markers.convertTo(mark, CvType.CV_8UC1); + Core.bitwise_not(mark, mark); + // imshow("Markers_v2", mark); // uncomment this if you want to see how the mark + // image looks like at that point + + // Generate random colors + Random rng = new Random(12345); + List colors = new ArrayList<>(contours.size()); + for (int i = 0; i < contours.size(); i++) { + int b = rng.nextInt(256); + int g = rng.nextInt(256); + int r = rng.nextInt(256); + + colors.add(new Scalar(b, g, r)); + } + + // Create the result image + Mat dst = Mat.zeros(markers.size(), CvType.CV_8UC3); + byte[] dstData = new byte[(int) (dst.total() * dst.channels())]; + dst.get(0, 0, dstData); + + // Fill labeled objects with random colors + int[] markersData = new int[(int) (markers.total() * markers.channels())]; + markers.get(0, 0, markersData); + for (int i = 0; i < markers.rows(); i++) { + for (int j = 0; j < markers.cols(); j++) { + int index = markersData[i * markers.cols() + j]; + if (index > 0 && index <= contours.size()) { + dstData[(i * dst.cols() + j) * 3 + 0] = (byte) colors.get(index - 1).val[0]; + dstData[(i * dst.cols() + j) * 3 + 1] = (byte) colors.get(index - 1).val[1]; + dstData[(i * dst.cols() + j) * 3 + 2] = (byte) colors.get(index - 1).val[2]; + } else { + dstData[(i * dst.cols() + j) * 3 + 0] = 0; + dstData[(i * dst.cols() + j) * 3 + 1] = 0; + dstData[(i * dst.cols() + j) * 3 + 2] = 0; + } + } + } + dst.put(0, 0, dstData); + + // Visualize the final image + HighGui.imshow("Final Result", dst); + //! [watershed] + + HighGui.waitKey(); + System.exit(0); + } +} + +public class ImageSegmentationDemo { + public static void main(String[] args) { + // Load the native OpenCV library + System.loadLibrary(Core.NATIVE_LIBRARY_NAME); + + new ImageSegmentation().run(args); + } +} diff --git a/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py b/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py new file mode 100644 index 0000000000..e679001bc1 --- /dev/null +++ b/samples/python/tutorial_code/ImgTrans/distance_transformation/imageSegmentation.py @@ -0,0 +1,138 @@ +from __future__ import print_function +import cv2 as cv +import numpy as np +import argparse +import random as rng + +rng.seed(12345) + +## [load_image] +# Load the image +parser = argparse.ArgumentParser(description='Code for Image Segmentation with Distance Transform and Watershed Algorithm.\ + Sample code showing how to segment overlapping objects using Laplacian filtering, \ + in addition to Watershed and Distance Transformation') +parser.add_argument('--input', help='Path to input image.', default='../data/cards.png') +args = parser.parse_args() + +src = cv.imread(args.input) +if src is None: + print('Could not open or find the image:', args.input) + exit(0) + +# Show source image +cv.imshow('Source Image', src) +## [load_image] + +## [black_bg] +# Change the background from white to black, since that will help later to extract +# better results during the use of Distance Transform +src[np.all(src == 255, axis=2)] = 0 + +# Show output image +cv.imshow('Black Background Image', src) +## [black_bg] + +## [sharp] +# Create a kernel that we will use to sharpen our image +# an approximation of second derivative, a quite strong kernel +kernel = np.array([[1, 1, 1], [1, -8, 1], [1, 1, 1]], dtype=np.float32) + +# do the laplacian filtering as it is +# well, we need to convert everything in something more deeper then CV_8U +# because the kernel has some negative values, +# and we can expect in general to have a Laplacian image with negative values +# BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255 +# so the possible negative number will be truncated +imgLaplacian = cv.filter2D(src, cv.CV_32F, kernel) +sharp = np.float32(src) +imgResult = sharp - imgLaplacian + +# convert back to 8bits gray scale +imgResult = np.clip(imgResult, 0, 255) +imgResult = imgResult.astype('uint8') +imgLaplacian = np.clip(imgLaplacian, 0, 255) +imgLaplacian = np.uint8(imgLaplacian) + +#cv.imshow('Laplace Filtered Image', imgLaplacian) +cv.imshow('New Sharped Image', imgResult) +## [sharp] + +## [bin] +# Create binary image from source image +bw = cv.cvtColor(imgResult, cv.COLOR_BGR2GRAY) +_, bw = cv.threshold(bw, 40, 255, cv.THRESH_BINARY | cv.THRESH_OTSU) +cv.imshow('Binary Image', bw) +## [bin] + +## [dist] +# Perform the distance transform algorithm +dist = cv.distanceTransform(bw, cv.DIST_L2, 3) + +# Normalize the distance image for range = {0.0, 1.0} +# so we can visualize and threshold it +cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX) +cv.imshow('Distance Transform Image', dist) +## [dist] + +## [peaks] +# Threshold to obtain the peaks +# This will be the markers for the foreground objects +_, dist = cv.threshold(dist, 0.4, 1.0, cv.THRESH_BINARY) + +# Dilate a bit the dist image +kernel1 = np.ones((3,3), dtype=np.uint8) +dist = cv.dilate(dist, kernel1) +cv.imshow('Peaks', dist) +## [peaks] + +## [seeds] +# Create the CV_8U version of the distance image +# It is needed for findContours() +dist_8u = dist.astype('uint8') + +# Find total markers +_, contours, _ = cv.findContours(dist_8u, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE) + +# Create the marker image for the watershed algorithm +markers = np.zeros(dist.shape, dtype=np.int32) + +# Draw the foreground markers +for i in range(len(contours)): + cv.drawContours(markers, contours, i, (i+1), -1) + +# Draw the background marker +cv.circle(markers, (5,5), 3, (255,255,255), -1) +cv.imshow('Markers', markers*10000) +## [seeds] + +## [watershed] +# Perform the watershed algorithm +cv.watershed(imgResult, markers) + +#mark = np.zeros(markers.shape, dtype=np.uint8) +mark = markers.astype('uint8') +mark = cv.bitwise_not(mark) +# uncomment this if you want to see how the mark +# image looks like at that point +#cv.imshow('Markers_v2', mark) + +# Generate random colors +colors = [] +for contour in contours: + colors.append((rng.randint(0,256), rng.randint(0,256), rng.randint(0,256))) + +# Create the result image +dst = np.zeros((markers.shape[0], markers.shape[1], 3), dtype=np.uint8) + +# Fill labeled objects with random colors +for i in range(markers.shape[0]): + for j in range(markers.shape[1]): + index = markers[i,j] + if index > 0 and index <= len(contours): + dst[i,j,:] = colors[index-1] + +# Visualize the final image +cv.imshow('Final Result', dst) +## [watershed] + +cv.waitKey() diff --git a/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py index d22f9a8a6f..1a65d324fd 100644 --- a/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py +++ b/samples/python/tutorial_code/features2D/feature_flann_matcher/SURF_FLANN_matching_Demo.py @@ -28,10 +28,9 @@ knn_matches = matcher.knnMatch(descriptors1, descriptors2, 2) #-- Filter matches using the Lowe's ratio test ratio_thresh = 0.7 good_matches = [] -for matches in knn_matches: - if len(matches) > 1: - if matches[0].distance / matches[1].distance <= ratio_thresh: - good_matches.append(matches[0]) +for m,n in knn_matches: + if m.distance / n.distance <= ratio_thresh: + good_matches.append(m) #-- Draw matches img_matches = np.empty((max(img1.shape[0], img2.shape[0]), img1.shape[1]+img2.shape[1], 3), dtype=np.uint8) diff --git a/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py index 8820addce2..5172b4f303 100644 --- a/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py +++ b/samples/python/tutorial_code/features2D/feature_homography/SURF_FLANN_matching_homography_Demo.py @@ -28,10 +28,9 @@ knn_matches = matcher.knnMatch(descriptors_obj, descriptors_scene, 2) #-- Filter matches using the Lowe's ratio test ratio_thresh = 0.75 good_matches = [] -for matches in knn_matches: - if len(matches) > 1: - if matches[0].distance / matches[1].distance <= ratio_thresh: - good_matches.append(matches[0]) +for m,n in knn_matches: + if m.distance / n.distance <= ratio_thresh: + good_matches.append(m) #-- Draw matches img_matches = np.empty((max(img_object.shape[0], img_scene.shape[0]), img_object.shape[1]+img_scene.shape[1], 3), dtype=np.uint8) -- cgit v1.2.3