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