/// <summary> /// Marks a color space tag. /// </summary> /// <param name="cube">The cube.</param> /// <param name="label">A label.</param> private void Mark(ColorRange 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++) { tag[GetIndex(r, g, b)] = label; } } } }
/// <summary> /// Builds the cube. /// </summary> /// <param name="colorCount">The color count.</param> private ColorRange[] BuildCube(ref int colorCount) { var cube = new ColorRange[colorCount]; double[] vv = new double[colorCount]; for (int i = 0; i < colorCount; i++) { cube[i] = new ColorRange(); } cube[0].R0 = cube[0].G0 = cube[0].B0 = 0; cube[0].R1 = cube[0].G1 = cube[0].B1 = IndexCount - 1; int next = 0; for (int i = 1; i < colorCount; i++) { if (Cut(cube[next], cube[i])) { vv[next] = cube[next].Volume > 1 ? Variance(cube[next]) : 0.0; vv[i] = cube[i].Volume > 1 ? Variance(cube[i]) : 0.0; } else { vv[next] = 0.0; i--; } next = 0; double temp = vv[0]; for (int k = 1; k <= i; k++) { if (vv[k] > temp) { temp = vv[k]; next = k; } } if (temp <= 0.0) { colorCount = i + 1; break; } } return(cube); }
/// <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="cube">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="wholeW">The whole weight.</param> /// <returns>The result.</returns> private double Maximize(ColorRange cube, int direction, int first, int last, out int cut, double wholeR, double wholeG, double wholeB, double wholeW) { long baseR = Bottom(cube, direction, vmr); long baseG = Bottom(cube, direction, vmg); long baseB = Bottom(cube, direction, vmb); long baseW = Bottom(cube, direction, vwt); double max = 0.0; cut = -1; for (int i = first; i < last; i++) { double halfR = baseR + Top(cube, direction, i, vmr); double halfG = baseG + Top(cube, direction, i, vmg); double halfB = baseB + Top(cube, direction, i, vmb); double halfW = baseW + Top(cube, direction, i, vwt); if (halfW == 0) { continue; } double temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB)) / halfW; halfR = wholeR - halfR; halfG = wholeG - halfG; halfB = wholeB - halfB; halfW = wholeW - halfW; if (halfW == 0) { continue; } temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB)) / halfW; if (temp > max) { max = temp; cut = i; } } return(max); }
/// <summary> /// Computes the weighted variance of a box. /// </summary> /// <param name="cube">The cube.</param> /// <returns>The result.</returns> private double Variance(ColorRange cube) { double dr = Volume(cube, vmr); double dg = Volume(cube, vmg); double db = Volume(cube, vmb); double xx = m2[GetIndex(cube.R1, cube.G1, cube.B1)] - m2[GetIndex(cube.R1, cube.G1, cube.B0)] - m2[GetIndex(cube.R1, cube.G0, cube.B1)] + m2[GetIndex(cube.R1, cube.G0, cube.B0)] - m2[GetIndex(cube.R0, cube.G1, cube.B1)] + m2[GetIndex(cube.R0, cube.G1, cube.B0)] + m2[GetIndex(cube.R0, cube.G0, cube.B1)] - m2[GetIndex(cube.R0, cube.G0, cube.B0)]; return(xx - (((dr * dr) + (dg * dg) + (db * db)) / Volume(cube, vwt))); }
/// <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(ColorRange set1, ColorRange set2) { double wholeR = Volume(set1, vmr); double wholeG = Volume(set1, vmg); double wholeB = Volume(set1, vmb); double wholeW = Volume(set1, vwt); int cutr; int cutg; int cutb; double maxr = Maximize(set1, 2, set1.R0 + 1, set1.R1, out cutr, wholeR, wholeG, wholeB, wholeW); double maxg = Maximize(set1, 1, set1.G0 + 1, set1.G1, out cutg, wholeR, wholeG, wholeB, wholeW); double maxb = Maximize(set1, 0, set1.B0 + 1, set1.B1, out cutb, wholeR, wholeG, wholeB, wholeW); int dir; if ((maxr >= maxg) && (maxr >= maxb)) { dir = 2; if (cutr < 0) { return(false); } } else if ((maxg >= maxr) && (maxg >= maxb)) { dir = 1; } else { dir = 0; } set2.R1 = set1.R1; set2.G1 = set1.G1; set2.B1 = set1.B1; switch (dir) { // Red case 2: set2.R0 = set1.R1 = cutr; set2.G0 = set1.G0; set2.B0 = set1.B0; break; // Green case 1: set2.G0 = set1.G1 = cutg; set2.R0 = set1.R0; set2.B0 = set1.B0; break; // Blue case 0: set2.B0 = set1.B1 = cutb; set2.R0 = set1.R0; set2.G0 = set1.G0; break; } set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0); set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0); return(true); }