用OpenCVSharp 4.5 跑一遍 OpenCV 官方教程
原OpenCV 官方教程链接:OpenCV: Image Segmentation with Distance Transform and Watershed Algorithm
核心函数:
using System;
using OpenCvSharp;
namespace ConsoleApp1
{
class tutorial29 : ITutorial
{
public void Run()
{
// Load the image
Mat src = Cv2.ImRead("I:\\csharp\\images\\cards.png");
if (src.Empty())
{
Console.WriteLine("Could not open or find the image!");
return;
}
// Show source image
Cv2.ImShow("Source Image", src);
// Change the background from white to black, since that will help later to extract
// better results during the use of Distance Transform
for (int i = 0; i < src.Rows; i++)
{
for (int j = 0; j < src.Cols; j++)
{
if (src.At<Vec3b>(i, j) == new Vec3b(255, 255, 255))
{
src.At<Vec3b>(i, j)[0] = 0;
src.At<Vec3b>(i, j)[1] = 0;
src.At<Vec3b>(i, j)[2] = 0;
}
}
}
// Show output image
Cv2.ImShow("Black Background Image", src);
// Create a kernel that we will use to sharpen our image
double[,] k = { {1.0, 1.0, 1.0 },
{ 1, -8, 1 },
{ 1, 1, 1} };
Mat kernel = Mat.FromArray(k);
// 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
// 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();
Cv2.Filter2D(src, imgLaplacian, MatType.CV_32F, kernel);
Mat sharp = new Mat();
src.ConvertTo(sharp, MatType.CV_32F);
Mat imgResult = sharp - imgLaplacian;
// convert back to 8bits gray scale
imgResult.ConvertTo(imgResult, MatType.CV_8UC3);
imgLaplacian.ConvertTo(imgLaplacian, MatType.CV_8UC3);
// imshow( "Laplace Filtered Image", imgLaplacian );
Cv2.ImShow("New Sharped Image", imgResult);
// Create binary image from source image
Mat bw = new Mat();
Cv2.CvtColor(imgResult, bw, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(bw, bw, 40, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
Cv2.ImShow("Binary Image", bw);
// Perform the distance transform algorithm
Mat dist = new Mat();
Cv2.DistanceTransform(bw, dist, DistanceTypes.L2, DistanceTransformMasks.Mask3);
// Normalize the distance image for range = {0.0, 1.0}
// so we can visualize and threshold it
Cv2.Normalize(dist, dist, 0, 1.0, NormTypes.MinMax);
Cv2.ImShow("Distance Transform Image", dist);
// Threshold to obtain the peaks
// This will be the markers for the foreground objects
Cv2.Threshold(dist, dist, 0.4, 1.0, ThresholdTypes.Binary);
// Dilate a bit the dist image
Mat kernel1 = Mat.Ones(new Size(3, 3), MatType.CV_8U);
Cv2.Dilate(dist, dist, kernel1);
Cv2.ImShow("Peaks", dist);
// Create the CV_8U version of the distance image
// It is needed for findContours()
Mat dist_8u = new Mat();
dist.ConvertTo(dist_8u, MatType.CV_8U);
// Find total markers
Point[][] contours;
HierarchyIndex[] hierarchyIndices;
Cv2.FindContours(dist_8u, out contours, out hierarchyIndices, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
// Create the marker image for the watershed algorithm
Mat markers = Mat.Zeros(dist.Size(), MatType.CV_32S);
// Draw the foreground markers
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(markers, contours, (int)i, new Scalar((int)i + 1), -1);
}
// Draw the background marker
Cv2.Circle(markers, new Point(5, 5), 3, new Scalar(255), -1);
Mat markers1 = markers * 10000;
markers1.ConvertTo(markers1, MatType.CV_16S);
Cv2.ImShow("Markers", markers1);
// Perform the watershed algorithm
Cv2.Watershed(imgResult, markers);
Mat mark = new Mat();
markers.ConvertTo(mark, MatType.CV_8U);
Cv2.BitwiseNot(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
Vec3b[] colors = new Vec3b[contours.Length];
RNG rng = new RNG((ulong)DateTime.Now.Ticks);
for (int i = 0; i < contours.Length; i++)
{
colors[i] = new Vec3b((byte)rng.Uniform(0, 256), (byte)rng.Uniform(0, 256), (byte)rng.Uniform(0, 256));
}
// Create the result image
Mat dst = Mat.Zeros(markers.Size(), MatType.CV_8UC3);
// Fill labeled objects with random colors
for (int i = 0; i < markers.Rows; i++)
{
for (int j = 0; j < markers.Cols; j++)
{
int index = markers.At<int>(i, j);
if (index > 0 && index <= contours.Length)
{
dst.At<Vec3b>(i, j) = colors[index - 1];
}
}
}
// Visualize the final image
Cv2.ImShow("Final Result", dst);
Cv2.WaitKey();
}
}
}