diff --git a/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
new file mode 100644
index 0000000000..1cd29f8b43
--- /dev/null
+++ b/src/ImageSharp/Processing/Contrast/HistogramEqualizationExtension.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Processing.Contrast
+{
+ ///
+ /// Adds extension that allows applying an HistogramEqualization to the image.
+ ///
+ public static class HistogramEqualizationExtension
+ {
+ ///
+ /// Equalizes the histogram of an image to increases the global contrast.
+ ///
+ /// The pixel format.
+ /// The image this method extends.
+ /// The .
+ public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source)
+ where TPixel : struct, IPixel
+ => source.ApplyProcessor(new HistogramEqualizationProcessor());
+ }
+}
diff --git a/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
new file mode 100644
index 0000000000..2bacb98ce4
--- /dev/null
+++ b/src/ImageSharp/Processing/Contrast/HistogramEqualizationProcessor.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System;
+using SixLabors.ImageSharp.Advanced;
+using SixLabors.ImageSharp.PixelFormats;
+using SixLabors.ImageSharp.Processing.Processors;
+using SixLabors.Primitives;
+
+namespace SixLabors.ImageSharp.Processing.Contrast
+{
+ internal class HistogramEqualizationProcessor : ImageProcessor
+ where TPixel : struct, IPixel
+ {
+ ///
+ protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration)
+ {
+ var rgb = default(Rgb24);
+ int numberOfPixels = source.Width * source.Height;
+
+ // build the histogram of the grayscale levels
+ int luminanceLevels = 256;
+ int[] histogram = new int[luminanceLevels];
+ for (int y = 0; y < source.Height; y++)
+ {
+ Span row = source.GetPixelRowSpan(y);
+ for (int x = 0; x < source.Width; x++)
+ {
+ TPixel sourcePixel = row[x];
+ sourcePixel.ToRgb24(ref rgb);
+
+ // Convert to grayscale using ITU-R Recommendation BT.709 if required
+ int luminance = (int)((.2126F * rgb.R) + (.7152F * rgb.G) + (.0722F * rgb.B));
+ histogram[luminance]++;
+ }
+ }
+
+ // calculate the cumulative distribution function
+ double[] cdf = new double[luminanceLevels];
+ double sum = 0.0d;
+ for (int i = 0; i < histogram.Length; i++)
+ {
+ double p = (double)histogram[i] / numberOfPixels;
+ sum += p;
+ cdf[i] = sum;
+ }
+
+ // apply the cdf to each pixel of the image
+ for (int y = 0; y < source.Height; y++)
+ {
+ Span row = source.GetPixelRowSpan(y);
+ for (int x = 0; x < source.Width; x++)
+ {
+ TPixel sourcePixel = row[x];
+ sourcePixel.ToRgb24(ref rgb);
+ int luminance = (int)((.2126F * rgb.R) + (.7152F * rgb.G) + (.0722F * rgb.B));
+ byte luminanceEqualized = (byte)(cdf[luminance] * luminance);
+
+ row[x].PackFromRgba32(new Rgba32(luminanceEqualized, luminanceEqualized, luminanceEqualized));
+ }
+ }
+ }
+ }
+}