/// <summary> /// Computes the weighted variance of a box. /// </summary> /// <param name="c">The cube.</param> /// <returns>The result.</returns> private float Variance(ref Data.Box c) { float dr = WuQuant.Volume(ref c, this.vmr); float dg = WuQuant.Volume(ref c, this.vmg); float db = WuQuant.Volume(ref c, this.vmb); float da = WuQuant.Volume(ref c, this.vma); float xx = this.m2[WuQuant.GetIndex(c.R1, c.G1, c.B1, c.A1)] - this.m2[WuQuant.GetIndex(c.R1, c.G1, c.B1, c.A0)] - this.m2[WuQuant.GetIndex(c.R1, c.G1, c.B0, c.A1)] + this.m2[WuQuant.GetIndex(c.R1, c.G1, c.B0, c.A0)] - this.m2[WuQuant.GetIndex(c.R1, c.G0, c.B1, c.A1)] + this.m2[WuQuant.GetIndex(c.R1, c.G0, c.B1, c.A0)] + this.m2[WuQuant.GetIndex(c.R1, c.G0, c.B0, c.A1)] - this.m2[WuQuant.GetIndex(c.R1, c.G0, c.B0, c.A0)] - this.m2[WuQuant.GetIndex(c.R0, c.G1, c.B1, c.A1)] + this.m2[WuQuant.GetIndex(c.R0, c.G1, c.B1, c.A0)] + this.m2[WuQuant.GetIndex(c.R0, c.G1, c.B0, c.A1)] - this.m2[WuQuant.GetIndex(c.R0, c.G1, c.B0, c.A0)] + this.m2[WuQuant.GetIndex(c.R0, c.G0, c.B1, c.A1)] - this.m2[WuQuant.GetIndex(c.R0, c.G0, c.B1, c.A0)] - this.m2[WuQuant.GetIndex(c.R0, c.G0, c.B0, c.A1)] + this.m2[WuQuant.GetIndex(c.R0, c.G0, c.B0, c.A0)]; return(xx - (((dr * dr) + (dg * dg) + (db * db) + (da * da)) / WuQuant.Volume(ref c, this.vwt))); }
private static int Top(ref Data.Box cube, int direction, int position, int[] moment) { switch (direction) { // Red case 3: return(moment[WuQuant.GetIndex(position, cube.G1, cube.B1, cube.A1)] - moment[WuQuant.GetIndex(position, cube.G1, cube.B1, cube.A0)] - moment[WuQuant.GetIndex(position, cube.G1, cube.B0, cube.A1)] + moment[WuQuant.GetIndex(position, cube.G1, cube.B0, cube.A0)] - moment[WuQuant.GetIndex(position, cube.G0, cube.B1, cube.A1)] + moment[WuQuant.GetIndex(position, cube.G0, cube.B1, cube.A0)] + moment[WuQuant.GetIndex(position, cube.G0, cube.B0, cube.A1)] - moment[WuQuant.GetIndex(position, cube.G0, cube.B0, cube.A0)]); // Green case 2: return(moment[WuQuant.GetIndex(cube.R1, position, cube.B1, cube.A1)] - moment[WuQuant.GetIndex(cube.R1, position, cube.B1, cube.A0)] - moment[WuQuant.GetIndex(cube.R1, position, cube.B0, cube.A1)] + moment[WuQuant.GetIndex(cube.R1, position, cube.B0, cube.A0)] - moment[WuQuant.GetIndex(cube.R0, position, cube.B1, cube.A1)] + moment[WuQuant.GetIndex(cube.R0, position, cube.B1, cube.A0)] + moment[WuQuant.GetIndex(cube.R0, position, cube.B0, cube.A1)] - moment[WuQuant.GetIndex(cube.R0, position, cube.B0, cube.A0)]); // Blue case 1: return(moment[WuQuant.GetIndex(cube.R1, cube.G1, position, cube.A1)] - moment[WuQuant.GetIndex(cube.R1, cube.G1, position, cube.A0)] - moment[WuQuant.GetIndex(cube.R1, cube.G0, position, cube.A1)] + moment[WuQuant.GetIndex(cube.R1, cube.G0, position, cube.A0)] - moment[WuQuant.GetIndex(cube.R0, cube.G1, position, cube.A1)] + moment[WuQuant.GetIndex(cube.R0, cube.G1, position, cube.A0)] + moment[WuQuant.GetIndex(cube.R0, cube.G0, position, cube.A1)] - moment[WuQuant.GetIndex(cube.R0, cube.G0, position, cube.A0)]); // Alpha case 0: return(moment[WuQuant.GetIndex(cube.R1, cube.G1, cube.B1, position)] - moment[WuQuant.GetIndex(cube.R1, cube.G1, cube.B0, position)] - moment[WuQuant.GetIndex(cube.R1, cube.G0, cube.B1, position)] + moment[WuQuant.GetIndex(cube.R1, cube.G0, cube.B0, position)] - moment[WuQuant.GetIndex(cube.R0, cube.G1, cube.B1, position)] + moment[WuQuant.GetIndex(cube.R0, cube.G1, cube.B0, position)] + moment[WuQuant.GetIndex(cube.R0, cube.G0, cube.B1, position)] - moment[WuQuant.GetIndex(cube.R0, cube.G0, cube.B0, position)]); default: throw new ArgumentOutOfRangeException("direction"); } }
/// <summary> /// We want to minimize the sum of the variances of two sub-boxes. /// The sum(c^2) terms can be ignored since their sum over both sub-boxes /// is the same (the sum for the whole box) no matter where we split. /// The remaining terms have a minus sign in the variance formula, /// so we drop the minus sign and maximize the sum of the two terms. /// </summary> /// <param name="c">The cube.</param> /// <param name="direction">The direction.</param> /// <param name="first">The first position.</param> /// <param name="last">The last position.</param> /// <param name="cut">The cutting point.</param> /// <param name="wholeR">The whole red.</param> /// <param name="wholeG">The whole green.</param> /// <param name="wholeB">The whole blue.</param> /// <param name="wholeA">The whole alpha.</param> /// <param name="wholeW">The whole weight.</param> /// <returns>The result.</returns> private float Maximize(ref Data.Box c, int direction, int first, int last, out int cut, float wholeR, float wholeG, float wholeB, float wholeA, float wholeW) { int baseR = Bottom(ref c, direction, this.vmr); int baseG = WuQuant.Bottom(ref c, direction, this.vmg); int baseB = WuQuant.Bottom(ref c, direction, this.vmb); int baseA = WuQuant.Bottom(ref c, direction, this.vma); int baseW = WuQuant.Bottom(ref c, direction, this.vwt); float max = 0.0f; cut = -1; for (int i = first; i < last; i++) { float halfR = baseR + WuQuant.Top(ref c, direction, i, this.vmr); float halfG = baseG + WuQuant.Top(ref c, direction, i, this.vmg); float halfB = baseB + WuQuant.Top(ref c, direction, i, this.vmb); float halfA = baseA + WuQuant.Top(ref c, direction, i, this.vma); float halfW = baseW + WuQuant.Top(ref c, direction, i, this.vwt); if (halfW == 0) { continue; } float temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; halfR = wholeR - halfR; halfG = wholeG - halfG; halfB = wholeB - halfB; halfA = wholeA - halfA; halfW = wholeW - halfW; if (halfW == 0) { continue; } temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW; if (temp > max) { max = temp; cut = i; } } return(max); }
/// <summary> /// Marks a color space tag. /// </summary> /// <param name="c">The cube.</param> /// <param name="label">A label.</param> private void Mark(ref Data.Box c, byte label) { for (int r = c.R0 + 1; r <= c.R1; r++) { for (int g = c.G0 + 1; g <= c.G1; g++) { for (int b = c.B0 + 1; b <= c.B1; b++) { for (int a = c.A0 + 1; a <= c.A1; a++) { tag[GetIndex(r, g, b, a)] = label; } } } } }
private static float Volume(ref Data.Box cube, int[] moment) { return((float)(moment[GetIndex(cube.R1, cube.G1, cube.B1, cube.A1)] - moment[GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)] - moment[GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)] + moment[GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)] - moment[GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)] + moment[GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)] + moment[GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)] - moment[GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)] - moment[GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)] + moment[GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)] + moment[GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)] - moment[GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)] + moment[GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)] - moment[GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)] - moment[GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)] + moment[GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)])); }
/// <summary> /// Cuts a box. /// </summary> /// <param name="set1">The first set.</param> /// <param name="set2">The second set.</param> /// <returns>Returns a value indicating whether the box has been split.</returns> private bool Cut(ref Data.Box set1, ref Data.Box set2) { float wholeR = WuQuant.Volume(ref set1, this.vmr); float wholeG = WuQuant.Volume(ref set1, this.vmg); float wholeB = WuQuant.Volume(ref set1, this.vmb); float wholeA = WuQuant.Volume(ref set1, this.vma); float wholeW = WuQuant.Volume(ref set1, this.vwt); int cutr; int cutg; int cutb; int cuta; float maxr = this.Maximize(ref set1, 3, set1.R0 + 1, set1.R1, out cutr, wholeR, wholeG, wholeB, wholeA, wholeW); float maxg = this.Maximize(ref set1, 2, set1.G0 + 1, set1.G1, out cutg, wholeR, wholeG, wholeB, wholeA, wholeW); float maxb = this.Maximize(ref set1, 1, set1.B0 + 1, set1.B1, out cutb, wholeR, wholeG, wholeB, wholeA, wholeW); float maxa = this.Maximize(ref set1, 0, set1.A0 + 1, set1.A1, out cuta, wholeR, wholeG, wholeB, wholeA, wholeW); int dir; if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa)) { dir = 3; if (cutr < 0) { return(false); } } else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa)) { dir = 2; } else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa)) { dir = 1; } else { dir = 0; } set2.R1 = set1.R1; set2.G1 = set1.G1; set2.B1 = set1.B1; set2.A1 = set1.A1; switch (dir) { // Red case 3: set2.R0 = set1.R1 = cutr; set2.G0 = set1.G0; set2.B0 = set1.B0; set2.A0 = set1.A0; break; // Green case 2: set2.G0 = set1.G1 = cutg; set2.R0 = set1.R0; set2.B0 = set1.B0; set2.A0 = set1.A0; break; // Blue case 1: set2.B0 = set1.B1 = cutb; set2.R0 = set1.R0; set2.G0 = set1.G0; set2.A0 = set1.A0; break; // Alpha case 0: set2.A0 = set1.A1 = cuta; set2.R0 = set1.R0; set2.G0 = set1.G0; set2.B0 = set1.B0; break; } set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0); set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0); return(true); }