@d5techs/3dgs-lib 1.4.17 → 1.4.19
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.cjs +156 -31
- package/dist/3dgs-lib.cjs.map +1 -1
- package/dist/3dgs-lib.js +156 -31
- package/dist/3dgs-lib.js.map +1 -1
- package/dist/gs/SOGEncoder.d.ts +12 -6
- package/package.json +1 -1
package/dist/3dgs-lib.cjs
CHANGED
|
@@ -6150,14 +6150,43 @@ const SH_C0 = 0.28209479177387814;
|
|
|
6150
6150
|
function symLog(x) {
|
|
6151
6151
|
return Math.sign(x) * Math.log(1 + Math.abs(x));
|
|
6152
6152
|
}
|
|
6153
|
-
function
|
|
6153
|
+
function kmeansppInit(values, k) {
|
|
6154
|
+
const n = values.length;
|
|
6155
|
+
const centers = new Float64Array(k);
|
|
6156
|
+
centers[0] = values[Math.floor(Math.random() * n)];
|
|
6157
|
+
const dist = new Float64Array(n);
|
|
6158
|
+
dist.fill(Infinity);
|
|
6159
|
+
for (let c = 1; c < k; c++) {
|
|
6160
|
+
let totalDist = 0;
|
|
6161
|
+
for (let i = 0; i < n; i++) {
|
|
6162
|
+
const d = (values[i] - centers[c - 1]) ** 2;
|
|
6163
|
+
if (d < dist[i]) dist[i] = d;
|
|
6164
|
+
totalDist += dist[i];
|
|
6165
|
+
}
|
|
6166
|
+
let target = Math.random() * totalDist;
|
|
6167
|
+
let chosen = 0;
|
|
6168
|
+
for (let i = 0; i < n; i++) {
|
|
6169
|
+
target -= dist[i];
|
|
6170
|
+
if (target <= 0) {
|
|
6171
|
+
chosen = i;
|
|
6172
|
+
break;
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
centers[c] = values[chosen];
|
|
6176
|
+
}
|
|
6177
|
+
return centers;
|
|
6178
|
+
}
|
|
6179
|
+
function buildCodebook(values, size = 256, iterations = 20) {
|
|
6154
6180
|
if (values.length === 0) return new Array(size).fill(0);
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6181
|
+
if (values.length <= size) {
|
|
6182
|
+
const cb = Array.from(new Set(Array.from(values))).sort((a, b) => a - b);
|
|
6183
|
+
while (cb.length < size) cb.push(cb[cb.length - 1]);
|
|
6184
|
+
return cb.slice(0, size);
|
|
6159
6185
|
}
|
|
6186
|
+
const codebook = kmeansppInit(values, size);
|
|
6160
6187
|
const assignments = new Uint8Array(values.length);
|
|
6188
|
+
const sums = new Float64Array(size);
|
|
6189
|
+
const counts = new Uint32Array(size);
|
|
6161
6190
|
for (let iter = 0; iter < iterations; iter++) {
|
|
6162
6191
|
for (let i = 0; i < values.length; i++) {
|
|
6163
6192
|
let bestIdx = 0;
|
|
@@ -6171,29 +6200,74 @@ function buildCodebook(values, size = 256, iterations = 8) {
|
|
|
6171
6200
|
}
|
|
6172
6201
|
assignments[i] = bestIdx;
|
|
6173
6202
|
}
|
|
6174
|
-
|
|
6175
|
-
|
|
6203
|
+
sums.fill(0);
|
|
6204
|
+
counts.fill(0);
|
|
6176
6205
|
for (let i = 0; i < values.length; i++) {
|
|
6177
6206
|
sums[assignments[i]] += values[i];
|
|
6178
6207
|
counts[assignments[i]]++;
|
|
6179
6208
|
}
|
|
6209
|
+
let changed = false;
|
|
6180
6210
|
for (let j = 0; j < size; j++) {
|
|
6181
|
-
if (counts[j] > 0)
|
|
6211
|
+
if (counts[j] > 0) {
|
|
6212
|
+
const newVal = sums[j] / counts[j];
|
|
6213
|
+
if (newVal !== codebook[j]) {
|
|
6214
|
+
codebook[j] = newVal;
|
|
6215
|
+
changed = true;
|
|
6216
|
+
}
|
|
6217
|
+
}
|
|
6182
6218
|
}
|
|
6219
|
+
if (!changed) break;
|
|
6183
6220
|
}
|
|
6184
|
-
|
|
6221
|
+
const result = Array.from(codebook);
|
|
6222
|
+
result.sort((a, b) => a - b);
|
|
6223
|
+
return result;
|
|
6185
6224
|
}
|
|
6186
|
-
function findNearest(
|
|
6187
|
-
let
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
bestDist = d;
|
|
6193
|
-
bestIdx = j;
|
|
6194
|
-
}
|
|
6225
|
+
function findNearest(sortedCodebook, value) {
|
|
6226
|
+
let lo = 0, hi = sortedCodebook.length - 1;
|
|
6227
|
+
while (lo < hi) {
|
|
6228
|
+
const mid = lo + hi >> 1;
|
|
6229
|
+
if (sortedCodebook[mid] < value) lo = mid + 1;
|
|
6230
|
+
else hi = mid;
|
|
6195
6231
|
}
|
|
6196
|
-
|
|
6232
|
+
if (lo > 0 && Math.abs(sortedCodebook[lo - 1] - value) <= Math.abs(sortedCodebook[lo] - value)) {
|
|
6233
|
+
return lo - 1;
|
|
6234
|
+
}
|
|
6235
|
+
return lo;
|
|
6236
|
+
}
|
|
6237
|
+
function mortonSort(positions, count) {
|
|
6238
|
+
let minx = Infinity, miny = Infinity, minz = Infinity;
|
|
6239
|
+
let maxx = -Infinity, maxy = -Infinity, maxz = -Infinity;
|
|
6240
|
+
for (let i = 0; i < count; i++) {
|
|
6241
|
+
const x = positions[i * 3], y = positions[i * 3 + 1], z = positions[i * 3 + 2];
|
|
6242
|
+
if (x < minx) minx = x;
|
|
6243
|
+
if (x > maxx) maxx = x;
|
|
6244
|
+
if (y < miny) miny = y;
|
|
6245
|
+
if (y > maxy) maxy = y;
|
|
6246
|
+
if (z < minz) minz = z;
|
|
6247
|
+
if (z > maxz) maxz = z;
|
|
6248
|
+
}
|
|
6249
|
+
const xlen = maxx - minx || 1;
|
|
6250
|
+
const ylen = maxy - miny || 1;
|
|
6251
|
+
const zlen = maxz - minz || 1;
|
|
6252
|
+
const part1By2 = (x) => {
|
|
6253
|
+
x &= 1023;
|
|
6254
|
+
x = (x ^ x << 16) & 4278190335;
|
|
6255
|
+
x = (x ^ x << 8) & 50393103;
|
|
6256
|
+
x = (x ^ x << 4) & 51130563;
|
|
6257
|
+
x = (x ^ x << 2) & 153391689;
|
|
6258
|
+
return x;
|
|
6259
|
+
};
|
|
6260
|
+
const morton = new Uint32Array(count);
|
|
6261
|
+
const indices = new Uint32Array(count);
|
|
6262
|
+
for (let i = 0; i < count; i++) {
|
|
6263
|
+
const ix = Math.min(1023, Math.floor(1024 * (positions[i * 3] - minx) / xlen));
|
|
6264
|
+
const iy = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 1] - miny) / ylen));
|
|
6265
|
+
const iz = Math.min(1023, Math.floor(1024 * (positions[i * 3 + 2] - minz) / zlen));
|
|
6266
|
+
morton[i] = (part1By2(iz) << 2) + (part1By2(iy) << 1) + part1By2(ix);
|
|
6267
|
+
indices[i] = i;
|
|
6268
|
+
}
|
|
6269
|
+
indices.sort((a, b) => morton[a] - morton[b]);
|
|
6270
|
+
return indices;
|
|
6197
6271
|
}
|
|
6198
6272
|
function calcImageDimensions(count) {
|
|
6199
6273
|
const width = Math.ceil(Math.sqrt(count));
|
|
@@ -6229,13 +6303,56 @@ async function encodeWebP(width, height, pixelData) {
|
|
|
6229
6303
|
}
|
|
6230
6304
|
return new Uint8Array(await blob.arrayBuffer());
|
|
6231
6305
|
}
|
|
6232
|
-
async function serializeSOG(data) {
|
|
6233
|
-
const { count,
|
|
6306
|
+
async function serializeSOG(data, coordinateSystem = "blender") {
|
|
6307
|
+
const { count, colors, opacities } = data;
|
|
6308
|
+
let { positions, scales, rotations } = data;
|
|
6309
|
+
if (coordinateSystem === "blender") {
|
|
6310
|
+
positions = new Float32Array(positions);
|
|
6311
|
+
scales = new Float32Array(scales);
|
|
6312
|
+
rotations = new Float32Array(rotations);
|
|
6313
|
+
for (let i = 0; i < count; i++) {
|
|
6314
|
+
const i3 = i * 3, i4 = i * 4;
|
|
6315
|
+
const py = positions[i3 + 1], pz = positions[i3 + 2];
|
|
6316
|
+
positions[i3 + 1] = -pz;
|
|
6317
|
+
positions[i3 + 2] = py;
|
|
6318
|
+
const sy = scales[i3 + 1], sz = scales[i3 + 2];
|
|
6319
|
+
scales[i3 + 1] = sz;
|
|
6320
|
+
scales[i3 + 2] = sy;
|
|
6321
|
+
const ry = rotations[i4 + 2], rz = rotations[i4 + 3];
|
|
6322
|
+
rotations[i4 + 2] = -rz;
|
|
6323
|
+
rotations[i4 + 3] = ry;
|
|
6324
|
+
}
|
|
6325
|
+
}
|
|
6326
|
+
const sortOrder = mortonSort(positions, count);
|
|
6327
|
+
const sortedPositions = new Float32Array(count * 3);
|
|
6328
|
+
const sortedScales = new Float32Array(count * 3);
|
|
6329
|
+
const sortedRotations = new Float32Array(count * 4);
|
|
6330
|
+
const sortedColors = new Float32Array(count * 3);
|
|
6331
|
+
const sortedOpacities = new Float32Array(count);
|
|
6332
|
+
for (let dst = 0; dst < count; dst++) {
|
|
6333
|
+
const src = sortOrder[dst];
|
|
6334
|
+
const s3 = src * 3, d3 = dst * 3;
|
|
6335
|
+
sortedPositions[d3] = positions[s3];
|
|
6336
|
+
sortedPositions[d3 + 1] = positions[s3 + 1];
|
|
6337
|
+
sortedPositions[d3 + 2] = positions[s3 + 2];
|
|
6338
|
+
sortedScales[d3] = scales[s3];
|
|
6339
|
+
sortedScales[d3 + 1] = scales[s3 + 1];
|
|
6340
|
+
sortedScales[d3 + 2] = scales[s3 + 2];
|
|
6341
|
+
sortedColors[d3] = colors[s3];
|
|
6342
|
+
sortedColors[d3 + 1] = colors[s3 + 1];
|
|
6343
|
+
sortedColors[d3 + 2] = colors[s3 + 2];
|
|
6344
|
+
const s4 = src * 4, d4 = dst * 4;
|
|
6345
|
+
sortedRotations[d4] = rotations[s4];
|
|
6346
|
+
sortedRotations[d4 + 1] = rotations[s4 + 1];
|
|
6347
|
+
sortedRotations[d4 + 2] = rotations[s4 + 2];
|
|
6348
|
+
sortedRotations[d4 + 3] = rotations[s4 + 3];
|
|
6349
|
+
sortedOpacities[dst] = opacities[src];
|
|
6350
|
+
}
|
|
6234
6351
|
const { width, height } = calcImageDimensions(count);
|
|
6235
6352
|
const totalPixels = width * height;
|
|
6236
6353
|
const logPositions = new Float32Array(count * 3);
|
|
6237
6354
|
for (let i = 0; i < count * 3; i++) {
|
|
6238
|
-
logPositions[i] = symLog(
|
|
6355
|
+
logPositions[i] = symLog(sortedPositions[i]);
|
|
6239
6356
|
}
|
|
6240
6357
|
const mins = [Infinity, Infinity, Infinity];
|
|
6241
6358
|
const maxs = [-Infinity, -Infinity, -Infinity];
|
|
@@ -6264,7 +6381,7 @@ async function serializeSOG(data) {
|
|
|
6264
6381
|
}
|
|
6265
6382
|
const logScales = new Float32Array(count * 3);
|
|
6266
6383
|
for (let i = 0; i < count * 3; i++) {
|
|
6267
|
-
logScales[i] = Math.log(Math.max(1e-8,
|
|
6384
|
+
logScales[i] = Math.log(Math.max(1e-8, sortedScales[i]));
|
|
6268
6385
|
}
|
|
6269
6386
|
const scaleCodebook = buildCodebook(logScales);
|
|
6270
6387
|
const scalesData = new Uint8ClampedArray(totalPixels * 4);
|
|
@@ -6281,10 +6398,18 @@ async function serializeSOG(data) {
|
|
|
6281
6398
|
const SQRT2 = Math.SQRT2;
|
|
6282
6399
|
for (let i = 0; i < count; i++) {
|
|
6283
6400
|
const off = i * 4;
|
|
6284
|
-
let qw =
|
|
6285
|
-
let qx =
|
|
6286
|
-
let qy =
|
|
6287
|
-
let qz =
|
|
6401
|
+
let qw = sortedRotations[i * 4 + 0];
|
|
6402
|
+
let qx = sortedRotations[i * 4 + 1];
|
|
6403
|
+
let qy = sortedRotations[i * 4 + 2];
|
|
6404
|
+
let qz = sortedRotations[i * 4 + 3];
|
|
6405
|
+
const len = Math.sqrt(qw * qw + qx * qx + qy * qy + qz * qz);
|
|
6406
|
+
if (len > 0) {
|
|
6407
|
+
const inv = 1 / len;
|
|
6408
|
+
qw *= inv;
|
|
6409
|
+
qx *= inv;
|
|
6410
|
+
qy *= inv;
|
|
6411
|
+
qz *= inv;
|
|
6412
|
+
}
|
|
6288
6413
|
const comps = [qw, qx, qy, qz];
|
|
6289
6414
|
let largest = 0;
|
|
6290
6415
|
let largestAbs = Math.abs(comps[0]);
|
|
@@ -6326,9 +6451,9 @@ async function serializeSOG(data) {
|
|
|
6326
6451
|
}
|
|
6327
6452
|
const dcValues = new Float32Array(count * 3);
|
|
6328
6453
|
for (let i = 0; i < count; i++) {
|
|
6329
|
-
dcValues[i * 3 + 0] = (
|
|
6330
|
-
dcValues[i * 3 + 1] = (
|
|
6331
|
-
dcValues[i * 3 + 2] = (
|
|
6454
|
+
dcValues[i * 3 + 0] = (sortedColors[i * 3 + 0] - 0.5) / SH_C0;
|
|
6455
|
+
dcValues[i * 3 + 1] = (sortedColors[i * 3 + 1] - 0.5) / SH_C0;
|
|
6456
|
+
dcValues[i * 3 + 2] = (sortedColors[i * 3 + 2] - 0.5) / SH_C0;
|
|
6332
6457
|
}
|
|
6333
6458
|
const dcCodebook = buildCodebook(dcValues);
|
|
6334
6459
|
const sh0Data = new Uint8ClampedArray(totalPixels * 4);
|
|
@@ -6338,7 +6463,7 @@ async function serializeSOG(data) {
|
|
|
6338
6463
|
sh0Data[off + 0] = findNearest(dcCodebook, dcValues[i * 3 + 0]);
|
|
6339
6464
|
sh0Data[off + 1] = findNearest(dcCodebook, dcValues[i * 3 + 1]);
|
|
6340
6465
|
sh0Data[off + 2] = findNearest(dcCodebook, dcValues[i * 3 + 2]);
|
|
6341
|
-
sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255,
|
|
6466
|
+
sh0Data[off + 3] = Math.round(Math.max(0, Math.min(255, sortedOpacities[i] * 255)));
|
|
6342
6467
|
}
|
|
6343
6468
|
const [meansLWebP, meansUWebP, scalesWebP, quatsWebP, sh0WebP] = await Promise.all([
|
|
6344
6469
|
encodeWebP(width, height, meansLData),
|