mirror of https://github.com/opencv/opencv.git
Merge pull request #11849 from catree:add_tutorial_imgproc_java_python4
commit
e4b51fa8ad
7 changed files with 555 additions and 89 deletions
@ -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<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
|
||||||
|
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<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); |
||||||
|
} |
||||||
|
} |
@ -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() |
Loading…
Reference in new issue