private static Rectangle FindAutoCropRectangle(BitmapBuffer buffer, Point colorPoint) { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = buffer.GetColorAtWithoutAlpha(colorPoint.X, colorPoint.Y); Point min = new Point(int.MaxValue, int.MaxValue); Point max = new Point(int.MinValue, int.MinValue); if (conf.AutoCropDifference > 0) { for (int y = 0; y < buffer.Height; y++) { for (int x = 0; x < buffer.Width; x++) { Color currentColor = buffer.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffG = Math.Abs(currentColor.G - referenceColor.G); int diffB = Math.Abs(currentColor.B - referenceColor.B); if (((diffR + diffG + diffB) / 3) > conf.AutoCropDifference) { if (x < min.X) { min.X = x; } if (y < min.Y) { min.Y = y; } if (x > max.X) { max.X = x; } if (y > max.Y) { max.Y = y; } } } } } else { for (int y = 0; y < buffer.Height; y++) { for (int x = 0; x < buffer.Width; x++) { Color currentColor = buffer.GetColorAtWithoutAlpha(x, y); if (referenceColor.Equals(currentColor)) { if (x < min.X) { min.X = x; } if (y < min.Y) { min.Y = y; } if (x > max.X) { max.X = x; } if (y > max.Y) { max.Y = y; } } } } } if (!(Point.Empty.Equals(min) && max.Equals(new Point(buffer.Width - 1, buffer.Height - 1)))) { if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } return(cropRectangle); }
/// <summary> /// Helper method for the FindAutoCropRectangle /// </summary> /// <param name="buffer"></param> /// <param name="colorPoint"></param> /// <returns></returns> private static Rectangle FindAutoCropRectangle(BitmapBuffer buffer, Point colorPoint, int cropDifference) { Rectangle cropRectangle = Rectangle.Empty; Color referenceColor = buffer.GetColorAtWithoutAlpha(colorPoint.X,colorPoint.Y); Point min = new Point(int.MaxValue, int.MaxValue); Point max = new Point(int.MinValue, int.MinValue); if (cropDifference > 0) { for(int y = 0; y < buffer.Height; y++) { for(int x = 0; x < buffer.Width; x++) { Color currentColor = buffer.GetColorAt(x, y); int diffR = Math.Abs(currentColor.R - referenceColor.R); int diffG = Math.Abs(currentColor.G - referenceColor.G); int diffB = Math.Abs(currentColor.B - referenceColor.B); if (((diffR + diffG + diffB) / 3) > cropDifference) { if (x < min.X) min.X = x; if (y < min.Y) min.Y = y; if (x > max.X) max.X = x; if (y > max.Y) max.Y = y; } } } } else { for(int y = 0; y < buffer.Height; y++) { for(int x = 0; x < buffer.Width; x++) { Color currentColor = buffer.GetColorAtWithoutAlpha(x, y); if (referenceColor.Equals(currentColor)) { if (x < min.X) min.X = x; if (y < min.Y) min.Y = y; if (x > max.X) max.X = x; if (y > max.Y) max.Y = y; } } } } if (!(Point.Empty.Equals(min) && max.Equals(new Point(buffer.Width-1, buffer.Height-1)))) { if (!(min.X == int.MaxValue || min.Y == int.MaxValue || max.X == int.MinValue || min.X == int.MinValue)) { cropRectangle = new Rectangle(min.X, min.Y, max.X - min.X + 1, max.Y - min.Y + 1); } } return cropRectangle; }
/// <summary> /// See <see cref="IColorQuantizer.Prepare"/> for more details. /// </summary> public WuQuantizer(Bitmap sourceBitmap) { this.sourceBitmap = sourceBitmap; // Make sure the color count variables are reset BitArray bitArray = new BitArray((int)Math.Pow(2, 24)); colorCount = 0; // 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]; Int32[] table = new Int32[256]; for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) { table[tableIndex] = tableIndex * tableIndex; } // Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format8bppIndexed); using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { bbbSrc.Lock(); using (BitmapBuffer bbbDest = new BitmapBuffer(resultBitmap, false)) { bbbDest.Lock(); for (int y = 0; y < bbbSrc.Height; y++) { for (int x = 0; x < bbbSrc.Width; x++) { Color color = bbbSrc.GetColorAtWithoutAlpha(x, y); // To count the colors int index = color.ToArgb() & 0x00ffffff; // Check if we already have this color if (!bitArray.Get(index)) { // If not, add 1 to the single colors colorCount++; bitArray.Set(index, true); } Int32 indexRed = (color.R >> 3) + 1; Int32 indexGreen = (color.G >> 3) + 1; Int32 indexBlue = (color.B >> 3) + 1; weights[indexRed, indexGreen, indexBlue]++; momentsRed[indexRed, indexGreen, indexBlue] += color.R; momentsGreen[indexRed, indexGreen, indexBlue] += color.G; momentsBlue[indexRed, indexGreen, indexBlue] += color.B; moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B]; // Store the initial "match" Int32 paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue; bbbDest.SetColorIndexAt(x, y, (byte)(paletteIndex & 0xff)); } } } } }
/// <summary> /// Get the image /// </summary> public Bitmap GetQuantizedImage(int allowedColorCount) { // preprocess the colors CalculateMoments(); LOG.Info("Calculated the moments..."); Int32 next = 0; Single[] volumeVariance = new Single[MAXCOLOR]; // processes the cubes for (Int32 cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex) { // if cut is possible; make it if (Cut(cubes[next], cubes[cubeIndex])) { volumeVariance[next] = cubes[next].Volume > 1 ? CalculateVariance(cubes[next]) : 0.0f; volumeVariance[cubeIndex] = cubes[cubeIndex].Volume > 1 ? CalculateVariance(cubes[cubeIndex]) : 0.0f; } else { // the cut was not possible, revert the index volumeVariance[next] = 0.0f; cubeIndex--; } next = 0; Single temp = volumeVariance[0]; for (Int32 index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] > temp) { temp = volumeVariance[index]; next = index; } } if (temp <= 0.0) { allowedColorCount = cubeIndex + 1; break; } } Int32[] lookupRed = new Int32[MAXCOLOR]; Int32[] lookupGreen = new Int32[MAXCOLOR]; Int32[] lookupBlue = new Int32[MAXCOLOR]; tag = new byte[MAXVOLUME]; // precalculates lookup tables for (byte k = 0; k < allowedColorCount; ++k) { Mark(cubes[k], k, tag); long weight = Volume(cubes[k], weights); if (weight > 0) { lookupRed[k] = (int)(Volume(cubes[k], momentsRed) / weight); lookupGreen[k] = (int)(Volume(cubes[k], momentsGreen) / weight); lookupBlue[k] = (int)(Volume(cubes[k], momentsBlue) / weight); } else { lookupRed[k] = 0; lookupGreen[k] = 0; lookupBlue[k] = 0; } } reds = new Int32[allowedColorCount + 1]; greens = new Int32[allowedColorCount + 1]; blues = new Int32[allowedColorCount + 1]; sums = new Int32[allowedColorCount + 1]; LOG.Info("Starting bitmap reconstruction..."); using (BitmapBuffer bbbDest = new BitmapBuffer(resultBitmap, false)) { bbbDest.Lock(); using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { bbbSrc.Lock(); Dictionary<Color, byte> lookup = new Dictionary<Color, byte>(); byte bestMatch; for (int y = 0; y < bbbSrc.Height; y++) { for (int x = 0; x < bbbSrc.Width; x++) { Color color = bbbSrc.GetColorAtWithoutAlpha(x, y); // Check if we already matched the color if (!lookup.ContainsKey(color)) { // If not we need to find the best match // First get initial match bestMatch = bbbDest.GetColorIndexAt(x, y); bestMatch = tag[bestMatch]; Int32 bestDistance = 100000000; for (byte lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++) { Int32 foundRed = lookupRed[lookupIndex]; Int32 foundGreen = lookupGreen[lookupIndex]; Int32 foundBlue = lookupBlue[lookupIndex]; Int32 deltaRed = color.R - foundRed; Int32 deltaGreen = color.G - foundGreen; Int32 deltaBlue = color.B - foundBlue; Int32 distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue; if (distance < bestDistance) { bestDistance = distance; bestMatch = lookupIndex; } } lookup.Add(color, bestMatch); } else { // Already matched, so we just use the lookup bestMatch = lookup[color]; } reds[bestMatch] += color.R; greens[bestMatch] += color.G; blues[bestMatch] += color.B; sums[bestMatch]++; bbbDest.SetColorIndexAt(x, y, bestMatch); } } } } ColorPalette imagePalette = resultBitmap.Palette; // generates palette for (Int32 paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++) { if (sums[paletteIndex] > 0) { reds[paletteIndex] /= sums[paletteIndex]; greens[paletteIndex] /= sums[paletteIndex]; blues[paletteIndex] /= sums[paletteIndex]; } imagePalette.Entries[paletteIndex] = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]); } resultBitmap.Palette = imagePalette; return resultBitmap; }
/// <summary> /// Get the image /// </summary> public Bitmap GetQuantizedImage(int allowedColorCount) { // preprocess the colors CalculateMoments(); LOG.Info("Calculated the moments..."); Int32 next = 0; Single[] volumeVariance = new Single[MAXCOLOR]; // processes the cubes for (Int32 cubeIndex = 1; cubeIndex < allowedColorCount; ++cubeIndex) { // if cut is possible; make it if (Cut(cubes[next], cubes[cubeIndex])) { volumeVariance[next] = cubes[next].Volume > 1 ? CalculateVariance(cubes[next]) : 0.0f; volumeVariance[cubeIndex] = cubes[cubeIndex].Volume > 1 ? CalculateVariance(cubes[cubeIndex]) : 0.0f; } else { // the cut was not possible, revert the index volumeVariance[next] = 0.0f; cubeIndex--; } next = 0; Single temp = volumeVariance[0]; for (Int32 index = 1; index <= cubeIndex; ++index) { if (volumeVariance[index] > temp) { temp = volumeVariance[index]; next = index; } } if (temp <= 0.0) { allowedColorCount = cubeIndex + 1; break; } } Int32[] lookupRed = new Int32[MAXCOLOR]; Int32[] lookupGreen = new Int32[MAXCOLOR]; Int32[] lookupBlue = new Int32[MAXCOLOR]; tag = new byte[MAXVOLUME]; // precalculates lookup tables for (byte k = 0; k < allowedColorCount; ++k) { Mark(cubes[k], k, tag); long weight = Volume(cubes[k], weights); if (weight > 0) { lookupRed[k] = (int)(Volume(cubes[k], momentsRed) / weight); lookupGreen[k] = (int)(Volume(cubes[k], momentsGreen) / weight); lookupBlue[k] = (int)(Volume(cubes[k], momentsBlue) / weight); } else { lookupRed[k] = 0; lookupGreen[k] = 0; lookupBlue[k] = 0; } } reds = new Int32[allowedColorCount + 1]; greens = new Int32[allowedColorCount + 1]; blues = new Int32[allowedColorCount + 1]; sums = new Int32[allowedColorCount + 1]; LOG.Info("Starting bitmap reconstruction..."); using (BitmapBuffer bbbDest = new BitmapBuffer(resultBitmap, false)) { bbbDest.Lock(); using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { bbbSrc.Lock(); Dictionary <Color, byte> lookup = new Dictionary <Color, byte>(); byte bestMatch; for (int y = 0; y < bbbSrc.Height; y++) { for (int x = 0; x < bbbSrc.Width; x++) { Color color = bbbSrc.GetColorAtWithoutAlpha(x, y); // Check if we already matched the color if (!lookup.ContainsKey(color)) { // If not we need to find the best match // First get initial match bestMatch = bbbDest.GetColorIndexAt(x, y); bestMatch = tag[bestMatch]; Int32 bestDistance = 100000000; for (byte lookupIndex = 0; lookupIndex < allowedColorCount; lookupIndex++) { Int32 foundRed = lookupRed[lookupIndex]; Int32 foundGreen = lookupGreen[lookupIndex]; Int32 foundBlue = lookupBlue[lookupIndex]; Int32 deltaRed = color.R - foundRed; Int32 deltaGreen = color.G - foundGreen; Int32 deltaBlue = color.B - foundBlue; Int32 distance = deltaRed * deltaRed + deltaGreen * deltaGreen + deltaBlue * deltaBlue; if (distance < bestDistance) { bestDistance = distance; bestMatch = lookupIndex; } } lookup.Add(color, bestMatch); } else { // Already matched, so we just use the lookup bestMatch = lookup[color]; } reds[bestMatch] += color.R; greens[bestMatch] += color.G; blues[bestMatch] += color.B; sums[bestMatch]++; bbbDest.SetColorIndexAt(x, y, bestMatch); } } } } ColorPalette imagePalette = resultBitmap.Palette; // generates palette for (Int32 paletteIndex = 0; paletteIndex < allowedColorCount; paletteIndex++) { if (sums[paletteIndex] > 0) { reds[paletteIndex] /= sums[paletteIndex]; greens[paletteIndex] /= sums[paletteIndex]; blues[paletteIndex] /= sums[paletteIndex]; } imagePalette.Entries[paletteIndex] = Color.FromArgb(255, reds[paletteIndex], greens[paletteIndex], blues[paletteIndex]); } resultBitmap.Palette = imagePalette; return(resultBitmap); }
/// <summary> /// See <see cref="IColorQuantizer.Prepare"/> for more details. /// </summary> public WuQuantizer(Bitmap sourceBitmap) { this.sourceBitmap = sourceBitmap; // Make sure the color count variables are reset BitArray bitArray = new BitArray((int)Math.Pow(2, 24)); colorCount = 0; // 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]; Int32[] table = new Int32[256]; for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex) { table[tableIndex] = tableIndex * tableIndex; } // Use a bitmap to store the initial match, which is just as good as an array and saves us 2x the storage resultBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height, PixelFormat.Format8bppIndexed); using (BitmapBuffer bbbSrc = new BitmapBuffer(sourceBitmap, false)) { bbbSrc.Lock(); using (BitmapBuffer bbbDest = new BitmapBuffer(resultBitmap, false)) { bbbDest.Lock(); for (int y = 0; y < bbbSrc.Height; y++) { for (int x = 0; x < bbbSrc.Width; x++) { Color color = bbbSrc.GetColorAtWithoutAlpha(x, y); // To count the colors int index = color.ToArgb() & 0x00ffffff; // Check if we already have this color if (!bitArray.Get(index)) { // If not, add 1 to the single colors colorCount++; bitArray.Set(index, true); } Int32 indexRed = (color.R >> 3) + 1; Int32 indexGreen = (color.G >> 3) + 1; Int32 indexBlue = (color.B >> 3) + 1; weights[indexRed, indexGreen, indexBlue]++; momentsRed[indexRed, indexGreen, indexBlue] += color.R; momentsGreen[indexRed, indexGreen, indexBlue] += color.G; momentsBlue[indexRed, indexGreen, indexBlue] += color.B; moments[indexRed, indexGreen, indexBlue] += table[color.R] + table[color.G] + table[color.B]; // Store the initial "match" Int32 paletteIndex = (indexRed << 10) + (indexRed << 6) + indexRed + (indexGreen << 5) + indexGreen + indexBlue; bbbDest.SetColorIndexAt(x, y, (byte)(paletteIndex & 0xff)); } } } } }