private void SetSegmentProbas() { int[] p = new int[NumMbSegments]; int n; for (n = 0; n < this.Mbw * this.Mbh; ++n) { Vp8MacroBlockInfo mb = this.MbInfo[n]; ++p[mb.Segment]; } if (this.SegmentHeader.NumSegments > 1) { byte[] probas = this.Proba.Segments; probas[0] = (byte)GetProba(p[0] + p[1], p[2] + p[3]); probas[1] = (byte)GetProba(p[0], p[1]); probas[2] = (byte)GetProba(p[2], p[3]); this.SegmentHeader.UpdateMap = probas[0] != 255 || probas[1] != 255 || probas[2] != 255; if (!this.SegmentHeader.UpdateMap) { this.ResetSegments(); } this.SegmentHeader.Size = (p[0] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(0, probas[1]))) + (p[1] * (LossyUtils.Vp8BitCost(0, probas[0]) + LossyUtils.Vp8BitCost(1, probas[1]))) + (p[2] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(0, probas[2]))) + (p[3] * (LossyUtils.Vp8BitCost(1, probas[0]) + LossyUtils.Vp8BitCost(1, probas[2]))); } else { this.SegmentHeader.UpdateMap = false; this.SegmentHeader.Size = 0; } }
// Simplified k-Means, to assign Nb segments based on alpha-histogram. private void AssignSegments(int[] alphas) { int nb = this.SegmentHeader.NumSegments < NumMbSegments ? this.SegmentHeader.NumSegments : NumMbSegments; int[] centers = new int[NumMbSegments]; int weightedAverage = 0; int[] map = new int[WebpConstants.MaxAlpha + 1]; int n, k; int[] accum = new int[NumMbSegments]; int[] distAccum = new int[NumMbSegments]; // Bracket the input. for (n = 0; n <= WebpConstants.MaxAlpha && alphas[n] == 0; ++n) { } int minA = n; for (n = WebpConstants.MaxAlpha; n > minA && alphas[n] == 0; --n) { } int maxA = n; int rangeA = maxA - minA; // Spread initial centers evenly. for (k = 0, n = 1; k < nb; ++k, n += 2) { centers[k] = minA + (n * rangeA / (2 * nb)); } for (k = 0; k < MaxItersKMeans; ++k) { // Reset stats. for (n = 0; n < nb; ++n) { accum[n] = 0; distAccum[n] = 0; } // Assign nearest center for each 'a' n = 0; // track the nearest center for current 'a' int a; for (a = minA; a <= maxA; ++a) { if (alphas[a] != 0) { while (n + 1 < nb && Math.Abs(a - centers[n + 1]) < Math.Abs(a - centers[n])) { n++; } map[a] = n; // Accumulate contribution into best centroid. distAccum[n] += a * alphas[a]; accum[n] += alphas[a]; } } // All point are classified. Move the centroids to the center of their respective cloud. int displaced = 0; weightedAverage = 0; int totalWeight = 0; for (n = 0; n < nb; ++n) { if (accum[n] != 0) { int newCenter = (distAccum[n] + (accum[n] / 2)) / accum[n]; displaced += Math.Abs(centers[n] - newCenter); centers[n] = newCenter; weightedAverage += newCenter * accum[n]; totalWeight += accum[n]; } } weightedAverage = (weightedAverage + (totalWeight / 2)) / totalWeight; if (displaced < 5) { break; // no need to keep on looping... } } // Map each original value to the closest centroid for (n = 0; n < this.Mbw * this.Mbh; ++n) { Vp8MacroBlockInfo mb = this.MbInfo[n]; int alpha = mb.Alpha; mb.Segment = map[alpha]; mb.Alpha = centers[map[alpha]]; } // TODO: add possibility for SmoothSegmentMap this.SetSegmentAlphas(centers, weightedAverage); }