1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
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.0, 1.0, Core.NORM_MINMAX);
Mat distDisplayScaled = new Mat();
Core.multiply(dist, new Scalar(255), distDisplayScaled);
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, 0.4, 1.0, 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);
Core.multiply(distDisplay2, new Scalar(255), distDisplay2);
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<MatOfPoint> 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
Mat markersScaled = new Mat();
markers.convertTo(markersScaled, CvType.CV_32F);
Core.normalize(markersScaled, markersScaled, 0.0, 255.0, Core.NORM_MINMAX);
Imgproc.circle(markersScaled, new Point(5, 5), 3, new Scalar(255, 255, 255), -1);
Mat markersDisplay = new Mat();
markersScaled.convertTo(markersDisplay, CvType.CV_8U);
HighGui.imshow("Markers", markersDisplay);
Imgproc.circle(markers, new Point(5, 5), 3, new Scalar(255, 255, 255), -1);
//! [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<Scalar> 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);
}
}
|