Skip to content
This repository has been archived by the owner on Nov 19, 2020. It is now read-only.

Commit

Permalink
Adding the Binary Watershed filter. Updates GH-654.
Browse files Browse the repository at this point in the history
  • Loading branch information
cesarsouza committed Jun 29, 2017
1 parent 0f16a45 commit febe21d
Show file tree
Hide file tree
Showing 8 changed files with 1,201 additions and 99 deletions.
1 change: 1 addition & 0 deletions Sources/Accord.Imaging/Accord.Imaging.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
<Compile Include="AForge.Imaging\Filters\Normalized RGB\ExtractNormalizedRGBChannel.cs" />
<Compile Include="AForge.Imaging\Filters\Other\ApplyMask.cs" />
<Compile Include="Blob Processing\IBlobsFilter.cs" />
<Compile Include="Filters\BinaryWatershed.cs" />
<Compile Include="Filters\DistanceTransform.cs" />
<Compile Include="Filters\Other\BlobsFiltering.cs" />
<Compile Include="AForge.Imaging\Filters\Other\CanvasCrop.cs" />
Expand Down
934 changes: 934 additions & 0 deletions Sources/Accord.Imaging/Filters/BinaryWatershed.cs

Large diffs are not rendered by default.

245 changes: 148 additions & 97 deletions Sources/Accord.Imaging/Filters/DistanceTransform.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,38 @@
// Accord Imaging Library
// The Accord.NET Framework
// http://accord-framework.net
//
// Copyright © Diego Catalano, 2017
// diego.catalano at live.com
//
// Copyright © César Souza, 2009-2017
// cesarsouza at gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//

namespace Accord.Imaging.Filters
{
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using Accord.Imaging;
// Accord Imaging Library
// The Accord.NET Framework
// http://accord-framework.net
//
// Copyright © Diego Catalano, 2017
// diego.catalano at live.com
//
// Copyright © César Souza, 2009-2017
// cesarsouza at gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//

namespace Accord.Imaging.Filters
{
using System;
using System.Collections.Generic;
using System.Drawing.Imaging;
using Accord.Imaging;
using Accord.Imaging.Filters;
using Accord.Math;
using Accord.Diagnostics;
using Accord.Math.Distances;

/// <summary>
/// Distance functions that can be used with <see cref="DistanceTransform"/>.
Expand All @@ -48,72 +49,118 @@ public enum DistanceTransformMethod
/// <summary>
/// Euclidean distance.
/// </summary>
///
Euclidean,

/// <summary>
/// Manhattan distance.
/// </summary>
///
Manhattan,

/// <summary>
/// Squared Euclidean distance.
/// </summary>
///
SquaredEuclidean
};

/// <summary>
/// Distance transform filter.
/// </summary>
///
/// <remarks>
/// Simple exp image filter. Applies the <see cref="System.Math.Exp"/>
/// function for each pixel in the image, clipping values as needed.
/// The resultant image can be converted back using the <see cref="Logarithm"/>
/// filter.
/// </remarks>
///
/// <example>
/// <code>
/// Bitmap input = ...
///
/// // Apply the transform
/// DistanceTransform dt = new DistanceTransform();
/// Bitmap output = dt.Apply(input);
///
/// // Show results on screen
/// ImageBox.Show("input", input);
/// ImageBox.Show("output", output);
/// ImageBox.Show("reconstruction", reconstruction);
/// </code>
/// </example>
///
public class DistanceTransform : BaseInPlaceFilter
};

