This project has moved and is read-only. For the latest updates, please go here.

Complementary Colour for Image

Dec 23, 2016 at 1:10 AM
HI

I am writing text over an image and would like to find the 'best' colour for this - realising that there will be no 'right' answer if the image is multicoloured - but many images have a predominant colour.

I found this snippet, that I think reduces the image to 1 pixel to find out the colour, but I can't figure out how to do that in Magick.NET. Ideally I would like to get the complementary colour into a variable to use in image.Settings.FillColor = new MagickColor(#xxxxxx);

$ convert Waffle.jpg -scale 1x1! -format '%[pixel:u]' info:-

Any help you can give would be very much appreciated. Thank you.
Dec 24, 2016 at 10:19 AM
Your command would translate to something like this:
MagickColor fillColor;
using (MagickImage image = new MagickImage("Waffle.jpg"))
{
  image.Scale(new MagickGeometry("1x1!"));
  using (PixelCollection pixels = image.GetPixels())
  {
    fillColor = pixels.GetPixel(0, 0).ToColor();
  }
}
Dec 25, 2016 at 12:38 AM
Thanks that definitely helps - I am now getting the 'average' color for the picture - and it appears to be what you would expect. Is there an easy way to get the complementary color to the average? I tried putting an image.Negate() before scaling but the color that comes thru is not the complement of the 'average' color. Thanks
Dec 26, 2016 at 12:05 PM
I also figured out to try something like:
image.Modulate(new ImageMagick.Percentage(100), new ImageMagick.Percentage(100), new ImageMagick.Percentage(0));
but it doesn't give the expected results.

Is there a way to actually manipulate fillColor once I have got it, rather than manipulating the image.

Thanks
Dec 26, 2016 at 3:16 PM
Starting with Magick.NET 7.0.4.100 (just released) you can do this:
MagickColor fillColor;
using (MagickImage image = new MagickImage("Waffle.jpg"))
{
  image.Scale(new MagickGeometry("1x1!"));
  using (PixelCollection pixels = image.GetPixels())
  {
    ColorRGB average = pixels.GetPixel(0, 0).ToColor();
    fillColor = average.ComplementaryColor();
  }
}
Dec 27, 2016 at 3:22 PM
Edited Dec 27, 2016 at 3:26 PM
Nice addition. Thanks.

I'm now thinking that some of the 'unexpected' results I saw earlier were due to a 'traditional' thinking about complementary colors (aka RYB colorspace) - I still feel the best complement for red is green not cyan - but I'm guessing there is no easy way to do that in ImageMagick - can't even find any good code - just talk of solving simultaneous equations.

Thanks again for the help.
Dec 27, 2016 at 3:56 PM
Edited Dec 27, 2016 at 4:05 PM
The determination of the complementary color is done in C# code so please let me know if improvements can be made. This is not part of the ImageMagick library and I used the implementation referenced in this post: http://stackoverflow.com/questions/1664140/js-function-to-calculate-complementary-colour. I assumed the first implementation was the correct one but the last post seems to get you a green color instead of cyan. If you can help me figure out how that implementation works I might be able to add it to Magick.NET.
Dec 27, 2016 at 6:34 PM
Yes, think there could be a problem - see this image: http://imgur.com/a/VNIa6

Added this to my code:

using (PixelCollection pixels = image5.GetPixels())
                {
                    ColorRGB average = pixels.GetPixel(0, 0).ToColor();
                    var cr1 = ((int)(average.R / 256)).ToString();
                    var cb1 = ((int)(average.B / 256)).ToString();
                    var cg1 = ((int)(average.G / 256)).ToString();
                    var tx1 = "AV: R:" + cr1 + " G:" + cg1 + " B:" + cb1;
                    quote += tx1;
                    fillColor = average.ComplementaryColor();
                    var cr = ((int)(fillColor.R / 256)).ToString();
                    var cb = ((int)(fillColor.B / 256)).ToString();
                    var cg = ((int)(fillColor.G / 256)).ToString();
                    var tx = "   COMP: R:" + cr + " G:" + cg + " B:" + cb;
                    quote += tx;
                }
The average is 153 160 30 - yellowish green - so that's right.

The complement 160 153 156 (text color) is too grey.

If I go to https://www.sessions.edu/color-calculator/ and set RGB mode
my average color is #9aa01e and the complement is a dark blue - #251ea0

I did find this: https://sharpsnippets.wordpress.com/2014/03/11/c-extension-complementary-color/
If you ignore the first paragraph - which is RYB and so doesn't match his RGB code - maybe the c# code will help.
Some more (php) here: http://serennu.com/colour/rgbtohsl.php

Found this on RGB<->RYB - looks complicated http://stackoverflow.com/questions/4945457/conversion-between-rgb-and-ryb-color-spaces

"Take the equations for a trilinear interpolation. Substitute the first equations into the last, the expand and collect the coefficients for: Xd, Yd, Zd, XdYd, XdZd, YdZd, ZdYdZd and the constant. Then find the partial differentiation of the equation in each of the 3 dimensions each in respect to Xd, Yd and Zd. Use these new equations to populate the (3x3) Jacobian matrix and then use Newton's method to solve in software."

The guy at https://www.sessions.edu/color-calculator/ seems to have the code - maybe he would share for a non-profit project??
Dec 27, 2016 at 7:51 PM
It looks like there was something wrong in the RGB to HSV conversion code. The following test now passes:
ColorRGB green = new MagickColor("#9aa01e");
ColorRGB blue = green.ComplementaryColor();
ColorAssert.AreEqual(new MagickColor("#231ea0"), blue);
This will be resolved in the next version of Magick.NET.