rm-webgpu-compute-tasks 0.0.10 → 0.0.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rm-webgpu-compute-tasks",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "",
5
5
  "main": "./src/index.js",
6
6
  "types": "./src/index.d.ts",
package/src/index.d.ts CHANGED
@@ -419,7 +419,8 @@ declare function obstacleDetection_(opt: ICreateObstacleDetection): Promise<{
419
419
  declare interface Option_2 {
420
420
  base: string;
421
421
  urls: string[];
422
- dilateMaskSize?: number;
422
+ maskSize?: number;
423
+ contractionSize?: number;
423
424
  kernelSize?: number;
424
425
  groundZ?: number;
425
426
  }
package/src/index.js CHANGED
@@ -2216,6 +2216,22 @@ async function trajectoryHandle(baseUrl, trajectoryData) {
2216
2216
  pointCloudArray
2217
2217
  };
2218
2218
  }
2219
+ function getPointsCenter(arr) {
2220
+ let x = 0, y = 0, z = 0;
2221
+ for (let i = 0; i < arr.length; i += 3) {
2222
+ x += arr[i];
2223
+ y += arr[i + 1];
2224
+ z += arr[i + 2];
2225
+ }
2226
+ const count = arr.length / 3;
2227
+ return { x: x / count, y: y / count, z: z / count };
2228
+ }
2229
+ function getPointDistance(p1, p2) {
2230
+ const dx = p2.x - p1.x;
2231
+ const dy = p2.y - p1.y;
2232
+ const dz = p2.z - p1.z;
2233
+ return Math.sqrt(dx * dx + dy * dy + dz * dz);
2234
+ }
2219
2235
  class OcclusionMaterial extends ShaderMaterial {
2220
2236
  constructor(opt) {
2221
2237
  super({
@@ -2650,8 +2666,8 @@ class PolyAreaIndexMaterial extends ShaderMaterial {
2650
2666
  side: "none",
2651
2667
  uniforms: {
2652
2668
  imageSize: { value: new Vector2(width, height) }
2653
- },
2654
- topology: "point-list"
2669
+ }
2670
+ // topology: "point-list"
2655
2671
  });
2656
2672
  this.resultTexture = resultTexture;
2657
2673
  }
@@ -2851,15 +2867,15 @@ const mainCode$1 = (
2851
2867
  }
2852
2868
 
2853
2869
  // 获取多边形当前坐标有没有点云(获取深度值判断也是一样的, 深度值和点云索引图一一对应)
2854
- // let packedIndex = textureLoad(pcIndexMap, vec2<i32>(x, y), 0);
2855
- // let pointIndex = unPackUint(packedIndex);
2856
- // if(isEmpty(pointIndex)) {
2857
- // return;
2858
- // }
2870
+ let packedIndex = textureLoad(pcIndexMap, vec2<i32>(x, y), 0);
2871
+ let pointIndex = unPackUint(packedIndex);
2872
+ if(isEmpty(pointIndex)) {
2873
+ return;
2874
+ }
2859
2875
 
2860
2876
  // 获取当前点云深度值
2861
- // let depthU32 = pcDepth[idx];
2862
- // let depthF32 = orderedUintToFloat(depthU32);
2877
+ let depthU32 = pcDepth[idx];
2878
+ let depthF32 = orderedUintToFloat(depthU32);
2863
2879
 
2864
2880
  // 获取范围内最小深度值和对应点云索引
2865
2881
  let maxDepth = 10000.0;
@@ -2894,9 +2910,9 @@ const mainCode$1 = (
2894
2910
  }
2895
2911
 
2896
2912
  // 判断当前点深度和最小深度的差值是否小于阈值
2897
- // if(abs(depthF32 - minDepth) > depthTol) {
2898
- // return;
2899
- // }
2913
+ if(abs(depthF32 - minDepth) > depthTol) {
2914
+ return;
2915
+ }
2900
2916
 
2901
2917
  let finalPackedIndex = textureLoad(pcIndexMap, minPcUv, 0);
2902
2918
  let finalPointIndex = unPackUint(finalPackedIndex);
@@ -2933,6 +2949,50 @@ class SOPCComputeMaterial extends ComputeMaterial {
2933
2949
  this.uniforms.result.value = this.result;
2934
2950
  }
2935
2951
  }
