Home > Uncategorized > Determine the colour of an object in an image using C# #ImageProcessing

Determine the colour of an object in an image using C# #ImageProcessing

1270032243_097173627

We are going to add a new field to the data on CarImagery.com that shows the colour of the vehicle, and here is the image processing script that I used to determine the colour of the image.

First off, here’s the steps in broad strokes.

  1. Load the image from a URL
  2. Crop the image to the middle half, to discard some of the background.
  3. Average all the colours in the selected area
  4. Match the colour to the closest named colour, like “Red”, “Blue”, “Black” etc.

So, step 1, here’s how I load an image from a URL into a 2D array of Color[,] objects.

public static Color[,] ToColourArray(Uri url)
{
var wc = new WebClient();
var bData = wc.DownloadData(url);
return ToColourArray(bData);
}

public static Color[,] ToColourArray(byte[] img)
{
Stream sInput = new MemoryStream(img);
var imgOriginal = Image.FromStream(sInput) as Bitmap;
var img2D = new Color[imgOriginal.Width, imgOriginal.Height];
for (var i = 0; i < imgOriginal.Width; i++)
{
for (var j = 0; j < imgOriginal.Height; j++)
{
var pixel = imgOriginal.GetPixel(i, j);
img2D[i, j] = pixel;
}
}
return img2D;
}

Now, once I have an array of Color[,] objects, I can more easily manipulate them. Here is how I crop the image to the center, something like this;

box

public static Color[,] CropMiddle(Color[,] img)
{
var width = img.GetLength(0);
var height = img.GetLength(1);
var rectangle = new Rectangle((int)(width * 0.25), (int)(height * 0.25), width / 2, height /2 );
var imgOutput = new Color[width/2,height/2];
for (var i = rectangle.Left; i < rectangle.Right; i++)
{
for (var j = rectangle.Top; j < rectangle.Bottom; j++)
{
imgOutput[i – rectangle.Left, j – rectangle.Top] = img[i, j];
}
}
return imgOutput;
}

Now, I average the colours within this area;

public static Color AverageColour(Color[,] img)
{
var width = img.GetLength(0);
var height = img.GetLength(1);
var rectangle = new Rectangle(0, 0, width , height);
int r = 0 , g = 0, b = 0;
for (var i = rectangle.Left; i < rectangle.Right; i++)
{
for (var j = rectangle.Top; j < rectangle.Bottom; j++)
{
r += img[i, j].R;
g += img[i, j].G;
b += img[i, j].B;
}
}
r /= width * height;
g /= width * height;
b /= width * height;
return Color.FromArgb(r, g, b);
}

Which leaves me with a Dark brown, specifically #45373B

brown

Then, we need to map this colour to a set of named colours, there are a number of ways of doing this. The most simple way is to measure the euclidean distance (Root of the square of the distance) between the R, G, and B values. However, I found that comparing the Hue and Saturation works better in terms of how we perceive colours.

public static Color GetClosestColour(Color baseColour)
{
Color[] colourArray =
{
Color.Red,
Color.Black,
Color.White,
Color.Blue,
Color.Yellow,
Color.Green,
Color.Gray
};
var colors = colourArray.Select(x => new { Value = x, Diff = CustomColourDifference(x, baseColour) }).ToList();
var min = colors.Min(x => x.Diff);
return colors.Find(x => x.Diff == min).Value;
}

Specifically, what I did here, was say that if the Saturation is less than 10%, then the colour is grayscale (i.e. White, black or gray), otherwise it’s a fully saturated colour (Red, Blue, Green, Yellow, etc.), I’ve called this the CustomColourDifference Function

 private static int CustomColourDifference(Color c1, Color c2)
{
int distance = 0;
if (c2.GetSaturation() < 0.1)
{
// Near 0 is grayscale
distance = (int)Math.Abs(c1.GetSaturation() – c2.GetSaturation());
}
else
{
distance = (int)Math.Abs(c1.GetHue() – c2.GetHue());
}
return distance;
}

Which gives me Color.Red. – Which I went on to store in the database, which is outside of the scope of this article.

 

Categories: Uncategorized
  1. July 26, 2019 at 10:21 am

    Very good… Do you have any code to determine our login credentials for your API?

    Like

  2. Felix
    March 5, 2020 at 3:43 pm

    Very helpful, good & easy to understand!

    Like

  1. No trackbacks yet.

Leave a comment