/// <summary> /// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction). /// </summary> /// <param name="cube">The cube.</param> /// <param name="direction">The direction.</param> /// <param name="position">The position.</param> /// <param name="moment">The moment.</param> /// <returns>The result.</returns> private static long Top(Box cube, int direction, int position, long[] moment) { switch (direction) { // Red case 2: return(moment[WuColorQuantizer.GetIndex(position, cube.G1, cube.B1)] - moment[WuColorQuantizer.GetIndex(position, cube.G1, cube.B0)] - moment[WuColorQuantizer.GetIndex(position, cube.G0, cube.B1)] + moment[WuColorQuantizer.GetIndex(position, cube.G0, cube.B0)]); // Green case 1: return(moment[WuColorQuantizer.GetIndex(cube.R1, position, cube.B1)] - moment[WuColorQuantizer.GetIndex(cube.R1, position, cube.B0)] - moment[WuColorQuantizer.GetIndex(cube.R0, position, cube.B1)] + moment[WuColorQuantizer.GetIndex(cube.R0, position, cube.B0)]); // Blue case 0: return(moment[WuColorQuantizer.GetIndex(cube.R1, cube.G1, position)] - moment[WuColorQuantizer.GetIndex(cube.R1, cube.G0, position)] - moment[WuColorQuantizer.GetIndex(cube.R0, cube.G1, position)] + moment[WuColorQuantizer.GetIndex(cube.R0, cube.G0, position)]); default: throw new ArgumentOutOfRangeException("direction"); } }
/// <summary> /// Computes sum over a box of any given statistic. /// </summary> /// <param name="cube">The cube.</param> /// <param name="moment">The moment.</param> /// <returns>The result.</returns> private static double Volume(Box cube, long[] moment) { return(moment[WuColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1)] - moment[WuColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0)] - moment[WuColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1)] + moment[WuColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0)] - moment[WuColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1)] + moment[WuColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0)] + moment[WuColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1)] - moment[WuColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0)]); }
/// <summary> /// Marks a color space tag. /// </summary> /// <param name="cube">The cube.</param> /// <param name="label">A label.</param> private void Mark(Box cube, byte label) { for (int r = cube.R0 + 1; r <= cube.R1; r++) { for (int g = cube.G0 + 1; g <= cube.G1; g++) { for (int b = cube.B0 + 1; b <= cube.B1; b++) { this.tag[WuColorQuantizer.GetIndex(r, g, b)] = label; } } } }
/// <summary> /// Converts the histogram into moments so that we can rapidly calculate /// the sums of the above quantities over any desired box. /// </summary> private void Get3DMoments() { long[] area = new long[WuColorQuantizer.IndexCount]; long[] areaR = new long[WuColorQuantizer.IndexCount]; long[] areaG = new long[WuColorQuantizer.IndexCount]; long[] areaB = new long[WuColorQuantizer.IndexCount]; double[] area2 = new double[WuColorQuantizer.IndexCount]; for (int r = 1; r < WuColorQuantizer.IndexCount; r++) { Array.Clear(area, 0, WuColorQuantizer.IndexCount); Array.Clear(areaR, 0, WuColorQuantizer.IndexCount); Array.Clear(areaG, 0, WuColorQuantizer.IndexCount); Array.Clear(areaB, 0, WuColorQuantizer.IndexCount); Array.Clear(area2, 0, WuColorQuantizer.IndexCount); for (int g = 1; g < WuColorQuantizer.IndexCount; g++) { long line = 0; long lineR = 0; long lineG = 0; long lineB = 0; double line2 = 0; for (int b = 1; b < WuColorQuantizer.IndexCount; b++) { int ind1 = WuColorQuantizer.GetIndex(r, g, b); line += this.vwt[ind1]; lineR += this.vmr[ind1]; lineG += this.vmg[ind1]; lineB += this.vmb[ind1]; line2 += this.m2[ind1]; area[b] += line; areaR[b] += lineR; areaG[b] += lineG; areaB[b] += lineB; area2[b] += line2; int ind2 = ind1 - WuColorQuantizer.GetIndex(1, 0, 0); this.vwt[ind1] = this.vwt[ind2] + area[b]; this.vmr[ind1] = this.vmr[ind2] + areaR[b]; this.vmg[ind1] = this.vmg[ind2] + areaG[b]; this.vmb[ind1] = this.vmb[ind2] + areaB[b]; this.m2[ind1] = this.m2[ind2] + area2[b]; } } } }
/// <summary> /// Computes the weighted variance of a box. /// </summary> /// <param name="cube">The cube.</param> /// <returns>The result.</returns> private double Variance(Box cube) { double dr = WuColorQuantizer.Volume(cube, this.vmr); double dg = WuColorQuantizer.Volume(cube, this.vmg); double db = WuColorQuantizer.Volume(cube, this.vmb); double xx = this.m2[WuColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1)] - this.m2[WuColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0)] - this.m2[WuColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1)] + this.m2[WuColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0)] - this.m2[WuColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1)] + this.m2[WuColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0)] + this.m2[WuColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1)] - this.m2[WuColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0)]; return(xx - (((dr * dr) + (dg * dg) + (db * db)) / WuColorQuantizer.Volume(cube, this.vwt))); }
/// <summary> /// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>. /// </summary> /// <param name="image">The image.</param> private void Build3DHistogram(byte[] image) { for (int i = 0; i < image.Length; i += 4) { int r = image[i + 2]; int g = image[i + 1]; int b = image[i]; int inr = r >> (8 - WuColorQuantizer.IndexBits); int ing = g >> (8 - WuColorQuantizer.IndexBits); int inb = b >> (8 - WuColorQuantizer.IndexBits); int ind = WuColorQuantizer.GetIndex(inr + 1, ing + 1, inb + 1); this.vwt[ind]++; this.vmr[ind] += r; this.vmg[ind] += g; this.vmb[ind] += b; this.m2[ind] += (r * r) + (g * g) + (b * b); } }
/// <summary> /// Generates the quantized result. /// </summary> /// <param name="image">The image.</param> /// <param name="colorCount">The color count.</param> /// <param name="cube">The cube.</param> /// <returns>The result.</returns> private ColorQuantizerResult GenerateResult(byte[] image, int colorCount, Box[] cube) { var quantizedImage = new ColorQuantizerResult(image.Length / 4, colorCount); for (int k = 0; k < colorCount; k++) { this.Mark(cube[k], (byte)k); double weight = WuColorQuantizer.Volume(cube[k], this.vwt); if (weight != 0) { quantizedImage.Palette[(k * 4) + 3] = 0xff; quantizedImage.Palette[(k * 4) + 2] = (byte)(WuColorQuantizer.Volume(cube[k], this.vmr) / weight); quantizedImage.Palette[(k * 4) + 1] = (byte)(WuColorQuantizer.Volume(cube[k], this.vmg) / weight); quantizedImage.Palette[k * 4] = (byte)(WuColorQuantizer.Volume(cube[k], this.vmb) / weight); } else { quantizedImage.Palette[(k * 4) + 3] = 0xff; quantizedImage.Palette[(k * 4) + 2] = 0; quantizedImage.Palette[(k * 4) + 1] = 0; quantizedImage.Palette[k * 4] = 0; } } for (int i = 0; i < image.Length / 4; i++) { int r = image[(i * 4) + 2] >> (8 - WuColorQuantizer.IndexBits); int g = image[(i * 4) + 1] >> (8 - WuColorQuantizer.IndexBits); int b = image[i * 4] >> (8 - WuColorQuantizer.IndexBits); int ind = WuColorQuantizer.GetIndex(r + 1, g + 1, b + 1); quantizedImage.Bytes[i] = this.tag[ind]; } return(quantizedImage); }