2952
+ function euclideanClusterExtraction(points, distanceThreshold = 0.2, minClusterSize = 1, maxClusterSize = Infinity) {
2953
+ const pointCount = points.length / 3;
2954
+ const visited = new Uint8Array(pointCount);
2955
+ const clusters = [];
2956
+ const thresholdSq = distanceThreshold * distanceThreshold;
2957
+ function distanceSq(a, b) {
2958
+ const ai = a * 3;
2959
+ const bi = b * 3;
2960
+ const dx = points[ai] - points[bi];
2961
+ const dy = points[ai + 1] - points[bi + 1];
2962
+ const dz = points[ai + 2] - points[bi + 2];
2963
+ return dx * dx + dy * dy + dz * dz;
2964
+ }
2965
+ function regionQuery(index) {
2966
+ const neighbors = [];
2967
+ for (let i = 0; i < pointCount; i++) {
2968
+ if (distanceSq(index, i) <= thresholdSq) {
2969
+ neighbors.push(i);
2970
+ }
2971
+ }
2972
+ return neighbors;
2973
+ }
2974
+ for (let i = 0; i < pointCount; i++) {
2975
+ if (visited[i]) continue;
2976
+ visited[i] = 1;
2977
+ const cluster = [];
2978
+ const queue = [i];
2979
+ while (queue.length > 0) {
2980
+ const current = queue.pop();
2981
+ cluster.push(current);
2982
+ const neighbors = regionQuery(current);
2983
+ for (const neighbor of neighbors) {
2984
+ if (!visited[neighbor]) {
2985
+ visited[neighbor] = 1;
2986
+ queue.push(neighbor);
2987
+ }
2988
+ }
2989
+ }
2990
+ if (cluster.length >= minClusterSize && cluster.length <= maxClusterSize) {
2991
+ clusters.push(cluster);
2992
+ }
2993
+ }
2994
+ return clusters;
2995
+ }
2936
2996
  const beforeCcde = (
2937
2997
  /* wgsl*/
2938
2998
  `
@@ -2950,11 +3010,11 @@ const beforeCcde = (
2950
3010
  let g = u32(color.g * 255.0);
2951
3011
  let b = u32(color.b * 255.0);
2952
3012
  let a = u32(color.a * 255.0);
2953
- return (a << 24u) | (b << 16u) | (g << 8u) | r;
3013
+ return (a << 24u) | (b << 16u) | (g << 8u) | r;
2954
3014
  }
2955
3015
 
2956
3016
  fn isEmpty(value: u32) -> bool {
2957
- return value == 0xff000000 || value == 0x0;
3017
+ return value == 0xff000000 || value == 0x0 ;
2958
3018
  }
2959
3019
  `
2960
3020
  );
