public ClusterFit(ColourSet colours, SquishFlags flags, float?metric) : base(colours, flags) { // set the iteration count _mIterationCount = (MFlags & SquishFlags.KColourIterativeClusterFit) != 0 ? 8 : 1; // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f ); } else { _mMetric = new Vector4(1.0f); } // initialise the best error _mBesterror = new Vector4(float.MaxValue); // cache some values var count = MColours.Count; var values = MColours.Points; // get the covariance matrix var covariance = Sym3X3.ComputeWeightedCovariance(count, values, MColours.Weights); // compute the principle component _mPrinciple = Sym3X3.ComputePrincipleComponent(covariance); }
public static Sym3X3 ComputeWeightedCovariance(int n, Vector3[] points, float[] weights) { // compute the centroid var total = 0.0f; var centroid = new Vector3(0.0f); for (var i = 0; i < n; ++i) { total += weights[i]; centroid += weights[i] * points[i]; } if (total > float.Epsilon) { centroid /= total; } // accumulate the covariance matrix var covariance = new Sym3X3(0.0f); for (var i = 0; i < n; ++i) { var a = points[i] - centroid; var b = weights[i] * a; covariance[0] += a.X * b.X; covariance[1] += a.X * b.Y; covariance[2] += a.X * b.Z; covariance[3] += a.Y * b.Y; covariance[4] += a.Y * b.Z; covariance[5] += a.Z * b.Z; } // return it return(covariance); }
public static Vector3 ComputePrincipleComponent(Sym3X3 matrix) { var row0 = new Vector4(matrix[0], matrix[1], matrix[2], 0.0f); var row1 = new Vector4(matrix[1], matrix[3], matrix[4], 0.0f); var row2 = new Vector4(matrix[2], matrix[4], matrix[5], 0.0f); var v = new Vector4(1.0f); for (var i = 0; i < 8; ++i) { // matrix multiply var w = row0 * v.SplatX(); w = Helpers.MultiplyAdd(row1, v.SplatY(), w); w = Helpers.MultiplyAdd(row2, v.SplatZ(), w); // get max component from xyz in all channels var a = Vector4.Max(w.SplatX(), Vector4.Max(w.SplatY(), w.SplatZ())); // divide through and advance v = w * Helpers.Reciprocal(a); } return(v.ToVector3()); }
public RangeFit(ColourSet colours, SquishFlags flags, float?metric) : base(colours, flags) { // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f) if (metric != null) { //m_metric = new Vector3( metric[0], metric[1], metric[2] ); } else { _mMetric = new Vector3(1.0f); } // initialise the best error _mBesterror = float.MaxValue; // cache some values var count = MColours.Count; var values = MColours.Points; var weights = MColours.Weights; // get the covariance matrix var covariance = Sym3X3.ComputeWeightedCovariance(count, values, weights); // compute the principle component var principle = Sym3X3.ComputePrincipleComponent(covariance); // get the min and max range as the codebook endpoints var start = new Vector3(0.0f); var end = new Vector3(0.0f); if (count > 0) { float max; // compute the range start = end = values[0]; var min = max = Vector3.Dot(values[0], principle); for (var i = 1; i < count; ++i) { var val = Vector3.Dot(values[i], principle); if (val < min) { start = values[i]; min = val; } else if (val > max) { end = values[i]; max = val; } } } // clamp the output to [0, 1] var one = new Vector3(1.0f); var zero = new Vector3(0.0f); start = Vector3.Min(one, Vector3.Max(zero, start)); end = Vector3.Min(one, Vector3.Max(zero, end)); // clamp to the grid and save var grid = new Vector3(31.0f, 63.0f, 31.0f); var gridrcp = new Vector3(1.0f / 31.0f, 1.0f / 63.0f, 1.0f / 31.0f); var half = new Vector3(0.5f); _mStart = Helpers.Truncate(grid * start + half) * gridrcp; _mEnd = Helpers.Truncate(grid * end + half) * gridrcp; }