/// <summary> /// Finds the optimal (maximal) position for the cut. /// </summary> private Single Maximize(WuColorCube cube, Int32 direction, Int32 first, Int32 last, IList<Int32> cut, Int64 wholeRed, Int64 wholeGreen, Int64 wholeBlue, Int64 wholeWeight) { Int64 bottomRed = Bottom(cube, direction, momentsRed); Int64 bottomGreen = Bottom(cube, direction, momentsGreen); Int64 bottomBlue = Bottom(cube, direction, momentsBlue); Int64 bottomWeight = Bottom(cube, direction, weights); Single result = 0.0f; cut[0] = -1; for (Int32 position = first; position < last; ++position) { // determines the cube cut at a certain position Int64 halfRed = bottomRed + Top(cube, direction, position, momentsRed); Int64 halfGreen = bottomGreen + Top(cube, direction, position, momentsGreen); Int64 halfBlue = bottomBlue + Top(cube, direction, position, momentsBlue); Int64 halfWeight = bottomWeight + Top(cube, direction, position, weights); // the cube cannot be cut at bottom (this would lead to empty cube) if (halfWeight != 0) { Single halfDistance = halfRed*halfRed + halfGreen*halfGreen + halfBlue*halfBlue; Single temp = halfDistance/halfWeight; halfRed = wholeRed - halfRed; halfGreen = wholeGreen - halfGreen; halfBlue = wholeBlue - halfBlue; halfWeight = wholeWeight - halfWeight; if (halfWeight != 0) { halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue; temp += halfDistance / halfWeight; if (temp > result) { result = temp; cut[0] = position; } } } } return result; }
/// <summary> /// Calculates statistical variance for a given cube. /// </summary> private Single CalculateVariance(WuColorCube cube) { Single volumeRed = Volume(cube, momentsRed); Single volumeGreen = Volume(cube, momentsGreen); Single volumeBlue = Volume(cube, momentsBlue); Single volumeMoment = VolumeFloat(cube, moments); Single volumeWeight = Volume(cube, weights); Single distance = volumeRed*volumeRed + volumeGreen*volumeGreen + volumeBlue*volumeBlue; return volumeMoment - (distance/volumeWeight); }
/// <summary> /// Cuts a cube with another one. /// </summary> private Boolean Cut(WuColorCube first, WuColorCube second) { Int32 direction; Int32[] cutRed = { 0 }; Int32[] cutGreen = { 0 }; Int32[] cutBlue = { 0 }; Int64 wholeRed = Volume(first, momentsRed); Int64 wholeGreen = Volume(first, momentsGreen); Int64 wholeBlue = Volume(first, momentsBlue); Int64 wholeWeight = Volume(first, weights); Single maxRed = Maximize(first, Red, first.RedMinimum + 1, first.RedMaximum, cutRed, wholeRed, wholeGreen, wholeBlue, wholeWeight); Single maxGreen = Maximize(first, Green, first.GreenMinimum + 1, first.GreenMaximum, cutGreen, wholeRed, wholeGreen, wholeBlue, wholeWeight); Single maxBlue = Maximize(first, Blue, first.BlueMinimum + 1, first.BlueMaximum, cutBlue, wholeRed, wholeGreen, wholeBlue, wholeWeight); if ((maxRed >= maxGreen) && (maxRed >= maxBlue)) { direction = Red; // cannot split empty cube if (cutRed[0] < 0) return false; } else { if ((maxGreen >= maxRed) && (maxGreen >= maxBlue)) { direction = Green; } else { direction = Blue; } } second.RedMaximum = first.RedMaximum; second.GreenMaximum = first.GreenMaximum; second.BlueMaximum = first.BlueMaximum; // cuts in a certain direction switch (direction) { case Red: second.RedMinimum = first.RedMaximum = cutRed[0]; second.GreenMinimum = first.GreenMinimum; second.BlueMinimum = first.BlueMinimum; break; case Green: second.GreenMinimum = first.GreenMaximum = cutGreen[0]; second.RedMinimum = first.RedMinimum; second.BlueMinimum = first.BlueMinimum; break; case Blue: second.BlueMinimum = first.BlueMaximum = cutBlue[0]; second.RedMinimum = first.RedMinimum; second.GreenMinimum = first.GreenMinimum; break; } // determines the volumes after cut first.Volume = (first.RedMaximum - first.RedMinimum)*(first.GreenMaximum - first.GreenMinimum)*(first.BlueMaximum - first.BlueMinimum); second.Volume = (second.RedMaximum - second.RedMinimum)*(second.GreenMaximum - second.GreenMinimum)*(second.BlueMaximum - second.BlueMinimum); // the cut was successfull return true; }
/// <summary> /// Computes the volume of the cube in a specific moment. For the floating-point values. /// </summary> private static Single VolumeFloat(WuColorCube cube, Single[,,] moment) { return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; }
/// <summary> /// Computes the volume of the cube in a specific moment. /// </summary> private static Int64 Volume(WuColorCube cube, Int64[,,] moment) { return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] - moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] - moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] + moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] - moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] + moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] + moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] - moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]; }
/// <summary> /// Splits the cube in given position, and color direction. /// </summary> private static Int64 Top(WuColorCube cube, Int32 direction, Int32 position, Int64[,,] moment) { switch (direction) { case Red: return (moment[position, cube.GreenMaximum, cube.BlueMaximum] - moment[position, cube.GreenMaximum, cube.BlueMinimum] - moment[position, cube.GreenMinimum, cube.BlueMaximum] + moment[position, cube.GreenMinimum, cube.BlueMinimum]); case Green: return (moment[cube.RedMaximum, position, cube.BlueMaximum] - moment[cube.RedMaximum, position, cube.BlueMinimum] - moment[cube.RedMinimum, position, cube.BlueMaximum] + moment[cube.RedMinimum, position, cube.BlueMinimum]); case Blue: return (moment[cube.RedMaximum, cube.GreenMaximum, position] - moment[cube.RedMaximum, cube.GreenMinimum, position] - moment[cube.RedMinimum, cube.GreenMaximum, position] + moment[cube.RedMinimum, cube.GreenMinimum, position]); default: return 0; } }
/// <summary> /// Marks all the tags with a given label. /// </summary> private static void Mark(WuColorCube cube, Int32 label, IList<Int32> tag) { for (Int32 redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex) { for (Int32 greenIndex = cube.GreenMinimum + 1; greenIndex <= cube.GreenMaximum; ++greenIndex) { for (Int32 blueIndex = cube.BlueMinimum + 1; blueIndex <= cube.BlueMaximum; ++blueIndex) { tag[(redIndex << 10) + (redIndex << 6) + redIndex + (greenIndex << 5) + greenIndex + blueIndex] = label; } } } }
/// <summary> /// See <see cref="BaseColorQuantizer.OnPrepare"/> for more details. /// </summary> protected override void OnPrepare(ImageBuffer image) { // creates all the cubes cubes = new WuColorCube[MaxColor]; // initializes all the cubes for (Int32 cubeIndex = 0; cubeIndex < MaxColor; cubeIndex++) { cubes[cubeIndex] = new WuColorCube(); } // resets the reference minimums cubes[0].RedMinimum = 0; cubes[0].GreenMinimum = 0; cubes[0].BlueMinimum = 0; // resets the reference maximums cubes[0].RedMaximum = MaxSideIndex; cubes[0].GreenMaximum = MaxSideIndex; cubes[0].BlueMaximum = MaxSideIndex; weights = new Int64[SideSize, SideSize, SideSize]; momentsRed = new Int64[SideSize, SideSize, SideSize]; momentsGreen = new Int64[SideSize, SideSize, SideSize]; momentsBlue = new Int64[SideSize, SideSize, SideSize]; moments = new Single[SideSize, SideSize, SideSize]; table = new Int32[256]; for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) { table[tableIndex] = tableIndex * tableIndex; } pixelIndex = 0; imageWidth = image.Width; imageSize = image.Width * image.Height; quantizedPixels = new Int32[imageSize]; pixels = new Int32[imageSize]; }