我可以从深度和 RGB 图像创建点云吗?

Can I create point cloud from depth and rgb image?

我是 3js 的新手,我有 2 张图像,即 RGB 图像和深度图像。我可以使用 3js 创建结合这两者的点云吗? 如果是那么怎么办?

为了解决这个问题,我去了three.js examples and searched for "point". I checked each matching sample for one that had different colors for each particle. Then I clicked the "view source" button to checkout the code. I ended up starting with this example and looked at the source。它非常清楚如何制作一组不同颜色的点。

所以之后我只需要加载 2 个图像,RGB 和深度,制作一个点网格,为每个点将 Z 位置设置为深度,将颜色设置为图像的颜色。

我用 phone 使用 this app

拍摄了这些 RGB 和深度图像

为了获取数据,我将图像绘制到 canvas 中,然后调用 getImageData。这为我提供了每个通道的值从 0 到 255 的数据,红色、绿色、蓝色、alpha。

然后我编写了一个函数来获取单个像素和 return 0 到 1 范围内的颜色。为了安全起见,它会检查边界。

// return the pixel at UV coordinates (0 to 1) in 0 to 1 values
function getPixel(imageData, u, v) {
  const x = u * (imageData.width  - 1) | 0;
  const y = v * (imageData.height - 1) | 0;
  if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) {
    return [0, 0, 0, 0];
  } else {
    const offset = (y * imageData.width + x) * 4;
    return Array.from(imageData.data.slice(offset, offset + 4)).map(v => v / 255);
  }
}

结果

'use strict';

/* global THREE */

function loadImage(url) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.crossOrigin = "anonymous";
    img.onload = (e) => { resolve(img); };
    img.onerror = reject;
    img.src = url;
  });
}

function getImageData(img) {  
  const ctx = document.createElement("canvas").getContext("2d");
  ctx.canvas.width = img.width;
  ctx.canvas.height = img.height;
  ctx.drawImage(img, 0, 0);
  return ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
}

// return the pixel at UV coordinates (0 to 1) in 0 to 1 values
function getPixel(imageData, u, v) {
  const x = u * (imageData.width  - 1) | 0;
  const y = v * (imageData.height - 1) | 0;
  if (x < 0 || x >= imageData.width || y < 0 || y >= imageData.height) {
    return [0, 0, 0, 0];
  } else {
    const offset = (y * imageData.width + x) * 4;
    return Array.from(imageData.data.slice(offset, offset + 4)).map(v => v / 255);
  }
}

async function main() {
  const images = await Promise.all([
    loadImage("https://i.imgur.com/UKBsvV0.jpg"),  // RGB
    loadImage("https://i.imgur.com/arPMCZl.jpg"),  // Depth
  ]);
  const data = images.map(getImageData);
  
  const canvas = document.querySelector('canvas');
  const renderer = new THREE.WebGLRenderer({canvas: canvas});

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 1;
  const far = 4000;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 2000;

  const controls = new THREE.OrbitControls(camera, canvas);
  controls.target.set(0, 0, 0);
  controls.update();

  const scene = new THREE.Scene();

  const rgbData = data[0];
  const depthData = data[1];
  
  const skip = 20;
  const across = Math.ceil(rgbData.width / skip);
  const down = Math.ceil(rgbData.height / skip);
  
  const positions = [];
  const colors = [];
  const color = new THREE.Color();
  const spread = 1000;
  const depthSpread = 1000;
  const imageAspect = rgbData.width / rgbData.height;
  
  for (let y = 0; y < down; ++y) {
    const v = y / (down - 1);
    for (let x = 0; x < across; ++x) {
      const u = x / (across - 1);
      const rgb = getPixel(rgbData, u, v);
      const depth = 1 - getPixel(depthData, u, v)[0];
      
      positions.push( 
         (u *  2 - 1) * spread * imageAspect, 
         (v * -2 + 1) * spread, 
         depth * depthSpread,
      );
      colors.push( ...rgb.slice(0,3) );
    }
  }
  
  const geometry = new THREE.BufferGeometry();
  geometry.addAttribute( 'position', new THREE.Float32BufferAttribute( positions, 3 ) );
  geometry.addAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
  geometry.computeBoundingSphere();
  const material = new THREE.PointsMaterial( { size: 15, vertexColors: THREE.VertexColors } );
 const points = new THREE.Points( geometry, material );
  scene.add( points );

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }

  function render(time) {
    time *= 0.001;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
}

main();
body {
  margin: 0;
}
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}
<canvas></canvas>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/three.min.js"></script>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r94/js/controls/OrbitControls.js"></script>