@d5techs/3dgs-lib 1.4.21 → 1.4.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/3dgs-lib.js CHANGED
@@ -6195,83 +6195,109 @@ function inverseSHTransformYZSwap(sh, base, perChannel) {
6195
6195
  }
6196
6196
  }
6197
6197
  }
6198
- function kmeansppInit1D(values, k) {
6199
- const n = values.length;
6200
- const centers = new Float64Array(k);
6201
- centers[0] = values[Math.floor(Math.random() * n)];
6202
- const dist = new Float64Array(n);
6203
- dist.fill(Infinity);
6204
- for (let c = 1; c < k; c++) {
6205
- let totalDist = 0;
6206
- for (let i = 0; i < n; i++) {
6207
- const d = (values[i] - centers[c - 1]) ** 2;
6208
- if (d < dist[i]) dist[i] = d;
6209
- totalDist += dist[i];
6210
- }
6211
- let target = Math.random() * totalDist;
6212
- let chosen = 0;
6213
- for (let i = 0; i < n; i++) {
6214
- target -= dist[i];
6215
- if (target <= 0) {
6216
- chosen = i;
6217
- break;
6218
- }
6219
- }
6220
- centers[c] = values[chosen];
6221
- }
6222
- return centers;
6223
- }
6224
- function buildCodebook(values, size = 256, iterations = 10) {
6225
- if (values.length === 0) return new Array(size).fill(0);
6226
- if (values.length <= size) {
6227
- const cb = Array.from(new Set(Array.from(values))).sort((a, b) => a - b);
6228
- while (cb.length < size) cb.push(cb[cb.length - 1]);
6229
- return cb.slice(0, size);
6230
- }
6231
- const codebook = kmeansppInit1D(values, size);
6232
- const sums = new Float64Array(size);
6233
- const counts = new Uint32Array(size);
6234
- for (let iter = 0; iter < iterations; iter++) {
6235
- sums.fill(0);
6236
- counts.fill(0);
6237
- for (let i = 0; i < values.length; i++) {
6238
- let bestIdx = 0;
6239
- let bestDist = Math.abs(values[i] - codebook[0]);
6240
- for (let j = 1; j < size; j++) {
6241
- const d = Math.abs(values[i] - codebook[j]);
6242
- if (d < bestDist) {
6243
- bestDist = d;
6244
- bestIdx = j;
6245
- }
6246
- }
6247
- sums[bestIdx] += values[i];
6248
- counts[bestIdx]++;
6249
- }
6250
- let changed = false;
6251
- for (let j = 0; j < size; j++) {
6252
- if (counts[j] > 0) {
6253
- const v = sums[j] / counts[j];
6254
- if (v !== codebook[j]) {
6255
- codebook[j] = v;
6256
- changed = true;
6198
+ function buildCodebook(values, k = 256) {
6199
+ const N = values.length;
6200
+ if (N === 0) return new Array(k).fill(0);
6201
+ const sorted = new Float32Array(values);
6202
+ sorted.sort();
6203
+ const vMin = sorted[0];
6204
+ const vMax = sorted[N - 1];
6205
+ if (vMax - vMin < 1e-20) return new Array(k).fill(vMin);
6206
+ const H = Math.min(1024, N);
6207
+ const vRange = vMax - vMin;
6208
+ const iqr = sorted[Math.floor(N * 0.75)] - sorted[Math.floor(N * 0.25)];
6209
+ const beta = Math.max(0.5, Math.min(0.999, 1 - iqr / vRange));
6210
+ const counts = new Float64Array(H);
6211
+ const sums = new Float64Array(H);
6212
+ for (let i = 0; i < N; i++) {
6213
+ const uniformPos = (sorted[i] - vMin) / vRange;
6214
+ const quantilePos = i / N;
6215
+ const bin = Math.min(H - 1, Math.floor(H * (beta * quantilePos + (1 - beta) * uniformPos)));
6216
+ counts[bin]++;
6217
+ sums[bin] += sorted[i];
6218
+ }
6219
+ const centers = new Float64Array(H);
6220
+ for (let i = 0; i < H; i++) {
6221
+ centers[i] = counts[i] > 0 ? sums[i] / counts[i] : vMin + (i + 0.5) / H * vRange;
6222
+ }
6223
+ const alpha = 0.5;
6224
+ const weights = new Float64Array(H);
6225
+ for (let i = 0; i < H; i++) {
6226
+ weights[i] = counts[i] > 0 ? Math.pow(counts[i], alpha) : 0;
6227
+ }
6228
+ const prefW = new Float64Array(H + 1);
6229
+ const prefWX = new Float64Array(H + 1);
6230
+ const prefWXX = new Float64Array(H + 1);
6231
+ for (let i = 0; i < H; i++) {
6232
+ prefW[i + 1] = prefW[i] + weights[i];
6233
+ prefWX[i + 1] = prefWX[i] + weights[i] * centers[i];
6234
+ prefWXX[i + 1] = prefWXX[i] + weights[i] * centers[i] * centers[i];
6235
+ }
6236
+ const rangeCost = (a, b) => {
6237
+ const w = prefW[b + 1] - prefW[a];
6238
+ if (w <= 0) return 0;
6239
+ const wx = prefWX[b + 1] - prefWX[a];
6240
+ const wxx = prefWXX[b + 1] - prefWXX[a];
6241
+ return wxx - wx * wx / w;
6242
+ };
6243
+ const rangeMean = (a, b) => {
6244
+ const w = prefW[b + 1] - prefW[a];
6245
+ if (w <= 0) return (centers[a] + centers[b]) * 0.5;
6246
+ return (prefWX[b + 1] - prefWX[a]) / w;
6247
+ };
6248
+ let nonEmpty = 0;
6249
+ for (let i = 0; i < H; i++) if (counts[i] > 0) nonEmpty++;
6250
+ const effectiveK = Math.min(k, nonEmpty);
6251
+ const INF = 1e30;
6252
+ let dpPrev = new Float64Array(H).fill(INF);
6253
+ let dpCurr = new Float64Array(H).fill(INF);
6254
+ const splitTable = new Array(effectiveK + 1);
6255
+ const split1 = new Int32Array(H);
6256
+ for (let j = 0; j < H; j++) {
6257
+ dpPrev[j] = rangeCost(0, j);
6258
+ split1[j] = -1;
6259
+ }
6260
+ splitTable[1] = split1;
6261
+ for (let m = 2; m <= effectiveK; m++) {
6262
+ dpCurr.fill(INF);
6263
+ const splitM = new Int32Array(H);
6264
+ for (let j = m - 1; j < H; j++) {
6265
+ let bestCost = INF;
6266
+ let bestS = m - 2;
6267
+ for (let s = m - 2; s < j; s++) {
6268
+ const cost = dpPrev[s] + rangeCost(s + 1, j);
6269
+ if (cost < bestCost) {
6270
+ bestCost = cost;
6271
+ bestS = s;
6257
6272
  }
6258
6273
  }
6259
- }
6260
- if (!changed) break;
6261
- }
6262
- const result = Array.from(codebook);
6263
- result.sort((a, b) => a - b);
6274
+ dpCurr[j] = bestCost;
6275
+ splitM[j] = bestS;
6276
+ }
6277
+ splitTable[m] = splitM;
6278
+ const tmp = dpPrev;
6279
+ dpPrev = dpCurr;
6280
+ dpCurr = tmp;
6281
+ }
6282
+ const centroidValues = new Float32Array(effectiveK);
6283
+ let jj = H - 1;
6284
+ for (let m = effectiveK; m >= 1; m--) {
6285
+ const s = m > 1 ? splitTable[m][jj] : -1;
6286
+ centroidValues[m - 1] = rangeMean(s + 1, jj);
6287
+ jj = s;
6288
+ }
6289
+ centroidValues.sort();
6290
+ const result = new Array(k);
6291
+ for (let i = 0; i < effectiveK; i++) result[i] = centroidValues[i];
6292
+ for (let i = effectiveK; i < k; i++) result[i] = centroidValues[effectiveK - 1];
6264
6293
  return result;
6265
6294
  }
6266
6295
  function findNearest(sortedCB, value) {
6267
6296
  let lo = 0, hi = sortedCB.length - 1;
6268
6297
  while (lo < hi) {
6269
6298
  const mid = lo + hi >> 1;
6270
- if (sortedCB[mid] < value) lo = mid + 1;
6271
- else hi = mid;
6272
- }
6273
- if (lo > 0 && Math.abs(sortedCB[lo - 1] - value) <= Math.abs(sortedCB[lo] - value)) {
6274
- return lo - 1;
6299
+ if (value < (sortedCB[mid] + sortedCB[mid + 1]) * 0.5) hi = mid;
6300
+ else lo = mid + 1;
6275
6301
  }
6276
6302
  return lo;
6277
6303
  }
@@ -6528,7 +6554,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
6528
6554
  }
6529
6555
  const logScales = new Float32Array(count * 3);
6530
6556
  for (let i = 0; i < count * 3; i++) logScales[i] = Math.log(Math.max(1e-8, sSca[i]));
6531
- const scaleCodebook = buildCodebook(logScales, 256, iterations);
6557
+ const scaleCodebook = buildCodebook(logScales, 256);
6532
6558
  const scalesData = new Uint8ClampedArray(totalPixels * 4);
6533
6559
  scalesData.fill(255);
6534
6560
  for (let i = 0; i < count; i++) {
@@ -6596,7 +6622,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
6596
6622
  dcValues[i * 3 + 1] = (sCol[i * 3 + 1] - 0.5) / SH_C0;
6597
6623
  dcValues[i * 3 + 2] = (sCol[i * 3 + 2] - 0.5) / SH_C0;
6598
6624
  }
6599
- const dcCodebook = buildCodebook(dcValues, 256, iterations);
6625
+ const dcCodebook = buildCodebook(dcValues, 256);
6600
6626
  const sh0Data = new Uint8ClampedArray(totalPixels * 4);
6601
6627
  sh0Data.fill(255);
6602
6628
  for (let i = 0; i < count; i++) {
@@ -6631,7 +6657,7 @@ async function serializeSOG(data, coordinateSystem = "blender", options = {}) {
6631
6657
  for (let c = 0; c < centroids.length; c++) {
6632
6658
  for (let d = 0; d < shDim; d++) allCoeffValues[c * shDim + d] = centroids[c][d];
6633
6659
  }
6634
- const shCodebook = buildCodebook(allCoeffValues, 256, iterations);
6660
+ const shCodebook = buildCodebook(allCoeffValues, 256);
6635
6661
  const centW = 64 * numCoeffs;
6636
6662
  const centH = Math.ceil(effectivePaletteSize / 64);
6637
6663
  const centData = new Uint8ClampedArray(centW * centH * 4);