private static ColorMoment Bottom(Box cube, int direction, ColorMoment[, , ,] moment) { switch (direction) { case Alpha: return (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); case Red: return (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); case Green: return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); case Blue: return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]) - (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); default: return new ColorMoment(); } }
private static ColorMoment Volume(Box cube, ColorMoment[, , ,] moment) { return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); }
private static CubeCut Maximize(ColorMoment[, , ,] moments, Box cube, int direction, byte first, byte last, ColorMoment whole) { var bottom = Bottom(cube, direction, moments); var result = 0.0f; byte? cutPoint = null; for (var position = first; position < last; ++position) { var half = bottom + Top(cube, direction, position, moments); if (half.Weight == 0) continue; var temp = half.WeightedDistance(); half = whole - half; if (half.Weight != 0) { temp += half.WeightedDistance(); if (temp > result) { result = temp; cutPoint = position; } } } return new CubeCut(cutPoint, result); }
private static Box[] SplitData(ref int colorCount, int maxColors, ColorMoment[, , ,] moments) { --colorCount; var next = 0; var volumeVariance = new float[maxColors]; var cubes = new Box[maxColors]; cubes[0].AlphaMaximum = MaxSideIndex; cubes[0].RedMaximum = MaxSideIndex; cubes[0].GreenMaximum = MaxSideIndex; cubes[0].BlueMaximum = MaxSideIndex; for (var cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex) { if (Cut(moments, ref cubes[next], ref cubes[cubeIndex])) { volumeVariance[next] = cubes[next].Size > 1 ? CalculateVariance(moments, cubes[next]) : 0.0f; volumeVariance[cubeIndex] = cubes[cubeIndex].Size > 1 ? CalculateVariance(moments, cubes[cubeIndex]) : 0.0f; } else { volumeVariance[next] = 0.0f; cubeIndex--; } next = 0; var temp = volumeVariance[0]; for (var index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] <= temp) continue; temp = volumeVariance[index]; next = index; } if (temp > 0.0) continue; colorCount = cubeIndex + 1; break; } return cubes.Take(colorCount).ToArray(); }
private static float CalculateVariance(ColorMoment[, , ,] moments, Box cube) { ColorMoment volume = Volume(cube, moments); return volume.Variance(); }
private static bool Cut(ColorMoment[, , ,] moments, ref Box first, ref Box second) { int direction; var whole = Volume(first, moments); var maxAlpha = Maximize(moments, first, Alpha, (byte)(first.AlphaMinimum + 1), first.AlphaMaximum, whole); var maxRed = Maximize(moments, first, Red, (byte)(first.RedMinimum + 1), first.RedMaximum, whole); var maxGreen = Maximize(moments, first, Green, (byte)(first.GreenMinimum + 1), first.GreenMaximum, whole); var maxBlue = Maximize(moments, first, Blue, (byte)(first.BlueMinimum + 1), first.BlueMaximum, whole); if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value)) { direction = Alpha; if (!maxAlpha.Position.HasValue) return false; } else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) && (maxRed.Value >= maxBlue.Value)) direction = Red; else { if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) && (maxGreen.Value >= maxBlue.Value)) direction = Green; else direction = Blue; } second.AlphaMaximum = first.AlphaMaximum; second.RedMaximum = first.RedMaximum; second.GreenMaximum = first.GreenMaximum; second.BlueMaximum = first.BlueMaximum; switch (direction) { case Alpha: second.AlphaMinimum = first.AlphaMaximum = maxAlpha.Position.Value; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Red: second.RedMinimum = first.RedMaximum = maxRed.Position.Value; second.AlphaMinimum = first.AlphaMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Green: second.GreenMinimum = first.GreenMaximum = maxGreen.Position.Value; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.BlueMinimum = first.BlueMinimum; break; case Blue: second.BlueMinimum = first.BlueMaximum = maxBlue.Position.Value; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; break; } first.Size = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum); second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum); return true; }
private static float VolumeFloat(Box cube, float[,,,] moment) { return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); }
private static Pixel[] BuildLookups(Box[] cubes, ColorMoment[, , ,] moments) { Pixel[] lookups = new Pixel[cubes.Length]; for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++) { var volume = Volume(cubes[cubeIndex], moments); if (volume.Weight <= 0) continue; var lookup = new Pixel { Alpha = (byte)(volume.Alpha / volume.Weight), Red = (byte)(volume.Red / volume.Weight), Green = (byte)(volume.Green / volume.Weight), Blue = (byte)(volume.Blue / volume.Weight) }; lookups[cubeIndex] = lookup; } return lookups; }
private static long Top(Box cube, int direction, int position, long[,,,] moment) { switch (direction) { case Alpha: return (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] - moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) - (moment[position, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[position, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[position, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[position, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]); case Red: return (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMaximum]) - (moment[cube.AlphaMaximum, position, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.AlphaMaximum, position, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.AlphaMinimum, position, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.AlphaMinimum, position, cube.GreenMinimum, cube.BlueMinimum]); case Green: return (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMaximum] - moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMaximum] - moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMaximum] + moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMaximum]) - (moment[cube.AlphaMaximum, cube.RedMaximum, position, cube.BlueMinimum] - moment[cube.AlphaMaximum, cube.RedMinimum, position, cube.BlueMinimum] - moment[cube.AlphaMinimum, cube.RedMaximum, position, cube.BlueMinimum] + moment[cube.AlphaMinimum, cube.RedMinimum, position, cube.BlueMinimum]); case Blue: return (moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, position] - moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, position] - moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, position] + moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, position]) - (moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, position] - moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, position] - moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, position] + moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, position]); default: return 0; } }
private static IList<Box> SplitData(ref int colorCount, ColorData data) { --colorCount; var next = 0; var volumeVariance = new float[MaxColor]; var cubes = new Box[MaxColor]; cubes[0].AlphaMaximum = MaxSideIndex; cubes[0].RedMaximum = MaxSideIndex; cubes[0].GreenMaximum = MaxSideIndex; cubes[0].BlueMaximum = MaxSideIndex; for (var cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex) { if (Cut(data, ref cubes[next], ref cubes[cubeIndex])) { volumeVariance[next] = cubes[next].Size > 1 ? CalculateVariance(data, cubes[next]) : 0.0f; volumeVariance[cubeIndex] = cubes[cubeIndex].Size > 1 ? CalculateVariance(data, cubes[cubeIndex]) : 0.0f; } else { volumeVariance[next] = 0.0f; cubeIndex--; } next = 0; var temp = volumeVariance[0]; for (var index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] <= temp) continue; temp = volumeVariance[index]; next = index; } if (temp > 0.0) continue; colorCount = cubeIndex + 1; break; } return cubes.Take(colorCount).ToList(); }
private static CubeCut Maximize(ColorData data, Box cube, int direction, byte first, byte last, long wholeAlpha, long wholeRed, long wholeGreen, long wholeBlue, long wholeWeight) { var bottomAlpha = Bottom(cube, direction, data.MomentsAlpha); var bottomRed = Bottom(cube, direction, data.MomentsRed); var bottomGreen = Bottom(cube, direction, data.MomentsGreen); var bottomBlue = Bottom(cube, direction, data.MomentsBlue); var bottomWeight = Bottom(cube, direction, data.Weights); var result = 0.0f; byte? cutPoint = null; for (var position = first; position < last; ++position) { var halfAlpha = bottomAlpha + Top(cube, direction, position, data.MomentsAlpha); var halfRed = bottomRed + Top(cube, direction, position, data.MomentsRed); var halfGreen = bottomGreen + Top(cube, direction, position, data.MomentsGreen); var halfBlue = bottomBlue + Top(cube, direction, position, data.MomentsBlue); var halfWeight = bottomWeight + Top(cube, direction, position, data.Weights); if (halfWeight == 0) continue; var halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue; var temp = halfDistance / halfWeight; halfAlpha = wholeAlpha - halfAlpha; halfRed = wholeRed - halfRed; halfGreen = wholeGreen - halfGreen; halfBlue = wholeBlue - halfBlue; halfWeight = wholeWeight - halfWeight; if (halfWeight != 0) { halfDistance = halfAlpha * halfAlpha + halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue; temp += halfDistance / halfWeight; if (temp > result) { result = temp; cutPoint = position; } } } return new CubeCut(cutPoint, result); }
private static bool Cut(ColorData data, ref Box first,ref Box second) { int direction; var wholeAlpha = Volume(first, data.MomentsAlpha); var wholeRed = Volume(first, data.MomentsRed); var wholeGreen = Volume(first, data.MomentsGreen); var wholeBlue = Volume(first, data.MomentsBlue); var wholeWeight = Volume(first, data.Weights); var maxAlpha = Maximize(data, first, Alpha, (byte) (first.AlphaMinimum + 1), first.AlphaMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight); var maxRed = Maximize(data, first, Red, (byte) (first.RedMinimum + 1), first.RedMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight); var maxGreen = Maximize(data, first, Green, (byte) (first.GreenMinimum + 1), first.GreenMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight); var maxBlue = Maximize(data, first, Blue, (byte) (first.BlueMinimum + 1), first.BlueMaximum, wholeAlpha, wholeRed, wholeGreen, wholeBlue, wholeWeight); if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value)) { direction = Alpha; if (maxAlpha.Position == null) return false; } else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) && (maxRed.Value >= maxBlue.Value)) direction = Red; else { if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) && (maxGreen.Value >= maxBlue.Value)) direction = Green; else direction = Blue; } second.AlphaMaximum = first.AlphaMaximum; second.RedMaximum = first.RedMaximum; second.GreenMaximum = first.GreenMaximum; second.BlueMaximum = first.BlueMaximum; switch (direction) { case Alpha: second.AlphaMinimum = first.AlphaMaximum = (byte) maxAlpha.Position; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Red: second.RedMinimum = first.RedMaximum = (byte) maxRed.Position; second.AlphaMinimum = first.AlphaMinimum; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Green: second.GreenMinimum = first.GreenMaximum = (byte) maxGreen.Position; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.BlueMinimum = first.BlueMinimum; break; case Blue: second.BlueMinimum = first.BlueMaximum = (byte) maxBlue.Position; second.AlphaMinimum = first.AlphaMinimum; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; break; } first.Size = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum); second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum); return true; }
private static float CalculateVariance(ColorData data, Box cube) { float volumeAlpha = Volume(cube, data.MomentsAlpha); float volumeRed = Volume(cube, data.MomentsRed); float volumeGreen = Volume(cube, data.MomentsGreen); float volumeBlue = Volume(cube, data.MomentsBlue); float volumeMoment = VolumeFloat(cube, data.Moments); float volumeWeight = Volume(cube, data.Weights); float distance = volumeAlpha * volumeAlpha + volumeRed * volumeRed + volumeGreen * volumeGreen + volumeBlue * volumeBlue; var result = volumeMoment - distance / volumeWeight; return result.ToString() == "NaN" ? 0.0f : result; }