Reputation: 35891
I have the following piece of code:
public List<Tuple<double, double, double>> GetNormalizedPixels(Bitmap image)
{
System.Drawing.Imaging.BitmapData data = image.LockBits(
new Rectangle(0, 0, image.Width, image.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
image.PixelFormat);
int pixelSize = Image.GetPixelFormatSize(image.PixelFormat) / 8;
var result = new List<Tuple<double, double, double>>();
unsafe
{
for (int y = 0; y < data.Height; ++y)
{
byte* row = (byte*)data.Scan0 + (y * data.Stride);
for (int x = 0; x < data.Width; ++x)
{
Color c = Color.FromArgb(
row[x * pixelSize + 3],
row[x * pixelSize + 2],
row[x * pixelSize + 1],
row[x * pixelSize]);
// (*)
result.Add(Tuple.Create(
1.0 * c.R / 255,
1.0 * c.G / 255,
1.0 * c.B / 255);
}
}
}
image.UnlockBits(data);
return result;
}
The key fragment (*) is this:
result.Add(Tuple.Create(
1.0 * c.R / 255,
1.0 * c.G / 255,
1.0 * c.B / 255);
which adds a pixel with its components scaled to range [0, 1]
to be further used in classification tasks with different classifiers. Some of them require the attributes to be normalized like this, others don't care - hence this function.
However, what should I do when I'd like to classify pixels in a different colour space than RGB
, like L*a*b*
? While values of all coordinates in RGB
colour space fall into range [0,256)
in L*a*b*
colour space a*
and b*
are said to be unbounded.
So when changing the fragment (*) to:
Lab lab = c.ToLab();
result.Add(Tuple.Create(
1.0 * lab.L / 100,
1.0 * lab.A / ?,
1.0 * lab.B / ?);
(ToLab
is an extension method, implemented using appropriate algorithms from here)
what should I put for the question marks?
Upvotes: 19
Views: 22560
Reputation: 35891
In practice the number of all possible RGB
colours is finite, so the L*a*b*
space is bounded. It is easy to find the ranges of coordinates with the following simple program:
Color c;
double maxL = double.MinValue;
double maxA = double.MinValue;
double maxB = double.MinValue;
double minL = double.MaxValue;
double minA = double.MaxValue;
double minB = double.MaxValue;
for (int r = 0; r < 256; ++r)
for (int g = 0; g < 256; ++g)
for (int b = 0; b < 256; ++b)
{
c = Color.FromArgb(r, g, b);
Lab lab = c.ToLab();
maxL = Math.Max(maxL, lab.L);
maxA = Math.Max(maxA, lab.A);
maxB = Math.Max(maxB, lab.B);
minL = Math.Min(minL, lab.L);
minA = Math.Min(minA, lab.A);
minB = Math.Min(minB, lab.B);
}
Console.WriteLine("maxL = " + maxL + ", maxA = " + maxA + ", maxB = " + maxB);
Console.WriteLine("minL = " + minL + ", minA = " + minA + ", minB = " + minB);
or a similar one using any other language.
So, CIELAB
space coordinate ranges are as follows:
L in [0, 100]
A in [-86.185, 98.254]
B in [-107.863, 94.482]
and the answer is:
Lab lab = c.ToLab();
result.Add(Tuple.Create(
1.0 * lab.L / 100,
1.0 * (lab.A + 86.185) / 184.439,
1.0 * (lab.B + 107.863) / 202.345);
Upvotes: 31
Reputation: 1089
If the Lab-conversion code is implemented in accord with the Lab-colors definition (see, for example Lab color space), then function f(...)
, which is used for defining L
, a
and b
changes within [4/29,1], thefore
L = 116 * f(y) - 16 is in [0,100]
a = 500 * (f(x)-f(y)) is in [-500*25/29, 500*25/29]
b = 200 * (f(y)-f(z)) is in [-200*25/29, 200*25/29]
Some people (like bortizj in his reply) normalize these values to range, which a byte-variable can hold. So you have to analyze the code in order to determine, what range it produces. But again, the formulas in Wiki will give you the range above. The same range will give you the code here
Upvotes: 1