1 /** 2 * @function Watershed_and_Distance_Transform.cpp 3 * @brief Sample code showing how to segment overlapping objects using Laplacian filtering, in addition to Watershed and Distance Transformation 4 * @author OpenCV Team 5 */ 6 7 #include <opencv2/opencv.hpp> 8 #include <iostream> 9 10 using namespace std; 11 using namespace cv; 12 13 int main(int, char** argv) 14 { 15 //! [load_image] 16 // Load the image 17 Mat src = imread(argv[1]); 18 19 // Check if everything was fine 20 if (!src.data) 21 return -1; 22 23 // Show source image 24 imshow("Source Image", src); 25 //! [load_image] 26 27 //! [black_bg] 28 // Change the background from white to black, since that will help later to extract 29 // better results during the use of Distance Transform 30 for( int x = 0; x < src.rows; x++ ) { 31 for( int y = 0; y < src.cols; y++ ) { 32 if ( src.at<Vec3b>(x, y) == Vec3b(255,255,255) ) { 33 src.at<Vec3b>(x, y)[0] = 0; 34 src.at<Vec3b>(x, y)[1] = 0; 35 src.at<Vec3b>(x, y)[2] = 0; 36 } 37 } 38 } 39 40 // Show output image 41 imshow("Black Background Image", src); 42 //! [black_bg] 43 44 //! [sharp] 45 // Create a kernel that we will use for accuting/sharpening our image 46 Mat kernel = (Mat_<float>(3,3) << 47 1, 1, 1, 48 1, -8, 1, 49 1, 1, 1); // an approximation of second derivative, a quite strong kernel 50 51 // do the laplacian filtering as it is 52 // well, we need to convert everything in something more deeper then CV_8U 53 // because the kernel has some negative values, 54 // and we can expect in general to have a Laplacian image with negative values 55 // BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255 56 // so the possible negative number will be truncated 57 Mat imgLaplacian; 58 Mat sharp = src; // copy source image to another temporary one 59 filter2D(sharp, imgLaplacian, CV_32F, kernel); 60 src.convertTo(sharp, CV_32F); 61 Mat imgResult = sharp - imgLaplacian; 62 63 // convert back to 8bits gray scale 64 imgResult.convertTo(imgResult, CV_8UC3); 65 imgLaplacian.convertTo(imgLaplacian, CV_8UC3); 66 67 // imshow( "Laplace Filtered Image", imgLaplacian ); 68 imshow( "New Sharped Image", imgResult ); 69 //! [sharp] 70 71 src = imgResult; // copy back 72 73 //! [bin] 74 // Create binary image from source image 75 Mat bw; 76 cvtColor(src, bw, CV_BGR2GRAY); 77 threshold(bw, bw, 40, 255, CV_THRESH_BINARY | CV_THRESH_OTSU); 78 imshow("Binary Image", bw); 79 //! [bin] 80 81 //! [dist] 82 // Perform the distance transform algorithm 83 Mat dist; 84 distanceTransform(bw, dist, CV_DIST_L2, 3); 85 86 // Normalize the distance image for range = {0.0, 1.0} 87 // so we can visualize and threshold it 88 normalize(dist, dist, 0, 1., NORM_MINMAX); 89 imshow("Distance Transform Image", dist); 90 //! [dist] 91 92 //! [peaks] 93 // Threshold to obtain the peaks 94 // This will be the markers for the foreground objects 95 threshold(dist, dist, .4, 1., CV_THRESH_BINARY); 96 97 // Dilate a bit the dist image 98 Mat kernel1 = Mat::ones(3, 3, CV_8UC1); 99 dilate(dist, dist, kernel1); 100 imshow("Peaks", dist); 101 //! [peaks] 102 103 //! [seeds] 104 // Create the CV_8U version of the distance image 105 // It is needed for findContours() 106 Mat dist_8u; 107 dist.convertTo(dist_8u, CV_8U); 108 109 // Find total markers 110 vector<vector<Point> > contours; 111 findContours(dist_8u, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); 112 113 // Create the marker image for the watershed algorithm 114 Mat markers = Mat::zeros(dist.size(), CV_32SC1); 115 116 // Draw the foreground markers 117 for (size_t i = 0; i < contours.size(); i++) 118 drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i)+1), -1); 119 120 // Draw the background marker 121 circle(markers, Point(5,5), 3, CV_RGB(255,255,255), -1); 122 imshow("Markers", markers*10000); 123 //! [seeds] 124 125 //! [watershed] 126 // Perform the watershed algorithm 127 watershed(src, markers); 128 129 Mat mark = Mat::zeros(markers.size(), CV_8UC1); 130 markers.convertTo(mark, CV_8UC1); 131 bitwise_not(mark, mark); 132 // imshow("Markers_v2", mark); // uncomment this if you want to see how the mark 133 // image looks like at that point 134 135 // Generate random colors 136 vector<Vec3b> colors; 137 for (size_t i = 0; i < contours.size(); i++) 138 { 139 int b = theRNG().uniform(0, 255); 140 int g = theRNG().uniform(0, 255); 141 int r = theRNG().uniform(0, 255); 142 143 colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r)); 144 } 145 146 // Create the result image 147 Mat dst = Mat::zeros(markers.size(), CV_8UC3); 148 149 // Fill labeled objects with random colors 150 for (int i = 0; i < markers.rows; i++) 151 { 152 for (int j = 0; j < markers.cols; j++) 153 { 154 int index = markers.at<int>(i,j); 155 if (index > 0 && index <= static_cast<int>(contours.size())) 156 dst.at<Vec3b>(i,j) = colors[index-1]; 157 else 158 dst.at<Vec3b>(i,j) = Vec3b(0,0,0); 159 } 160 } 161 162 // Visualize the final image 163 imshow("Final Result", dst); 164 //! [watershed] 165 166 waitKey(0); 167 return 0; 168 }