1

Trying to figure out a way to determine the best contrasting color for an areas of a photo. The contrasting color is to be used as the color of some overlaying text.

Using Six Labor ImageSharp, far I've been able to:

  1. Loaded the image stream into a Sixlabor ImageSharp image:
    myImage = Image.Load(imageStream)
  2. Mutated using Crop to carve out the approx area of where the text should be:
    myImage.Mutate(x =>x.Crop(rectangle))

But how do I determine the average/dominate color of this cropped area?

I've seen somewhere that one approach is to resize the cropped area into the size of one pixel. This is easy to do (next step would be: myImage.Mutate(x => x.Resize(1,1))), but how do I then extract the color of this one pixel then?

As I get this color, I plan on using this approach to calculate the contrasting color.

1iveowl
  • 1,622
  • 1
  • 18
  • 31

2 Answers2

1

Here is how I ended up solving this, using this algorithm to determine the best contrasting font color (black or white).

private Color GetContrastColorBW(int x, int y, int height, int width, stream photoAsStream)
{
    var rect = new SixLabors.Primitives.Rectangle(x,y, height, width);

    var sizeOfOne = new SixLabors.Primitives.Size(1,1);

    using var image = Image.Load<Rgba32>(photoAsStream);

    var croppedImageResizedToOnePixel = image.Clone(
        img => img.Crop(rect)
        .Resize(sizeOfOne));

    var averageColor = croppedImageResizedToOnePixel[0, 0];

    var luminance = (0.299 * averageColor.R + 0.587 * averageColor.G + 0.114 * averageColor.B) / 255;

    return luminance > 0.5 ? Color.Black : Color.White;
}
1iveowl
  • 1,622
  • 1
  • 18
  • 31
1

I've rewritten you answer. This should be a little faster and more accurate and uses the existing API.

private Color GetContrastColorBW(int x, int y, int height, int width, stream photoAsStream)
{
    var rect = new Rectangle(x, y, height, width);

    using Image<Rgba32> image = Image.Load<Rgba32>(photoAsStream);

    // Reduce the color palette to the the dominant color without dithering.
    var quantizer = new OctreeQuantizer(false, 1);
    image.Mutate( // No need to clone.
        img => img.Crop(rect) // Intial crop
                  .Quantize(quantizer) // Find the dominant color, cheaper and more accurate than resizing.
                  .Crop(new Rectangle(Point.Empty, new Size(1, 1))) // Crop again so the next command is faster
                  .BinaryThreshold(.5F, Color.Black, Color.White)); // Threshold to High-Low color. // Threshold to High-Low color, default White/Black

    return image[0, 0];
}
James South
  • 10,147
  • 4
  • 59
  • 115
  • 1
    Thank you James. Unfortunately, the step .Quantize(quantizer) generates a SIGSEGV crash in my Xamarin Forms project when running on a iPad device (iOS 13.2.3). Not sure what trigggers this crash. – 1iveowl Nov 27 '19 at 18:45
  • You're probably suffering from issues with the AOT compiler. I would try upgrading to the latest nightly and if that doesn't work please comment on this issue https://github.com/SixLabors/ImageSharp/issues/946 – James South Nov 27 '19 at 23:48