@@ -2973,15 +3033,16 @@ const mainCode = (
2973
3033
  // 当前像素
2974
3034
  let center = unPackUint(textureLoad(map, vec2<i32>(x, y), 0));
2975
3035
 
2976
- // 当前非空,保持原值
2977
- if (!isEmpty(center)) {
2978
- result[idx] = center;
3036
+ // 如果当前就是空,直接空
3037
+ if ( isEmpty(center) ) {
3038
+ result[idx] = 0xff000000;
2979
3039
  return;
2980
3040
  }
2981
3041
 
2982
- // 膨胀:空像素尝试被邻域填充
2983
- var dilatedValue: u32 = 0xff000000;
3042
+ // 是否被腐蚀
3043
+ var eroded = false;
2984
3044
 
3045
+ // 遍历邻域
2985
3046
  for (var ky = -half; ky <= half; ky = ky + 1) {
2986
3047
  let ny = y + ky;
2987
3048
 
@@ -2998,22 +3059,27 @@ const mainCode = (
2998
3059
 
2999
3060
  let neighbor = unPackUint(textureLoad(map, vec2<i32>(nx, ny), 0));
3000
3061
 
3001
- // 只要找到非空邻居,直接采用它的值
3002
- if (!isEmpty(neighbor)) {
3003
- dilatedValue = neighbor;
3062
+ // 只要有一个是空 → 腐蚀
3063
+ if (isEmpty(neighbor) || neighbor != center) {
3064
+ eroded = true;
3004
3065
  break;
3005
3066
  }
3006
3067
  }
3007
3068
 
3008
- if (!isEmpty(dilatedValue)) {
3069
+ if (eroded) {
3009
3070
  break;
3010
3071
  }
3011
3072
  }
3012
3073
 
3013
- result[idx] = dilatedValue;
3074
+ // 写结果
3075
+ if (eroded) {
3076
+ result[idx] = 0xff000000;
3077
+ } else {
3078
+ result[idx] = center;
3079
+ }
3014
3080
  `
3015
3081
  );
3016
- class DilateMaskComputeMaterial extends ComputeMaterial {
3082
+ class ErodeMaskComputeMaterial extends ComputeMaterial {
3017
3083
  constructor(width = 1920, height = 1200, texture, kernelSize = 3) {
3018
3084
  super({
3019
3085
  maxSize: [width, height],
@@ -3023,6 +3089,7 @@ class DilateMaskComputeMaterial extends ComputeMaterial {
3023
3089
  map: { value: texture },
3024
3090
  size: { value: new Vector2(width, height) },
3025
3091
  kernelSize: { value: kernelSize },
3092
+ // 建议从3开始
3026
3093
  result: {
3027
3094
  value: new Uint32Array(width * height),
3028
3095
  write: true
@@ -3031,49 +3098,96 @@ class DilateMaskComputeMaterial extends ComputeMaterial {
3031
3098
  });
3032
3099
  }
3033
3100
  }
3034
- function euclideanClusterExtraction(points, distanceThreshold = 0.2, minClusterSize = 1, maxClusterSize = Infinity) {
3035
- const pointCount = points.length / 3;
3036
- const visited = new Uint8Array(pointCount);
3037
- const clusters = [];
3038
- const thresholdSq = distanceThreshold * distanceThreshold;
3039
- function distanceSq(a, b) {
3040
- const ai = a * 3;
3041
- const bi = b * 3;
3042
- const dx = points[ai] - points[bi];
3043
- const dy = points[ai + 1] - points[bi + 1];
3044
- const dz = points[ai + 2] - points[bi + 2];
3045
- return dx * dx + dy * dy + dz * dz;
3046
- }
3047
- function regionQuery(index) {
3048
- const neighbors = [];
3049
- for (let i = 0; i < pointCount; i++) {
3050
- if (distanceSq(index, i) <= thresholdSq) {
3051
- neighbors.push(i);
3052
- }
3053
- }
3054
- return neighbors;
3101
+ function ringTriangleGenerator(path, inner = 3, outer = 0) {
3102
+ if (path.length < 3) {
3103
+ return {
3104
+ outerPath: [],
3105
+ innerPath: []
3106
+ };
3055
3107
  }
3056
- for (let i = 0; i < pointCount; i++) {
3057
- if (visited[i]) continue;
3058
- visited[i] = 1;
3059
- const cluster = [];
3060
- const queue = [i];
3061
- while (queue.length > 0) {
3062
- const current = queue.pop();
3063
- cluster.push(current);
3064
- const neighbors = regionQuery(current);
3065
- for (const neighbor of neighbors) {
3066
- if (!visited[neighbor]) {
3067
- visited[neighbor] = 1;
3068
- queue.push(neighbor);
3069
- }
3108
+ const len = path.length;
3109
+ let area = 0;
3110
+ for (let i = 0; i < len; i++) {
3111
+ const [x1, y1] = path[i];
3112
+ const [x2, y2] = path[(i + 1) % len];
3113
+ area += x1 * y2 - x2 * y1;
3114
+ }
3115
+ const isCCW = area > 0;
3116
+ const movePath = (distance) => {
3117
+ return path.map((curr, i) => {
3118
+ const prev = path[(i - 1 + len) % len];
3119
+ const next = path[(i + 1) % len];
3120
+ const dx1 = curr[0] - prev[0];
3121
+ const dy1 = curr[1] - prev[1];
3122
+ const l1 = Math.hypot(dx1, dy1) || 1;
3123
+ const dx2 = next[0] - curr[0];
3124
+ const dy2 = next[1] - curr[1];
3125
+ const l2 = Math.hypot(dx2, dy2) || 1;
3126
+ let nx1, ny1;
3127
+ let nx2, ny2;
3128
+ if (isCCW) {
3129
+ nx1 = dy1 / l1;
3130
+ ny1 = -dx1 / l1;
3131
+ nx2 = dy2 / l2;
3132
+ ny2 = -dx2 / l2;
3133
+ } else {
3134
+ nx1 = -dy1 / l1;
3135
+ ny1 = dx1 / l1;
3136
+ nx2 = -dy2 / l2;
3137
+ ny2 = dx2 / l2;
3070
3138
  }
3071
- }
3072
- if (cluster.length >= minClusterSize && cluster.length <= maxClusterSize) {
3073
- clusters.push(cluster);
3074
- }
3139
+ let nx = nx1 + nx2;
3140
+ let ny = ny1 + ny2;
3141
+ const nl = Math.hypot(nx, ny) || 1;
3142
+ nx /= nl;
3143
+ ny /= nl;
3144
+ return [
3145
+ curr[0] + nx * distance,
3146
+ curr[1] + ny * distance
3147
+ ];
3148
+ });
3149
+ };
3150
+ return {
3151
+ outerPath: movePath(outer),
3152
+ innerPath: movePath(-inner)
3153
+ };
3154
+ }
3155
+ function ringPathsToMesh(outerPath, innerPath, z = 0) {
3156
+ const len = Math.min(outerPath.length, innerPath.length);
3157
+ if (len < 2) {
3158
+ return [];
3159
+ }
3160
+ const vertices = [];
3161
+ for (let i = 0; i < len; i++) {
3162
+ const next = (i + 1) % len;
3163
+ const o0 = outerPath[i];
3164
+ const o1 = outerPath[next];
3165
+ const i0 = innerPath[i];
3166
+ const i1 = innerPath[next];
3167
+ vertices.push(
3168
+ o0[0],
3169
+ o0[1],
3170
+ z,
3171
+ o1[0],
3172
+ o1[1],
3173
+ z,
3174
+ i0[0],
3175
+ i0[1],
3176
+ z
3177
+ );
3178
+ vertices.push(
3179
+ o1[0],
3180
+ o1[1],
3181
+ z,
3182
+ i1[0],
3183
+ i1[1],
3184
+ z,
3185
+ i0[0],
3186
+ i0[1],
3187
+ z
3188
+ );
3075
3189
  }
3076
- return clusters;
3190
+ return vertices;
3077
3191
  }
3078
3192
  async function load(base, urls) {
3079
3193
  let points = await loadPCDCustom(base + "/data/rgb_global_map.pcd");
@@ -3082,7 +3196,7 @@ async function load(base, urls) {
3082
3196
  const height = itemsFiles[0].imageSize[1];
3083
3197
  return { points, itemsFiles, width, height };
3084
3198
  }
3085
- function getInfo(itemsFile, cindex) {
3199
+ function getInfo(itemsFile, cindex, contractionSize = 10) {
3086
3200
  const width = itemsFile.imageSize[0];
3087
3201
  const height = itemsFile.imageSize[1];
3088
3202
  const T_wi = new THREE.Matrix4();
@@ -3105,9 +3219,9 @@ function getInfo(itemsFile, cindex) {
3105
3219
  for (let i = 0; i < itemsFile.sam3Objects.length; i++) {
3106
3220
  const item = itemsFile.sam3Objects[i];
3107
3221
  const index = cindex * 200 + i + 1;
3108
- item.coordinates.map((p) => {
3109
- itemPosition.push(p[0], p[1], index);
3110
- });
3222
+ const { innerPath, outerPath } = ringTriangleGenerator(item.coordinates, contractionSize, 0);
3223
+ const vertices = ringPathsToMesh(outerPath, innerPath, index);
3224
+ itemPosition.push(...vertices);
3111
3225
  }
3112
3226
  return {
3113
3227
  cameraMat,
@@ -3121,7 +3235,8 @@ async function sopc_(opt) {
3121
3235
  const {
3122
3236
  base,
3123
3237
  urls,
3124
- dilateMaskSize = 2,
3238
+ maskSize = 3,
3239
+ contractionSize = 10,
3125
3240
  kernelSize = 15,
3126
3241
  groundZ = 0
3127
3242
  } = opt;
@@ -3133,14 +3248,14 @@ async function sopc_(opt) {
3133
3248
  canvasWidth = window.innerWidth ?? canvasWidth;
3134
3249
  canvasHeight = window.innerHeight ?? canvasHeight;
3135
3250
  }
3136
- const renderer = new Renderer(canvasWidth, canvasHeight, "rgba8unorm"), camera = new PerspectiveCamera(75, canvasWidth / canvasHeight, 1e-3, 100), { points: pointsArray, itemsFiles, width, height } = await load(base, urls), pointCount = pointsArray.length / 3, pcIndexMaterial = new PointCloudsIndexMaterial(width, height), polyAreaIndexMaterial = new PolyAreaIndexMaterial(width, height), dilateMaskComputeMaterial = new DilateMaskComputeMaterial(width, height, polyAreaIndexMaterial.resultTexture, dilateMaskSize), sopcComputeMaterial = new SOPCComputeMaterial(width, height, pointCount, pcIndexMaterial.resultTexture, kernelSize), points = new Object3D(
3251
+ const renderer = new Renderer(canvasWidth, canvasHeight, "rgba8unorm"), camera = new PerspectiveCamera(75, canvasWidth / canvasHeight, 1e-3, 100), { points: pointsArray, itemsFiles, width, height } = await load(base, urls), pointCount = pointsArray.length / 3, pcIndexMaterial = new PointCloudsIndexMaterial(width, height), polyAreaIndexMaterial = new PolyAreaIndexMaterial(width, height), erodeMaskComputeMaterial = new ErodeMaskComputeMaterial(width, height, polyAreaIndexMaterial.resultTexture, maskSize), sopcComputeMaterial = new SOPCComputeMaterial(width, height, pointCount, pcIndexMaterial.resultTexture, kernelSize), points = new Object3D(
3137
3252
  new BufferGeometry().setAttribute("position", new THREE.BufferAttribute(pointsArray, 3)),
3138
3253
  pcIndexMaterial
3139
3254
  ), mesh = new Object3D(
3140
3255
  new BufferGeometry().setAttribute("position", new THREE.BufferAttribute(new Float32Array(), 3)),
3141
3256
  polyAreaIndexMaterial
3142
3257
  );
3143
- camera.position.set(1, 1, 1);
3258
+ camera.position.set(1, 1, 10);
3144
3259
  pcIndexMaterial.groundZ = groundZ;
3145
3260
  console.log("文件加载耗时:", performance.now() - t);
3146
3261
  t = performance.now();
@@ -3150,7 +3265,7 @@ async function sopc_(opt) {
3150
3265
  console.log("第" + i + "个没有物品");
3151
3266
  continue;
3152
3267
  }
3153
- const { T_cw, cameraMat, itemPosition } = getInfo(itemsFile, i);
3268
+ const { T_cw, cameraMat, itemPosition } = getInfo(itemsFile, i, contractionSize);
3154
3269
  mesh.geometry.setAttribute("position", new THREE.BufferAttribute(itemPosition, 3));
3155
3270
  pcIndexMaterial.T_cw = T_cw;
3156
3271
  pcIndexMaterial.cameraMat = cameraMat;
@@ -3161,9 +3276,9 @@ async function sopc_(opt) {
3161
3276
  renderer.renderTarget = polyAreaIndexMaterial.resultTexture.view;
3162
3277
  renderer.depthTarget = polyAreaIndexMaterial.resultTexture.depthView;
3163
3278
  renderer.render([mesh], camera);
3164
- renderer.compute(dilateMaskComputeMaterial);
3279
+ renderer.compute(erodeMaskComputeMaterial);
3165
3280
  sopcComputeMaterial.setStorageBuffer("pcDepth", pcIndexMaterial.getStorageBuffer("depth"));
3166
- sopcComputeMaterial.setStorageBuffer("polyAreaIndex", dilateMaskComputeMaterial.getStorageBuffer("result"));
3281
+ sopcComputeMaterial.setStorageBuffer("polyAreaIndex", erodeMaskComputeMaterial.getStorageBuffer("result"));
3167
3282
  renderer.compute(sopcComputeMaterial);
3168
3283
  }
3169
3284
  const resultsBuffer = await readGPUBuffer(sopcComputeMaterial.getStorageBuffer("result"), pointCount * 4, Uint32Array);
@@ -3182,15 +3297,21 @@ async function sopc_(opt) {
3182
3297
  }
3183
3298
  arrayMap.append(itemIndex - 1, i);
3184
3299
  }
3185
- const results = new Array(itemsFiles.length).fill([]).map(() => []);
3300
+ const results = Array.from({ length: itemsFiles.length }, () => []);
3186
3301
  const sort = (pointarr) => {
3187
- let list = euclideanClusterExtraction(pointarr, 0.25).sort((a, b) => b.length - a.length);
3302
+ let list = euclideanClusterExtraction(pointarr, 0.2).sort((a, b) => b.length - a.length);
3188
3303
  if (list.length === 1) return pointarr;
3189
- return list[0].flatMap((i) => [
3304
+ const maxCount = list[0].length;
3305
+ list = list.filter((item) => item.length > 4 && item.length / maxCount > 0.3);
3306
+ list = list.map((item) => item.flatMap((i) => [
3190
3307
  pointarr[i * 3],
3191
3308
  pointarr[i * 3 + 1],
3192
3309
  pointarr[i * 3 + 2]
3193
- ]);
3310
+ ]));
3311
+ const centers = list.map((item) => getPointsCenter(item));
3312
+ const maxCenter = centers[0];
3313
+ const distList = centers.map((center) => getPointDistance(center, maxCenter));
3314
+ return list.filter((_, i) => distList[i] < 0.4).flat();
3194
3315
  };
3195
3316
  map.forEach((arrMap, fileIndex) => {
3196
3317
  arrMap.forEach((pointIndexs, itemIndex) => {
@@ -3203,7 +3324,10 @@ async function sopc_(opt) {
3203
3324
  );
3204
3325
  });
3205
3326
  pointarr = sort(pointarr);
3206
- if (!results[fileIndex]) results[fileIndex] = new Array(itemsFiles[fileIndex].sam3Objects.length).fill([]).map(() => []);
3327
+ if (results[fileIndex].length === 0) {
3328
+ const count = itemsFiles[fileIndex].sam3Objects.length;
3329
+ results[fileIndex] = Array.from({ length: count }, () => []);
3330
+ }
3207
3331
  results[fileIndex][itemIndex] = pointarr;
3208
3332
  });
3209
3333
  });