/// <summary>
/// Distance transform filter.
/// </summary>
///
/// <remarks>
/// <para>
/// A distance transform, also known as distance map or distance field, is a derived
/// representation of a digital image.The choice of the term depends on the point of
/// view on the object in question: whether the initial image is transformed into another
/// representation, or it is simply endowed with an additional map or field.</para>
/// <para>
/// Distance fields can also be signed, in the case where it is important to distinguish whether
/// the point is inside or outside of the shape. The map labels each pixel of the image with
/// the distance to the nearest obstacle pixel. A most common type of obstacle pixel is a boundary
/// pixel in a binary image.See the image for an example of a chessboard distance transform
/// on a binary image.</para>
///
/// <para>
/// Usually the transform/map is qualified with the chosen metric.For example, one may
/// speak of <see cref="DistanceTransformMethod.Manhattan"/>distance transform, if the
/// underlying metric is <see cref="Manhattan">Manhattan distance</see>. Common metrics are:
/// The <see cref="DistanceTransformMethod.Euclidean">Euclidean distance</see>; the Taxicab
/// geometry, also known as City block distance or <see cref="DistanceTransformMethod.Manhattan">Manhattan
/// distance</see>; and the <see cref="DistanceTransformMethod.Chessboard">Chessboard distance</see>.
/// </para>
///
/// <para>
/// References:
/// <list type="bullet">
/// <item><description><a href="https://en.wikipedia.org/wiki/Distance_transform">
/// Wikipedia contributors. "Distance transform." Wikipedia, The Free Encyclopedia.
/// Available on: https://en.wikipedia.org/wiki/Distance_transform </a>
/// </description></item>
/// </list></para>
/// </remarks>
///
/// <example>
/// <code>
/// Bitmap input = ...
///
/// // Apply the transform
/// DistanceTransform dt = new DistanceTransform();
/// Bitmap output = dt.Apply(input);
///
/// // Show results on screen
/// ImageBox.Show("input", input);
/// ImageBox.Show("output", output);
/// </code>
/// </example>
///
public class DistanceTransform : BaseInPlaceFilter
{
private float max = 0;
private IntPoint ued;
private DistanceTransformMethod distance = DistanceTransformMethod.Euclidean;
private Dictionary<PixelFormat, PixelFormat> formatTranslations;

/// <summary>
/// Format translations dictionary.
/// </summary>
///
public override Dictionary<PixelFormat, PixelFormat> FormatTranslations
{
get { return formatTranslations; }
}

private Dictionary<PixelFormat, PixelFormat> formatTranslations;

float[] fPixels;

/// <summary>
/// Format translations dictionary.
/// </summary>
///
public override Dictionary<PixelFormat, PixelFormat> FormatTranslations
{
get { return formatTranslations; }
}

/// <summary>
/// Gets the resulting pixels of the last transfomed image as a float[] array.
/// </summary>
///
public float[] Pixels
{
get { return fPixels; }
}

/// <summary>
/// Initializes a new instance of the <see cref="DistanceTransform"/> class.
/// </summary>
///
public DistanceTransform()
{
formatTranslations = new Dictionary<PixelFormat, PixelFormat>();
formatTranslations[PixelFormat.Format8bppIndexed] = PixelFormat.Format8bppIndexed;
/// </summary>
///
public DistanceTransform()
{
formatTranslations = new Dictionary<PixelFormat, PixelFormat>();
formatTranslations[PixelFormat.Format8bppIndexed] = PixelFormat.Format8bppIndexed;
}

/// <summary>
/// Initializes a new instance of the <see cref="DistanceTransform"/> class.
/// </summary>
///
public DistanceTransform(DistanceTransformMethod method)
: this()
{
this.distance = method;
}

/// <summary>
/// Gets the maximum distance from the transform.
Expand All @@ -134,26 +181,26 @@ public IntPoint UltimateErodedPoint
}


/// <summary>
/// Process the filter on the specified image.
/// </summary>
///
/// <param name="image">Source image data.</param>
///
protected unsafe override void ProcessFilter(UnmanagedImage image)
{
int width = image.Width;
int height = image.Height;
PixelFormat format = image.PixelFormat;
int pixelSize = System.Drawing.Bitmap.GetPixelFormatSize(format) / 8;

Debug.Assert(pixelSize == 1);
Debug.Assert(image.Stride == width);

/// <summary>
/// Process the filter on the specified image.
/// </summary>
///
/// <param name="image">Source image data.</param>
///
protected unsafe override void ProcessFilter(UnmanagedImage image)
{
int width = image.Width;
int height = image.Height;
PixelFormat format = image.PixelFormat;
int pixelSize = System.Drawing.Bitmap.GetPixelFormatSize(format) / 8;

Debug.Assert(pixelSize == 1);
// Debug.Assert(image.Stride == width);

byte[] bPixels = image.ToByteArray();
float[] fPixels = new float[bPixels.Length];
this.fPixels = new float[bPixels.Length];

for (int i = 0; i < bPixels.Length; i++)
for (int i = 0; i < fPixels.Length; i++)
{
if (bPixels[i] != 0)
fPixels[i] = float.MaxValue;
Expand Down Expand Up @@ -218,11 +265,15 @@ protected unsafe override void ProcessFilter(UnmanagedImage image)
// Normalize and store
fixed (float* srcPtr = fPixels)
{
int offset = image.Offset;
byte* dst = (byte*)image.ImageData.ToPointer();
float* src = srcPtr;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++, src++, dst++)
(*dst) = (byte)Vector.Scale(*src, 0, max, 0, 255);
dst += offset;
}
}
}

Expand Down Expand Up @@ -354,5 +405,5 @@ private bool NeedSquareRoot(DistanceTransformMethod distance)
throw new Exception();
}

}
}
}
}
78 changes: 78 additions & 0 deletions Unit Tests/Accord.Tests.Imaging/Filters/BinaryWatershedTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Accord Unit Tests
// The Accord.NET Framework
// http://accord-framework.net
//
// Copyright © César Souza, 2009-2017
// cesarsouza at gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
//

namespace Accord.Tests.Imaging
{
using Accord.DataSets;
using Accord.Imaging.Converters;
using Accord.Imaging.Filters;
using Accord.Math;
using NUnit.Framework;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

[TestFixture]
public class BinaryWatershedTest
{

[Test]
public void ApplyTest1()
{
Bitmap shapes = Properties.Resources.water;
shapes.Save(@"c:\Temp\shapes.jpg");

var bw = new BinaryWatershed();
Bitmap result = bw.Apply(shapes);

Assert.AreEqual(746, result.Width);
Assert.AreEqual(643, result.Height);
Assert.AreEqual(PixelFormat.Format8bppIndexed, result.PixelFormat);

Assert.AreEqual(9, bw.MaxPoints.Count);

string strX = bw.MaxPoints.Select(i => i.X).ToArray().ToCSharp();
string strY = bw.MaxPoints.Select(i => i.Y).ToArray().ToCSharp();

double[] x = new double[] { 310, 546, 136, 254, 429, 612, 398, 345, 498 };
double[] y = new double[] { 436, 153, 392, 201, 336, 339, 242, 183, 319 };

Assert.AreEqual(x, bw.MaxPoints.Select(i => i.X).ToArray());
Assert.AreEqual(y, bw.MaxPoints.Select(i => i.Y).ToArray());

result.Save(@"c:\Temp\watershed.jpg");

GrayscaleToRGB toRGB = new GrayscaleToRGB();
result = toRGB.Apply(result);

PointsMarker marker = new PointsMarker(Color.Red, 5);
marker.Points = bw.MaxPoints;
Bitmap marked = marker.Apply(result);

marked.Save(@"c:\Temp\watershed-marks.jpg");

Assert.IsNotNull(result);
Assert.IsNotNull(marked);
}

}
}
Loading

0 comments on commit febe21d

Please sign in to comment.